Skip to content

Commit 6a79607

Browse files
committed
test: add dateRange edge case tests for invalid/None/empty dates
Adds 7 new tests covering: - Seat activity dateRange extracted from reportTime - dateRange values are valid Date objects (toISOString won't throw) - Empty lastActivityAt doesn't corrupt dateRange - 'None' and 'N/A' values are filtered from dateRange - All 6 real example files produce valid parseable dateRange dates These tests would have caught the RangeError: Invalid time value crash from the previous commit.
1 parent d954e82 commit 6a79607

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

src/lib/csv-parser.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,91 @@ describe('parseCSV — Copilot Seat Activity (real file)', () => {
788788
expect(row).not.toHaveProperty('grossAmount');
789789
expect(row).not.toHaveProperty('quantity');
790790
});
791+
792+
it('computes a valid dateRange from reportTime', () => {
793+
const report = parseCSV(csv, 'seats.csv');
794+
expect(report.dateRange.start).toMatch(/^\d{4}-\d{2}-\d{2}$/);
795+
expect(report.dateRange.end).toMatch(/^\d{4}-\d{2}-\d{2}$/);
796+
expect(report.dateRange.start <= report.dateRange.end).toBe(true);
797+
});
798+
799+
it('dateRange values are parseable as valid Date objects', () => {
800+
const report = parseCSV(csv, 'seats.csv');
801+
const start = new Date(report.dateRange.start + 'T00:00:00');
802+
const end = new Date(report.dateRange.end + 'T00:00:00');
803+
expect(isNaN(start.getTime())).toBe(false);
804+
expect(isNaN(end.getTime())).toBe(false);
805+
expect(start.toISOString()).toBeTruthy();
806+
expect(end.toISOString()).toBeTruthy();
807+
});
808+
});
809+
810+
// ─── dateRange Edge Cases ──────────────────────────────────────────────────────
811+
812+
describe('parseCSV — dateRange edge cases', () => {
813+
const seatHeader = 'Report Time,Login,Last Authenticated At,Last Activity At,Last Surface Used,Organization';
814+
815+
it('handles rows where lastActivityAt is empty', () => {
816+
const csv = [
817+
seatHeader,
818+
'2026-03-28T06:54:33Z,user1,2026-03-27T03:52:53Z,,None,org1',
819+
'2026-03-28T06:54:33Z,user2,2026-03-27T03:52:53Z,,None,org1',
820+
].join('\n');
821+
const report = parseCSV(csv, 'seats.csv');
822+
expect(report.dateRange.start).toMatch(/^\d{4}-\d{2}-\d{2}$/);
823+
expect(report.dateRange.end).toMatch(/^\d{4}-\d{2}-\d{2}$/);
824+
expect(isNaN(new Date(report.dateRange.end + 'T00:00:00').getTime())).toBe(false);
825+
});
826+
827+
it('handles rows where lastActivityAt is "None"', () => {
828+
const csv = [
829+
seatHeader,
830+
'2026-03-28T06:54:33Z,user1,2026-03-27T03:52:53Z,None,None,org1',
831+
].join('\n');
832+
const report = parseCSV(csv, 'seats.csv');
833+
expect(report.dateRange.start).toMatch(/^\d{4}-\d{2}-\d{2}$/);
834+
expect(report.dateRange.end).toMatch(/^\d{4}-\d{2}-\d{2}$/);
835+
// "None" must NOT leak into dateRange
836+
expect(report.dateRange.start).not.toBe('None');
837+
expect(report.dateRange.end).not.toBe('None');
838+
});
839+
840+
it('handles rows where lastActivityAt is "N/A"', () => {
841+
const csv = [
842+
seatHeader,
843+
'2026-03-28T06:54:33Z,user1,2026-03-27T03:52:53Z,N/A,None,org1',
844+
].join('\n');
845+
const report = parseCSV(csv, 'seats.csv');
846+
expect(report.dateRange.start).not.toBe('N/A');
847+
expect(report.dateRange.end).not.toBe('N/A');
848+
});
849+
850+
it('all real example files produce valid dateRange dates', () => {
851+
const files = [
852+
'premiumRequestUsageReport_1_c6fca30f0acd458098a95808eaf43399.csv',
853+
'Token.Usage.Report.csv',
854+
'usageReport_1_7f2ed6006ee54fb8af73f5cbb7ac1f1d.csv',
855+
'ghas_active_committers_octodemo_2026-03-27T1521.csv',
856+
'export-octodemo-1774679438.csv',
857+
'octodemo-seat-activity-1774680875.csv',
858+
];
859+
for (const file of files) {
860+
const csvText = loadExample(file);
861+
const report = parseCSV(csvText, file);
862+
if (report.dateRange.start) {
863+
expect(report.dateRange.start).toMatch(/^\d{4}-\d{2}-\d{2}$/);
864+
const start = new Date(report.dateRange.start + 'T00:00:00');
865+
expect(isNaN(start.getTime())).toBe(false);
866+
}
867+
if (report.dateRange.end) {
868+
expect(report.dateRange.end).toMatch(/^\d{4}-\d{2}-\d{2}$/);
869+
const end = new Date(report.dateRange.end + 'T00:00:00');
870+
expect(isNaN(end.getTime())).toBe(false);
871+
// This is the exact operation that crashed: toISOString() must not throw
872+
expect(() => end.toISOString()).not.toThrow();
873+
}
874+
}
875+
});
791876
});
792877

793878
// ─── Updated Cross-Report Schema Validation ───────────────────────────────────

0 commit comments

Comments
 (0)