From c99e6d2204d0f5db2cac67686c410ef2cc50ae06 Mon Sep 17 00:00:00 2001 From: nocoo Date: Thu, 16 Apr 2026 08:11:48 +0800 Subject: [PATCH 1/2] test(swift): fix race condition in monitor cleanup integration test Use a longer-running command (sleep 2) and poll for running state instead of asserting immediately after execute(), which raced with the background process completing before the assertion. --- .../Tests/RunnerTests/IntegrationTests.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/runner-swift/Tests/RunnerTests/IntegrationTests.swift b/runner-swift/Tests/RunnerTests/IntegrationTests.swift index 91ed175..ca69840 100644 --- a/runner-swift/Tests/RunnerTests/IntegrationTests.swift +++ b/runner-swift/Tests/RunnerTests/IntegrationTests.swift @@ -158,22 +158,28 @@ struct IntegrationTests { let (tempDir, storage) = try await createTempStorage() defer { cleanup(tempDir) } - // Execute a fast task + // Execute a task that takes long enough to be observed as "running" let task = Task( id: "cleanup_test", executor: .shell, description: "Cleanup test", timeout: 60, - command: "echo done", + command: "sleep 2 && echo done", prompt: nil, workdir: nil ) - + let executor = makeExecutor(storage: storage, tempDir: tempDir) let result = try await executor.execute(task: task, trigger: "test") - - // Task is running - let runningBefore = try await storage.getRunningTasks() + + // Poll until the task appears as running (executor spawns a background process) + var runningBefore: [RunSummary] = [] + let pollDeadline = Date().addingTimeInterval(3) + while Date() < pollDeadline { + runningBefore = try await storage.getRunningTasks() + if runningBefore.count == 1 { break } + try await _Concurrency.Task.sleep(nanoseconds: 100_000_000) // 100ms + } #expect(runningBefore.count == 1) // Wait for completion From 17dba135bc34b1dd93a6a7504d27d7cc33b20971 Mon Sep 17 00:00:00 2001 From: nocoo Date: Thu, 16 Apr 2026 08:11:53 +0800 Subject: [PATCH 2/2] test(dashboard): fix upcoming tasks countdown test timing Use wildcard cron schedule (hour: *, minute: */10) and real-time Date() instead of fixed mock time, since calculateUpcomingTasks internally uses new Date() regardless of the timeProvider. --- dashboard/src/viewmodels/__tests__/useTasksVM.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboard/src/viewmodels/__tests__/useTasksVM.test.ts b/dashboard/src/viewmodels/__tests__/useTasksVM.test.ts index 8d54b0d..4f69a67 100644 --- a/dashboard/src/viewmodels/__tests__/useTasksVM.test.ts +++ b/dashboard/src/viewmodels/__tests__/useTasksVM.test.ts @@ -172,10 +172,10 @@ describe("useTasksVM", () => { { id: "heartbeat", executor: "shell", description: "Heartbeat", timeout: 60, command: "echo hi" }, ]; const schedules: Schedule[] = [ - { task: "heartbeat", hour: 9, minute: 0, weekday: "*" }, + { task: "heartbeat", hour: "*", minute: "*/10", weekday: "*" }, ]; - const now = new Date("2026-01-30T08:50:00Z"); + const now = new Date(); const { result } = renderHook(() => useTasksVM({ fetchTasks: async () => tasks,