@@ -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