Skip to content
This repository has been archived by the owner on Oct 30, 2022. It is now read-only.

Commit

Permalink
Add pathname and query to request. (#196)
Browse files Browse the repository at this point in the history
* Add pathname and query to request.

* Fix lint.

* Use full path in serialize, does not work with query params.

* Fix type errors.

* Add a test that fails with query params.

* Fix usage of path.

* Remove unnecessary lowercasing.
  • Loading branch information
Kimmo Sääskilahti authored Aug 29, 2019
1 parent 83ff85f commit 4d3083a
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 12 deletions.
3 changes: 3 additions & 0 deletions packages/unmock-core/src/__tests__/backend/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ describe("Node.js interceptor", () => {
describe("Unmock node package", () => {
const nodeInterceptor = new NodeBackend({ servicesDirectory });
const unmock = new UnmockPackage(nodeInterceptor);
afterAll(() => {
unmock.off();
});
describe("service spy", () => {
let petstore: Service;
beforeAll(() => {
Expand Down
9 changes: 9 additions & 0 deletions packages/unmock-core/src/__tests__/backend/states.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ describe("Node.js interceptor", () => {
expect(response.data).toBe("foo");
});

test("gets correct state with query parameter when setting textual response", async () => {
filestackApi.state("foo");
const response = await axios(
"https://cloud.filestackapi.com/prefetch?apikey=fake",
);
expect(response.status).toBe(200);
expect(response.data).toBe("foo");
});

test("gets correct state when setting textual response with path", async () => {
filestackApi.state("/prefetch", "bar");
const response = await axios("https://cloud.filestackapi.com/prefetch");
Expand Down
12 changes: 12 additions & 0 deletions packages/unmock-core/src/__tests__/generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ describe("Tests generator", () => {
host: "petstore.swagger.io",
method: "post",
path: "/v1/pets",
pathname: "/v1/pets",
protocol: "http",
query: {},
});
expect(resp).toBeDefined();
if (resp !== undefined) {
Expand All @@ -68,7 +70,9 @@ describe("Tests generator", () => {
host: "petstore.swagger.io",
method: "post",
path: "/v1/pets",
pathname: "/v1/pets",
protocol: "http",
query: {},
});
expect(resp).toBeDefined();
if (resp !== undefined) {
Expand All @@ -91,7 +95,9 @@ describe("Tests generator", () => {
host: "cloud.filestackapi.com",
method: "options",
path: "/prefetch",
pathname: "/prefetch",
protocol: "https",
query: {},
});
expect(resp).toBeDefined();
if (resp !== undefined) {
Expand All @@ -103,7 +109,9 @@ describe("Tests generator", () => {
host: "cloud.filestackapi.com",
method: "get",
path: "/prefetch",
pathname: "/prefetch",
protocol: "https",
query: {},
});
expect(resp).toBeDefined();
if (resp !== undefined) {
Expand All @@ -122,7 +130,9 @@ describe("Tests generator", () => {
host: "petstore.swagger.io",
method: "get",
path: "/v1/pets",
pathname: "/v1/pets",
protocol: "http",
query: {},
});
expect(resp).toBeDefined();
if (resp && resp.body !== undefined) {
Expand All @@ -138,7 +148,9 @@ describe("Tests generator", () => {
host: "petstore.swagger.io",
method: "get",
path: "/v1/pets",
pathname: "/v1/pets",
protocol: "http",
query: {},
});
expect(resp).toBeDefined();
if (resp && resp.body !== undefined) {
Expand Down
2 changes: 2 additions & 0 deletions packages/unmock-core/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ describe("Imports", () => {
host: "github.com",
method: "get",
path: "/v1",
pathname: "/v1",
protocol: "http",
query: {},
};
const response: UnmockResponse = { body: "asdf", statusCode: 200 };
expect(request).toBeDefined();
Expand Down
3 changes: 3 additions & 0 deletions packages/unmock-core/src/__tests__/matcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ describe("OASMatcher", () => {
host: "petstore.swagger.io",
method: "get",
path: "/v1/pets",
pathname: "/v1/pets",
protocol: "http",
query: {},
};
it("matches a correct request", () => {
const sreq: UnmockRequest = validRequest;
Expand All @@ -29,6 +31,7 @@ describe("OASMatcher", () => {
const sreq: UnmockRequest = {
...validRequest,
path: "/v1",
pathname: "/v1",
};
const responseTemplate = matcher.matchToOperationObject(sreq);
expect(responseTemplate).toBeUndefined();
Expand Down
7 changes: 5 additions & 2 deletions packages/unmock-core/src/__tests__/serialize/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ describe("Request serializer", () => {

test("serializes GET request", done => {
const testHost = "example.org";
const testPath = "/v1?name=erkki";

mitm.on("request", async (req: IncomingMessage) => {
const serializedRequest = await serializeRequest(req);
expect(serializedRequest.host).toBe(testHost);
expect(serializedRequest.method.toLowerCase()).toBe("get");
expect(serializedRequest.path).toBe("/");
expect(serializedRequest.pathname).toBe("/v1");
expect(serializedRequest.path).toBe(testPath);
expect(serializedRequest.query).toEqual({ name: "erkki" });
expect(serializedRequest.protocol).toBe("http");
expect(serializedRequest.body).toBeUndefined();
done();
});
http.get(`http://${testHost}`);
http.get(`http://${testHost}${testPath}`);
});

function sendHttpsPostRequest(host: string, body: any) {
Expand Down
2 changes: 2 additions & 0 deletions packages/unmock-core/src/__tests__/spy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const fakeRequest: ISerializedRequest = {
host: "github.com",
protocol: "https",
path: "/v3",
pathname: "/v3",
query: {},
};

const fakeResponse: ISerializedResponse = {
Expand Down
2 changes: 2 additions & 0 deletions packages/unmock-core/src/__tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ export const PetstoreServiceWithDynamicPaths = (
export const testRequest: ISerializedRequest = {
method: "get",
path: "/v3",
pathname: "/v3",
host: "api.github.com",
protocol: "https",
query: {},
};

export const testResponse: ISerializedResponse = {
Expand Down
4 changes: 2 additions & 2 deletions packages/unmock-core/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ const errorForMissingTemplate = (sreq: ISerializedRequest) => {
return `No matching template found for intercepted request. Please ensure that
1. You have defined a service for host ${serverUrl}
2. The service has a path matching "${sreq.method} ${sreq.path}"
2. The service has a path matching "${sreq.method} ${sreq.pathname}"
For example, add the following to your service:
servers:
- url: ${sreq.protocol}://${sreq.host}
paths:
${sreq.path}:
${sreq.pathname}:
${sreq.method.toLowerCase()}:
// OpenAPI operation object
responses:
Expand Down
15 changes: 15 additions & 0 deletions packages/unmock-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,27 @@ export interface IOutgoingHeaders {
[header: string]: string | string[] | number | undefined;
}

export interface IIncomingQuery {
[key: string]: string | string[] | undefined;
}

export interface ISerializedRequest {
body?: string | object;
headers?: IIncomingHeaders;
host: string;
method: HTTPMethod;
/**
* Full path containing query parameters
*/
path: string;
/**
* Path name not containing query parameters
*/
pathname: string;
/**
* Query parameters
*/
query: IIncomingQuery;
protocol: "http" | "https";
}

Expand Down
19 changes: 16 additions & 3 deletions packages/unmock-core/src/serialize/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import url from "url";
import {
HTTPMethod,
IIncomingHeaders,
IIncomingQuery,
ISerializedRequest,
isRESTMethod,
} from "../interfaces";
Expand Down Expand Up @@ -46,10 +47,12 @@ class BodySerializer extends readable.Transform {
function extractVars(
interceptedRequest: http.IncomingMessage,
): {
headers: IIncomingHeaders;
method: HTTPMethod;
host: string;
path: string;
headers: IIncomingHeaders;
pathname: string;
query: IIncomingQuery;
} {
const headers = interceptedRequest.headers;

Expand All @@ -69,12 +72,16 @@ function extractVars(
throw new Error("Missing method");
}

const { pathname: path } = url.parse(requestUrl, true);
const { path, pathname, query } = url.parse(requestUrl, true);

if (!path) {
throw new Error("Could not parse path");
}

if (!pathname) {
throw new Error("Could not parse pathname");
}

// https://nodejs.org/api/http.html#http_message_method
const method = methodNode.toLowerCase();

Expand All @@ -87,6 +94,8 @@ function extractVars(
host,
method,
path,
pathname,
query,
};
}

Expand All @@ -110,7 +119,9 @@ const safelyParseJson = (body: string): string | object => {
export const serializeRequest = async (
interceptedRequest: http.IncomingMessage,
): Promise<ISerializedRequest> => {
const { headers, host, method, path } = extractVars(interceptedRequest);
const { headers, host, method, path, pathname, query } = extractVars(
interceptedRequest,
);

const isEncrypted = (interceptedRequest.connection as any).encrypted;
const protocol = isEncrypted ? "https" : "http";
Expand All @@ -128,7 +139,9 @@ export const serializeRequest = async (
host,
method,
path,
pathname,
protocol,
query,
};
return serializedRequest;
};
Expand Down
6 changes: 3 additions & 3 deletions packages/unmock-core/src/service/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,18 @@ export class OASMatcher {

debugLog(
`Testing: ${protocol} vs. ${sreq.protocol}, ${serverUrl.hostname} ` +
`vs ${sreq.host}, ${sreq.path} vs ${serverUrl.pathname}`,
`vs ${sreq.host}, ${sreq.pathname} vs ${serverUrl.pathname}`,
);
if (serverUrl.pathname === undefined) {
throw new Error("Got undefined pathname");
}
if (
protocol === sreq.protocol &&
serverUrl.hostname === sreq.host &&
sreq.path.startsWith(serverUrl.pathname)
sreq.pathname.startsWith(serverUrl.pathname)
) {
const reqPathWithoutServerPrefix = OASMatcher.normalizeRequestPathToServerPath(
sreq.path,
sreq.pathname,
serverUrl.pathname,
);

Expand Down
3 changes: 1 addition & 2 deletions packages/unmock-core/src/service/serviceCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class ServiceCore implements IServiceCore {
return undefined;
}

const state = this.getState(sreq.method as HTTPMethod, sreq.path);
const state = this.getState(sreq.method, sreq.pathname);
return {
operation: maybeOp,
state,
Expand Down Expand Up @@ -101,7 +101,6 @@ export class ServiceCore implements IServiceCore {

public getState(method: HTTPMethod, endpoint: string) {
// TODO at some point we'd probably want to move to regex for case insensitivity
method = method.toLowerCase() as HTTPMethod;
endpoint = endpoint.toLowerCase();
const realEndpoint = this.matcher.findEndpoint(endpoint);
if (realEndpoint === undefined) {
Expand Down

0 comments on commit 4d3083a

Please sign in to comment.