Skip to content

Commit

Permalink
update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bodrovis committed Dec 1, 2024
1 parent 43966e9 commit 5e27672
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 139 deletions.
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": { "enabled": true },
"vcs": {
"enabled": true,
Expand Down
140 changes: 2 additions & 138 deletions test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,151 +1,15 @@
import * as dotenv from "dotenv";
dotenv.config();
import { readFile } from "node:fs/promises";
import { MockAgent, setGlobalDispatcher } from "undici";
import type { IncomingHttpHeaders } from "undici/types/header.js";
import type {
Interceptable,
MockInterceptor,
} from "undici/types/mock-interceptor.js";

import { describe, expect, it } from "vitest";
import type { Keyable } from "../src/interfaces/keyable.js";
import { LokalisePkg } from "../src/lokalise/pkg.js";
import {
LokaliseApi,
LokaliseApiOAuth,
LokaliseApiOta,
LokaliseAuth,
LokaliseOtaBundles,
} from "../src/main.js";
import type { HttpMethod } from "../src/types/http_method.js";

const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);
mockAgent.disableNetConnect();

type StubParams = {
uri: string;
fixture?: string;
method?: HttpMethod;
query?: Keyable;
body?: Keyable;
reqHeaders?: Keyable;
respHeaders?: IncomingHttpHeaders;
status?: number;
doFail?: boolean;
rootUrl?: string;
skipApiToken?: boolean;
version?: string;
data?: string;
};

class Stub {
readonly uriPath: string;
readonly fixturePath: string = "";
readonly httpMethod: HttpMethod;
readonly requestHeaders: Keyable | undefined = undefined;
readonly requestBody: string | undefined = undefined;
readonly responseHeaders: IncomingHttpHeaders | undefined = undefined;
readonly statusCode: number;
readonly doFail: boolean;
readonly rootUrl: string;
readonly skipApiToken: boolean;
readonly version: string;
readonly data: string | undefined = undefined;

constructor({
uri,
fixture,
method = "GET",
query,
body,
reqHeaders,
respHeaders,
status = 200,
doFail = false,
rootUrl = "https://api.lokalise.com",
skipApiToken = false,
version = "api2",
data,
}: StubParams) {
this.uriPath = query
? `${uri}?${new URLSearchParams(query).toString()}`
: uri;
this.fixturePath = fixture ? `./fixtures/${fixture}` : "";
this.httpMethod = method;
this.requestBody = body ? JSON.stringify(body) : undefined;
this.requestHeaders = reqHeaders;
this.responseHeaders = respHeaders;
this.statusCode = status;
this.doFail = doFail;
this.rootUrl = rootUrl;
this.skipApiToken = skipApiToken;
this.version = version;
this.data = data;
}

async setStub() {
const mockPool = mockAgent.get(this.rootUrl);

const mockOpts: MockInterceptor.Options = {
method: this.httpMethod,
path: `/${this.version}/${this.uriPath}`,
};

const requestHeaders = {
Accept: "application/json",
"User-Agent": `node-lokalise-api/${await LokalisePkg.getVersion()}`,
...(this.skipApiToken
? {}
: { "x-api-token": String(process.env.API_KEY) }),
...(this.requestBody ? { "Content-type": "application/json" } : {}),
...this.requestHeaders,
};

mockOpts.headers = requestHeaders;

const respOpts: MockInterceptor.MockResponseOptions = {};

if (this.responseHeaders) {
respOpts.headers = this.responseHeaders;
}

try {
await this.respond(mockPool, mockOpts, this.doFail, respOpts);
} catch (error) {
console.error("Error setting up test mock:", error);
}
}

private async respond(
mockPool: Interceptable,
mockOpts: MockInterceptor.Options,
isError: boolean,
respOpts: MockInterceptor.MockResponseOptions,
) {
if (isError) {
mockPool.intercept(mockOpts).replyWithError(new Error("Fail"));
} else {
mockPool
.intercept(mockOpts)
.reply(
this.statusCode,
this.data ?? (await this.readFixture()),
respOpts,
);
}
}

private async readFixture(): Promise<string> {
if (this.fixturePath === "") {
return "";
}

return JSON.parse(
(await readFile(new URL(this.fixturePath, import.meta.url))).toString(),
);
}
}
import { Stub } from "./stubs.js";

export {
LokaliseApi,
Expand Down
170 changes: 170 additions & 0 deletions test/stubs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { readFile } from "node:fs/promises";
import { MockAgent, setGlobalDispatcher } from "undici";
import type { IncomingHttpHeaders } from "undici/types/header.js";
import type {
Interceptable,
MockInterceptor,
} from "undici/types/mock-interceptor.js";
import type { Keyable } from "../src/interfaces/keyable.js";
import { LokalisePkg } from "../src/lokalise/pkg.js";
import type { HttpMethod } from "../src/types/http_method.js";

const packageVersion = await LokalisePkg.getVersion();

const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);
mockAgent.disableNetConnect();

type StubParams = Partial<{
uri: string;
fixture: string;
method: HttpMethod;
query: Keyable;
body: Keyable;
reqHeaders: Keyable;
respHeaders: IncomingHttpHeaders;
status: number;
doFail: boolean;
rootUrl: string;
skipApiToken: boolean;
version: string;
data: string;
}>;

export class Stub {
private readonly uriPath: string;
private readonly fixturePath: string;
private readonly httpMethod: HttpMethod;
private readonly requestHeaders: Keyable | undefined;
private readonly requestBody: string | undefined;
private readonly responseHeaders: IncomingHttpHeaders | undefined;
private readonly statusCode: number;
private readonly doFail: boolean;
private readonly rootUrl: string;
private readonly version: string;
private readonly data: string | undefined;

constructor(params: StubParams) {
if (!params.uri) {
throw new Error("The 'uri' parameter is required.");
}

const {
uri,
fixture,
method = "GET",
query,
body,
reqHeaders,
respHeaders,
status = 200,
doFail = false,
rootUrl = "https://api.lokalise.com",
skipApiToken = false,
version = "api2",
data,
} = params;

this.uriPath = this.buildUriPath(uri, query);
this.fixturePath = fixture ? `./fixtures/${fixture}` : "";
this.httpMethod = method;
this.requestHeaders = this.buildRequestHeaders(
skipApiToken,
body,
reqHeaders,
);
this.responseHeaders = respHeaders;
this.requestBody = body ? JSON.stringify(body) : undefined;
this.statusCode = status;
this.doFail = doFail;
this.rootUrl = rootUrl;
this.version = version;
this.data = data;
}

async setStub() {
const mockPool = mockAgent.get(this.rootUrl);

try {
await this.setupMock(mockPool);
} catch (error) {
throw new Error(
`Failed to set up mock for URI: ${this.uriPath} - ${error.message}`,
);
}
}

private async setupMock(mockPool: Interceptable) {
const mockOpts = this.buildMockOptions();
const respOpts = this.buildResponseOptions();

await this.respond(mockPool, mockOpts, this.doFail, respOpts);
}

private async respond(
mockPool: Interceptable,
mockOpts: MockInterceptor.Options,
isError: boolean,
respOpts: MockInterceptor.MockResponseOptions,
) {
if (isError) {
mockPool.intercept(mockOpts).replyWithError(new Error("Fail"));
} else {
const responseData = this.data ?? (await this.readFixture());
mockPool
.intercept({ ...mockOpts, body: this.requestBody })
.reply(this.statusCode, responseData, respOpts);
}
}

private async readFixture(): Promise<Record<string, any> | string> {
if (!this.fixturePath) {
return "";
}

try {
const fileContent = await readFile(
new URL(this.fixturePath, import.meta.url),
);
return JSON.parse(fileContent.toString());
} catch (error) {
console.error(
`Error reading fixture file at ${this.fixturePath}:`,
error,
);
throw error;
}
}

private buildRequestHeaders(
skipApiToken: boolean,
body: Keyable | undefined,
reqHeaders?: Keyable,
): Keyable {
return {
Accept: "application/json",
"User-Agent": `node-lokalise-api/${packageVersion}`,
...(skipApiToken ? {} : { "x-api-token": String(process.env.API_KEY) }),
...(body ? { "Content-type": "application/json" } : {}),
...reqHeaders,
};
}

private buildUriPath(uri: string, query?: Keyable): string {
return query ? `${uri}?${new URLSearchParams(query).toString()}` : uri;
}

private buildMockOptions(): MockInterceptor.Options {
return {
method: this.httpMethod,
path: `/${this.version}/${this.uriPath}`,
headers: this.requestHeaders,
};
}

private buildResponseOptions(): MockInterceptor.MockResponseOptions {
return {
headers: this.responseHeaders,
};
}
}

0 comments on commit 5e27672

Please sign in to comment.