Skip to content

Commit

Permalink
[MS-780] feat: More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrgrundas committed Oct 16, 2024
1 parent 38f5bc5 commit f553101
Show file tree
Hide file tree
Showing 9 changed files with 417 additions and 35 deletions.
24 changes: 2 additions & 22 deletions src/emails-sender.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
import "./instrument.emails-sender";

import * as Sentry from "@sentry/aws-serverless";
import {
type Context,
type SQSBatchResponse,
type SQSEvent,
type SQSRecord,
} from "aws-lambda";
import { type Context, type SQSBatchResponse, type SQSEvent } from "aws-lambda";

import { CONFIG } from "@/config";
import { parseRecord } from "@/lib/aws/serverless/utils";
import { TEMPLATES_MAP } from "@/lib/emails/const";
import { EmailParsePayloadError } from "@/lib/emails/errors";
import { type SerializedPayload } from "@/lib/emails/events/helpers";
import { getJSONFormatHeader } from "@/lib/saleor/apps/utils";
import { getEmailProvider } from "@/providers/email";
import { getLogger } from "@/providers/logger";

export const logger = getLogger();

const parseRecord = (record: SQSRecord) => {
try {
// Proxy events has invalid types.
const data = JSON.parse((record as any).Body);
return data as SerializedPayload;
} catch (error) {
logger.error("Failed to parse record payload.", { record, error });

throw new EmailParsePayloadError("Failed to parse record payload.", {
cause: { source: error as Error },
});
}
};

export const handler = Sentry.wrapHandler(
async (event: SQSEvent, context: Context) => {
const failures: string[] = [];
Expand Down
56 changes: 56 additions & 0 deletions src/lib/aws/serverless/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { type SQSRecord } from "aws-lambda";
import { describe, expect, test, vi } from "vitest";

import { EmailParsePayloadError } from "@/lib/emails/errors";
import { getLogger } from "@/providers/logger"; // Mock the logger provider

import { parseRecord } from "./utils";

describe("utils", () => {
describe("parseRecord", () => {
vi.mock("@/providers/logger", () => {
const mockLogger = {
error: vi.fn(),
};

return {
getLogger: () => mockLogger,
};
});

test("should parse valid record and return data", () => {
// given
const data = {
event: "some_event",
data: { key: "value" },
};
const validRecord = {
Body: JSON.stringify(data),
} as any as SQSRecord;

// when
const result = parseRecord(validRecord);

// when
expect(result).toEqual(data);
});

test("should log and throw error when parsing fails", () => {
// given
const invalidRecord = {
Body: "{invalidJson",
} as any as SQSRecord;
const logger = getLogger();

// when & then
expect(() => parseRecord(invalidRecord)).toThrow(EmailParsePayloadError);
expect(logger.error).toHaveBeenCalledWith(
"Failed to parse record payload.",
expect.objectContaining({
record: invalidRecord,
error: expect.any(Error),
})
);
});
});
});
23 changes: 23 additions & 0 deletions src/lib/aws/serverless/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type SQSRecord } from "aws-lambda";

import { EmailParsePayloadError } from "@/lib/emails/errors";
import { type SerializedPayload } from "@/lib/emails/events/helpers";
import { getLogger } from "@/providers/logger";

const logger = getLogger();

export const parseRecord = (record: SQSRecord) => {
try {
// Proxy events has invalid types.
const data = JSON.parse((record as any).Body);

return data as SerializedPayload;
} catch (error) {
logger.error("Failed to parse record payload.", { record, error });

// TODO: Should be non transient error
throw new EmailParsePayloadError("Failed to parse record payload.", {
cause: { source: error as Error },
});
}
};
61 changes: 61 additions & 0 deletions src/lib/regions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { describe, expect, test, vi } from "vitest";

import { CONFIG } from "@/config";

import { DEFAULT_REGION, getRegion, REGIONS } from "./regions";

describe("regions", () => {
describe("getRegion", () => {
test("should return the correct region for a valid slug", () => {
// given
const validSlug = "channel-us";

// when
const result = getRegion(validSlug);

// then
expect(result).toEqual(REGIONS.US);
});

test("should return the default region when slug is not found", () => {
// given
const invalidSlug = "channel-invalid";

// when
const result = getRegion(invalidSlug);

// then
expect(result).toEqual(DEFAULT_REGION);
});

test("should throw an error if no default region exists in CONFIG", () => {
// given
const invalidSlug = "channel-invalid";
const originalDefaultRegion = CONFIG.DEFAULT_REGION;
vi.spyOn(CONFIG, "DEFAULT_REGION", "get").mockReturnValue(
undefined as any
);

// when / then
expect(() => getRegion(invalidSlug)).toThrow(
`Region not found for channel slug ${invalidSlug}.`
);

// Clean up
vi.spyOn(CONFIG, "DEFAULT_REGION", "get").mockReturnValue(
originalDefaultRegion
);
});

test("should return region even if slug case is different", () => {
// given
const slugWithDifferentCase = "CHANNEL-UK";

// when
const result = getRegion(slugWithDifferentCase.toLowerCase());

// then
expect(result).toEqual(REGIONS.GB);
});
});
});
62 changes: 62 additions & 0 deletions src/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, expect, test } from "vitest";

import { isURL } from "./utils";

describe("utils", () => {
describe("isURL", () => {
test("should return true for a valid URL", () => {
// given
const validUrl = "http://example.com";

// when
const result = isURL(validUrl);

// then
expect(result).toBe(true);
});

test("should return false for an invalid URL", () => {
// given
const invalidUrl = "not a valid url";

// when
const result = isURL(invalidUrl);

// then
expect(result).toBe(false);
});

test("should return false for an empty string", () => {
// given
const emptyString = "";

// when
const result = isURL(emptyString);

// then
expect(result).toBe(false);
});

test("should return true for a valid URL with query parameters", () => {
// given
const validUrlWithParams = "https://example.com?query=test";

// when
const result = isURL(validUrlWithParams);

// then
expect(result).toBe(true);
});

test("should return false for a malformed URL", () => {
// given
const malformedUrl = "http://example.com:port";

// when
const result = isURL(malformedUrl);

// then
expect(result).toBe(false);
});
});
});
108 changes: 108 additions & 0 deletions src/lib/zod/env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { describe, expect, test } from "vitest";
import { z } from "zod";

import { envBool, envToStrList } from "./env";

describe("env", () => {
describe("envBool", () => {
test('should return true for "true"', () => {
// given
const input = "true";

// when
const result = envBool.parse(input);

// then
expect(result).toBe(true);
});

test('should return false for "false"', () => {
// given
const input = "false";

// when
const result = envBool.parse(input);

// then
expect(result).toBe(false);
});

test("should return false for an empty string", () => {
// given
const input = "";

// when
const result = envBool.parse(input);

// then
expect(result).toBe(false);
});

test("should throw an error for invalid values", () => {
// given
const input = "invalid";

// when / then
expect(() => envBool.parse(input)).toThrow(z.ZodError);
});
});

describe("envToStrList", () => {
test("should return an array of strings for a valid comma-separated string", () => {
// given
const input = "value1,value2,value3";

// when
const result = envToStrList(input);

// then
expect(result).toEqual(["value1", "value2", "value3"]);
});

test("should return an empty array when env is undefined and defaultEmpty is false", () => {
// given
const input = undefined;
const defaultEmpty = false;

// when
const result = envToStrList(input, defaultEmpty);

// then
expect(result).toEqual([]);
});

test("should return undefined when env is undefined and defaultEmpty is true", () => {
// given
const input = undefined;
const defaultEmpty = true;

// when
const result = envToStrList(input, defaultEmpty);

// then
expect(result).toBeUndefined();
});

test("should filter out empty values in a comma-separated string", () => {
// given
const input = "value1,,value3";

// when
const result = envToStrList(input);

// then
expect(result).toEqual(["value1", "value3"]);
});

test("should return an empty array when env is an empty string", () => {
// given
const input = "";

// when
const result = envToStrList(input);

// then
expect(result).toEqual([]);
});
});
});
Loading

0 comments on commit f553101

Please sign in to comment.