Skip to content

Commit

Permalink
More unit tests (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
zoe-codez authored Jun 19, 2024
1 parent 6dc54d6 commit 4ab3c42
Show file tree
Hide file tree
Showing 2 changed files with 372 additions and 0 deletions.
197 changes: 197 additions & 0 deletions src/testing/fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,203 @@ describe("Fetch Extension", () => {
).rejects.toThrow("Invalid input");
});
});

describe("Response handling", () => {
it("returns raw response when process is 'raw'", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const rawResponse = new Response("raw response");
jest.spyOn(global, "fetch").mockResolvedValue(rawResponse);
const response = await internal.boilerplate
.fetch({ baseUrl: "http://foo.bar", context })
.fetch({ process: "raw", url: "/foo" });
expect(response).toBe(rawResponse);
});
});

it("returns text response as string when process is 'text'", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "Some plain text response";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({ baseUrl: "http://foo.bar", context })
.fetch({ process: "text", url: "/foo" });
expect(response).toBe(textResponse);
});
});

it("deserializes JSON response correctly when process is not 'text' or 'raw'", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const jsonResponse = { key: "value" };
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(JSON.stringify(jsonResponse)),
});
const response = await internal.boilerplate
.fetch({ baseUrl: "http://foo.bar", context })
.fetch({});
expect(response).toEqual(jsonResponse);
});
});

it("returns non-JSON text response as string", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "Unexpected response";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({ baseUrl: "http://foo.bar", context })
.fetch({});
expect(response).toBe(textResponse);
});
});

it("handles 'OK' text response correctly", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "OK";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toBe(textResponse);
});
});

it("handles unexpected API response correctly", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "Unexpected response";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toBe(textResponse);
});
});
});

describe("Response handling based on leading characters", () => {
it("deserializes JSON response starting with '{'", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const jsonResponse = { key: "value" };
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(JSON.stringify(jsonResponse)),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toEqual(jsonResponse);
});
});

it("deserializes JSON response starting with '['", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const jsonResponse = [{ key: "value" }];
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(JSON.stringify(jsonResponse)),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toEqual(jsonResponse);
});
});

it("returns text response as string when leading character is not '{' or '['", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "Some plain text response";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toBe(textResponse);
});
});

it("handles 'OK' text response correctly when leading character is not '{' or '['", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "OK";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toBe(textResponse);
});
});

it("returns text response as string for unexpected API response", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal, context }) => {
const textResponse = "Unexpected response";
// @ts-expect-error testing
jest.spyOn(global, "fetch").mockResolvedValue({
ok: true,
text: jest.fn().mockResolvedValue(textResponse),
});
const response = await internal.boilerplate
.fetch({
baseUrl: "http://foo.bar",
context,
})
.fetch({});
expect(response).toBe(textResponse);
});
});
});
});
});
});
175 changes: 175 additions & 0 deletions src/testing/internal.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { CreateApplication, is } from "..";

Check warning on line 1 in src/testing/internal.spec.ts

View workflow job for this annotation

GitHub Actions / lint-and-build

'is' is defined but never used. Allowed unused vars must match /_|logger/u
import { BASIC_BOOT, ServiceTest } from "./testing.helper";

describe("Fetch Extension", () => {
beforeAll(async () => {
// @ts-expect-error testing
const preload = CreateApplication({ name: "testing" });
await preload.bootstrap(BASIC_BOOT);
await preload.teardown();
});

afterEach(async () => {
jest.restoreAllMocks();
});

describe("TitleCase", () => {
test("converts single word to title case", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("word")).toBe("Word");
});
});

test("converts multiple words separated by spaces to title case", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("multiple words here")).toBe(
"Multiple Words Here",
);
});
});

test("converts multiple words separated by underscores to title case", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("multiple_words_here")).toBe(
"Multiple Words Here",
);
});
});

test("converts multiple words separated by hyphens to title case", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("multiple-words-here")).toBe(
"Multiple Words Here",
);
});
});

test("inserts spaces between camel case words and converts to title case", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("camelCaseWordsHere")).toBe(
"Camel Case Words Here",
);
});
});

test("handles empty string input", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("")).toBe("");
});
});

test("handles single character input", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("a")).toBe("A");
});
});

test("handles input with mixed delimiters", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
expect(internal.utils.TitleCase("mixed_delimiters-here now")).toBe(
"Mixed Delimiters Here Now",
);
});
});
});

describe("internal.utils.object.del", () => {
test("deletes a top-level property", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: 1, b: 2 };
internal.utils.object.del(object, "a");
expect(object).toEqual({ b: 2 });
});
});

test("deletes a nested property", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: { b: { c: 3 } } };
internal.utils.object.del(object, "a.b.c");
expect(object).toEqual({ a: { b: {} } });
});
});

test("does nothing if the path does not exist", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: { b: { c: 3 } } };
internal.utils.object.del(object, "a.b.x");
expect(object).toEqual({ a: { b: { c: 3 } } });
});
});

test("handles path to a non-object gracefully", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: 1 };
internal.utils.object.del(object, "a.b.c");
expect(object).toEqual({ a: 1 });
});
});

test("handles deleting from an empty path", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: 1 };
internal.utils.object.del(object, "");
expect(object).toEqual({ a: 1 });
});
});

test("handles null or undefined values in the path", async () => {
expect.assertions(1);
await ServiceTest(({ internal }) => {
const object = { a: { b: null } } as object;
internal.utils.object.del(object, "a.b.c");
expect(object).toEqual({ a: { b: null } });
});
});
});

describe("internal.safeExec", () => {
test("executes the provided function successfully", async () => {
expect.assertions(1);
await ServiceTest(async ({ internal }) => {
const mockFunction = jest.fn();

await internal.safeExec(mockFunction);

expect(mockFunction).toHaveBeenCalled();
});
});

test("catches and logs errors thrown by the provided function", async () => {
expect.assertions(2);
await ServiceTest(async ({ internal }) => {
const mockFunction = jest.fn().mockImplementation(() => {
throw new Error("Test error");
});
const mockLogger = jest.spyOn(
internal.boilerplate.logger.systemLogger,
"error",
);

await internal.safeExec(mockFunction);

expect(mockFunction).toHaveBeenCalled();
expect(mockLogger).toHaveBeenCalledWith(
expect.objectContaining({ error: expect.any(Error) }),
"callback threw error",
);

mockLogger.mockRestore();
});
});
});
});

0 comments on commit 4ab3c42

Please sign in to comment.