diff --git a/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.test.ts b/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.test.ts index 70d535da51..d5f5a183bc 100644 --- a/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.test.ts +++ b/packages/cli/src/modules/insights/database/repositories/__tests__/insights-by-period.repository.test.ts @@ -14,6 +14,52 @@ describe('InsightsByPeriodRepository', () => { await testDb.init(); }); + describe('getInsightsByTime', () => { + test.each([ + '2023-10-01T00:00:00Z', + '2023-10-01T00:00:00.000Z', + '2023-10-01T00:00:00+00:00', + '2023-10-01T00:00:00.000+00:00', + '2023-10-01 00:00:00', + '2023-10-01 00:00:00.000', + '2023-10-01 00:00:00+00', + '2023-10-01 00:00:00-02', + '2023-10-01 00:00:00-00', + ])( + 'should parse correctly valid date %s when calling insights by time', + async (periodStart) => { + // ARRANGE + const insightsByPeriodRepository = Container.get(InsightsByPeriodRepository); + + // Mock the manager.queryBuilder.getRawMany to return a mocked value + const mockResult = [{ periodStart, runTime: 0, succeeded: 0, failed: 0, timeSaved: 0 }]; + + const queryBuilderMock = { + addCommonTableExpression: jest.fn().mockReturnThis(), + select: jest.fn().mockReturnThis(), + innerJoin: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), + orderBy: jest.fn().mockReturnThis(), + getRawMany: jest.fn().mockResolvedValue(mockResult), + }; + + jest + .spyOn(insightsByPeriodRepository.manager, 'createQueryBuilder') + .mockReturnValueOnce(queryBuilderMock as any); + + const result = await insightsByPeriodRepository.getInsightsByTime({ + maxAgeInDays: 1, + periodUnit: 'day', + }); + + // ASSERT + expect(result[0]?.periodStart).not.toBeNull(); + expect(new Date(result[0]?.periodStart).toString()).not.toBe('Invalid Date'); + }, + ); + }); + describe('Avoid deadlock error', () => { let defaultBatchSize: number; beforeAll(() => { diff --git a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts index c59b5b9671..edb6294640 100644 --- a/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts +++ b/packages/cli/src/modules/insights/database/repositories/insights-by-period.repository.ts @@ -41,13 +41,19 @@ const aggregatedInsightsByWorkflowParser = z const aggregatedInsightsByTimeParser = z .object({ - periodStart: z - .union([z.date(), z.string()]) - .transform((value) => - value instanceof Date - ? value.toISOString() - : DateTime.fromSQL(value.toString(), { zone: 'utc' }).toISO(), - ), + periodStart: z.union([z.date(), z.string()]).transform((value) => { + if (value instanceof Date) { + return value.toISOString(); + } + + const parsedDatetime = DateTime.fromSQL(value.toString(), { zone: 'utc' }); + if (parsedDatetime.isValid) { + return parsedDatetime.toISO(); + } + + // fallback on native date parsing + return new Date(value).toISOString(); + }), runTime: z.union([z.number(), z.string()]).transform((value) => Number(value)), succeeded: z.union([z.number(), z.string()]).transform((value) => Number(value)), failed: z.union([z.number(), z.string()]).transform((value) => Number(value)), @@ -381,7 +387,7 @@ export class InsightsByPeriodRepository extends Repository { ]) .innerJoin('date_range', 'date_range', '1=1') .where(`${this.escapeField('periodStart')} >= date_range.start_date`) - .addGroupBy(this.getPeriodStartExpr(periodUnit)) + .groupBy(this.getPeriodStartExpr(periodUnit)) .orderBy(this.getPeriodStartExpr(periodUnit), 'ASC'); const rawRows = await rawRowsQuery.getRawMany();