Skip to content

Commit

Permalink
feat: add some tests around the conusmer DSL matchers
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Holshausen committed Jun 26, 2020
1 parent aecb37b commit e6a153f
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 0 deletions.
26 changes: 26 additions & 0 deletions native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ use pact_matching::models::generators::{Generators, GeneratorCategory, Generator
use pact_matching::time_utils::generate_string;
use pact_mock_server::server_manager::ServerManager;
use pact_mock_server_ffi::bodies::{process_json, request_multipart, response_multipart, file_as_multipart_body};
use pact_mock_server_ffi::{generate_regex_value, StringResult};
use env_logger::{Builder, Target};
use uuid::Uuid;
use std::sync::Mutex;
use serde_json::Value;
use log::*;
use std::collections::HashMap;
use std::fs;
use std::ffi::CStr;
use std::ffi::CString;

mod verify;
mod xml;
Expand Down Expand Up @@ -110,6 +113,28 @@ fn generate_datetime_string(mut cx: FunctionContext) -> JsResult<JsString> {
}
}

fn generate_regex_string(mut cx: FunctionContext) -> JsResult<JsString> {
let pattern = cx.argument::<JsString>(0)?;
let c_str = CString::new(pattern.value()).unwrap();
let result = unsafe { generate_regex_value(c_str.into_raw()) };
match result {
StringResult::Ok(value) => {
let c_str = unsafe { CStr::from_ptr(value) };
match c_str.to_str() {
Ok(value) => Ok(cx.string(value)),
Err(err) => cx.throw_error(err.to_string())
}
},
StringResult::Failed(err) => {
let c_str = unsafe { CStr::from_ptr(err) };
match c_str.to_str() {
Ok(value) => cx.throw_error(value),
Err(err) => cx.throw_error(err.to_string())
}
}
}
}

fn get_request_path(cx: &mut CallContext<JsPact>, request: Handle<JsObject>) -> Option<(String, Option<MatchingRule>, Option<Generator>)> {
let js_path = request.get(cx, "path");
match js_path {
Expand Down Expand Up @@ -739,5 +764,6 @@ register_module!(mut m, {
m.export_class::<JsPact>("Pact")?;
m.export_function("verify_provider", verify::verify_provider)?;
m.export_function("generate_datetime_string", generate_datetime_string)?;
m.export_function("generate_regex_string", generate_regex_string)?;
Ok(())
});
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"@types/lodash": "^4.14.73",
"@types/lodash.isnil": "^4.0.3",
"@types/mocha": "^2.2.41",
"@types/mockery": "^1.4.29",
"@types/nock": "^9.1.2",
"@types/node": "^8.0.24",
"@types/q": "^1.5.2",
Expand Down Expand Up @@ -149,6 +150,7 @@
"lodash.clone": "^4.5.0",
"mocha": "^5.1.1",
"mocha-lcov-reporter": "^1.3.0",
"mockery": "^2.1.0",
"neon-cli": "^0.3.3",
"nock": "^9.1.6",
"nyc": "^15.0.0-beta.0",
Expand Down
245 changes: 245 additions & 0 deletions src/v3/matchers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import * as mockery from "mockery"
const MockNative = {

}
mockery.registerMock("../native", MockNative)

import * as chai from "chai"
import * as matchers from "./matchers"

const expect = chai.expect

describe("V3 Matchers", () => {
describe("#like", () => {
it("returns a JSON representation of a like matcher", () => {
let result = matchers.like({
a: 'b'
})
expect(result).to.deep.equal({
"pact:matcher:type": "type",
value: {
a: "b"
}
})
})
})

describe("#eachLike", () => {
it("returns a JSON representation of an eachLike matcher", () => {
let result = matchers.eachLike({
a: 'b'
})
expect(result).to.deep.equal({
"pact:matcher:type": "type",
value: [{
a: "b"
}]
})
})
})

describe("#atLeastOneLike", () => {
describe("with no examples", () => {
it("returns a JSON representation of an atLeastOneLike matcher", () => {
let result = matchers.atLeastOneLike({
a: 'b'
})
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 1,
value: [{
a: "b"
}]
})
})
})

describe("when provided examples", () => {
it("returns a JSON representation of an atLeastOneLike matcher with the correct number of examples", () => {
let result = matchers.atLeastOneLike({
a: 'b'
}, 4)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 1,
"value": [{"a": "b"}, {"a": "b"}, {"a": "b"}, {"a": "b"}]
})
})
})
})

describe("#atLeastLike", () => {
describe("with no examples", () => {
it("returns a JSON representation of an atLeastLike matcher", () => {
let result = matchers.atLeastLike({
a: 'b'
}, 2)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 2,
value: [{a: "b"}, {a: "b"}]
})
})
})

describe("when provided examples", () => {
it("returns a JSON representation of an atLeastLike matcher with the correct number of examples", () => {
let result = matchers.atLeastLike({
a: 'b'
}, 2, 4)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 2,
"value": [{"a": "b"}, {"a": "b"}, {"a": "b"}, {"a": "b"}]
})
})
})

it("throws an error if the number of examples is less than the minimum", () => {
expect(() => matchers.atLeastLike({a: 'b'}, 4, 2)).to.throw("atLeastLike has a minimum of 4 but 2 elements where requested. Make sure the count is greater than or equal to the min.")
})
})

describe("#atMostLike", () => {
describe("with no examples", () => {
it("returns a JSON representation of an atMostLike matcher", () => {
let result = matchers.atMostLike({
a: 'b'
}, 2)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
max: 2,
value: [{a: "b"}]
})
})
})

describe("when provided examples", () => {
it("returns a JSON representation of an atMostLike matcher with the correct number of examples", () => {
let result = matchers.atMostLike({
a: 'b'
}, 4, 4)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
max: 4,
"value": [{"a": "b"}, {"a": "b"}, {"a": "b"}, {"a": "b"}]
})
})
})

it("throws an error if the number of examples is more than the maximum", () => {
expect(() => matchers.atMostLike({a: 'b'}, 2, 4)).to.throw("atMostLike has a maximum of 2 but 4 elements where requested. Make sure the count is less than or equal to the max.")
})
})

describe("#constrainedArrayLike", () => {
describe("with no examples", () => {
it("returns a JSON representation of an constrainedArrayLike matcher", () => {
let result = matchers.constrainedArrayLike({
a: 'b'
}, 2, 4)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 2,
max: 4,
value: [{a: "b"}, {a: "b"}]
})
})
})

describe("when provided examples", () => {
it("returns a JSON representation of an constrainedArrayLike matcher with the correct number of examples", () => {
let result = matchers.constrainedArrayLike({
a: 'b'
}, 2, 4, 3)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
min: 2,
max: 4,
"value": [{"a": "b"}, {"a": "b"}, {"a": "b"}]
})
})
})

it("throws an error if the number of examples is less than the minimum", () => {
expect(() => matchers.constrainedArrayLike({a: 'b'}, 4, 6, 2)).to.throw("constrainedArrayLike has a minimum of 4 but 2 elements where requested. Make sure the count is greater than or equal to the min.")
})

it("throws an error if the number of examples is more than the maximum", () => {
expect(() => matchers.constrainedArrayLike({a: 'b'}, 4, 6, 8)).to.throw("constrainedArrayLike has a maximum of 6 but 8 elements where requested. Make sure the count is less than or equal to the max.")
})
})

describe("#integer", () => {
it("returns a JSON representation of an integer matcher", () => {
let result = matchers.integer(100)
expect(result).to.deep.equal({
"pact:matcher:type": "integer",
value: 100
})
})

describe("when no example is given", () => {
it("also includes a random integer generator", () => {
let result = matchers.integer()
expect(result).to.deep.equal({
"pact:matcher:type": "integer",
"pact:generator:type": "RandomInt",
value: 101
})
})
})
})

describe("#decimal", () => {
it("returns a JSON representation of an decimal matcher", () => {
let result = matchers.decimal(100.3)
expect(result).to.deep.equal({
"pact:matcher:type": "decimal",
value: 100.3
})
})

describe("when no example is given", () => {
it("also includes a random decimal generator", () => {
let result = matchers.decimal()
expect(result).to.deep.equal({
"pact:matcher:type": "decimal",
"pact:generator:type": "RandomDecimal",
value: 12.34
})
})
})
})

describe("#number", () => {
it("returns a JSON representation of an number matcher", () => {
let result = matchers.number(100.3)
expect(result).to.deep.equal({
"pact:matcher:type": "number",
value: 100.3
})
})

describe("when no example is given", () => {
it("also includes a random integer generator", () => {
let result = matchers.number()
expect(result).to.deep.equal({
"pact:matcher:type": "number",
"pact:generator:type": "RandomInt",
value: 1234
})
})
})
})

describe("#boolean", () => {
it("returns a JSON representation of a like matcher", () => {
let result = matchers.boolean(true)
expect(result).to.deep.equal({
"pact:matcher:type": "type",
value: true
})
})
})
})
29 changes: 29 additions & 0 deletions src/v3/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,32 @@ export function nullValue() {
"pact:matcher:type": "null",
}
}

/**
* Matches a URL composed of a base path and a list of path fragments
* @param basePath Base path of the URL
* @param pathFragments list of path fragments, can be regular expressions
*/
export function url(basePath: string, pathFragments: Array<any>) {
let regex = ""
let example = ""
for (let p of pathFragments) {
if (p instanceof Object && p["pact:matcher:type"] == "regex") {
regex += "\\/" + p["regex"]
example += "/" + p["value"]
} else if (p instanceof RegExp) {
regex += "\\/" + p.source
example += "/" + PactNative.generate_regex_string(p.source)
} else if (p instanceof string) {
regex += "\\/" + p
example += "/" + p
} else {
throw new Error("Only regex matchers, regular expressions or strings can be use a path fragments")
}
}
return {
"pact:matcher:type": "regex",
regex,
value: example
}
}
4 changes: 4 additions & 0 deletions test/helper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"use strict"

import pact from "@pact-foundation/pact-node"
import * as mockery from "mockery"

// used to kill any left over mock server instances
process.on("SIGINT", () => {
pact.removeAllServers()
})

mockery.enable()
mockery.warnOnUnregistered(false)

0 comments on commit e6a153f

Please sign in to comment.