Skip to content

Commit 2e8f63e

Browse files
committed
integrations tests
1 parent 83bb85e commit 2e8f63e

3 files changed

Lines changed: 705 additions & 0 deletions

File tree

tests/drivers/db-real.test.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { unlinkSync } from 'fs';
3+
import Database from 'better-sqlite3';
4+
import { DbQueue } from '../../src/drivers/db.ts';
5+
import { SQLiteDatabaseAdapter } from '../../src/adapters/sqlite.ts';
6+
import { TestDatabaseAdapter } from '../mocks/test-database-adapter.ts';
7+
8+
interface TestJobs {
9+
'simple-job': { data: string };
10+
'failing-job': { shouldFail: boolean };
11+
'delayed-job': { message: string };
12+
}
13+
14+
describe('DbQueue with Real SQLite Adapter', () => {
15+
const testDbPath = './test-db-real.db';
16+
let db: Database.Database;
17+
let realQueue: DbQueue<TestJobs>;
18+
let mockQueue: DbQueue<TestJobs>;
19+
let realAdapter: SQLiteDatabaseAdapter;
20+
let mockAdapter: TestDatabaseAdapter;
21+
22+
beforeEach(() => {
23+
// Clean up any existing test database
24+
try {
25+
unlinkSync(testDbPath);
26+
} catch {}
27+
28+
// Setup real SQLite adapter
29+
db = new Database(testDbPath);
30+
realAdapter = new SQLiteDatabaseAdapter(db);
31+
realQueue = new DbQueue<TestJobs>(realAdapter);
32+
33+
// Setup mock adapter for comparison
34+
mockAdapter = new TestDatabaseAdapter();
35+
mockQueue = new DbQueue<TestJobs>(mockAdapter);
36+
});
37+
38+
afterEach(() => {
39+
db.close();
40+
try {
41+
unlinkSync(testDbPath);
42+
} catch {}
43+
});
44+
45+
describe('Adapter Compatibility', () => {
46+
it('should behave the same as mock adapter for basic operations', async () => {
47+
// Test job insertion
48+
const realId = await realQueue.addJob('simple-job', { payload: { data: 'test' } });
49+
const mockId = await mockQueue.addJob('simple-job', { payload: { data: 'test' } });
50+
51+
expect(realId).toBeTruthy();
52+
expect(mockId).toBeTruthy();
53+
54+
// Test status checking
55+
expect(await realQueue.status(realId)).toBe('waiting');
56+
expect(await mockQueue.status(mockId)).toBe('waiting');
57+
});
58+
59+
it('should handle job processing identically', async () => {
60+
const realProcessed: string[] = [];
61+
const mockProcessed: string[] = [];
62+
63+
realQueue.onJob('simple-job', async (payload) => {
64+
realProcessed.push(payload.data);
65+
});
66+
67+
mockQueue.onJob('simple-job', async (payload) => {
68+
mockProcessed.push(payload.data);
69+
});
70+
71+
// Add same jobs to both queues
72+
await realQueue.addJob('simple-job', { payload: { data: 'test1' } });
73+
await realQueue.addJob('simple-job', { payload: { data: 'test2' } });
74+
await mockQueue.addJob('simple-job', { payload: { data: 'test1' } });
75+
await mockQueue.addJob('simple-job', { payload: { data: 'test2' } });
76+
77+
// Process jobs
78+
await realQueue.run(false);
79+
await mockQueue.run(false);
80+
81+
expect(realProcessed.sort()).toEqual(mockProcessed.sort());
82+
});
83+
});
84+
85+
describe('Real SQLite Features', () => {
86+
it('should persist data to actual database file', async () => {
87+
await realQueue.addJob('simple-job', { payload: { data: 'persistent' } });
88+
89+
// Verify data exists in database (check by partial payload content)
90+
const jobs = db.prepare('SELECT * FROM jobs').all();
91+
expect(jobs).toHaveLength(1);
92+
93+
const job = jobs[0];
94+
expect(job.status).toBe('waiting');
95+
96+
// Verify the payload contains our data (it's a serialized job request)
97+
const payloadStr = job.payload.toString();
98+
expect(payloadStr).toContain('persistent');
99+
});
100+
101+
it('should handle concurrent access correctly', async () => {
102+
// Add multiple jobs
103+
const jobs = await Promise.all([
104+
realQueue.addJob('simple-job', { payload: { data: 'job1' } }),
105+
realQueue.addJob('simple-job', { payload: { data: 'job2' } }),
106+
realQueue.addJob('simple-job', { payload: { data: 'job3' } }),
107+
]);
108+
109+
expect(jobs).toHaveLength(3);
110+
expect(new Set(jobs).size).toBe(3); // All IDs should be unique
111+
112+
// Verify all jobs are in database
113+
const jobCount = db.prepare('SELECT COUNT(*) as count FROM jobs').get();
114+
expect(jobCount.count).toBe(3);
115+
});
116+
117+
it('should properly handle database transactions', async () => {
118+
const processedJobs: string[] = [];
119+
let jobIdDuringProcessing: string;
120+
121+
realQueue.onJob('simple-job', async (payload) => {
122+
processedJobs.push(payload.data);
123+
// We can't easily check the job status during processing since we don't have the ID here
124+
});
125+
126+
const jobId = await realQueue.addJob('simple-job', { payload: { data: 'transaction test' } });
127+
await realQueue.run(false);
128+
129+
// After processing, job should be marked as done
130+
const status = await realQueue.status(jobId);
131+
expect(status).toBe('done');
132+
expect(processedJobs).toEqual(['transaction test']);
133+
});
134+
135+
it('should handle priority ordering correctly', async () => {
136+
const processedJobs: number[] = [];
137+
138+
realQueue.onJob('simple-job', async (payload) => {
139+
processedJobs.push(parseInt(payload.data));
140+
});
141+
142+
// Add jobs with different priorities (SQLite should order by priority DESC)
143+
await realQueue.addJob('simple-job', { payload: { data: '1' }, priority: 1 });
144+
await realQueue.addJob('simple-job', { payload: { data: '5' }, priority: 5 });
145+
await realQueue.addJob('simple-job', { payload: { data: '3' }, priority: 3 });
146+
147+
await realQueue.run(false);
148+
149+
// Should process in priority order (highest first)
150+
expect(processedJobs).toEqual([5, 3, 1]);
151+
});
152+
});
153+
154+
describe('Error Handling', () => {
155+
it('should handle database errors gracefully', async () => {
156+
// Close database to force error
157+
db.close();
158+
159+
// Should throw error when trying to add job
160+
await expect(
161+
realQueue.addJob('simple-job', { payload: { data: 'error test' } })
162+
).rejects.toThrow();
163+
});
164+
});
165+
166+
describe('Performance Characteristics', () => {
167+
it('should handle large number of jobs efficiently', async () => {
168+
const jobCount = 100;
169+
const startTime = Date.now();
170+
171+
// Add many jobs
172+
const promises = [];
173+
for (let i = 0; i < jobCount; i++) {
174+
promises.push(
175+
realQueue.addJob('simple-job', { payload: { data: `job-${i}` } })
176+
);
177+
}
178+
179+
await Promise.all(promises);
180+
const insertTime = Date.now() - startTime;
181+
182+
// Verify all jobs were inserted
183+
const count = db.prepare('SELECT COUNT(*) as count FROM jobs').get();
184+
expect(count.count).toBe(jobCount);
185+
186+
// Should be reasonably fast (less than 1 second for 100 jobs)
187+
expect(insertTime).toBeLessThan(1000);
188+
});
189+
});
190+
});

0 commit comments

Comments
 (0)