diff --git a/package-lock.json b/package-lock.json index 0688e17eda..cf6acc709c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -133,19 +133,6 @@ "node": ">=6.0.0" } }, - "node_modules/@anatine/zod-mock": { - "version": "3.13.4", - "resolved": "https://registry.npmjs.org/@anatine/zod-mock/-/zod-mock-3.13.4.tgz", - "integrity": "sha512-yO/KeuyYsEDCTcQ+7CiRuY3dnafMHIZUMok6Ci7aERRCTQ+/XmsiPk/RnMx5wlLmWBTmX9kw+PavbMsjM+sAJA==", - "dev": true, - "dependencies": { - "randexp": "^0.5.3" - }, - "peerDependencies": { - "@faker-js/faker": "^7.0.0 || ^8.0.0", - "zod": "^3.21.4" - } - }, "node_modules/@aws-cdk/asset-awscli-v1": { "version": "2.2.209", "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.209.tgz", @@ -1882,23 +1869,6 @@ "node": ">=18" } }, - "node_modules/@faker-js/faker": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.2.tgz", - "integrity": "sha512-nI/FP30ZGXb+UaR7yXawVTH40NVKXPIx0tA3GKjkKLjorqBoMAeq4iSEacl8mJmpVhOCDa0vYHwYDmOOcFMrYw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fakerjs" - } - ], - "peer": true, - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0" - } - }, "node_modules/@gerrit0/mini-shiki": { "version": "1.26.1", "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.26.1.tgz", @@ -6374,15 +6344,6 @@ "node": ">=12" } }, - "node_modules/drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -12152,19 +12113,6 @@ "node": ">=8" } }, - "node_modules/randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", - "dev": true, - "dependencies": { - "drange": "^1.0.2", - "ret": "^0.2.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -12586,15 +12534,6 @@ "node": ">=8" } }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -15583,9 +15522,6 @@ "dependencies": { "@aws-lambda-powertools/commons": "^2.13.0" }, - "devDependencies": { - "@anatine/zod-mock": "^3.13.3" - }, "peerDependencies": { "@middy/core": "4.x || 5.x || 6.x", "zod": ">=3.x" diff --git a/packages/parser/package.json b/packages/parser/package.json index caf8da2320..73d3b6c19c 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -379,8 +379,5 @@ "@middy/core": { "optional": true } - }, - "devDependencies": { - "@anatine/zod-mock": "^3.13.3" } } diff --git a/packages/parser/src/envelopes/envelope.ts b/packages/parser/src/envelopes/envelope.ts index 312dc022ec..f088bec785 100644 --- a/packages/parser/src/envelopes/envelope.ts +++ b/packages/parser/src/envelopes/envelope.ts @@ -1,82 +1,7 @@ -import type { ZodSchema, z } from 'zod'; -import { ParseError } from '../errors.js'; -import type { ParsedResult } from '../types/parser.js'; - -/* v8 ignore start */ -const Envelope = { - /** - * Abstract function to parse the content of the envelope using provided schema. - * Both inputs are provided as unknown by the user. - * We expect the data to be either string that can be parsed to json or object. - * @internal - * @param data data to parse - * @param schema schema - */ - parse(data: unknown, schema: T): z.infer { - if (typeof data !== 'object' && typeof data !== 'string') { - throw new ParseError( - `Invalid data type for envelope. Expected string or object, got ${typeof data}` - ); - } - try { - if (typeof data === 'string') { - return schema.parse(JSON.parse(data)); - } - if (typeof data === 'object') { - return schema.parse(data); - } - } catch (e) { - throw new ParseError('Failed to parse envelope', { cause: e as Error }); - } - }, - - /** - * Abstract function to safely parse the content of the envelope using provided schema. - * safeParse is used to avoid throwing errors, thus we catuch all errors and wrap them in the result. - * @param input - * @param schema - */ - safeParse( - input: unknown, - schema: T - ): ParsedResult> { - try { - if (typeof input !== 'object' && typeof input !== 'string') { - return { - success: false, - error: new Error( - `Invalid data type for envelope. Expected string or object, got ${typeof input}` - ), - }; - } - - const parsed = schema.safeParse( - typeof input === 'string' ? JSON.parse(input) : input - ); - - return parsed.success - ? { - success: true, - data: parsed.data, - } - : { - success: false, - error: parsed.error, - }; - } catch (e) { - return { - success: false, - error: e as Error, - }; - } - }, -}; - /** * This is a discriminator to differentiate whether an envelope returns an array or an object * @hidden */ const envelopeDiscriminator = Symbol.for('returnType'); -export { Envelope, envelopeDiscriminator }; -/* v8 ignore stop */ +export { envelopeDiscriminator }; diff --git a/packages/parser/src/envelopes/vpc-lattice.ts b/packages/parser/src/envelopes/vpc-lattice.ts index 64d031c668..6c8190ae8c 100644 --- a/packages/parser/src/envelopes/vpc-lattice.ts +++ b/packages/parser/src/envelopes/vpc-lattice.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { VpcLatticeSchema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope, envelopeDiscriminator } from './envelope.js'; +import { envelopeDiscriminator } from './envelope.js'; /** * Amazon VPC Lattice envelope to extract data within body key @@ -15,35 +15,38 @@ export const VpcLatticeEnvelope = { */ [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { - const parsedEnvelope = VpcLatticeSchema.parse(data); - - return Envelope.parse(parsedEnvelope.body, schema); - }, - - safeParse(data: unknown, schema: T): ParsedResult> { - const parsedEnvelope = VpcLatticeSchema.safeParse(data); - if (!parsedEnvelope.success) { - return { - success: false, - error: new ParseError('Failed to parse VpcLattice envelope', { - cause: parsedEnvelope.error, - }), - originalEvent: data, - }; + try { + return VpcLatticeSchema.extend({ + body: schema, + }).parse(data).body; + } catch (error) { + throw new ParseError('Failed to parse VPC Lattice body', { + cause: error as Error, + }); } + }, - const parsedBody = Envelope.safeParse(parsedEnvelope.data.body, schema); + safeParse( + data: unknown, + schema: T + ): ParsedResult> { + const result = VpcLatticeSchema.extend({ + body: schema, + }).safeParse(data); - if (!parsedBody.success) { + if (!result.success) { return { success: false, - error: new ParseError('Failed to parse VpcLattice envelope body', { - cause: parsedBody.error, + error: new ParseError('Failed to parse VPC Lattice body', { + cause: result.error, }), originalEvent: data, }; } - return parsedBody; + return { + success: true, + data: result.data.body, + }; }, }; diff --git a/packages/parser/src/envelopes/vpc-latticev2.ts b/packages/parser/src/envelopes/vpc-latticev2.ts index 8a40f0f927..0ff99dffe6 100644 --- a/packages/parser/src/envelopes/vpc-latticev2.ts +++ b/packages/parser/src/envelopes/vpc-latticev2.ts @@ -2,7 +2,7 @@ import type { ZodSchema, z } from 'zod'; import { ParseError } from '../errors.js'; import { VpcLatticeV2Schema } from '../schemas/index.js'; import type { ParsedResult } from '../types/index.js'; -import { Envelope, envelopeDiscriminator } from './envelope.js'; +import { envelopeDiscriminator } from './envelope.js'; /** * Amazon VPC Lattice envelope to extract data within body key @@ -14,35 +14,38 @@ export const VpcLatticeV2Envelope = { */ [envelopeDiscriminator]: 'object' as const, parse(data: unknown, schema: T): z.infer { - const parsedEnvelope = VpcLatticeV2Schema.parse(data); - - return Envelope.parse(parsedEnvelope.body, schema); - }, - - safeParse(data: unknown, schema: T): ParsedResult> { - const parsedEnvelope = VpcLatticeV2Schema.safeParse(data); - if (!parsedEnvelope.success) { - return { - success: false, - error: new ParseError('Failed to parse VpcLatticeV2 envelope.', { - cause: parsedEnvelope.error, - }), - originalEvent: data, - }; + try { + return VpcLatticeV2Schema.extend({ + body: schema, + }).parse(data).body; + } catch (error) { + throw new ParseError('Failed to parse VPC Lattice v2 body', { + cause: error as Error, + }); } + }, - const parsedBody = Envelope.safeParse(parsedEnvelope.data.body, schema); + safeParse( + data: unknown, + schema: T + ): ParsedResult> { + const result = VpcLatticeV2Schema.extend({ + body: schema, + }).safeParse(data); - if (!parsedBody.success) { + if (!result.success) { return { success: false, - error: new ParseError('Failed to parse VpcLatticeV2 body.', { - cause: parsedBody.error, + error: new ParseError('Failed to parse VPC Lattice v2 body', { + cause: result.error, }), originalEvent: data, }; } - return parsedBody; + return { + success: true, + data: result.data.body, + }; }, }; diff --git a/packages/parser/tests/events/vpcLatticeV2Event.json b/packages/parser/tests/events/vpc-lattice-v2/base.json similarity index 100% rename from packages/parser/tests/events/vpcLatticeV2Event.json rename to packages/parser/tests/events/vpc-lattice-v2/base.json diff --git a/packages/parser/tests/events/vpcLatticeEvent.json b/packages/parser/tests/events/vpc-lattice/base.json similarity index 79% rename from packages/parser/tests/events/vpcLatticeEvent.json rename to packages/parser/tests/events/vpc-lattice/base.json index 936bfb22d1..39880b4365 100644 --- a/packages/parser/tests/events/vpcLatticeEvent.json +++ b/packages/parser/tests/events/vpc-lattice/base.json @@ -10,6 +10,6 @@ "query_string_parameters": { "order-id": "1" }, - "body": "eyJ0ZXN0IjogImV2ZW50In0=", - "is_base64_encoded": true -} + "body": "{\"message\": \"Hello from Lambda!\"}", + "is_base64_encoded": false +} \ No newline at end of file diff --git a/packages/parser/tests/events/vpcLatticeEventPathTrailingSlash.json b/packages/parser/tests/events/vpcLatticeEventPathTrailingSlash.json deleted file mode 100644 index 7f6c0cfd9a..0000000000 --- a/packages/parser/tests/events/vpcLatticeEventPathTrailingSlash.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "raw_path": "/testpath/", - "method": "GET", - "headers": { - "user_agent": "curl/7.64.1", - "x-forwarded-for": "10.213.229.10", - "host": "test-lambda-service-3908sdf9u3u.dkfjd93.vpc-lattice-svcs.us-east-2.on.aws", - "accept": "*/*" - }, - "query_string_parameters": { - "order-id": "1" - }, - "body": "eyJ0ZXN0IjogImV2ZW50In0=", - "is_base64_encoded": true -} diff --git a/packages/parser/tests/events/vpcLatticeEventV2PathTrailingSlash.json b/packages/parser/tests/events/vpcLatticeEventV2PathTrailingSlash.json deleted file mode 100644 index 9c0005f99b..0000000000 --- a/packages/parser/tests/events/vpcLatticeEventV2PathTrailingSlash.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": "2.0", - "path": "/newpath/", - "method": "GET", - "headers": { - "user_agent": "curl/7.64.1", - "x-forwarded-for": "10.213.229.10", - "host": "test-lambda-service-3908sdf9u3u.dkfjd93.vpc-lattice-svcs.us-east-2.on.aws", - "accept": "*/*" - }, - "queryStringParameters": { - "order-id": "1" - }, - "body": "{\"message\": \"Hello from Lambda!\"}", - "isBase64Encoded": false, - "requestContext": { - "serviceNetworkArn": "arn:aws:vpc-lattice:us-east-2:123456789012:servicenetwork/sn-0bf3f2882e9cc805a", - "serviceArn": "arn:aws:vpc-lattice:us-east-2:123456789012:service/svc-0a40eebed65f8d69c", - "targetGroupArn": "arn:aws:vpc-lattice:us-east-2:123456789012:targetgroup/tg-6d0ecf831eec9f09", - "identity": { - "sourceVpcArn": "arn:aws:ec2:region:123456789012:vpc/vpc-0b8276c84697e7339", - "type": "AWS_IAM", - "principal": "arn:aws:sts::123456789012:assumed-role/example-role/057d00f8b51257ba3c853a0f248943cf", - "sessionName": "057d00f8b51257ba3c853a0f248943cf", - "x509SanDns": "example.com" - }, - "region": "us-east-2", - "timeEpoch": "1696331543569073" - } -} diff --git a/packages/parser/tests/unit/envelope.test.ts b/packages/parser/tests/unit/envelope.test.ts deleted file mode 100644 index 17659a6941..0000000000 --- a/packages/parser/tests/unit/envelope.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { ZodError, z } from 'zod'; -import { Envelope } from '../../src/envelopes/envelope.js'; - -describe('envelope: ', () => { - describe('parseSafe', () => { - it('returns success response when input is object', () => { - const result = Envelope.safeParse( - '{"name": "John"}', - z.object({ name: z.string() }) - ); - expect(result).toEqual({ - success: true, - data: { name: 'John' }, - }); - }); - it('returns success response when input is string', () => { - const result = Envelope.safeParse( - { name: 'John' }, - z.object({ name: z.string() }) - ); - expect(result).toEqual({ - success: true, - data: { name: 'John' }, - }); - }); - it('returns error when input does not match schema', () => { - const result = Envelope.safeParse( - { name: 123 }, - z.object({ name: z.string() }) - ); - expect(result).toEqual({ - success: false, - error: expect.any(Error), - }); - }); - - it('returns error when input is invalid JSON string', () => { - const result = Envelope.safeParse( - '{name: "John"}', - z.object({ name: z.string() }) - ); - expect(result).toEqual({ - success: false, - error: expect.any(Error), - }); - }); - }); - - describe('parse', () => { - it('returns parsed data when input is object', () => { - const result = Envelope.parse( - { name: 'John' }, - z.object({ name: z.string() }) - ); - expect(result).toEqual({ name: 'John' }); - }); - it('returns parsed data when input is string', () => { - const result = Envelope.parse( - '{"name": "John"}', - z.object({ name: z.string() }) - ); - expect(result).toEqual({ name: 'John' }); - }); - it('throw custom error if input is not string or object', () => { - expect(() => Envelope.parse(123, z.object({ name: z.string() }))).toThrow( - 'Invalid data type for envelope. Expected string or object, got number' - ); - }); - it('throws error when input does not match schema', () => { - expect(() => - Envelope.parse({ name: 123 }, z.object({ name: z.string() })) - ).toThrow(); - }); - it('includes the ZodError as the cause of the ParseError', () => { - try { - Envelope.parse('{"name": "John"}', z.object({ name: z.number() })); - } catch (error) { - expect((error as Error).cause).toBeInstanceOf(ZodError); - } - }); - }); -}); diff --git a/packages/parser/tests/unit/envelopes/apigw.test.ts b/packages/parser/tests/unit/envelopes/apigw.test.ts index b298e1eec4..07b554d8cc 100644 --- a/packages/parser/tests/unit/envelopes/apigw.test.ts +++ b/packages/parser/tests/unit/envelopes/apigw.test.ts @@ -4,7 +4,7 @@ import { ApiGatewayEnvelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; import type { APIGatewayProxyEvent } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from '../schema/utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Envelope: API Gateway REST', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/apigwv2.test.ts b/packages/parser/tests/unit/envelopes/apigwv2.test.ts index edf1b9de3b..0f25aa6d31 100644 --- a/packages/parser/tests/unit/envelopes/apigwv2.test.ts +++ b/packages/parser/tests/unit/envelopes/apigwv2.test.ts @@ -4,7 +4,7 @@ import { ApiGatewayV2Envelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; import type { APIGatewayProxyEventV2 } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from '../schema/utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Envelope: API Gateway HTTP', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/cloudwatch.test.ts b/packages/parser/tests/unit/envelopes/cloudwatch.test.ts index 505e95d34a..42d4a9fdc2 100644 --- a/packages/parser/tests/unit/envelopes/cloudwatch.test.ts +++ b/packages/parser/tests/unit/envelopes/cloudwatch.test.ts @@ -5,7 +5,7 @@ import { ParseError } from '../../../src'; import { CloudWatchEnvelope } from '../../../src/envelopes/index.js'; import { DecompressError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; const decompressRecordToJSON = ( data: string diff --git a/packages/parser/tests/unit/envelopes/dynamodb.test.ts b/packages/parser/tests/unit/envelopes/dynamodb.test.ts index 372dfb732d..dcc3c9d0aa 100644 --- a/packages/parser/tests/unit/envelopes/dynamodb.test.ts +++ b/packages/parser/tests/unit/envelopes/dynamodb.test.ts @@ -4,7 +4,7 @@ import { ZodError, z } from 'zod'; import { DynamoDBStreamEnvelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import type { DynamoDBStreamEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Envelope: DynamoDB Stream', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/eventbridge.test.ts b/packages/parser/tests/unit/envelopes/eventbridge.test.ts index c66658ba97..505ab80702 100644 --- a/packages/parser/tests/unit/envelopes/eventbridge.test.ts +++ b/packages/parser/tests/unit/envelopes/eventbridge.test.ts @@ -3,7 +3,7 @@ import { ZodError, z } from 'zod'; import { EventBridgeEnvelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import type { EventBridgeEvent } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from '../schema/utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Envelope: EventBridgeEnvelope', () => { const schema = z.object({ diff --git a/packages/parser/tests/unit/envelopes/kafka.test.ts b/packages/parser/tests/unit/envelopes/kafka.test.ts index 3ac48dada7..f5a4a4a06a 100644 --- a/packages/parser/tests/unit/envelopes/kafka.test.ts +++ b/packages/parser/tests/unit/envelopes/kafka.test.ts @@ -3,7 +3,7 @@ import { ZodError, z } from 'zod'; import { KafkaEnvelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Envelope: Kafka', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts b/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts index b271721381..911f974197 100644 --- a/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts +++ b/packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts @@ -7,7 +7,7 @@ import type { KinesisFireHoseEvent, KinesisFireHoseSqsEvent, } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from '../schema/utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; const encode = (data: unknown) => Buffer.from(String(data)).toString('base64'); diff --git a/packages/parser/tests/unit/envelopes/kinesis.test.ts b/packages/parser/tests/unit/envelopes/kinesis.test.ts index df9d51dd0c..ff2531d01f 100644 --- a/packages/parser/tests/unit/envelopes/kinesis.test.ts +++ b/packages/parser/tests/unit/envelopes/kinesis.test.ts @@ -3,7 +3,7 @@ import { ZodError, z } from 'zod'; import { KinesisEnvelope } from '../../../src/envelopes/index.js'; import { ParseError } from '../../../src/errors.js'; import type { KinesisDataStreamEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; const encode = (data: unknown) => Buffer.from(String(data)).toString('base64'); diff --git a/packages/parser/tests/unit/envelopes/lambda.test.ts b/packages/parser/tests/unit/envelopes/lambda.test.ts index eee9ff28e8..fa8ff25227 100644 --- a/packages/parser/tests/unit/envelopes/lambda.test.ts +++ b/packages/parser/tests/unit/envelopes/lambda.test.ts @@ -4,7 +4,7 @@ import { ParseError } from '../../../src'; import { LambdaFunctionUrlEnvelope } from '../../../src/envelopes/index.js'; import { JSONStringified } from '../../../src/helpers'; import type { LambdaFunctionUrlEvent } from '../../../src/types'; -import { getTestEvent, omit } from '../schema/utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Envelope: Lambda function URL', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/sns.test.ts b/packages/parser/tests/unit/envelopes/sns.test.ts index be3e8c7925..f189e9528c 100644 --- a/packages/parser/tests/unit/envelopes/sns.test.ts +++ b/packages/parser/tests/unit/envelopes/sns.test.ts @@ -4,7 +4,7 @@ import { SnsEnvelope } from '../../../src/envelopes/sns.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; import type { SnsEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Envelope: SnsEnvelope', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/envelopes/snssqs.test.ts b/packages/parser/tests/unit/envelopes/snssqs.test.ts index a7986d2a09..7c15884c7a 100644 --- a/packages/parser/tests/unit/envelopes/snssqs.test.ts +++ b/packages/parser/tests/unit/envelopes/snssqs.test.ts @@ -4,7 +4,7 @@ import { SnsSqsEnvelope } from '../../../src/envelopes/snssqs.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; import type { SqsEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Envelope: SnsSqsEnvelope', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/sqs.test.ts b/packages/parser/tests/unit/envelopes/sqs.test.ts index ea6135df98..ffe4e08bdb 100644 --- a/packages/parser/tests/unit/envelopes/sqs.test.ts +++ b/packages/parser/tests/unit/envelopes/sqs.test.ts @@ -4,7 +4,7 @@ import { SqsEnvelope } from '../../../src/envelopes/sqs.js'; import { ParseError } from '../../../src/errors.js'; import { JSONStringified } from '../../../src/helpers.js'; import type { SqsEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from '../schema/utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Envelope: SqsEnvelope', () => { const schema = z diff --git a/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts b/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts index f9390315ad..33bf6f3350 100644 --- a/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts +++ b/packages/parser/tests/unit/envelopes/vpc-lattice.test.ts @@ -1,98 +1,124 @@ -import { generateMock } from '@anatine/zod-mock'; import { describe, expect, it } from 'vitest'; -import { ZodError } from 'zod'; -import { ParseError } from '../../../src'; +import { ZodError, z } from 'zod'; import { VpcLatticeEnvelope } from '../../../src/envelopes/index.js'; +import { ParseError } from '../../../src/errors.js'; +import { JSONStringified } from '../../../src/helpers.js'; import type { VpcLatticeEvent } from '../../../src/types/index.js'; -import { TestEvents, TestSchema } from '../schema/utils.js'; - -describe('VpcLatticeEnvelope', () => { - describe('parse', () => { - it('should parse VPC Lattice event', () => { - const mock = generateMock(TestSchema); - const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent; - - testEvent.body = JSON.stringify(mock); - - const resp = VpcLatticeEnvelope.parse(testEvent, TestSchema); - - expect(resp).toEqual(mock); - }); - - it('should parse VPC Lattice event with trailing slash', () => { - const mock = generateMock(TestSchema); - const testEvent = - TestEvents.vpcLatticeEventPathTrailingSlash as VpcLatticeEvent; - - testEvent.body = JSON.stringify(mock); - - const resp = VpcLatticeEnvelope.parse(testEvent, TestSchema); - expect(resp).toEqual(mock); - }); +import { getTestEvent, omit } from '../helpers/utils.js'; + +describe('Envelope: VPC Lattice', () => { + const schema = z + .object({ + message: z.string(), + }) + .strict(); + const baseEvent = getTestEvent({ + eventsPath: 'vpc-lattice', + filename: 'base', + }); - it('should throw if event is not a VPC Lattice event', () => { - expect(() => - VpcLatticeEnvelope.parse({ foo: 'bar' }, TestSchema) - ).toThrow(); + describe('Method: parse', () => { + it('throws if the payload does not match the schema', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act & Assess + expect(() => VpcLatticeEnvelope.parse(event, schema)).toThrow( + expect.objectContaining({ + message: expect.stringContaining('Failed to parse VPC Lattice body'), + cause: expect.objectContaining({ + issues: [ + { + code: 'invalid_type', + expected: 'object', + received: 'string', + path: ['body'], + message: 'Expected object, received string', + }, + ], + }), + }) + ); }); - it('should throw if body does not match schema', () => { - const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent; + it('parses a VPC Lattice event with plain text', () => { + // Prepare + const event = structuredClone(baseEvent); - testEvent.body = JSON.stringify({ foo: 'bar' }); + // Act + const result = VpcLatticeEnvelope.parse(event, z.string()); - expect(() => VpcLatticeEnvelope.parse(testEvent, TestSchema)).toThrow(); + // Assess + expect(result).toEqual('{"message": "Hello from Lambda!"}'); }); - }); - - describe('safeParse', () => { - it('should parse VPC Lattice event', () => { - const mock = generateMock(TestSchema); - const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent; - testEvent.body = JSON.stringify(mock); + it('parses an VPC Lattice event with JSON-stringified body', () => { + // Prepare + const event = structuredClone(baseEvent); - const resp = VpcLatticeEnvelope.safeParse(testEvent, TestSchema); + // Act + const result = VpcLatticeEnvelope.parse(event, JSONStringified(schema)); - expect(resp).toEqual({ success: true, data: mock }); + // Assess + expect(result).toStrictEqual({ message: 'Hello from Lambda!' }); }); - it('should parse VPC Lattice event with trailing slash', () => { - const mock = generateMock(TestSchema); - const testEvent = - TestEvents.vpcLatticeEventPathTrailingSlash as VpcLatticeEvent; + it('parses a VPC Lattice event with binary body', () => { + // Prepare + const event = structuredClone(baseEvent); + event.body = 'aGVsbG8gd29ybGQ='; // base64 encoded 'hello world' + event.headers['content-type'] = 'application/octet-stream'; + event.is_base64_encoded = true; - testEvent.body = JSON.stringify(mock); + // Act + const result = VpcLatticeEnvelope.parse(event, z.string()); - const resp = VpcLatticeEnvelope.safeParse(testEvent, TestSchema); - expect(resp).toEqual({ success: true, data: mock }); + // Assess + expect(result).toEqual('aGVsbG8gd29ybGQ='); }); + }); - it('should return error if event is not a VPC Lattice event', () => { - const resp = VpcLatticeEnvelope.safeParse({ foo: 'bar' }, TestSchema); - - expect(resp).toEqual({ - success: false, - error: expect.any(ParseError), - originalEvent: { foo: 'bar' }, + describe('Method: safeParse', () => { + it('parses a VPC Lattice event', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act + const result = VpcLatticeEnvelope.safeParse( + event, + JSONStringified(schema) + ); + + // Assess + expect(result).toEqual({ + success: true, + data: { message: 'Hello from Lambda!' }, }); }); - it('should return error if body does not match schema', () => { - const testEvent = TestEvents.vpcLatticeEvent as VpcLatticeEvent; + it('returns an error if the event is not a valid VPC Lattice event', () => { + // Prepare + const event = omit(['is_base64_encoded'], structuredClone(baseEvent)); - testEvent.body = JSON.stringify({ foo: 'bar' }); + // Act + const result = VpcLatticeEnvelope.safeParse(event, z.string()); - const parseResult = VpcLatticeEnvelope.safeParse(testEvent, TestSchema); - expect(parseResult).toEqual({ + // Assess + expect(result).toEqual({ success: false, - error: expect.any(ParseError), - originalEvent: testEvent, + error: new ParseError('Failed to parse VPC Lattice body', { + cause: new ZodError([ + { + code: 'invalid_type', + expected: 'boolean', + received: 'undefined', + path: ['is_base64_encoded'], + message: 'Required', + }, + ]), + }), + originalEvent: event, }); - - if (!parseResult.success && parseResult.error) { - expect(parseResult.error.cause).toBeInstanceOf(ZodError); - } }); }); }); diff --git a/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts b/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts index 83f656b766..34eed08816 100644 --- a/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts +++ b/packages/parser/tests/unit/envelopes/vpc-latticev2.test.ts @@ -1,98 +1,126 @@ -import { generateMock } from '@anatine/zod-mock'; import { describe, expect, it } from 'vitest'; -import { ZodError } from 'zod'; -import { ParseError } from '../../../src'; +import { ZodError, z } from 'zod'; import { VpcLatticeV2Envelope } from '../../../src/envelopes/index.js'; +import { ParseError } from '../../../src/errors.js'; +import { JSONStringified } from '../../../src/helpers.js'; import type { VpcLatticeEventV2 } from '../../../src/types/index.js'; -import { TestEvents, TestSchema } from '../schema/utils.js'; - -describe('VpcLatticeV2Envelope2', () => { - describe('parse', () => { - it('should parse VPC Lattice event', () => { - const mock = generateMock(TestSchema); - const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2; - - testEvent.body = JSON.stringify(mock); - - const resp = VpcLatticeV2Envelope.parse(testEvent, TestSchema); - - expect(resp).toEqual(mock); - }); - - it('should parse VPC Lattice event with trailing slash', () => { - const mock = generateMock(TestSchema); - const testEvent = - TestEvents.vpcLatticeEventV2PathTrailingSlash as VpcLatticeEventV2; - - testEvent.body = JSON.stringify(mock); - - const resp = VpcLatticeV2Envelope.parse(testEvent, TestSchema); - expect(resp).toEqual(mock); - }); +import { getTestEvent, omit } from '../helpers/utils.js'; + +describe('Envelope: VPC Lattice v2', () => { + const schema = z + .object({ + message: z.string(), + }) + .strict(); + const baseEvent = getTestEvent({ + eventsPath: 'vpc-lattice-v2', + filename: 'base', + }); - it('should throw if event is not a VPC Lattice event', () => { - expect(() => - VpcLatticeV2Envelope.parse({ foo: 'bar' }, TestSchema) - ).toThrow(); + describe('Method: parse', () => { + it('throws if the payload does not match the schema', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act & Assess + expect(() => VpcLatticeV2Envelope.parse(event, schema)).toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Failed to parse VPC Lattice v2 body' + ), + cause: expect.objectContaining({ + issues: [ + { + code: 'invalid_type', + expected: 'object', + received: 'string', + path: ['body'], + message: 'Expected object, received string', + }, + ], + }), + }) + ); }); - it('should throw if body does not match schema', () => { - const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2; + it('parses a VPC Lattice v2 event with plain text', () => { + // Prepare + const event = structuredClone(baseEvent); - testEvent.body = JSON.stringify({ foo: 'bar' }); + // Act + const result = VpcLatticeV2Envelope.parse(event, z.string()); - expect(() => VpcLatticeV2Envelope.parse(testEvent, TestSchema)).toThrow(); + // Assess + expect(result).toEqual('{"message": "Hello from Lambda!"}'); }); - }); - - describe('safeParse', () => { - it('should parse VPC Lattice event', () => { - const mock = generateMock(TestSchema); - const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2; - testEvent.body = JSON.stringify(mock); + it('parses an VPC Lattice v2 event with JSON-stringified body', () => { + // Prepare + const event = structuredClone(baseEvent); - const resp = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema); + // Act + const result = VpcLatticeV2Envelope.parse(event, JSONStringified(schema)); - expect(resp).toEqual({ success: true, data: mock }); + // Assess + expect(result).toStrictEqual({ message: 'Hello from Lambda!' }); }); - it('should parse VPC Lattice event with trailing slash', () => { - const mock = generateMock(TestSchema); - const testEvent = - TestEvents.vpcLatticeEventV2PathTrailingSlash as VpcLatticeEventV2; + it('parses an VPC Lattice v2 event with binary body', () => { + // Prepare + const event = structuredClone(baseEvent); + event.body = 'aGVsbG8gd29ybGQ='; // base64 encoded 'hello world' + event.headers['content-type'] = 'application/octet-stream'; + event.isBase64Encoded = true; - testEvent.body = JSON.stringify(mock); + // Act + const result = VpcLatticeV2Envelope.parse(event, z.string()); - const resp = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema); - expect(resp).toEqual({ success: true, data: mock }); + // Assess + expect(result).toEqual('aGVsbG8gd29ybGQ='); }); + }); - it('should return error if event is not a VPC Lattice event', () => { - const resp = VpcLatticeV2Envelope.safeParse({ foo: 'bar' }, TestSchema); - - expect(resp).toEqual({ - success: false, - error: expect.any(ParseError), - originalEvent: { foo: 'bar' }, + describe('Method: safeParse', () => { + it('parses a VPC Lattice v2 event', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act + const result = VpcLatticeV2Envelope.safeParse( + event, + JSONStringified(schema) + ); + + // Assess + expect(result).toEqual({ + success: true, + data: { message: 'Hello from Lambda!' }, }); }); - it('should return error if body does not match schema', () => { - const testEvent = TestEvents.vpcLatticeV2Event as VpcLatticeEventV2; + it('returns an error if the event is not a valid VPC Lattice v2 event', () => { + // Prepare + const event = omit(['path'], structuredClone(baseEvent)); - testEvent.body = JSON.stringify({ foo: 'bar' }); + // Act + const result = VpcLatticeV2Envelope.safeParse(event, z.string()); - const parseResult = VpcLatticeV2Envelope.safeParse(testEvent, TestSchema); - expect(parseResult).toEqual({ + // Assess + expect(result).toEqual({ success: false, - error: expect.any(ParseError), - originalEvent: testEvent, + error: new ParseError('Failed to parse VPC Lattice v2 body', { + cause: new ZodError([ + { + code: 'invalid_type', + expected: 'string', + received: 'undefined', + path: ['path'], + message: 'Required', + }, + ]), + }), + originalEvent: event, }); - - if (!parseResult.success && parseResult.error) { - expect(parseResult.error.cause).toBeInstanceOf(ZodError); - } }); }); }); diff --git a/packages/parser/tests/unit/helpers.test.ts b/packages/parser/tests/unit/helpers.test.ts index b8c49ac2d6..fd4b73eb14 100644 --- a/packages/parser/tests/unit/helpers.test.ts +++ b/packages/parser/tests/unit/helpers.test.ts @@ -17,7 +17,7 @@ import type { SnsEvent, SqsEvent, } from '../../src/types/schema.js'; -import { getTestEvent } from './schema/utils.js'; +import { getTestEvent } from './helpers/utils.js'; const bodySchema = z.object({ id: z.number(), diff --git a/packages/parser/tests/unit/helpers/utils.ts b/packages/parser/tests/unit/helpers/utils.ts new file mode 100644 index 0000000000..b319b78f99 --- /dev/null +++ b/packages/parser/tests/unit/helpers/utils.ts @@ -0,0 +1,47 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** + * Reads and parses a JSON file from the specified events path and filename, returning the parsed object. + * + * @template T - The expected type of the parsed JSON object. + * @param {Object} params - The parameters for the function. + * @param {string} params.eventsPath - The relative path to the directory containing the event files. + * @param {string} params.filename - The name of the JSON file (without extension) to be read and parsed. + */ +const getTestEvent = >({ + eventsPath, + filename, +}: { + eventsPath: string; + filename: string; +}): T => + JSON.parse( + readFileSync( + join(__dirname, '..', '..', 'events', eventsPath, `${filename}.json`), + 'utf-8' + ) + ) as T; + +/** + * Returns a new object with the specified keys omitted. + * + * @template T - The type of the object to omit keys from. + * @template Keys - The keys to omit from the object. + * @param {readonly Keys[]} keys - The keys to omit from the object. + * @param {T} obj - The object to omit keys from. + */ +const omit = , Keys extends keyof T>( + keys: readonly Keys[], + obj: T +): Omit => { + const result = { ...obj }; + + for (const key of keys) { + delete result[key]; + } + + return result; +}; + +export { getTestEvent, omit }; diff --git a/packages/parser/tests/unit/parser.decorator.test.ts b/packages/parser/tests/unit/parser.decorator.test.ts index 9224ae181f..1b843e8ecb 100644 --- a/packages/parser/tests/unit/parser.decorator.test.ts +++ b/packages/parser/tests/unit/parser.decorator.test.ts @@ -7,7 +7,7 @@ import { ParseError } from '../../src/errors.js'; import { parser } from '../../src/index.js'; import { EventBridgeSchema } from '../../src/schemas/index.js'; import type { EventBridgeEvent, ParsedResult } from '../../src/types/index.js'; -import { getTestEvent } from './schema/utils.js'; +import { getTestEvent } from './helpers/utils.js'; describe('Decorator: parser', () => { const schema = z.object({ diff --git a/packages/parser/tests/unit/parser.middy.test.ts b/packages/parser/tests/unit/parser.middy.test.ts index 50893dc10b..8fd9dcafb2 100644 --- a/packages/parser/tests/unit/parser.middy.test.ts +++ b/packages/parser/tests/unit/parser.middy.test.ts @@ -11,7 +11,7 @@ import type { ParsedResult, SqsEvent, } from '../../src/types/index.js'; -import { getTestEvent } from './schema/utils.js'; +import { getTestEvent } from './helpers/utils.js'; describe('Middleware: parser', () => { const schema = z diff --git a/packages/parser/tests/unit/schema/alb.test.ts b/packages/parser/tests/unit/schema/alb.test.ts index 01cea17b76..8e5a481415 100644 --- a/packages/parser/tests/unit/schema/alb.test.ts +++ b/packages/parser/tests/unit/schema/alb.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { AlbSchema } from '../../../src/schemas/alb.js'; import type { ALBEvent } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: ALB', () => { const eventsPath = 'alb'; diff --git a/packages/parser/tests/unit/schema/apigw.test.ts b/packages/parser/tests/unit/schema/apigw.test.ts index b9bedbcaec..62f92aabec 100644 --- a/packages/parser/tests/unit/schema/apigw.test.ts +++ b/packages/parser/tests/unit/schema/apigw.test.ts @@ -4,7 +4,7 @@ import { APIGatewayRequestAuthorizerEventSchema, APIGatewayTokenAuthorizerEventSchema, } from '../../../src/schemas/index.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: API Gateway REST', () => { const eventsPath = 'apigw-rest'; diff --git a/packages/parser/tests/unit/schema/apigwv2.test.ts b/packages/parser/tests/unit/schema/apigwv2.test.ts index b0dfe1e551..a205470143 100644 --- a/packages/parser/tests/unit/schema/apigwv2.test.ts +++ b/packages/parser/tests/unit/schema/apigwv2.test.ts @@ -6,7 +6,7 @@ import { APIGatewayRequestContextV2Schema, } from '../../../src/schemas/index.js'; import type { APIGatewayProxyEventV2 } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: API Gateway HTTP (v2)', () => { const eventsPath = 'apigw-http'; diff --git a/packages/parser/tests/unit/schema/appsync.test.ts b/packages/parser/tests/unit/schema/appsync.test.ts index 50c09dcc31..1b12d2bf2c 100644 --- a/packages/parser/tests/unit/schema/appsync.test.ts +++ b/packages/parser/tests/unit/schema/appsync.test.ts @@ -4,7 +4,7 @@ import { AppSyncResolverSchema, } from '../../../src/schemas/appsync.js'; import type { AppSyncResolverEvent } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: AppSync Resolver', () => { const eventsPath = 'appsync'; diff --git a/packages/parser/tests/unit/schema/cloudformation-custom-resource.test.ts b/packages/parser/tests/unit/schema/cloudformation-custom-resource.test.ts index 594069b926..f29c04558e 100644 --- a/packages/parser/tests/unit/schema/cloudformation-custom-resource.test.ts +++ b/packages/parser/tests/unit/schema/cloudformation-custom-resource.test.ts @@ -9,7 +9,7 @@ import type { CloudFormationCustomResourceDeleteEvent, CloudFormationCustomResourceUpdateEvent, } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: CloudFormationCustomResource ', () => { const eventsPath = 'cloudformation'; diff --git a/packages/parser/tests/unit/schema/cloudwatch.test.ts b/packages/parser/tests/unit/schema/cloudwatch.test.ts index b3d99d3a18..586d9735a0 100644 --- a/packages/parser/tests/unit/schema/cloudwatch.test.ts +++ b/packages/parser/tests/unit/schema/cloudwatch.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { CloudWatchLogsSchema } from '../../../src/schemas/cloudwatch.js'; import type { CloudWatchLogsEvent } from '../../../src/types/index.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: CloudWatchLogs', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/dynamodb.test.ts b/packages/parser/tests/unit/schema/dynamodb.test.ts index db684da308..62b4ab8d06 100644 --- a/packages/parser/tests/unit/schema/dynamodb.test.ts +++ b/packages/parser/tests/unit/schema/dynamodb.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { DynamoDBStreamSchema } from '../../../src/schemas/dynamodb.js'; import type { DynamoDBStreamEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: DynamoDB', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/eventbridge.test.ts b/packages/parser/tests/unit/schema/eventbridge.test.ts index 8e612ea425..9234b38c1c 100644 --- a/packages/parser/tests/unit/schema/eventbridge.test.ts +++ b/packages/parser/tests/unit/schema/eventbridge.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { EventBridgeSchema } from '../../../src/schemas/eventbridge.js'; import type { EventBridgeEvent } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: EventBridge', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/kafka.test.ts b/packages/parser/tests/unit/schema/kafka.test.ts index ac1c0316f8..845d20fcdf 100644 --- a/packages/parser/tests/unit/schema/kafka.test.ts +++ b/packages/parser/tests/unit/schema/kafka.test.ts @@ -7,7 +7,7 @@ import type { KafkaMskEvent, KafkaSelfManagedEvent, } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: Kafka', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/kinesis.test.ts b/packages/parser/tests/unit/schema/kinesis.test.ts index 4751348a7f..94f7bc944a 100644 --- a/packages/parser/tests/unit/schema/kinesis.test.ts +++ b/packages/parser/tests/unit/schema/kinesis.test.ts @@ -19,7 +19,7 @@ import type { KinesisFirehoseRecord, KinesisFirehoseSqsRecord, } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: Kinesis', () => { const eventsPath = 'kinesis'; diff --git a/packages/parser/tests/unit/schema/lambda.test.ts b/packages/parser/tests/unit/schema/lambda.test.ts index e57e0e4d23..b461627cfa 100644 --- a/packages/parser/tests/unit/schema/lambda.test.ts +++ b/packages/parser/tests/unit/schema/lambda.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { LambdaFunctionUrlSchema } from '../../../src/schemas/lambda.js'; import type { LambdaFunctionUrlEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: LambdaFunctionUrl', () => { const eventsPath = 'lambda'; diff --git a/packages/parser/tests/unit/schema/s3.test.ts b/packages/parser/tests/unit/schema/s3.test.ts index 600aa6a1bf..7afde06cfb 100644 --- a/packages/parser/tests/unit/schema/s3.test.ts +++ b/packages/parser/tests/unit/schema/s3.test.ts @@ -11,7 +11,7 @@ import type { S3ObjectLambdaEvent, S3SqsEventNotification, } from '../../../src/types/schema.js'; -import { getTestEvent, omit } from './utils.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; describe('Schema: S3', () => { const eventsPath = 's3'; diff --git a/packages/parser/tests/unit/schema/ses.test.ts b/packages/parser/tests/unit/schema/ses.test.ts index 3829dd8362..12873e9c9d 100644 --- a/packages/parser/tests/unit/schema/ses.test.ts +++ b/packages/parser/tests/unit/schema/ses.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { SesSchema } from '../../../src/schemas/ses.js'; import type { SesEvent } from '../../../src/types/index.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: SES', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/sns.test.ts b/packages/parser/tests/unit/schema/sns.test.ts index 808344e5fa..c4a7d283aa 100644 --- a/packages/parser/tests/unit/schema/sns.test.ts +++ b/packages/parser/tests/unit/schema/sns.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { SnsSchema } from '../../../src/schemas/sns.js'; import type { SnsEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: SNS', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/sqs.test.ts b/packages/parser/tests/unit/schema/sqs.test.ts index 4fcba4c30f..b9d4b07dee 100644 --- a/packages/parser/tests/unit/schema/sqs.test.ts +++ b/packages/parser/tests/unit/schema/sqs.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { SqsSchema } from '../../../src/schemas/sqs.js'; import type { SqsEvent } from '../../../src/types/schema.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent } from '../helpers/utils.js'; describe('Schema: SQS', () => { const baseEvent = getTestEvent({ diff --git a/packages/parser/tests/unit/schema/utils.ts b/packages/parser/tests/unit/schema/utils.ts deleted file mode 100644 index 8ca72681e1..0000000000 --- a/packages/parser/tests/unit/schema/utils.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { z } from 'zod'; - -export const TestSchema = z.object({ - name: z.string(), - age: z.number().min(18).max(99), -}); - -const filenames = [ - 'albEvent', - 'albEventPathTrailingSlash', - 'albMultiValueHeadersEvent', - 'kinesisFirehoseKinesisEvent', - 'kinesisFirehosePutEvent', - 'kinesisFirehoseSQSEvent', - 'kinesisStreamCloudWatchLogsEvent', - 'kinesisStreamEvent', - 'kinesisStreamEventOneRecord', - 'vpcLatticeEvent', - 'vpcLatticeEventPathTrailingSlash', - 'vpcLatticeEventV2PathTrailingSlash', - 'vpcLatticeV2Event', -] as const; - -type TestEvents = { [K in (typeof filenames)[number]]: unknown }; -const loadFileContent = (filename: string): string => - readFileSync( - join(__dirname, '..', '..', 'events', `${filename}.json`), - 'utf-8' - ); - -const createTestEvents = (fileList: readonly string[]): TestEvents => { - const testEvents: Partial = {}; - - for (const filename of fileList) { - Object.defineProperty(testEvents, filename, { - get: () => JSON.parse(loadFileContent(filename)), - }); - } - - return testEvents as TestEvents; -}; - -export const TestEvents = createTestEvents(filenames); - -/** - * Reads and parses a JSON file from the specified events path and filename, returning the parsed object. - * - * @template T - The expected type of the parsed JSON object. - * @param {Object} params - The parameters for the function. - * @param {string} params.eventsPath - The relative path to the directory containing the event files. - * @param {string} params.filename - The name of the JSON file (without extension) to be read and parsed. - */ -export const getTestEvent = >({ - eventsPath, - filename, -}: { - eventsPath: string; - filename: string; -}): T => - JSON.parse( - readFileSync( - join(__dirname, '..', '..', 'events', eventsPath, `${filename}.json`), - 'utf-8' - ) - ) as T; - -export function omit, Keys extends keyof T>( - keys: readonly Keys[], - obj: T -): Omit { - const result = { ...obj }; - - for (const key of keys) { - delete result[key]; - } - - return result; -} diff --git a/packages/parser/tests/unit/schema/vpc-lattice.test.ts b/packages/parser/tests/unit/schema/vpc-lattice.test.ts index 472e6514e6..cb1f84c43c 100644 --- a/packages/parser/tests/unit/schema/vpc-lattice.test.ts +++ b/packages/parser/tests/unit/schema/vpc-lattice.test.ts @@ -1,17 +1,30 @@ import { describe, expect, it } from 'vitest'; -import { VpcLatticeSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { VpcLatticeSchema } from '../../../src/schemas/vpc-lattice.js'; +import type { VpcLatticeEvent } from '../../../src/types/schema.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; -describe('VPC Lattice ', () => { - it('should parse vpc lattice event', () => { - const vpcLatticeEvent = TestEvents.vpcLatticeEvent; - expect(VpcLatticeSchema.parse(vpcLatticeEvent)).toEqual(vpcLatticeEvent); +describe('Schema: VPC Lattice', () => { + const baseEvent = getTestEvent({ + eventsPath: 'vpc-lattice', + filename: 'base', }); - it('should parse vpc lattice path trailing slash event', () => { - const vpcLatticeEventPathTrailingSlash = - TestEvents.vpcLatticeEventPathTrailingSlash; - expect(VpcLatticeSchema.parse(vpcLatticeEventPathTrailingSlash)).toEqual( - vpcLatticeEventPathTrailingSlash - ); + + it('throws when the event is invalid', () => { + // Prepare + const event = omit(['query_string_parameters'], structuredClone(baseEvent)); + + // Act & Assess + expect(() => VpcLatticeSchema.parse(event)).toThrow(); + }); + + it('parses a VPC Lattice event', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act + const result = VpcLatticeSchema.parse(event); + + // Assess + expect(result).toStrictEqual(event); }); }); diff --git a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts index 5c731a3a70..d13ee3e454 100644 --- a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts +++ b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts @@ -1,18 +1,30 @@ import { describe, expect, it } from 'vitest'; -import { VpcLatticeV2Schema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { VpcLatticeV2Schema } from '../../../src/schemas/vpc-latticev2.js'; +import type { VpcLatticeEventV2 } from '../../../src/types/schema.js'; +import { getTestEvent, omit } from '../helpers/utils.js'; -describe('VpcLatticeV2 ', () => { - it('should parse VpcLatticeV2 event', () => { - const vpcLatticeV2Event = TestEvents.vpcLatticeV2Event; - const parsed = VpcLatticeV2Schema.parse(vpcLatticeV2Event); - expect(parsed).toEqual(vpcLatticeV2Event); +describe('Schema: VPC Lattice v2', () => { + const baseEvent = getTestEvent({ + eventsPath: 'vpc-lattice-v2', + filename: 'base', }); - it('should parse VpcLatticeV2PathTrailingSlash event', () => { - const vpcLatticeEventV2PathTrailingSlash = - TestEvents.vpcLatticeEventV2PathTrailingSlash; - const parsed = VpcLatticeV2Schema.parse(vpcLatticeEventV2PathTrailingSlash); - expect(parsed).toEqual(vpcLatticeEventV2PathTrailingSlash); + it('throws when the event is invalid', () => { + // Prepare + const event = omit(['version', 'path'], structuredClone(baseEvent)); + + // Act & Assess + expect(() => VpcLatticeV2Schema.parse(event)).toThrow(); + }); + + it('parses a VPC Lattice v2 event', () => { + // Prepare + const event = structuredClone(baseEvent); + + // Act + const result = VpcLatticeV2Schema.parse(event); + + // Assess + expect(result).toStrictEqual(event); }); });