Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce internal config object #263

Merged
merged 4 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/serious-ears-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@labdigital/commercetools-mock": minor
---

Add a `strict` boolean option to make the mock service strict and throw errors when certain values are missing
6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { AbstractStorage } from "./storage";

export type Config = {
strict: boolean;
storage: AbstractStorage;
};
18 changes: 12 additions & 6 deletions src/ctMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { AbstractStorage } from "./storage";
import { InMemoryStorage } from "./storage";

// Services
import { warnDeprecation } from "./deprecation";
import type { Config } from "./config";
import { mapHeaderType } from "./helpers";
import type { RepositoryMap } from "./repositories";
import { createRepositories } from "./repositories";
Expand All @@ -29,6 +29,7 @@ export type CommercetoolsMockOptions = {
apiHost: RegExp | string;
authHost: RegExp | string;
silent: boolean;
strict: boolean;
};

type AppOptions = { silent?: boolean };
Expand All @@ -39,7 +40,8 @@ const DEFAULT_OPTIONS: CommercetoolsMockOptions = {
defaultProjectKey: undefined,
apiHost: DEFAULT_API_HOSTNAME,
authHost: DEFAULT_AUTH_HOSTNAME,
silent: false,
silent: true,
strict: false,
};

const _globalListeners: SetupServer[] = [];
Expand Down Expand Up @@ -74,7 +76,7 @@ export class CommercetoolsMock {
}

start() {
warnDeprecation(
process.emitWarning(
"The start method is deprecated, use .registerHandlers() to bind to an msw server instead",
);

Expand All @@ -84,7 +86,7 @@ export class CommercetoolsMock {
}

stop() {
warnDeprecation(
process.emitWarning(
"The stop method is deprecated, use .registerHandlers() to bind to an msw server instead",
);
this._mswServer?.close();
Expand Down Expand Up @@ -123,7 +125,11 @@ export class CommercetoolsMock {
}

private createApp(options?: AppOptions): express.Express {
this._repositories = createRepositories(this._storage);
const config: Config = {
strict: this.options.strict,
storage: this._storage,
};
this._repositories = createRepositories(config);
this._oauth2.setCustomerRepository(this._repositories.customer);

const app = express();
Expand Down Expand Up @@ -292,7 +298,7 @@ export class CommercetoolsMock {
if (this._mswServer !== undefined) {
throw new Error("Server already started");
} else {
console.warn("Server wasn't stopped properly, clearing");
process.emitWarning("Server wasn't stopped properly, clearing");
_globalListeners.forEach((listener) => listener.close());
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/deprecation.ts

This file was deleted.

36 changes: 20 additions & 16 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import { type InvalidTokenError } from "@commercetools/platform-sdk";
import got from "got";
import { expect, test } from "vitest";
import { setupServer } from "msw/node";
import { afterEach, beforeAll, expect, test } from "vitest";
import { CommercetoolsMock } from "./index";

const mswServer = setupServer();

beforeAll(() => {
mswServer.listen({ onUnhandledRequest: "error" });
});

afterEach(() => {
mswServer.resetHandlers();
});

test("node:fetch client", async () => {
const ctMock = new CommercetoolsMock({
enableAuthentication: true,
validateCredentials: true,
apiHost: "https://localhost",
authHost: "https://localhost:8080",
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const authHeader = "Basic " + Buffer.from("foo:bar").toString("base64");
let response = await fetch("https://localhost:8080/oauth/token", {
Expand Down Expand Up @@ -44,7 +55,6 @@ test("node:fetch client", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});

test("got client", async () => {
Expand All @@ -54,7 +64,7 @@ test("got client", async () => {
apiHost: "https://localhost",
authHost: "https://localhost:8080",
});
ctMock.start();
ctMock.registerHandlers(mswServer);

let response = await got.post<{ access_token: string }>(
"https://localhost:8080/oauth/token",
Expand Down Expand Up @@ -86,15 +96,14 @@ test("got client", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});

test("Options.validateCredentials: true (error)", async () => {
const ctMock = new CommercetoolsMock({
enableAuthentication: true,
validateCredentials: true,
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.get<InvalidTokenError>(
"https://api.europe-west1.gcp.commercetools.com/my-project/orders",
Expand All @@ -108,15 +117,14 @@ test("Options.validateCredentials: true (error)", async () => {
);
expect(response.statusCode).toBe(401);
expect(response.body.message).toBe("invalid_token");
ctMock.stop();
});

test("Options.validateCredentials: false", async () => {
const ctMock = new CommercetoolsMock({
enableAuthentication: true,
validateCredentials: false,
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.get(
"https://api.europe-west1.gcp.commercetools.com/my-project/orders",
Expand All @@ -135,15 +143,14 @@ test("Options.validateCredentials: false", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});

test("Options.enableAuthentication: false", async () => {
const ctMock = new CommercetoolsMock({
enableAuthentication: false,
validateCredentials: false,
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.get(
"https://api.europe-west1.gcp.commercetools.com/my-project/orders",
Expand All @@ -159,7 +166,6 @@ test("Options.enableAuthentication: false", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});

test("Options.apiHost: is overridden is set", async () => {
Expand All @@ -168,7 +174,7 @@ test("Options.apiHost: is overridden is set", async () => {
validateCredentials: false,
apiHost: "http://api.localhost",
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.get("http://api.localhost/my-project/orders", {
responseType: "json",
Expand All @@ -181,7 +187,6 @@ test("Options.apiHost: is overridden is set", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});

test("Options.authHost: is set", async () => {
Expand All @@ -190,7 +195,7 @@ test("Options.authHost: is set", async () => {
validateCredentials: true,
authHost: "http://auth.localhost",
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.post<{ access_token: string }>(
"http://auth.localhost/oauth/token",
Expand All @@ -216,7 +221,7 @@ test("apiHost mock proxy: querystring", async () => {
validateCredentials: false,
apiHost: "http://api.localhost",
});
ctMock.start();
ctMock.registerHandlers(mswServer);

const response = await got.get("http://api.localhost/my-project/orders", {
responseType: "json",
Expand All @@ -234,5 +239,4 @@ test("apiHost mock proxy: querystring", async () => {
limit: 20,
results: [],
});
ctMock.stop();
});
4 changes: 3 additions & 1 deletion src/oauth/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express from "express";
import supertest from "supertest";
import { beforeEach, describe, expect, it } from "vitest";
import type { Config } from "~src/config";
import { getBaseResourceProperties } from "../helpers";
import { hashPassword } from "../lib/password";
import { CustomerRepository } from "../repositories/customer";
Expand All @@ -21,7 +22,8 @@ describe("OAuth2Server", () => {
app.use(server.createRouter());

storage = new InMemoryStorage();
customerRepository = new CustomerRepository(storage);
const config: Config = { storage, strict: false };
customerRepository = new CustomerRepository(config);
server.setCustomerRepository(customerRepository);
});

Expand Down
5 changes: 3 additions & 2 deletions src/product-projection-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
RangeFacetResult,
TermFacetResult,
} from "@commercetools/platform-sdk";
import type { Config } from "./config";
import { CommercetoolsError } from "./exceptions";
import { nestedLookup } from "./helpers";
import type {
Expand Down Expand Up @@ -51,8 +52,8 @@ export type ProductProjectionSearchParams = {
export class ProductProjectionSearch {
protected _storage: AbstractStorage;

constructor(storage: AbstractStorage) {
this._storage = storage;
constructor(config: Config) {
this._storage = config.storage;
}

search(
Expand Down
5 changes: 3 additions & 2 deletions src/product-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
ProductSearchRequest,
ProductSearchResult,
} from "@commercetools/platform-sdk";
import type { Config } from "./config";
import { CommercetoolsError } from "./exceptions";
import { parseSearchQuery } from "./lib/productSearchFilter";
import { validateSearchQuery } from "./lib/searchQueryTypeChecker";
Expand All @@ -15,8 +16,8 @@ import type { AbstractStorage } from "./storage";
export class ProductSearch {
protected _storage: AbstractStorage;

constructor(storage: AbstractStorage) {
this._storage = storage;
constructor(config: Config) {
this._storage = config.storage;
}

search(
Expand Down
12 changes: 8 additions & 4 deletions src/repositories/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
UpdateAction,
} from "@commercetools/platform-sdk";
import deepEqual from "deep-equal";
import type { Config } from "~src/config";
import { CommercetoolsError } from "~src/exceptions";
import { cloneObject } from "../helpers";
import type { AbstractStorage } from "../storage";
Expand Down Expand Up @@ -40,10 +41,13 @@ export type RepositoryContext = {
export abstract class AbstractRepository<R extends BaseResource | Project> {
protected _storage: AbstractStorage;

protected config: Config;

protected actions: AbstractUpdateHandler | undefined;

constructor(storage: AbstractStorage) {
this._storage = storage;
constructor(config: Config) {
this.config = config;
this._storage = config.storage;
}

abstract saveNew({ projectKey }: RepositoryContext, resource: R): void;
Expand Down Expand Up @@ -92,8 +96,8 @@ export abstract class AbstractResourceRepository<
> extends AbstractRepository<ResourceMap[T]> {
protected _typeId: T;

constructor(typeId: T, storage: AbstractStorage) {
super(storage);
constructor(typeId: T, config: Config) {
super(config);
this._typeId = typeId;
}

Expand Down
6 changes: 3 additions & 3 deletions src/repositories/associate-role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import type {
AssociateRoleSetPermissionsAction,
AssociateRoleUpdateAction,
} from "@commercetools/platform-sdk";
import type { Config } from "~src/config";
import { getBaseResourceProperties } from "../helpers";
import type { AbstractStorage } from "../storage/abstract";
import type { Writable } from "../types";
import type { UpdateHandlerInterface } from "./abstract";
import {
Expand All @@ -22,8 +22,8 @@ import {
import { createCustomFields } from "./helpers";

export class AssociateRoleRepository extends AbstractResourceRepository<"associate-role"> {
constructor(storage: AbstractStorage) {
super("associate-role", storage);
constructor(config: Config) {
super("associate-role", config);
this.actions = new AssociateRoleUpdateHandler(this._storage);
}

Expand Down
6 changes: 3 additions & 3 deletions src/repositories/attribute-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type {
AttributeGroupSetKeyAction,
AttributeGroupUpdateAction,
} from "@commercetools/platform-sdk";
import type { Config } from "~src/config";
import { getBaseResourceProperties } from "../helpers";
import type { AbstractStorage } from "../storage/abstract";
import type { Writable } from "../types";
import type { UpdateHandlerInterface } from "./abstract";
import {
Expand All @@ -18,8 +18,8 @@ import {
} from "./abstract";

export class AttributeGroupRepository extends AbstractResourceRepository<"attribute-group"> {
constructor(storage: AbstractStorage) {
super("attribute-group", storage);
constructor(config: Config) {
super("attribute-group", config);
this.actions = new AttributeGroupUpdateHandler(this._storage);
}

Expand Down
6 changes: 3 additions & 3 deletions src/repositories/business-unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
type Company,
type Division,
} from "@commercetools/platform-sdk";
import type { AbstractStorage } from "~src/storage";
import type { Config } from "~src/config";
import { generateRandomString, getBaseResourceProperties } from "../helpers";
import type { Writable } from "../types";
import type { UpdateHandlerInterface } from "./abstract";
Expand All @@ -41,8 +41,8 @@ import {
} from "./helpers";

export class BusinessUnitRepository extends AbstractResourceRepository<"business-unit"> {
constructor(storage: AbstractStorage) {
super("business-unit", storage);
constructor(config: Config) {
super("business-unit", config);
this.actions = new BusinessUnitUpdateHandler(this._storage);
}

Expand Down
Loading
Loading