From ccffbf2c646ad3c506c028e51ccfe055a2dac9b7 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Fri, 25 Oct 2024 20:19:19 +0200 Subject: [PATCH 1/5] add aws region to kinesis event --- packages/parser/src/schemas/kinesis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/parser/src/schemas/kinesis.ts b/packages/parser/src/schemas/kinesis.ts index 558edd9925..39f03a71ae 100644 --- a/packages/parser/src/schemas/kinesis.ts +++ b/packages/parser/src/schemas/kinesis.ts @@ -31,6 +31,7 @@ const KinesisDataStreamRecord = z.object({ eventVersion: z.string(), eventID: z.string(), eventName: z.literal('aws:kinesis:record'), + awsRegion: z.string(), invokeIdentityArn: z.string(), eventSourceARN: z.string(), kinesis: KinesisDataStreamRecordPayload, From 22404fc6dd840d2437f3f9206c719bc3788c3f74 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Fri, 25 Oct 2024 22:47:43 +0200 Subject: [PATCH 2/5] add tests to verify missing properties in the schema --- packages/parser/tests/unit/schema/alb.test.ts | 17 ++++- .../parser/tests/unit/schema/apigw.test.ts | 53 ++++++++++++++- .../parser/tests/unit/schema/apigwv2.test.ts | 32 ++++++++- .../cloudformation-custom-resource.test.ts | 43 +++++++++++- .../parser/tests/unit/schema/dynamodb.test.ts | 8 ++- .../tests/unit/schema/eventbridge.test.ts | 9 ++- .../parser/tests/unit/schema/kafka.test.ts | 20 +++++- .../parser/tests/unit/schema/kinesis.test.ts | 16 ++++- .../parser/tests/unit/schema/lambda.test.ts | 12 +++- packages/parser/tests/unit/schema/s3.test.ts | 40 ++++++++++- packages/parser/tests/unit/schema/ses.test.ts | 8 ++- packages/parser/tests/unit/schema/sns.test.ts | 16 ++++- packages/parser/tests/unit/schema/sqs.test.ts | 8 ++- packages/parser/tests/unit/schema/utils.ts | 68 +++++++++++++++++++ .../tests/unit/schema/vpc-lattice.test.ts | 8 ++- .../tests/unit/schema/vpc-latticev2.test.ts | 10 ++- 16 files changed, 351 insertions(+), 17 deletions(-) diff --git a/packages/parser/tests/unit/schema/alb.test.ts b/packages/parser/tests/unit/schema/alb.test.ts index 1d9a21c67e..aa0d7bda79 100644 --- a/packages/parser/tests/unit/schema/alb.test.ts +++ b/packages/parser/tests/unit/schema/alb.test.ts @@ -4,7 +4,7 @@ * @group unit/parser/schema/ */ import { AlbMultiValueHeadersSchema, AlbSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('ALB ', () => { it('should parse alb event', () => { @@ -24,4 +24,19 @@ describe('ALB ', () => { albMultiValueHeadersEvent ); }); + + describe('should detect missing properties in schema for ', () => { + it('alb event', () => { + const albEvent = TestEvents.albEvent; + const strictSchema = AlbSchema.strict(); + expect(() => strictSchema.parse(albEvent)).not.toThrow(); + }); + it('alb event with multi value headers', () => { + const albMultiValueHeadersEvent = TestEvents.albMultiValueHeadersEvent; + const strictSchema = makeSchemaStrictForTesting( + AlbMultiValueHeadersSchema + ); + expect(() => strictSchema.parse(albMultiValueHeadersEvent)).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/apigw.test.ts b/packages/parser/tests/unit/schema/apigw.test.ts index 64a3ea965d..542bc0e50e 100644 --- a/packages/parser/tests/unit/schema/apigw.test.ts +++ b/packages/parser/tests/unit/schema/apigw.test.ts @@ -8,7 +8,7 @@ import { APIGatewayRequestAuthorizerEventSchema, APIGatewayTokenAuthorizerEventSchema, } from '../../../src/schemas/index.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent, makeSchemaStrictForTesting } from './utils.js'; describe('API Gateway REST Schemas', () => { const eventsPath = 'apigw-rest'; @@ -150,4 +150,55 @@ describe('API Gateway REST Schemas', () => { expect(parsedEvent).toEqual(event); }); }); + + describe('should detect missing properties in schema for ', () => { + it.each([ + 'console-test-ui', + 'iam-auth', + 'jwt-authorizer-auth', + 'lambda-authorizer-auth', + 'no-auth', + 'websocket', + ])(' %p example event', (filename) => { + // Prepare + const event = getTestEvent({ eventsPath, filename: filename }); + + const strictSchema = makeSchemaStrictForTesting( + APIGatewayProxyEventSchema + ); + + // Act & Assess + expect(() => strictSchema.parse(event)).not.toThrow(); + }); + + it('authorizer-request example event', () => { + // Prepare + const event = getTestEvent({ + eventsPath, + filename: 'authorizer-request', + }); + + const strictSchema = makeSchemaStrictForTesting( + APIGatewayRequestAuthorizerEventSchema + ); + + // Act & Assess + expect(() => strictSchema.parse(event)).not.toThrow(); + }); + + it('authorizer-token example event', () => { + // Prepare + const event = getTestEvent({ + eventsPath, + filename: 'authorizer-token', + }); + + const strictSchema = makeSchemaStrictForTesting( + APIGatewayTokenAuthorizerEventSchema + ); + + // Act & Assess + expect(() => strictSchema.parse(event)).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/apigwv2.test.ts b/packages/parser/tests/unit/schema/apigwv2.test.ts index 15716ab30f..55c44c417d 100644 --- a/packages/parser/tests/unit/schema/apigwv2.test.ts +++ b/packages/parser/tests/unit/schema/apigwv2.test.ts @@ -7,7 +7,7 @@ import { APIGatewayProxyEventV2Schema, APIGatewayRequestAuthorizerEventV2Schema, } from '../../../src/schemas/index.js'; -import { getTestEvent } from './utils.js'; +import { getTestEvent, makeSchemaStrictForTesting } from './utils.js'; describe('API Gateway HTTP (v2) Schemas', () => { const eventsPath = 'apigw-http'; @@ -100,4 +100,34 @@ describe('API Gateway HTTP (v2) Schemas', () => { expect(parsedEvent).toEqual(event); }); }); + + describe('should detect missing properties in schema for ', () => { + it.each([ + 'iam-auth', + 'jwt-authorizer-auth', + 'lambda-authorizer-auth', + 'no-auth', + ])('event %s', (filename) => { + // Prepare + const event = getTestEvent({ eventsPath, filename }); + const strictSchema = makeSchemaStrictForTesting( + APIGatewayProxyEventV2Schema + ); + // Act & Assess + expect(() => strictSchema.parse(event)).not.toThrow(); + }); + + it('authorizer-request event', () => { + // Prepare + const event = getTestEvent({ + eventsPath, + filename: 'authorizer-request', + }); + const strictSchema = makeSchemaStrictForTesting( + APIGatewayRequestAuthorizerEventV2Schema + ); + // Act & Assess + expect(() => strictSchema.parse(event)).not.toThrow(); + }); + }); }); 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 e00530f3bc..7bfbca007f 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 { CloudFormationCustomResourceDeleteSchema, CloudFormationCustomResourceUpdateSchema, } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('CloudFormationCustomResource ', () => { it('should parse create event', () => { @@ -42,4 +42,45 @@ describe('CloudFormationCustomResource ', () => { ) ).toEqual(cloudFormationCustomResourceDeleteEvent); }); + + describe('should detect missing properties in schema for ', () => { + it('CloudFormationCustomResourceCreateSchema', () => { + const cloudFormationCustomResourceCreateEvent = + TestEvents.cloudFormationCustomResourceCreateEvent; + + const strictSchema = makeSchemaStrictForTesting( + CloudFormationCustomResourceCreateSchema + ); + + expect(() => + strictSchema.parse(cloudFormationCustomResourceCreateEvent) + ).not.toThrow(); + }); + + it('CloudFormationCustomResourceUpdateSchema', () => { + const cloudFormationCustomResourceUpdateEvent = + TestEvents.cloudFormationCustomResourceUpdateEvent; + + const strictSchema = makeSchemaStrictForTesting( + CloudFormationCustomResourceUpdateSchema + ); + + expect(() => + strictSchema.parse(cloudFormationCustomResourceUpdateEvent) + ).not.toThrow(); + }); + + it('CloudFormationCustomResourceDeleteSchema', () => { + const cloudFormationCustomResourceDeleteEvent = + TestEvents.cloudFormationCustomResourceDeleteEvent; + + const strictSchema = makeSchemaStrictForTesting( + CloudFormationCustomResourceDeleteSchema + ); + + expect(() => + strictSchema.parse(cloudFormationCustomResourceDeleteEvent) + ).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/dynamodb.test.ts b/packages/parser/tests/unit/schema/dynamodb.test.ts index f0d90fbc5b..193a04b303 100644 --- a/packages/parser/tests/unit/schema/dynamodb.test.ts +++ b/packages/parser/tests/unit/schema/dynamodb.test.ts @@ -5,7 +5,7 @@ */ import { DynamoDBStreamSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('DynamoDB ', () => { const dynamoStreamEvent = TestEvents.dynamoStreamEvent; @@ -14,4 +14,10 @@ describe('DynamoDB ', () => { dynamoStreamEvent ); }); + + it('should detect missing properties in schema', () => { + const strictSchema = makeSchemaStrictForTesting(DynamoDBStreamSchema); + + expect(() => strictSchema.parse(dynamoStreamEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/eventbridge.test.ts b/packages/parser/tests/unit/schema/eventbridge.test.ts index 4423318874..cd7f4088d0 100644 --- a/packages/parser/tests/unit/schema/eventbridge.test.ts +++ b/packages/parser/tests/unit/schema/eventbridge.test.ts @@ -5,7 +5,7 @@ */ import { EventBridgeSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('EventBridge ', () => { it('should parse eventbridge event', () => { @@ -13,4 +13,11 @@ describe('EventBridge ', () => { expect(EventBridgeSchema.parse(eventBridgeEvent)).toEqual(eventBridgeEvent); }); + + it('should detect missing properties in schema', () => { + const eventBridgeEvent = TestEvents.eventBridgeEvent; + const strictSchema = makeSchemaStrictForTesting(EventBridgeSchema); + + expect(() => strictSchema.parse(eventBridgeEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/kafka.test.ts b/packages/parser/tests/unit/schema/kafka.test.ts index 6292f3001f..a1b0d8c43c 100644 --- a/packages/parser/tests/unit/schema/kafka.test.ts +++ b/packages/parser/tests/unit/schema/kafka.test.ts @@ -11,7 +11,7 @@ import { } from '../../../src/schemas/'; import type { KafkaSelfManagedEvent } from '../../../src/types'; import type { KafkaRecord } from '../../../src/types/schema'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('Kafka ', () => { const expectedTestEvent = { @@ -71,4 +71,22 @@ describe('Kafka ', () => { ); expect(parsedRecord.topic).toEqual('mytopic'); }); + + describe('should detect missing properties in schema for', () => { + it('KafkaMskEventSchema', () => { + const kafkaEventMsk = TestEvents.kafkaEventMsk; + + const strictSchema = makeSchemaStrictForTesting(KafkaMskEventSchema); + expect(() => strictSchema.parse(kafkaEventMsk)).not.toThrow(); + }); + + it('KafkaSelfManagedEventSchema', () => { + const kafkaEventSelfManaged = TestEvents.kafkaEventSelfManaged; + + const strictSchema = makeSchemaStrictForTesting( + KafkaSelfManagedEventSchema + ); + expect(() => strictSchema.parse(kafkaEventSelfManaged)).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/kinesis.test.ts b/packages/parser/tests/unit/schema/kinesis.test.ts index e107d1ae2f..0cb34c41d4 100644 --- a/packages/parser/tests/unit/schema/kinesis.test.ts +++ b/packages/parser/tests/unit/schema/kinesis.test.ts @@ -21,7 +21,7 @@ import type { KinesisFirehoseRecord, KinesisFirehoseSqsRecord, } from '../../../src/types/schema'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('Kinesis ', () => { it('should parse kinesis event', () => { @@ -30,6 +30,7 @@ describe('Kinesis ', () => { expect(parsed.Records[0].kinesis.data).toEqual('Hello, this is a test.'); }); + it('should parse single kinesis record', () => { const kinesisStreamEventOneRecord = TestEvents.kinesisStreamEventOneRecord; const parsed = KinesisDataStreamSchema.parse(kinesisStreamEventOneRecord); @@ -39,11 +40,13 @@ describe('Kinesis ', () => { username: 'test', }); }); + it('should parse Firehose event', () => { const kinesisFirehoseKinesisEvent = TestEvents.kinesisFirehoseKinesisEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehoseKinesisEvent); expect(parsed.records[0].data).toEqual('Hello World'); }); + it('should parse Kinesis Firehose PutEvents event', () => { const kinesisFirehosePutEvent = TestEvents.kinesisFirehosePutEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehosePutEvent); @@ -51,6 +54,7 @@ describe('Kinesis ', () => { Hello: 'World', }); }); + it('should parse Firehose event with SQS event', () => { const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent; const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); @@ -59,6 +63,7 @@ describe('Kinesis ', () => { body: 'Test message.', }); }); + it('should parse Kinesis event with CloudWatch event', () => { const kinesisStreamCloudWatchLogsEvent = TestEvents.kinesisStreamCloudWatchLogsEvent; @@ -73,6 +78,7 @@ describe('Kinesis ', () => { logStream: '2022/11/10/[$LATEST]26b6a45d574f442ea28438923cbf7bf7', }); }); + it('should return original value if cannot parse KinesisFirehoseSqsRecord', () => { const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent as { records: { data: string }[]; @@ -81,13 +87,13 @@ describe('Kinesis ', () => { const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); expect(parsed.records[0].data).toEqual('not a valid json'); }); + it('should parse a kinesis record from a kinesis event', () => { const kinesisStreamEvent: KinesisDataStreamEvent = TestEvents.kinesisStreamEvent as KinesisDataStreamEvent; const parsedRecord = KinesisDataStreamRecord.parse( kinesisStreamEvent.Records[0] ); - expect(parsedRecord.eventName).toEqual('aws:kinesis:record'); }); @@ -110,4 +116,10 @@ describe('Kinesis ', () => { '49640912821178817833517986466168945147170627572855734274000000' ); }); + + it('should catch any unknown fields in the example event', () => { + const kinesisStreamEvent = TestEvents.kinesisStreamEvent; + const strictSchema = makeSchemaStrictForTesting(KinesisDataStreamSchema); + expect(() => strictSchema.parse(kinesisStreamEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/lambda.test.ts b/packages/parser/tests/unit/schema/lambda.test.ts index e9b0a3449b..5047fbf112 100644 --- a/packages/parser/tests/unit/schema/lambda.test.ts +++ b/packages/parser/tests/unit/schema/lambda.test.ts @@ -5,11 +5,11 @@ */ import { LambdaFunctionUrlSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('Lambda ', () => { it('should parse lambda event', () => { - const lambdaFunctionUrlEvent = TestEvents.apiGatewayProxyV2Event; + const lambdaFunctionUrlEvent = TestEvents.lambdaFunctionUrlEvent; expect(LambdaFunctionUrlSchema.parse(lambdaFunctionUrlEvent)).toEqual( lambdaFunctionUrlEvent @@ -21,4 +21,12 @@ describe('Lambda ', () => { expect(LambdaFunctionUrlSchema.parse(urlIAMEvent)).toEqual(urlIAMEvent); }); + + it('should detect missing properties in schema for lambda event', () => { + const lambdaFunctionUrlEvent = TestEvents.lambdaFunctionUrlEvent; + + const strictSchema = makeSchemaStrictForTesting(LambdaFunctionUrlSchema); + + expect(() => strictSchema.parse(lambdaFunctionUrlEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/s3.test.ts b/packages/parser/tests/unit/schema/s3.test.ts index 8798155472..88613ed69e 100644 --- a/packages/parser/tests/unit/schema/s3.test.ts +++ b/packages/parser/tests/unit/schema/s3.test.ts @@ -10,7 +10,7 @@ import { S3Schema, S3SqsEventNotificationSchema, } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('S3 ', () => { it('should parse s3 event', () => { @@ -103,4 +103,42 @@ describe('S3 ', () => { parsed.userIdentity?.sessionContext?.attributes.mfaAuthenticated ).toEqual(false); }); + + describe('should detect missing properties in schema for ', () => { + it('s3 event', () => { + const s3Event = TestEvents.s3Event; + const strictSchema = makeSchemaStrictForTesting(S3Schema); + expect(strictSchema.parse(s3Event)).toEqual(s3Event); + }); + + it('s3 event bridge notification event created', () => { + const s3EventBridgeNotificationObjectCreatedEvent = + TestEvents.s3EventBridgeNotificationObjectCreatedEvent; + const strictSchema = makeSchemaStrictForTesting( + S3EventNotificationEventBridgeSchema + ); + expect(() => + strictSchema.parse(s3EventBridgeNotificationObjectCreatedEvent) + ).not.toThrow(); + }); + + it('s3 event bridge notification event detelted', () => { + const s3EventBridgeNotificationObjectDeletedEvent = + TestEvents.s3EventBridgeNotificationObjectDeletedEvent; + const strictSchema = makeSchemaStrictForTesting( + S3EventNotificationEventBridgeSchema + ); + expect(() => + strictSchema.parse(s3EventBridgeNotificationObjectDeletedEvent) + ).not.toThrow(); + }); + + it('s3 sqs notification event', () => { + const s3SqsEvent = TestEvents.s3SqsEvent; + const strictSchema = makeSchemaStrictForTesting( + S3SqsEventNotificationSchema + ); + expect(() => strictSchema.parse(s3SqsEvent)).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/ses.test.ts b/packages/parser/tests/unit/schema/ses.test.ts index 7c27a83d97..60ba5de7c1 100644 --- a/packages/parser/tests/unit/schema/ses.test.ts +++ b/packages/parser/tests/unit/schema/ses.test.ts @@ -7,7 +7,7 @@ import { SesRecordSchema, SesSchema } from '../../../src/schemas/'; import type { SesEvent } from '../../../src/types'; import type { SesRecord } from '../../../src/types/schema'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('SES', () => { it('should parse ses event', () => { @@ -21,4 +21,10 @@ describe('SES', () => { expect(parsed.ses.mail.source).toEqual('janedoe@example.com'); }); + + it('should detect missing properties in schema for ses event', () => { + const sesEvent = TestEvents.sesEvent; + const strictSchema = makeSchemaStrictForTesting(SesSchema); + expect(() => strictSchema.parse(sesEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/sns.test.ts b/packages/parser/tests/unit/schema/sns.test.ts index 7ddb7f1132..26edc66a36 100644 --- a/packages/parser/tests/unit/schema/sns.test.ts +++ b/packages/parser/tests/unit/schema/sns.test.ts @@ -16,7 +16,7 @@ import type { SnsRecord, SnsSqsNotification, } from '../../../src/types/schema'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('SNS', () => { it('should parse sns event', () => { @@ -45,4 +45,18 @@ describe('SNS', () => { 'arn:aws:sns:eu-west-1:231436140809:powertools265' ); }); + + describe('should detect missing properties in schema for ', () => { + it('sns event', () => { + const snsEvent = TestEvents.snsEvent; + const strictSchema = makeSchemaStrictForTesting(SnsSchema); + expect(() => strictSchema.parse(snsEvent)).not.toThrow(); + }); + + it('sns record', () => { + const snsEvent: SnsEvent = TestEvents.snsEvent as SnsEvent; + const strictSchema = makeSchemaStrictForTesting(SnsRecordSchema); + expect(() => strictSchema.parse(snsEvent.Records[0])).not.toThrow(); + }); + }); }); diff --git a/packages/parser/tests/unit/schema/sqs.test.ts b/packages/parser/tests/unit/schema/sqs.test.ts index 9514338395..620ccf38d8 100644 --- a/packages/parser/tests/unit/schema/sqs.test.ts +++ b/packages/parser/tests/unit/schema/sqs.test.ts @@ -7,7 +7,7 @@ import { SqsRecordSchema, SqsSchema } from '../../../src/schemas/'; import type { SqsEvent } from '../../../src/types'; import type { SqsRecord } from '../../../src/types/schema'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('SQS', () => { it('should parse sqs event', () => { @@ -19,4 +19,10 @@ describe('SQS', () => { const parsed: SqsRecord = SqsRecordSchema.parse(sqsEvent.Records[0]); expect(parsed.body).toEqual('Test message.'); }); + + it('should detect missing properties in schema for sqs event', () => { + const sqsEvent = TestEvents.sqsEvent; + const strictSchema = makeSchemaStrictForTesting(SqsSchema); + expect(() => strictSchema.parse(sqsEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/utils.ts b/packages/parser/tests/unit/schema/utils.ts index cb0647aed7..ce4a832be5 100644 --- a/packages/parser/tests/unit/schema/utils.ts +++ b/packages/parser/tests/unit/schema/utils.ts @@ -131,3 +131,71 @@ export const getTestEvent = >({ 'utf-8' ) ) as T; + +type ZodShape = { [k: string]: z.ZodTypeAny }; + +/** + * Creates a strict version of a schema for testing purposes without modifying the original + */ +export const makeSchemaStrictForTesting = ( + schema: T +): T => { + if (schema instanceof z.ZodObject) { + const shape = schema._def.shape() as ZodShape; + const newShape = Object.fromEntries( + Object.entries(shape).map(([key, value]) => [ + key, + makeSchemaStrictForTesting(value), + ]) + ); + + return z.object(newShape).strict() as unknown as T; + } + + if (schema instanceof z.ZodArray) { + const elementSchema = schema.element; + return z.array(makeSchemaStrictForTesting(elementSchema)) as unknown as T; + } + + if (schema instanceof z.ZodUnion) { + const options = schema._def.options as readonly [ + z.ZodTypeAny, + z.ZodTypeAny, + ...z.ZodTypeAny[], + ]; + const newOptions = options.map((option) => + makeSchemaStrictForTesting(option) + ); + // Ensure we have at least two elements for the union + return z.union([ + newOptions[0], + newOptions[1], + ...newOptions.slice(2), + ]) as unknown as T; + } + + if (schema instanceof z.ZodRecord) { + const keySchema = schema.keySchema; + const valueSchema = schema.valueSchema; + return z.record( + makeSchemaStrictForTesting(keySchema), + makeSchemaStrictForTesting(valueSchema) + ) as unknown as T; + } + + // Handle extended schemas + if (schema instanceof z.ZodObject && schema._def.shape instanceof Function) { + const shape = schema._def.shape() as ZodShape; + const newShape = Object.fromEntries( + Object.entries(shape).map(([key, value]) => [ + key, + makeSchemaStrictForTesting(value), + ]) + ); + + return z.object(newShape).strict() as unknown as T; + } + + // Return other types as-is + return schema; +}; diff --git a/packages/parser/tests/unit/schema/vpc-lattice.test.ts b/packages/parser/tests/unit/schema/vpc-lattice.test.ts index 643714fb47..7f6cef9d81 100644 --- a/packages/parser/tests/unit/schema/vpc-lattice.test.ts +++ b/packages/parser/tests/unit/schema/vpc-lattice.test.ts @@ -5,7 +5,7 @@ */ import { VpcLatticeSchema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('VPC Lattice ', () => { it('should parse vpc lattice event', () => { @@ -19,4 +19,10 @@ describe('VPC Lattice ', () => { vpcLatticeEventPathTrailingSlash ); }); + + it('should detect missing properties in schema for vpc lattice event', () => { + const vpcLatticeEvent = TestEvents.vpcLatticeEvent; + const strictSchema = makeSchemaStrictForTesting(VpcLatticeSchema); + expect(() => strictSchema.parse(vpcLatticeEvent)).not.toThrow(); + }); }); diff --git a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts index 5e1b6c7343..243ac640b4 100644 --- a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts +++ b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts @@ -5,7 +5,7 @@ */ import { VpcLatticeV2Schema } from '../../../src/schemas/'; -import { TestEvents } from './utils.js'; +import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; describe('VpcLatticeV2 ', () => { it('should parse VpcLatticeV2 event', () => { @@ -20,4 +20,12 @@ describe('VpcLatticeV2 ', () => { const parsed = VpcLatticeV2Schema.parse(vpcLatticeEventV2PathTrailingSlash); expect(parsed).toEqual(vpcLatticeEventV2PathTrailingSlash); }); + + it('should detect missing properties in schema for vpc lattice event v2', () => { + const vpcLatticeV2Event = TestEvents.vpcLatticeV2Event; + + const strictSchema = makeSchemaStrictForTesting(VpcLatticeV2Schema); + + expect(() => strictSchema.parse(vpcLatticeV2Event)).not.toThrow(); + }); }); From c7fba14ae848f2566518a6623ed5b93c99544eb0 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Mon, 28 Oct 2024 11:37:40 +0100 Subject: [PATCH 3/5] Revert "add tests to verify missing properties in the schema" This reverts commit 22404fc6dd840d2437f3f9206c719bc3788c3f74. --- packages/parser/tests/unit/schema/alb.test.ts | 17 +---- .../parser/tests/unit/schema/apigw.test.ts | 53 +-------------- .../parser/tests/unit/schema/apigwv2.test.ts | 32 +-------- .../cloudformation-custom-resource.test.ts | 43 +----------- .../parser/tests/unit/schema/dynamodb.test.ts | 8 +-- .../tests/unit/schema/eventbridge.test.ts | 9 +-- .../parser/tests/unit/schema/kafka.test.ts | 20 +----- .../parser/tests/unit/schema/kinesis.test.ts | 16 +---- .../parser/tests/unit/schema/lambda.test.ts | 12 +--- packages/parser/tests/unit/schema/s3.test.ts | 40 +---------- packages/parser/tests/unit/schema/ses.test.ts | 8 +-- packages/parser/tests/unit/schema/sns.test.ts | 16 +---- packages/parser/tests/unit/schema/sqs.test.ts | 8 +-- packages/parser/tests/unit/schema/utils.ts | 68 ------------------- .../tests/unit/schema/vpc-lattice.test.ts | 8 +-- .../tests/unit/schema/vpc-latticev2.test.ts | 10 +-- 16 files changed, 17 insertions(+), 351 deletions(-) diff --git a/packages/parser/tests/unit/schema/alb.test.ts b/packages/parser/tests/unit/schema/alb.test.ts index aa0d7bda79..1d9a21c67e 100644 --- a/packages/parser/tests/unit/schema/alb.test.ts +++ b/packages/parser/tests/unit/schema/alb.test.ts @@ -4,7 +4,7 @@ * @group unit/parser/schema/ */ import { AlbMultiValueHeadersSchema, AlbSchema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('ALB ', () => { it('should parse alb event', () => { @@ -24,19 +24,4 @@ describe('ALB ', () => { albMultiValueHeadersEvent ); }); - - describe('should detect missing properties in schema for ', () => { - it('alb event', () => { - const albEvent = TestEvents.albEvent; - const strictSchema = AlbSchema.strict(); - expect(() => strictSchema.parse(albEvent)).not.toThrow(); - }); - it('alb event with multi value headers', () => { - const albMultiValueHeadersEvent = TestEvents.albMultiValueHeadersEvent; - const strictSchema = makeSchemaStrictForTesting( - AlbMultiValueHeadersSchema - ); - expect(() => strictSchema.parse(albMultiValueHeadersEvent)).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/apigw.test.ts b/packages/parser/tests/unit/schema/apigw.test.ts index 542bc0e50e..64a3ea965d 100644 --- a/packages/parser/tests/unit/schema/apigw.test.ts +++ b/packages/parser/tests/unit/schema/apigw.test.ts @@ -8,7 +8,7 @@ import { APIGatewayRequestAuthorizerEventSchema, APIGatewayTokenAuthorizerEventSchema, } from '../../../src/schemas/index.js'; -import { getTestEvent, makeSchemaStrictForTesting } from './utils.js'; +import { getTestEvent } from './utils.js'; describe('API Gateway REST Schemas', () => { const eventsPath = 'apigw-rest'; @@ -150,55 +150,4 @@ describe('API Gateway REST Schemas', () => { expect(parsedEvent).toEqual(event); }); }); - - describe('should detect missing properties in schema for ', () => { - it.each([ - 'console-test-ui', - 'iam-auth', - 'jwt-authorizer-auth', - 'lambda-authorizer-auth', - 'no-auth', - 'websocket', - ])(' %p example event', (filename) => { - // Prepare - const event = getTestEvent({ eventsPath, filename: filename }); - - const strictSchema = makeSchemaStrictForTesting( - APIGatewayProxyEventSchema - ); - - // Act & Assess - expect(() => strictSchema.parse(event)).not.toThrow(); - }); - - it('authorizer-request example event', () => { - // Prepare - const event = getTestEvent({ - eventsPath, - filename: 'authorizer-request', - }); - - const strictSchema = makeSchemaStrictForTesting( - APIGatewayRequestAuthorizerEventSchema - ); - - // Act & Assess - expect(() => strictSchema.parse(event)).not.toThrow(); - }); - - it('authorizer-token example event', () => { - // Prepare - const event = getTestEvent({ - eventsPath, - filename: 'authorizer-token', - }); - - const strictSchema = makeSchemaStrictForTesting( - APIGatewayTokenAuthorizerEventSchema - ); - - // Act & Assess - expect(() => strictSchema.parse(event)).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/apigwv2.test.ts b/packages/parser/tests/unit/schema/apigwv2.test.ts index 55c44c417d..15716ab30f 100644 --- a/packages/parser/tests/unit/schema/apigwv2.test.ts +++ b/packages/parser/tests/unit/schema/apigwv2.test.ts @@ -7,7 +7,7 @@ import { APIGatewayProxyEventV2Schema, APIGatewayRequestAuthorizerEventV2Schema, } from '../../../src/schemas/index.js'; -import { getTestEvent, makeSchemaStrictForTesting } from './utils.js'; +import { getTestEvent } from './utils.js'; describe('API Gateway HTTP (v2) Schemas', () => { const eventsPath = 'apigw-http'; @@ -100,34 +100,4 @@ describe('API Gateway HTTP (v2) Schemas', () => { expect(parsedEvent).toEqual(event); }); }); - - describe('should detect missing properties in schema for ', () => { - it.each([ - 'iam-auth', - 'jwt-authorizer-auth', - 'lambda-authorizer-auth', - 'no-auth', - ])('event %s', (filename) => { - // Prepare - const event = getTestEvent({ eventsPath, filename }); - const strictSchema = makeSchemaStrictForTesting( - APIGatewayProxyEventV2Schema - ); - // Act & Assess - expect(() => strictSchema.parse(event)).not.toThrow(); - }); - - it('authorizer-request event', () => { - // Prepare - const event = getTestEvent({ - eventsPath, - filename: 'authorizer-request', - }); - const strictSchema = makeSchemaStrictForTesting( - APIGatewayRequestAuthorizerEventV2Schema - ); - // Act & Assess - expect(() => strictSchema.parse(event)).not.toThrow(); - }); - }); }); 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 7bfbca007f..e00530f3bc 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 { CloudFormationCustomResourceDeleteSchema, CloudFormationCustomResourceUpdateSchema, } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('CloudFormationCustomResource ', () => { it('should parse create event', () => { @@ -42,45 +42,4 @@ describe('CloudFormationCustomResource ', () => { ) ).toEqual(cloudFormationCustomResourceDeleteEvent); }); - - describe('should detect missing properties in schema for ', () => { - it('CloudFormationCustomResourceCreateSchema', () => { - const cloudFormationCustomResourceCreateEvent = - TestEvents.cloudFormationCustomResourceCreateEvent; - - const strictSchema = makeSchemaStrictForTesting( - CloudFormationCustomResourceCreateSchema - ); - - expect(() => - strictSchema.parse(cloudFormationCustomResourceCreateEvent) - ).not.toThrow(); - }); - - it('CloudFormationCustomResourceUpdateSchema', () => { - const cloudFormationCustomResourceUpdateEvent = - TestEvents.cloudFormationCustomResourceUpdateEvent; - - const strictSchema = makeSchemaStrictForTesting( - CloudFormationCustomResourceUpdateSchema - ); - - expect(() => - strictSchema.parse(cloudFormationCustomResourceUpdateEvent) - ).not.toThrow(); - }); - - it('CloudFormationCustomResourceDeleteSchema', () => { - const cloudFormationCustomResourceDeleteEvent = - TestEvents.cloudFormationCustomResourceDeleteEvent; - - const strictSchema = makeSchemaStrictForTesting( - CloudFormationCustomResourceDeleteSchema - ); - - expect(() => - strictSchema.parse(cloudFormationCustomResourceDeleteEvent) - ).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/dynamodb.test.ts b/packages/parser/tests/unit/schema/dynamodb.test.ts index 193a04b303..f0d90fbc5b 100644 --- a/packages/parser/tests/unit/schema/dynamodb.test.ts +++ b/packages/parser/tests/unit/schema/dynamodb.test.ts @@ -5,7 +5,7 @@ */ import { DynamoDBStreamSchema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('DynamoDB ', () => { const dynamoStreamEvent = TestEvents.dynamoStreamEvent; @@ -14,10 +14,4 @@ describe('DynamoDB ', () => { dynamoStreamEvent ); }); - - it('should detect missing properties in schema', () => { - const strictSchema = makeSchemaStrictForTesting(DynamoDBStreamSchema); - - expect(() => strictSchema.parse(dynamoStreamEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/eventbridge.test.ts b/packages/parser/tests/unit/schema/eventbridge.test.ts index cd7f4088d0..4423318874 100644 --- a/packages/parser/tests/unit/schema/eventbridge.test.ts +++ b/packages/parser/tests/unit/schema/eventbridge.test.ts @@ -5,7 +5,7 @@ */ import { EventBridgeSchema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('EventBridge ', () => { it('should parse eventbridge event', () => { @@ -13,11 +13,4 @@ describe('EventBridge ', () => { expect(EventBridgeSchema.parse(eventBridgeEvent)).toEqual(eventBridgeEvent); }); - - it('should detect missing properties in schema', () => { - const eventBridgeEvent = TestEvents.eventBridgeEvent; - const strictSchema = makeSchemaStrictForTesting(EventBridgeSchema); - - expect(() => strictSchema.parse(eventBridgeEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/kafka.test.ts b/packages/parser/tests/unit/schema/kafka.test.ts index a1b0d8c43c..6292f3001f 100644 --- a/packages/parser/tests/unit/schema/kafka.test.ts +++ b/packages/parser/tests/unit/schema/kafka.test.ts @@ -11,7 +11,7 @@ import { } from '../../../src/schemas/'; import type { KafkaSelfManagedEvent } from '../../../src/types'; import type { KafkaRecord } from '../../../src/types/schema'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('Kafka ', () => { const expectedTestEvent = { @@ -71,22 +71,4 @@ describe('Kafka ', () => { ); expect(parsedRecord.topic).toEqual('mytopic'); }); - - describe('should detect missing properties in schema for', () => { - it('KafkaMskEventSchema', () => { - const kafkaEventMsk = TestEvents.kafkaEventMsk; - - const strictSchema = makeSchemaStrictForTesting(KafkaMskEventSchema); - expect(() => strictSchema.parse(kafkaEventMsk)).not.toThrow(); - }); - - it('KafkaSelfManagedEventSchema', () => { - const kafkaEventSelfManaged = TestEvents.kafkaEventSelfManaged; - - const strictSchema = makeSchemaStrictForTesting( - KafkaSelfManagedEventSchema - ); - expect(() => strictSchema.parse(kafkaEventSelfManaged)).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/kinesis.test.ts b/packages/parser/tests/unit/schema/kinesis.test.ts index 0cb34c41d4..e107d1ae2f 100644 --- a/packages/parser/tests/unit/schema/kinesis.test.ts +++ b/packages/parser/tests/unit/schema/kinesis.test.ts @@ -21,7 +21,7 @@ import type { KinesisFirehoseRecord, KinesisFirehoseSqsRecord, } from '../../../src/types/schema'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('Kinesis ', () => { it('should parse kinesis event', () => { @@ -30,7 +30,6 @@ describe('Kinesis ', () => { expect(parsed.Records[0].kinesis.data).toEqual('Hello, this is a test.'); }); - it('should parse single kinesis record', () => { const kinesisStreamEventOneRecord = TestEvents.kinesisStreamEventOneRecord; const parsed = KinesisDataStreamSchema.parse(kinesisStreamEventOneRecord); @@ -40,13 +39,11 @@ describe('Kinesis ', () => { username: 'test', }); }); - it('should parse Firehose event', () => { const kinesisFirehoseKinesisEvent = TestEvents.kinesisFirehoseKinesisEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehoseKinesisEvent); expect(parsed.records[0].data).toEqual('Hello World'); }); - it('should parse Kinesis Firehose PutEvents event', () => { const kinesisFirehosePutEvent = TestEvents.kinesisFirehosePutEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehosePutEvent); @@ -54,7 +51,6 @@ describe('Kinesis ', () => { Hello: 'World', }); }); - it('should parse Firehose event with SQS event', () => { const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent; const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); @@ -63,7 +59,6 @@ describe('Kinesis ', () => { body: 'Test message.', }); }); - it('should parse Kinesis event with CloudWatch event', () => { const kinesisStreamCloudWatchLogsEvent = TestEvents.kinesisStreamCloudWatchLogsEvent; @@ -78,7 +73,6 @@ describe('Kinesis ', () => { logStream: '2022/11/10/[$LATEST]26b6a45d574f442ea28438923cbf7bf7', }); }); - it('should return original value if cannot parse KinesisFirehoseSqsRecord', () => { const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent as { records: { data: string }[]; @@ -87,13 +81,13 @@ describe('Kinesis ', () => { const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); expect(parsed.records[0].data).toEqual('not a valid json'); }); - it('should parse a kinesis record from a kinesis event', () => { const kinesisStreamEvent: KinesisDataStreamEvent = TestEvents.kinesisStreamEvent as KinesisDataStreamEvent; const parsedRecord = KinesisDataStreamRecord.parse( kinesisStreamEvent.Records[0] ); + expect(parsedRecord.eventName).toEqual('aws:kinesis:record'); }); @@ -116,10 +110,4 @@ describe('Kinesis ', () => { '49640912821178817833517986466168945147170627572855734274000000' ); }); - - it('should catch any unknown fields in the example event', () => { - const kinesisStreamEvent = TestEvents.kinesisStreamEvent; - const strictSchema = makeSchemaStrictForTesting(KinesisDataStreamSchema); - expect(() => strictSchema.parse(kinesisStreamEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/lambda.test.ts b/packages/parser/tests/unit/schema/lambda.test.ts index 5047fbf112..e9b0a3449b 100644 --- a/packages/parser/tests/unit/schema/lambda.test.ts +++ b/packages/parser/tests/unit/schema/lambda.test.ts @@ -5,11 +5,11 @@ */ import { LambdaFunctionUrlSchema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('Lambda ', () => { it('should parse lambda event', () => { - const lambdaFunctionUrlEvent = TestEvents.lambdaFunctionUrlEvent; + const lambdaFunctionUrlEvent = TestEvents.apiGatewayProxyV2Event; expect(LambdaFunctionUrlSchema.parse(lambdaFunctionUrlEvent)).toEqual( lambdaFunctionUrlEvent @@ -21,12 +21,4 @@ describe('Lambda ', () => { expect(LambdaFunctionUrlSchema.parse(urlIAMEvent)).toEqual(urlIAMEvent); }); - - it('should detect missing properties in schema for lambda event', () => { - const lambdaFunctionUrlEvent = TestEvents.lambdaFunctionUrlEvent; - - const strictSchema = makeSchemaStrictForTesting(LambdaFunctionUrlSchema); - - expect(() => strictSchema.parse(lambdaFunctionUrlEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/s3.test.ts b/packages/parser/tests/unit/schema/s3.test.ts index 88613ed69e..8798155472 100644 --- a/packages/parser/tests/unit/schema/s3.test.ts +++ b/packages/parser/tests/unit/schema/s3.test.ts @@ -10,7 +10,7 @@ import { S3Schema, S3SqsEventNotificationSchema, } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('S3 ', () => { it('should parse s3 event', () => { @@ -103,42 +103,4 @@ describe('S3 ', () => { parsed.userIdentity?.sessionContext?.attributes.mfaAuthenticated ).toEqual(false); }); - - describe('should detect missing properties in schema for ', () => { - it('s3 event', () => { - const s3Event = TestEvents.s3Event; - const strictSchema = makeSchemaStrictForTesting(S3Schema); - expect(strictSchema.parse(s3Event)).toEqual(s3Event); - }); - - it('s3 event bridge notification event created', () => { - const s3EventBridgeNotificationObjectCreatedEvent = - TestEvents.s3EventBridgeNotificationObjectCreatedEvent; - const strictSchema = makeSchemaStrictForTesting( - S3EventNotificationEventBridgeSchema - ); - expect(() => - strictSchema.parse(s3EventBridgeNotificationObjectCreatedEvent) - ).not.toThrow(); - }); - - it('s3 event bridge notification event detelted', () => { - const s3EventBridgeNotificationObjectDeletedEvent = - TestEvents.s3EventBridgeNotificationObjectDeletedEvent; - const strictSchema = makeSchemaStrictForTesting( - S3EventNotificationEventBridgeSchema - ); - expect(() => - strictSchema.parse(s3EventBridgeNotificationObjectDeletedEvent) - ).not.toThrow(); - }); - - it('s3 sqs notification event', () => { - const s3SqsEvent = TestEvents.s3SqsEvent; - const strictSchema = makeSchemaStrictForTesting( - S3SqsEventNotificationSchema - ); - expect(() => strictSchema.parse(s3SqsEvent)).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/ses.test.ts b/packages/parser/tests/unit/schema/ses.test.ts index 60ba5de7c1..7c27a83d97 100644 --- a/packages/parser/tests/unit/schema/ses.test.ts +++ b/packages/parser/tests/unit/schema/ses.test.ts @@ -7,7 +7,7 @@ import { SesRecordSchema, SesSchema } from '../../../src/schemas/'; import type { SesEvent } from '../../../src/types'; import type { SesRecord } from '../../../src/types/schema'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('SES', () => { it('should parse ses event', () => { @@ -21,10 +21,4 @@ describe('SES', () => { expect(parsed.ses.mail.source).toEqual('janedoe@example.com'); }); - - it('should detect missing properties in schema for ses event', () => { - const sesEvent = TestEvents.sesEvent; - const strictSchema = makeSchemaStrictForTesting(SesSchema); - expect(() => strictSchema.parse(sesEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/sns.test.ts b/packages/parser/tests/unit/schema/sns.test.ts index 26edc66a36..7ddb7f1132 100644 --- a/packages/parser/tests/unit/schema/sns.test.ts +++ b/packages/parser/tests/unit/schema/sns.test.ts @@ -16,7 +16,7 @@ import type { SnsRecord, SnsSqsNotification, } from '../../../src/types/schema'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('SNS', () => { it('should parse sns event', () => { @@ -45,18 +45,4 @@ describe('SNS', () => { 'arn:aws:sns:eu-west-1:231436140809:powertools265' ); }); - - describe('should detect missing properties in schema for ', () => { - it('sns event', () => { - const snsEvent = TestEvents.snsEvent; - const strictSchema = makeSchemaStrictForTesting(SnsSchema); - expect(() => strictSchema.parse(snsEvent)).not.toThrow(); - }); - - it('sns record', () => { - const snsEvent: SnsEvent = TestEvents.snsEvent as SnsEvent; - const strictSchema = makeSchemaStrictForTesting(SnsRecordSchema); - expect(() => strictSchema.parse(snsEvent.Records[0])).not.toThrow(); - }); - }); }); diff --git a/packages/parser/tests/unit/schema/sqs.test.ts b/packages/parser/tests/unit/schema/sqs.test.ts index 620ccf38d8..9514338395 100644 --- a/packages/parser/tests/unit/schema/sqs.test.ts +++ b/packages/parser/tests/unit/schema/sqs.test.ts @@ -7,7 +7,7 @@ import { SqsRecordSchema, SqsSchema } from '../../../src/schemas/'; import type { SqsEvent } from '../../../src/types'; import type { SqsRecord } from '../../../src/types/schema'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('SQS', () => { it('should parse sqs event', () => { @@ -19,10 +19,4 @@ describe('SQS', () => { const parsed: SqsRecord = SqsRecordSchema.parse(sqsEvent.Records[0]); expect(parsed.body).toEqual('Test message.'); }); - - it('should detect missing properties in schema for sqs event', () => { - const sqsEvent = TestEvents.sqsEvent; - const strictSchema = makeSchemaStrictForTesting(SqsSchema); - expect(() => strictSchema.parse(sqsEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/utils.ts b/packages/parser/tests/unit/schema/utils.ts index ce4a832be5..cb0647aed7 100644 --- a/packages/parser/tests/unit/schema/utils.ts +++ b/packages/parser/tests/unit/schema/utils.ts @@ -131,71 +131,3 @@ export const getTestEvent = >({ 'utf-8' ) ) as T; - -type ZodShape = { [k: string]: z.ZodTypeAny }; - -/** - * Creates a strict version of a schema for testing purposes without modifying the original - */ -export const makeSchemaStrictForTesting = ( - schema: T -): T => { - if (schema instanceof z.ZodObject) { - const shape = schema._def.shape() as ZodShape; - const newShape = Object.fromEntries( - Object.entries(shape).map(([key, value]) => [ - key, - makeSchemaStrictForTesting(value), - ]) - ); - - return z.object(newShape).strict() as unknown as T; - } - - if (schema instanceof z.ZodArray) { - const elementSchema = schema.element; - return z.array(makeSchemaStrictForTesting(elementSchema)) as unknown as T; - } - - if (schema instanceof z.ZodUnion) { - const options = schema._def.options as readonly [ - z.ZodTypeAny, - z.ZodTypeAny, - ...z.ZodTypeAny[], - ]; - const newOptions = options.map((option) => - makeSchemaStrictForTesting(option) - ); - // Ensure we have at least two elements for the union - return z.union([ - newOptions[0], - newOptions[1], - ...newOptions.slice(2), - ]) as unknown as T; - } - - if (schema instanceof z.ZodRecord) { - const keySchema = schema.keySchema; - const valueSchema = schema.valueSchema; - return z.record( - makeSchemaStrictForTesting(keySchema), - makeSchemaStrictForTesting(valueSchema) - ) as unknown as T; - } - - // Handle extended schemas - if (schema instanceof z.ZodObject && schema._def.shape instanceof Function) { - const shape = schema._def.shape() as ZodShape; - const newShape = Object.fromEntries( - Object.entries(shape).map(([key, value]) => [ - key, - makeSchemaStrictForTesting(value), - ]) - ); - - return z.object(newShape).strict() as unknown as T; - } - - // Return other types as-is - return schema; -}; diff --git a/packages/parser/tests/unit/schema/vpc-lattice.test.ts b/packages/parser/tests/unit/schema/vpc-lattice.test.ts index 7f6cef9d81..643714fb47 100644 --- a/packages/parser/tests/unit/schema/vpc-lattice.test.ts +++ b/packages/parser/tests/unit/schema/vpc-lattice.test.ts @@ -5,7 +5,7 @@ */ import { VpcLatticeSchema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('VPC Lattice ', () => { it('should parse vpc lattice event', () => { @@ -19,10 +19,4 @@ describe('VPC Lattice ', () => { vpcLatticeEventPathTrailingSlash ); }); - - it('should detect missing properties in schema for vpc lattice event', () => { - const vpcLatticeEvent = TestEvents.vpcLatticeEvent; - const strictSchema = makeSchemaStrictForTesting(VpcLatticeSchema); - expect(() => strictSchema.parse(vpcLatticeEvent)).not.toThrow(); - }); }); diff --git a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts index 243ac640b4..5e1b6c7343 100644 --- a/packages/parser/tests/unit/schema/vpc-latticev2.test.ts +++ b/packages/parser/tests/unit/schema/vpc-latticev2.test.ts @@ -5,7 +5,7 @@ */ import { VpcLatticeV2Schema } from '../../../src/schemas/'; -import { TestEvents, makeSchemaStrictForTesting } from './utils.js'; +import { TestEvents } from './utils.js'; describe('VpcLatticeV2 ', () => { it('should parse VpcLatticeV2 event', () => { @@ -20,12 +20,4 @@ describe('VpcLatticeV2 ', () => { const parsed = VpcLatticeV2Schema.parse(vpcLatticeEventV2PathTrailingSlash); expect(parsed).toEqual(vpcLatticeEventV2PathTrailingSlash); }); - - it('should detect missing properties in schema for vpc lattice event v2', () => { - const vpcLatticeV2Event = TestEvents.vpcLatticeV2Event; - - const strictSchema = makeSchemaStrictForTesting(VpcLatticeV2Schema); - - expect(() => strictSchema.parse(vpcLatticeV2Event)).not.toThrow(); - }); }); From 843713d05562122faabf932ce3032a3bf7987df7 Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Mon, 28 Oct 2024 13:25:01 +0100 Subject: [PATCH 4/5] modified tests to compare entire objects, removed unnecessary tests --- .../parser/src/schemas/kinesis-firehose.ts | 4 +- .../parser/tests/unit/schema/kinesis.test.ts | 151 +++++++++++------- 2 files changed, 99 insertions(+), 56 deletions(-) diff --git a/packages/parser/src/schemas/kinesis-firehose.ts b/packages/parser/src/schemas/kinesis-firehose.ts index a9831acfe3..a983a7b017 100644 --- a/packages/parser/src/schemas/kinesis-firehose.ts +++ b/packages/parser/src/schemas/kinesis-firehose.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { SqsRecordSchema } from './sqs.js'; -const KinesisRecordMetaData = z.object({ +const KinesisRecordMetadata = z.object({ shardId: z.string(), partitionKey: z.string(), approximateArrivalTimestamp: z.number().positive(), @@ -12,7 +12,7 @@ const KinesisRecordMetaData = z.object({ const KinesisFireHoseRecordBase = z.object({ recordId: z.string(), approximateArrivalTimestamp: z.number().positive(), - kinesisRecordMetaData: KinesisRecordMetaData.optional(), + kinesisRecordMetadata: KinesisRecordMetadata.nullish(), }); const KinesisFireHoseBaseSchema = z.object({ diff --git a/packages/parser/tests/unit/schema/kinesis.test.ts b/packages/parser/tests/unit/schema/kinesis.test.ts index e107d1ae2f..06f370062c 100644 --- a/packages/parser/tests/unit/schema/kinesis.test.ts +++ b/packages/parser/tests/unit/schema/kinesis.test.ts @@ -4,6 +4,7 @@ * @group unit/parser/schema/ */ +import { gunzipSync } from 'node:zlib'; import { KinesisDataStreamRecord, KinesisDataStreamSchema, @@ -11,6 +12,7 @@ import { KinesisFirehoseSchema, KinesisFirehoseSqsRecordSchema, KinesisFirehoseSqsSchema, + SqsRecordSchema, } from '../../../src/schemas/'; import type { KinesisDataStreamEvent, @@ -25,53 +27,122 @@ import { TestEvents } from './utils.js'; describe('Kinesis ', () => { it('should parse kinesis event', () => { - const kinesisStreamEvent = TestEvents.kinesisStreamEvent; + const kinesisStreamEvent = + TestEvents.kinesisStreamEvent as KinesisDataStreamEvent; const parsed = KinesisDataStreamSchema.parse(kinesisStreamEvent); - expect(parsed.Records[0].kinesis.data).toEqual('Hello, this is a test.'); + const transformedInput = { + Records: kinesisStreamEvent.Records.map((record, index) => { + return { + ...record, + kinesis: { + ...record.kinesis, + data: Buffer.from(record.kinesis.data, 'base64').toString(), + }, + }; + }), + }; + + expect(parsed).toStrictEqual(transformedInput); }); it('should parse single kinesis record', () => { - const kinesisStreamEventOneRecord = TestEvents.kinesisStreamEventOneRecord; + const kinesisStreamEventOneRecord = + TestEvents.kinesisStreamEventOneRecord as KinesisDataStreamEvent; const parsed = KinesisDataStreamSchema.parse(kinesisStreamEventOneRecord); - expect(parsed.Records[0].kinesis.data).toEqual({ - message: 'test message', - username: 'test', - }); + const transformedInput = { + Records: kinesisStreamEventOneRecord.Records.map((record, index) => { + return { + ...record, + kinesis: { + ...record.kinesis, + data: JSON.parse( + Buffer.from(record.kinesis.data, 'base64').toString() + ), + }, + }; + }), + }; + + expect(parsed).toStrictEqual(transformedInput); }); it('should parse Firehose event', () => { - const kinesisFirehoseKinesisEvent = TestEvents.kinesisFirehoseKinesisEvent; + const kinesisFirehoseKinesisEvent = + TestEvents.kinesisFirehoseKinesisEvent as KinesisFireHoseEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehoseKinesisEvent); - expect(parsed.records[0].data).toEqual('Hello World'); + + const transformedInput = { + ...kinesisFirehoseKinesisEvent, + records: kinesisFirehoseKinesisEvent.records.map((record) => { + return { + ...record, + data: Buffer.from(record.data, 'base64').toString(), + kinesisRecordMetadata: record.kinesisRecordMetadata, + }; + }), + }; + expect(parsed).toStrictEqual(transformedInput); }); it('should parse Kinesis Firehose PutEvents event', () => { - const kinesisFirehosePutEvent = TestEvents.kinesisFirehosePutEvent; + const kinesisFirehosePutEvent = + TestEvents.kinesisFirehosePutEvent as KinesisFireHoseEvent; const parsed = KinesisFirehoseSchema.parse(kinesisFirehosePutEvent); - expect(JSON.parse(parsed.records[1].data)).toEqual({ - Hello: 'World', - }); + + const transformedInput = { + ...kinesisFirehosePutEvent, + records: kinesisFirehosePutEvent.records.map((record) => { + return { + ...record, + data: Buffer.from(record.data, 'base64').toString(), + }; + }), + }; + + expect(parsed).toStrictEqual(transformedInput); }); it('should parse Firehose event with SQS event', () => { - const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent; + const kinesisFirehoseSQSEvent = + TestEvents.kinesisFirehoseSQSEvent as KinesisFireHoseSqsEvent; const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); - expect(parsed.records[0].data).toMatchObject({ - messageId: '5ab807d4-5644-4c55-97a3-47396635ac74', - body: 'Test message.', - }); + + const transformedInput = { + ...kinesisFirehoseSQSEvent, + records: kinesisFirehoseSQSEvent.records.map((record) => { + return { + ...record, + data: JSON.parse( + Buffer.from(record.data as string, 'base64').toString() + ), + }; + }), + }; + + expect(parsed).toStrictEqual(transformedInput); }); it('should parse Kinesis event with CloudWatch event', () => { const kinesisStreamCloudWatchLogsEvent = - TestEvents.kinesisStreamCloudWatchLogsEvent; + TestEvents.kinesisStreamCloudWatchLogsEvent as KinesisDataStreamEvent; const parsed = KinesisDataStreamSchema.parse( kinesisStreamCloudWatchLogsEvent ); - expect(parsed.Records[0].kinesis.data).toMatchObject({ - messageType: 'DATA_MESSAGE', - owner: '231436140809', - logGroup: '/aws/lambda/pt-1488-DummyLogDataFunction-gnWXPvL6jJyG', - logStream: '2022/11/10/[$LATEST]26b6a45d574f442ea28438923cbf7bf7', - }); + const transformedInput = { + Records: kinesisStreamCloudWatchLogsEvent.Records.map((record, index) => { + return { + ...record, + kinesis: { + ...record.kinesis, + data: JSON.parse( + gunzipSync(Buffer.from(record.kinesis.data, 'base64')).toString( + 'utf8' + ) + ), + }, + }; + }), + }; + + expect(parsed).toStrictEqual(transformedInput); }); it('should return original value if cannot parse KinesisFirehoseSqsRecord', () => { const kinesisFirehoseSQSEvent = TestEvents.kinesisFirehoseSQSEvent as { @@ -79,35 +150,7 @@ describe('Kinesis ', () => { }; kinesisFirehoseSQSEvent.records[0].data = 'not a valid json'; const parsed = KinesisFirehoseSqsSchema.parse(kinesisFirehoseSQSEvent); - expect(parsed.records[0].data).toEqual('not a valid json'); - }); - it('should parse a kinesis record from a kinesis event', () => { - const kinesisStreamEvent: KinesisDataStreamEvent = - TestEvents.kinesisStreamEvent as KinesisDataStreamEvent; - const parsedRecord = KinesisDataStreamRecord.parse( - kinesisStreamEvent.Records[0] - ); - - expect(parsedRecord.eventName).toEqual('aws:kinesis:record'); - }); - it('should parse a kinesis firehose record from a kinesis firehose event', () => { - const kinesisFirehoseEvent: KinesisFireHoseEvent = - TestEvents.kinesisFirehoseKinesisEvent as KinesisFireHoseEvent; - const parsedRecord: KinesisFirehoseRecord = - KinesisFirehoseRecordSchema.parse(kinesisFirehoseEvent.records[0]); - - expect(parsedRecord.data).toEqual('Hello World'); - }); - - it('should parse a sqs record from a kinesis firehose event', () => { - const kinesisFireHoseSqsEvent: KinesisFireHoseSqsEvent = - TestEvents.kinesisFirehoseSQSEvent as KinesisFireHoseSqsEvent; - const parsed: KinesisFirehoseSqsRecord = - KinesisFirehoseSqsRecordSchema.parse(kinesisFireHoseSqsEvent.records[0]); - - expect(parsed.recordId).toEqual( - '49640912821178817833517986466168945147170627572855734274000000' - ); + expect(parsed).toStrictEqual(kinesisFirehoseSQSEvent); }); }); From a9e46b708c67c2152927252ba06fb9e95e204dfe Mon Sep 17 00:00:00 2001 From: Alexander Schueren Date: Mon, 28 Oct 2024 13:49:44 +0100 Subject: [PATCH 5/5] revert to fix arcane coverage --- .../parser/tests/unit/schema/kinesis.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/parser/tests/unit/schema/kinesis.test.ts b/packages/parser/tests/unit/schema/kinesis.test.ts index 06f370062c..f8e7d93608 100644 --- a/packages/parser/tests/unit/schema/kinesis.test.ts +++ b/packages/parser/tests/unit/schema/kinesis.test.ts @@ -153,4 +153,34 @@ describe('Kinesis ', () => { expect(parsed).toStrictEqual(kinesisFirehoseSQSEvent); }); + it('should parse a kinesis record from a kinesis event', () => { + const kinesisStreamEvent: KinesisDataStreamEvent = + TestEvents.kinesisStreamEvent as KinesisDataStreamEvent; + const parsedRecord = KinesisDataStreamRecord.parse( + kinesisStreamEvent.Records[0] + ); + + expect(parsedRecord.eventSource).toEqual('aws:kinesis'); + expect(parsedRecord.eventName).toEqual('aws:kinesis:record'); + }); + + it('should parse a kinesis firehose record from a kinesis firehose event', () => { + const kinesisFirehoseEvent: KinesisFireHoseEvent = + TestEvents.kinesisFirehoseKinesisEvent as KinesisFireHoseEvent; + const parsedRecord: KinesisFirehoseRecord = + KinesisFirehoseRecordSchema.parse(kinesisFirehoseEvent.records[0]); + + expect(parsedRecord.data).toEqual('Hello World'); + }); + + it('should parse a sqs record from a kinesis firehose event', () => { + const kinesisFireHoseSqsEvent: KinesisFireHoseSqsEvent = + TestEvents.kinesisFirehoseSQSEvent as KinesisFireHoseSqsEvent; + const parsed: KinesisFirehoseSqsRecord = + KinesisFirehoseSqsRecordSchema.parse(kinesisFireHoseSqsEvent.records[0]); + + expect(parsed.recordId).toEqual( + '49640912821178817833517986466168945147170627572855734274000000' + ); + }); });