@@ -45,10 +45,10 @@ private function createProvider(string $id, string $taskTypeId): ISynchronousPro
4545 }
4646
4747 /**
48- * Helper to create a real Task with a given id.
48+ * Helper to create a real Task with a given id and optional task type .
4949 */
50- private function createTask (int $ id ): Task {
51- $ task = new Task (' test_task_type ' , [], 'testapp ' , null );
50+ private function createTask (int $ id, string $ type = ' test_task_type ' ): Task {
51+ $ task = new Task ($ type , [], 'testapp ' , null );
5252 $ task ->setId ($ id );
5353 return $ task ;
5454 }
@@ -227,26 +227,31 @@ public function testTimeoutExitsLoop(): void {
227227 $ this ->assertLessThanOrEqual (5 , $ elapsed );
228228 }
229229
230- public function testProcessesFirstMatchingProvider (): void {
230+ public function testProcessesCorrectProviderForReturnedTaskType (): void {
231231 $ taskTypeId1 = 'type_a ' ;
232232 $ taskTypeId2 = 'type_b ' ;
233233
234234 $ provider1 = $ this ->createProvider ('provider_a ' , $ taskTypeId1 );
235235 $ provider2 = $ this ->createProvider ('provider_b ' , $ taskTypeId2 );
236- $ task = $ this ->createTask (7 );
236+ // Task has type_a, so provider1 must be chosen to process it
237+ $ task = $ this ->createTask (7 , $ taskTypeId1 );
237238
238239 $ this ->manager ->expects ($ this ->once ())
239240 ->method ('getProviders ' )
240241 ->willReturn ([$ provider1 , $ provider2 ]);
241242
242- $ this ->manager ->expects ($ this ->once ())
243+ // Both providers are eligible, so getPreferredProvider is called for each
244+ $ this ->manager ->expects ($ this ->exactly (2 ))
243245 ->method ('getPreferredProvider ' )
244- ->with ($ taskTypeId1 )
245- ->willReturn ($ provider1 );
246+ ->willReturnMap ([
247+ [$ taskTypeId1 , $ provider1 ],
248+ [$ taskTypeId2 , $ provider2 ],
249+ ]);
246250
251+ // All eligible task types are passed in a single query
247252 $ this ->manager ->expects ($ this ->once ())
248253 ->method ('getNextScheduledTask ' )
249- ->with ([$ taskTypeId1] )
254+ ->with ($ this -> equalTo ( [$ taskTypeId1, $ taskTypeId2 ]) )
250255 ->willReturn ($ task );
251256
252257 $ this ->manager ->expects ($ this ->once ())
@@ -262,13 +267,53 @@ public function testProcessesFirstMatchingProvider(): void {
262267 $ this ->assertSame (0 , $ result );
263268 }
264269
270+ public function testPicksOldestTaskAcrossMultipleEligibleProviders (): void {
271+ $ taskTypeId1 = 'type_a ' ;
272+ $ taskTypeId2 = 'type_b ' ;
273+
274+ $ provider1 = $ this ->createProvider ('provider_a ' , $ taskTypeId1 );
275+ $ provider2 = $ this ->createProvider ('provider_b ' , $ taskTypeId2 );
276+ // getNextScheduledTask returns a type_b task (the globally oldest one)
277+ $ task = $ this ->createTask (3 , $ taskTypeId2 );
278+
279+ $ this ->manager ->expects ($ this ->once ())
280+ ->method ('getProviders ' )
281+ ->willReturn ([$ provider1 , $ provider2 ]);
282+
283+ $ this ->manager ->expects ($ this ->exactly (2 ))
284+ ->method ('getPreferredProvider ' )
285+ ->willReturnMap ([
286+ [$ taskTypeId1 , $ provider1 ],
287+ [$ taskTypeId2 , $ provider2 ],
288+ ]);
289+
290+ // Both eligible types are queried together to prevent starvation
291+ $ this ->manager ->expects ($ this ->once ())
292+ ->method ('getNextScheduledTask ' )
293+ ->with ($ this ->equalTo ([$ taskTypeId1 , $ taskTypeId2 ]))
294+ ->willReturn ($ task );
295+
296+ // provider2 must handle the task because the task has type_b
297+ $ this ->manager ->expects ($ this ->once ())
298+ ->method ('processTask ' )
299+ ->with ($ task , $ provider2 )
300+ ->willReturn (true );
301+
302+ $ input = new ArrayInput (['--once ' => true ], $ this ->command ->getDefinition ());
303+ $ output = new NullOutput ();
304+
305+ $ result = $ this ->command ->run ($ input , $ output );
306+
307+ $ this ->assertSame (0 , $ result );
308+ }
309+
265310 public function testTaskTypesWhitelistFiltersProviders (): void {
266311 $ taskTypeId1 = 'type_a ' ;
267312 $ taskTypeId2 = 'type_b ' ;
268313
269314 $ provider1 = $ this ->createProvider ('provider_a ' , $ taskTypeId1 );
270315 $ provider2 = $ this ->createProvider ('provider_b ' , $ taskTypeId2 );
271- $ task = $ this ->createTask (99 );
316+ $ task = $ this ->createTask (99 , $ taskTypeId2 );
272317
273318 $ this ->manager ->expects ($ this ->once ())
274319 ->method ('getProviders ' )
@@ -323,7 +368,7 @@ public function testTaskTypesWhitelistWithNoMatchingProviders(): void {
323368 public function testEmptyTaskTypesAllowsAllProviders (): void {
324369 $ taskTypeId = 'type_a ' ;
325370 $ provider = $ this ->createProvider ('provider_a ' , $ taskTypeId );
326- $ task = $ this ->createTask (5 );
371+ $ task = $ this ->createTask (5 , $ taskTypeId );
327372
328373 $ this ->manager ->expects ($ this ->once ())
329374 ->method ('getProviders ' )
0 commit comments