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