|
1 | 1 | /** |
2 | 2 | * @vitest-environment node |
3 | 3 | */ |
4 | | -import { asyncJobs, workflowExecutionLogs } from '@sim/db/schema' |
| 4 | +import { asyncJobs, tableJobs, workflowExecutionLogs } from '@sim/db/schema' |
5 | 5 | import { createMockRequest, dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' |
6 | 6 | import { beforeEach, describe, expect, it, vi } from 'vitest' |
7 | 7 | import { MAX_JOB_DURATION_SECONDS, MIN_JOB_DURATION_SECONDS } from '@/lib/core/async-jobs' |
@@ -141,6 +141,45 @@ describe('stale execution cleanup deadline grace', () => { |
141 | 141 | ) |
142 | 142 | }) |
143 | 143 |
|
| 144 | + it('keeps table-job heartbeat cleanup independent from workflow timeout policy', async () => { |
| 145 | + vi.useFakeTimers() |
| 146 | + vi.setSystemTime(new Date('2026-08-03T12:00:00.000Z')) |
| 147 | + |
| 148 | + try { |
| 149 | + const response = await GET(createRequest()) |
| 150 | + |
| 151 | + expect(response.status).toBe(200) |
| 152 | + const expectedThreshold = new Date('2026-08-03T10:25:00.000Z') |
| 153 | + const tableJobComparisons = dbChainMockFns.where.mock.calls |
| 154 | + .flatMap(([condition]) => flattenConditions(condition)) |
| 155 | + .filter( |
| 156 | + (condition) => |
| 157 | + condition.type === 'lt' && |
| 158 | + condition.left === tableJobs.updatedAt && |
| 159 | + condition.right instanceof Date && |
| 160 | + condition.right.getTime() === expectedThreshold.getTime() |
| 161 | + ) |
| 162 | + |
| 163 | + expect(tableJobComparisons).toHaveLength(2) |
| 164 | + expect(tableJobComparisons.map(({ right }) => right)).toEqual([ |
| 165 | + expectedThreshold, |
| 166 | + expectedThreshold, |
| 167 | + ]) |
| 168 | + |
| 169 | + const tableJobUpdateIndex = dbChainMockFns.update.mock.calls.findIndex( |
| 170 | + ([table]) => table === tableJobs |
| 171 | + ) |
| 172 | + const update = dbChainMockFns.set.mock.calls[tableJobUpdateIndex]?.[0] as { |
| 173 | + error: string |
| 174 | + } |
| 175 | + expect(update.error).toBe( |
| 176 | + 'Job terminated: no progress for more than 95 minutes (worker timeout or crash)' |
| 177 | + ) |
| 178 | + } finally { |
| 179 | + vi.useRealTimers() |
| 180 | + } |
| 181 | + }) |
| 182 | + |
144 | 183 | it('caps every bulk mutation and returns only scalar export cleanup fields', async () => { |
145 | 184 | const stateBatch = Array.from({ length: 1000 }, (_, index) => ({ id: `state-${index}` })) |
146 | 185 | const retentionBatch = Array.from({ length: 2000 }, (_, index) => ({ |
|
0 commit comments