Skip to content

Commit f4306c8

Browse files
committed
refactor: update MongoDB and SQS adapters to use AWS SDK v3, improve index initialization handling, and enhance test reliability
1 parent 52c0eed commit f4306c8

9 files changed

Lines changed: 78 additions & 73 deletions

File tree

src/adapters/mongodb.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ export interface MongoConfig {
2121
}
2222

2323
export class MongoDatabaseAdapter implements DatabaseAdapter {
24+
private indexesInitialized: Promise<void>;
25+
2426
constructor(private col: MongoCollection) {
25-
this.initializeIndexes();
27+
this.indexesInitialized = this.initializeIndexes();
2628
}
2729

2830
private async initializeIndexes(): Promise<void> {
@@ -48,6 +50,10 @@ export class MongoDatabaseAdapter implements DatabaseAdapter {
4850
}
4951
}
5052

53+
async ensureIndexes(): Promise<void> {
54+
await this.indexesInitialized;
55+
}
56+
5157
async insertJob(payload: Buffer, meta: JobMeta): Promise<string> {
5258
const now = new Date();
5359

src/adapters/sqs.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SQS as AWSClient, type SQSClientConfig, type SQS } from '@aws-sdk/client-sqs';
1+
import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';
22
import { SqsQueue } from '../drivers/sqs.ts';
33

44
export interface SQSConfig extends SQSClientConfig {
@@ -7,17 +7,17 @@ export interface SQSConfig extends SQSClientConfig {
77

88
// Main export - constructor pattern
99
export class SQSQueue<T = Record<string, any>> extends SqsQueue<T> {
10-
constructor(config: { client: SQS; queueUrl: string }) {
10+
constructor(config: { client: SQSClient; queueUrl: string }) {
1111
super(config.client, config.queueUrl);
1212
}
1313
}
1414

15-
// Convenience factory for AWS SDK
15+
// Convenience factory for AWS SDK v3
1616
export function createSQSQueue<T = Record<string, any>>(queueUrl: string, sqsConfig?: SQSConfig): SQSQueue<T> {
17-
const client = new AWSClient({
17+
const client = new SQSClient({
1818
region: sqsConfig?.region || process.env.AWS_REGION || 'us-east-1',
1919
...sqsConfig
20-
}) as any;
20+
});
2121

2222
return new SQSQueue<T>({ client, queueUrl });
2323
}

src/drivers/db.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export class DbQueue<TJobMap = Record<string, any>> extends Queue<TJobMap, DbJob
1010
super(options);
1111
}
1212

13+
get adapter(): DatabaseAdapter {
14+
return this.db;
15+
}
16+
1317
protected async pushMessage(payload: string, meta: JobMeta): Promise<string> {
1418
return await this.db.insertJob(Buffer.from(payload), meta);
1519
}

src/drivers/sqs.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import type { SQS } from "@aws-sdk/client-sqs";
1+
import {
2+
SQSClient,
3+
SendMessageCommand,
4+
ReceiveMessageCommand,
5+
DeleteMessageCommand,
6+
ChangeMessageVisibilityCommand,
7+
type SendMessageCommandInput,
8+
type ReceiveMessageCommandInput,
9+
type DeleteMessageCommandInput,
10+
type ChangeMessageVisibilityCommandInput,
11+
} from "@aws-sdk/client-sqs";
212
import { Queue } from "../core/queue.ts";
313
import type {
414
JobStatus,
@@ -7,17 +17,12 @@ import type {
717
SqsJobRequest,
818
} from "../interfaces/job.ts";
919

10-
export type SimplifiedSQSClient = Pick<
11-
SQS,
12-
"sendMessage" | "receiveMessage" | "changeMessageVisibility" | "deleteMessage"
13-
>;
14-
1520
export class SqsQueue<TJobMap = Record<string, any>> extends Queue<
1621
TJobMap,
1722
SqsJobRequest<any>
1823
> {
1924
constructor(
20-
private client: SimplifiedSQSClient,
25+
private client: SQSClient,
2126
private queueUrl: string,
2227
options: { ttrDefault?: number } = {}
2328
) {
@@ -43,12 +48,14 @@ export class SqsQueue<TJobMap = Record<string, any>> extends Queue<
4348
};
4449
}
4550

46-
const result = await this.client.sendMessage({
51+
const command = new SendMessageCommand({
4752
QueueUrl: this.queueUrl,
4853
MessageBody: payload,
4954
DelaySeconds: meta.delay || 0,
5055
MessageAttributes: messageAttributes,
5156
});
57+
58+
const result = await this.client.send(command);
5259

5360
if (!result.MessageId) {
5461
throw new Error("Failed to send message - no MessageId returned");
@@ -57,12 +64,14 @@ export class SqsQueue<TJobMap = Record<string, any>> extends Queue<
5764
}
5865

5966
protected async reserve(timeout: number): Promise<QueueMessage | null> {
60-
const result = await this.client.receiveMessage({
67+
const command = new ReceiveMessageCommand({
6168
QueueUrl: this.queueUrl,
6269
MaxNumberOfMessages: 1,
6370
WaitTimeSeconds: timeout,
6471
MessageAttributeNames: ["All"],
6572
});
73+
74+
const result = await this.client.send(command);
6675

6776
if (!result.Messages || result.Messages.length === 0) {
6877
return null;
@@ -88,11 +97,13 @@ export class SqsQueue<TJobMap = Record<string, any>> extends Queue<
8897
}
8998

9099
if (meta.ttr) {
91-
await this.client.changeMessageVisibility({
100+
const visibilityCommand = new ChangeMessageVisibilityCommand({
92101
QueueUrl: this.queueUrl,
93102
ReceiptHandle: message.ReceiptHandle,
94103
VisibilityTimeout: meta.ttr,
95104
});
105+
106+
await this.client.send(visibilityCommand);
96107
}
97108

98109
return {
@@ -112,10 +123,12 @@ export class SqsQueue<TJobMap = Record<string, any>> extends Queue<
112123
);
113124
}
114125

115-
await this.client.deleteMessage({
126+
const deleteCommand = new DeleteMessageCommand({
116127
QueueUrl: this.queueUrl,
117128
ReceiptHandle: message.meta.receiptHandle,
118129
});
130+
131+
await this.client.send(deleteCommand);
119132
}
120133

121134
async status(id: string): Promise<JobStatus> {

tests/drivers/sqs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('SqsQueue', () => {
1313

1414
beforeEach(() => {
1515
sqsClient = new TestSQSClient();
16-
queue = new SqsQueue<TestJobs>(sqsClient, 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue');
16+
queue = new SqsQueue<TestJobs>(sqsClient as any, 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue');
1717
});
1818

1919
describe('addJob and reserve cycle', () => {

tests/integration/mongodb.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ describe('MongoDB Integration Tests (with TestContainers)', () => {
6767
// Add a job to trigger index creation
6868
await queue.addJob('simple-job', { payload: { data: 'test' } });
6969

70-
// Wait a bit for async index creation to complete
71-
await new Promise(resolve => setTimeout(resolve, 100));
70+
// Wait for indexes to be created
71+
await (queue as any).adapter.ensureIndexes();
7272

7373
// Check that indexes exist
7474
const db = client.db(testDatabase);

tests/integration/sqs-localstack.test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
22
import { GenericContainer, type StartedTestContainer } from 'testcontainers';
33
import { SQSClient, CreateQueueCommand, DeleteQueueCommand, PurgeQueueCommand } from '@aws-sdk/client-sqs';
44
import { SqsQueue } from '../../src/drivers/sqs.ts';
5-
import { SQSClientV3Adapter } from '../utils/sqs-v3-adapter.ts';
65

76
interface TestJobs {
87
'process-data': { id: number; data: string };
@@ -73,9 +72,8 @@ describe('SQS Integration Tests (LocalStack)', () => {
7372
// Queue might be empty, ignore
7473
}
7574

76-
// Create fresh queue instance using the v3 adapter
77-
const adapter = new SQSClientV3Adapter(sqsClient);
78-
queue = new SqsQueue<TestJobs>(adapter, queueUrl);
75+
// Create fresh queue instance using SQSClient directly
76+
queue = new SqsQueue<TestJobs>(sqsClient, queueUrl);
7977
});
8078

8179
describe('Real SQS Operations', () => {
@@ -105,7 +103,7 @@ describe('SQS Integration Tests (LocalStack)', () => {
105103
// Register job handler
106104
queue.onJob('process-data', async (payload) => {
107105
processedJobs.push(payload);
108-
return `Processed: ${payload.data}`;
106+
// Just process without returning string to match void return type
109107
});
110108

111109
// Add job

tests/mocks/test-sqs-client.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import type { SimplifiedSQSClient } from "../../src/drivers/sqs.js";
2-
import type {
3-
ReceiveMessageCommandOutput,
4-
DeleteMessageCommandOutput,
5-
ChangeMessageVisibilityCommandOutput
1+
import {
2+
SendMessageCommand,
3+
ReceiveMessageCommand,
4+
DeleteMessageCommand,
5+
ChangeMessageVisibilityCommand,
6+
type SendMessageCommandOutput,
7+
type ReceiveMessageCommandOutput,
8+
type DeleteMessageCommandOutput,
9+
type ChangeMessageVisibilityCommandOutput
610
} from "@aws-sdk/client-sqs";
711

812
interface StoredMessage {
@@ -16,7 +20,7 @@ interface StoredMessage {
1620
visibilityTimeoutUntil?: Date;
1721
}
1822

19-
export class TestSQSClient implements SimplifiedSQSClient {
23+
export class TestSQSClient {
2024
private messages: Map<string, StoredMessage> = new Map();
2125
private nextMessageId = 1;
2226
private nextReceiptHandle = 1;
@@ -31,12 +35,25 @@ export class TestSQSClient implements SimplifiedSQSClient {
3135

3236
public deletedMessages: Array<{ MessageId: string; ReceiptHandle: string }> = [];
3337

34-
async sendMessage(params: {
38+
async send(command: SendMessageCommand | ReceiveMessageCommand | DeleteMessageCommand | ChangeMessageVisibilityCommand): Promise<any> {
39+
if (command instanceof SendMessageCommand) {
40+
return this.handleSendMessage(command.input as any);
41+
} else if (command instanceof ReceiveMessageCommand) {
42+
return this.handleReceiveMessage(command.input as any);
43+
} else if (command instanceof DeleteMessageCommand) {
44+
return this.handleDeleteMessage(command.input as any);
45+
} else if (command instanceof ChangeMessageVisibilityCommand) {
46+
return this.handleChangeMessageVisibility(command.input as any);
47+
}
48+
throw new Error(`Unsupported command type: ${(command as any).constructor.name}`);
49+
}
50+
51+
private async handleSendMessage(params: {
3552
QueueUrl: string;
3653
MessageBody: string;
3754
DelaySeconds?: number;
3855
MessageAttributes?: Record<string, { StringValue: string; DataType: string }>;
39-
}): Promise<{ MessageId: string, $metadata: any }> {
56+
}): Promise<SendMessageCommandOutput> {
4057
const messageId = this.nextMessageId.toString();
4158
this.nextMessageId++;
4259

@@ -65,11 +82,14 @@ export class TestSQSClient implements SimplifiedSQSClient {
6582

6683
return {
6784
MessageId: messageId,
68-
$metadata: {}
85+
$metadata: {
86+
httpStatusCode: 200,
87+
requestId: 'test-request-id'
88+
}
6989
};
7090
}
7191

72-
async receiveMessage(params: {
92+
private async handleReceiveMessage(params: {
7393
QueueUrl: string;
7494
MaxNumberOfMessages?: number;
7595
WaitTimeSeconds?: number;
@@ -106,7 +126,7 @@ export class TestSQSClient implements SimplifiedSQSClient {
106126
} as ReceiveMessageCommandOutput;
107127
}
108128

109-
async deleteMessage(params: {
129+
private async handleDeleteMessage(params: {
110130
QueueUrl: string;
111131
ReceiptHandle: string;
112132
}): Promise<DeleteMessageCommandOutput> {
@@ -127,7 +147,7 @@ export class TestSQSClient implements SimplifiedSQSClient {
127147
} as DeleteMessageCommandOutput;
128148
}
129149

130-
async changeMessageVisibility(params: {
150+
private async handleChangeMessageVisibility(params: {
131151
QueueUrl: string;
132152
ReceiptHandle: string;
133153
VisibilityTimeout: number;

tests/utils/sqs-v3-adapter.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)