diff --git a/mockserver.js b/mockserver.js index 2b780b0..853b81e 100644 --- a/mockserver.js +++ b/mockserver.js @@ -25,7 +25,7 @@ function parseStatus(header) { * Parses an HTTP header, splitting * by colon. */ -const parseHeader = function (header, context, request) { +const parseHeader = function(header, context, request) { header = header.split(': '); return { key: normalizeHeader(header[0]), value: parseValue(header[1], context, request) }; @@ -283,27 +283,87 @@ function getBody(req, callback) { } function getMockedContent(path, prefix, body, query) { - const mockName = prefix + (getBodyOrQueryString(body, query) || '') + '.mock'; - const mockFile = join(mockserver.directory, path, mockName); - let content; + // Check for an exact match + const exactName = prefix + (getBodyOrQueryString(body, query) || '') + '.mock'; + let content = handleMatch(path, exactName, fs.existsSync); - try { - content = fs.readFileSync(mockFile, { encoding: 'utf8' }); - if (mockserver.verbose) { - console.log( - 'Reading from ' + mockFile.yellow + ' file: ' + 'Matched'.green - ); + // Compare params without regard to order + if (!content && query && !body) { + content = testForQuery(path, prefix, query, false); + + // Compare params without regard to order and allow wildcards + if (!content) { + content = testForQuery(path, prefix, query, true); } - } catch (err) { - if (mockserver.verbose) { - console.log( - 'Reading from ' + mockFile.yellow + ' file: ' + 'Not matched'.red + } + + // fallback option (e.g. GET.mock). ignores body and query + if (!content) { + const fallbackName = prefix + '.mock'; + content = handleMatch(path, fallbackName, fs.existsSync); + } + + return content; +} + +function testForQuery(path, prefix, query, allowWildcards) { + // Find all files in the directory + return fs + .readdirSync(join(mockserver.directory, path)) + .filter(possibleFile => possibleFile.startsWith(prefix) && possibleFile.endsWith('.mock')) + .filter(possibleFile => possibleFile.match(/--[\s\S]*__/)) + .reduce((prev, possibleFile) => { + if (prev) { + return prev; + } + + let isMatch = true; + //get params from file + const paramMap = queryStringToMap(query); + const possibleFileParamMap = queryStringToMap( + possibleFile.replace('.mock', '').split('--')[1] ); + + for (const key in paramMap) { + if (!isMatch) { + continue; + } + isMatch = + possibleFileParamMap[key] === paramMap[key] || + (allowWildcards && possibleFileParamMap[key] === '__'); + } + + return handleMatch(path, possibleFile, isMatch); + }, undefined); +} + +function queryStringToMap(query) { + const result = {}; + query.split('&').forEach(param => { + const [key, val] = param.split('='); + result[key] = val; + }); + return result; +} + +function handleMatch(path, fileName, isMatchOrTest) { + const mockFile = join(mockserver.directory, path, fileName); + + let isMatch = isMatchOrTest; + if (typeof isMatchOrTest === 'function') { + isMatch = isMatchOrTest(mockFile); + } + + if (isMatch) { + if (mockserver.verbose) { + console.log('Reading from ' + mockFile.yellow + ' file: ' + 'Matched'.green); } - content = (body || query) && getMockedContent(path, prefix); + return fs.readFileSync(mockFile, { encoding: 'utf8' }); } - return content; + if (mockserver.verbose) { + console.log('Reading from ' + mockFile.yellow + ' file: ' + 'Not matched'.red); + } } function getContentFromPermutations(path, method, body, query, permutations) { diff --git a/test/mocks/wildcard-params/GET--foo=bar&buz=__.mock b/test/mocks/wildcard-params/GET--foo=bar&buz=__.mock new file mode 100644 index 0000000..de9a291 --- /dev/null +++ b/test/mocks/wildcard-params/GET--foo=bar&buz=__.mock @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK + +wildcard-params diff --git a/test/mockserver.js b/test/mockserver.js index 4ff6870..a812367 100644 --- a/test/mockserver.js +++ b/test/mockserver.js @@ -108,7 +108,7 @@ describe('mockserver', function() { it('should combine the identical headers names', function() { processRequest('/multiple-headers-same-name/', 'GET'); - + assert.equal(res.headers['Set-Cookie'].length, 3); }) @@ -431,6 +431,15 @@ describe('mockserver', function() { assert.equal(res.status, 404); }); }); + + describe("wildcard params", function() { + it("matches a file with wildcards as query params", function() { + processRequest("/wildcard-params?foo=bar&buz=baz", "GET"); + + assert.equal(res.status, 200); + }); + }); + describe('.getResponseDelay', function() { it('should return a value greater than zero when valid', function() { const ownValueHeaders = [