@@ -102,6 +102,7 @@ describe('queued-wf-tests-simple', () => {
102102 TestResumeQueues . queue ,
103103 TestResumeQueuesPartitioned . queue ,
104104 TestConcurrencyAcrossVersions . queue ,
105+ TestVersionlessDequeue . queue ,
105106 waitFirstQueue ,
106107 ] ) {
107108 await DBOS . registerQueue ( ref . name , { onConflict : 'always_update' , ...ref . config } ) ;
@@ -768,6 +769,18 @@ describe('queued-wf-tests-simple', () => {
768769 }
769770 }
770771
772+ class TestVersionlessDequeue {
773+ static queue : QueueRef = {
774+ name : 'TestVersionlessDequeue' ,
775+ config : { workerConcurrency : 1 , ...testPolling } ,
776+ } ;
777+
778+ @DBOS . workflow ( )
779+ static async versionlessWorkflow ( ) {
780+ return Promise . resolve ( DBOS . workflowID ) ;
781+ }
782+ }
783+
771784 test ( 'test-concurrency-across-versions' , async ( ) => {
772785 expect ( config . systemDatabaseUrl ) . toBeDefined ( ) ;
773786 const client = await DBOSClient . create ( { systemDatabaseUrl : config . systemDatabaseUrl ! } ) ;
@@ -790,6 +803,43 @@ describe('queued-wf-tests-simple', () => {
790803 await client . destroy ( ) ;
791804 } ) ;
792805
806+ test ( 'test-versionless-dequeue-requires-latest-version' , async ( ) => {
807+ expect ( config . systemDatabaseUrl ) . toBeDefined ( ) ;
808+ const client = await DBOSClient . create ( { systemDatabaseUrl : config . systemDatabaseUrl ! } ) ;
809+ const sysdb = DBOSExecutor . globalInstance ! . systemDatabase ;
810+ const workerVersion = globalParams . appVersion ;
811+
812+ // Register a newer application version so this worker is no longer the latest.
813+ const newerVersion = `newer-${ randomUUID ( ) } ` ;
814+ await sysdb . createApplicationVersion ( newerVersion ) ;
815+ await sysdb . updateApplicationVersionTimestamp ( newerVersion , Date . now ( ) + 1_000_000 ) ;
816+
817+ // Enqueue a version-less workflow (application_version IS NULL): no appVersion field.
818+ const versionlessHandle = await client . enqueue ( {
819+ queueName : TestVersionlessDequeue . queue . name ,
820+ workflowName : 'versionlessWorkflow' ,
821+ workflowClassName : 'TestVersionlessDequeue' ,
822+ } ) ;
823+
824+ // Enqueue a workflow tagged with this worker's own (non-latest) version.
825+ const taggedHandle = await DBOS . startWorkflow ( TestVersionlessDequeue , {
826+ queueName : TestVersionlessDequeue . queue . name ,
827+ } ) . versionlessWorkflow ( ) ;
828+
829+ // A matching-version worker still dequeues its own version-tagged workflow.
830+ await expect ( taggedHandle . getResult ( ) ) . resolves . toBeTruthy ( ) ;
831+
832+ // But the version-less workflow stays ENQUEUED: this worker is not the latest version.
833+ await sleepms ( 1000 ) ;
834+ expect ( ( await versionlessHandle . getStatus ( ) ) ?. status ) . toBe ( StatusString . ENQUEUED ) ;
835+
836+ // Make this worker the latest version again; the version-less workflow now completes.
837+ await sysdb . updateApplicationVersionTimestamp ( workerVersion , Date . now ( ) + 2_000_000 ) ;
838+ await expect ( versionlessHandle . getResult ( ) ) . resolves . toBeTruthy ( ) ;
839+
840+ await client . destroy ( ) ;
841+ } ) ;
842+
793843 test ( 'test_wait_first_queue' , async ( ) => {
794844 const numTasks = WaitFirstQueueTest . numTasks ;
795845 WaitFirstQueueTest . resetEvents ( ) ;
0 commit comments