diff --git a/backend/src/modules/catalog-etl/catalog-etl.service.spec.ts b/backend/src/modules/catalog-etl/catalog-etl.service.spec.ts index cd0a680..a57d5fe 100644 --- a/backend/src/modules/catalog-etl/catalog-etl.service.spec.ts +++ b/backend/src/modules/catalog-etl/catalog-etl.service.spec.ts @@ -45,6 +45,7 @@ describe('CatalogEtlService', () => { let mockEtlRunRepository: Record; let mockEtlWarningRepository: Record; let mockAdvisoryLockService: { withLock: jest.Mock }; + let mockDataSourceQuery: jest.Mock; beforeEach(async () => { mockEtlRunRepository = { @@ -93,7 +94,9 @@ describe('CatalogEtlService', () => { { provide: DataSource, useValue: { - query: jest.fn().mockResolvedValue([{ last_completed: null }]), + query: (mockDataSourceQuery = jest + .fn() + .mockResolvedValue([{ last_completed: null }])), }, }, { @@ -443,4 +446,27 @@ describe('CatalogEtlService', () => { expect(mockEtlRunRepository.create).not.toHaveBeenCalled(); }); }); + + describe('getLastSuccessfulStepRun', () => { + it('returns null when no completed run exists for the step', async () => { + mockDataSourceQuery.mockResolvedValueOnce([{ last_completed: null }]); + const result = await service.getLastSuccessfulStepRun('terminals-sync'); + expect(result).toBeNull(); + }); + + it('returns completed_at from a runStep() run matching step_name', async () => { + const ts = new Date('2026-01-01T00:00:00Z'); + mockDataSourceQuery.mockResolvedValueOnce([{ last_completed: ts }]); + const result = await service.getLastSuccessfulStepRun('terminals-sync'); + expect(result).toEqual(ts); + }); + + it('includes step_name IS NULL in the query to account for full runEtl() runs', async () => { + const ts = new Date('2026-01-01T00:00:00Z'); + mockDataSourceQuery.mockResolvedValueOnce([{ last_completed: ts }]); + await service.getLastSuccessfulStepRun('terminals-sync'); + const [sql] = mockDataSourceQuery.mock.calls[0] as [string]; + expect(sql).toContain('r.step_name IS NULL'); + }); + }); }); diff --git a/backend/src/modules/catalog-etl/catalog-etl.service.ts b/backend/src/modules/catalog-etl/catalog-etl.service.ts index 85fc8c5..aa76b98 100644 --- a/backend/src/modules/catalog-etl/catalog-etl.service.ts +++ b/backend/src/modules/catalog-etl/catalog-etl.service.ts @@ -189,7 +189,7 @@ export class CatalogEtlService { >( `SELECT MAX(r.completed_at) AS last_completed FROM station_etl_run r - WHERE r.step_name = $1 + WHERE (r.step_name = $1 OR r.step_name IS NULL) AND r.status = 'completed' AND r.steps_failed = 0 AND NOT EXISTS (