@@ -2,8 +2,10 @@ import { describe, expect, it } from 'vitest'
22import {
33 getUsageMetrics ,
44 InvalidReportError ,
5+ normalizeNativeAiCreditsReportDate ,
56 normalizeTokenUsageRecord ,
67 parseCsvRow ,
8+ parseNativeAiCreditsUsageRecord ,
79 parseNormalizedTokenUsageRecord ,
810 parseTokenUsageHeader ,
911 parseTokenUsageRecord ,
@@ -676,6 +678,134 @@ describe('parser and metric normalization', () => {
676678 } )
677679} )
678680
681+ describe ( 'native AI Credits parsing helpers' , ( ) => {
682+ it ( 'normalizes native short dates to ISO dates' , ( ) => {
683+ expect ( normalizeNativeAiCreditsReportDate ( '5/29/26' ) ) . toBe ( '2026-05-29' )
684+ expect ( normalizeNativeAiCreditsReportDate ( '05/09/2026' ) ) . toBe ( '2026-05-09' )
685+ expect ( normalizeNativeAiCreditsReportDate ( '2026-05-29' ) ) . toBe ( '2026-05-29' )
686+ } )
687+
688+ it ( 'parses native AI Credits usage and cost fields without enabling pipeline support' , ( ) => {
689+ const header = parseTokenUsageHeader ( HEADER_WITHOUT_EXCEEDS_QUOTA )
690+ const record = parseNativeAiCreditsUsageRecord (
691+ buildRow ( [
692+ '5/29/26' ,
693+ 'mona' ,
694+ 'copilot' ,
695+ 'copilot_ai_credit' ,
696+ 'Auto: Claude Haiku 4.5' ,
697+ '96.9990345' ,
698+ 'ai-credits' ,
699+ '0.01' ,
700+ '0.969990345' ,
701+ '0.15' ,
702+ '0.819990345' ,
703+ '3900' ,
704+ 'example-org' ,
705+ 'Cost Center A' ,
706+ '96.9990345' ,
707+ '0.969990345' ,
708+ ] ) ,
709+ header ,
710+ )
711+
712+ expect ( record ) . toMatchObject ( {
713+ date : '2026-05-29' ,
714+ username : 'mona' ,
715+ product : 'copilot' ,
716+ sku : 'copilot_ai_credit' ,
717+ quantity : 96.9990345 ,
718+ unit_type : 'ai-credits' ,
719+ gross_amount : 0.969990345 ,
720+ discount_amount : 0.15 ,
721+ net_amount : 0.819990345 ,
722+ total_monthly_quota : 3900 ,
723+ organization : 'example-org' ,
724+ cost_center_name : 'Cost Center A' ,
725+ aic_quantity : 96.9990345 ,
726+ aic_gross_amount : 0.969990345 ,
727+ aic_net_amount : 0.819990345 ,
728+ has_aic_quantity : true ,
729+ has_aic_gross_amount : true ,
730+ } )
731+ } )
732+
733+ it ( 'uses native quantity and cost fields as AIC aliases when alias columns are blank' , ( ) => {
734+ const header = parseTokenUsageHeader ( HEADER_WITHOUT_EXCEEDS_QUOTA )
735+ const record = parseNativeAiCreditsUsageRecord (
736+ buildRow ( [
737+ '5/29/26' ,
738+ 'hubot' ,
739+ 'spark' ,
740+ 'spark_ai_credit' ,
741+ 'GPT-5.2' ,
742+ '12.5' ,
743+ 'ai-credits' ,
744+ '0.01' ,
745+ '0.125' ,
746+ '0.025' ,
747+ '0.1' ,
748+ '7000' ,
749+ 'octodemo' ,
750+ '' ,
751+ '' ,
752+ '' ,
753+ ] ) ,
754+ header ,
755+ )
756+
757+ expect ( record ) . toMatchObject ( {
758+ date : '2026-05-29' ,
759+ quantity : 12.5 ,
760+ gross_amount : 0.125 ,
761+ discount_amount : 0.025 ,
762+ net_amount : 0.1 ,
763+ aic_quantity : 12.5 ,
764+ aic_gross_amount : 0.125 ,
765+ aic_net_amount : 0.1 ,
766+ has_aic_quantity : true ,
767+ has_aic_gross_amount : true ,
768+ } )
769+ } )
770+
771+ it ( 'keeps native quantity and cost fields authoritative when alias columns differ' , ( ) => {
772+ const header = parseTokenUsageHeader ( HEADER_WITHOUT_EXCEEDS_QUOTA )
773+ const record = parseNativeAiCreditsUsageRecord (
774+ buildRow ( [
775+ '5/29/26' ,
776+ 'octocat' ,
777+ 'copilot' ,
778+ 'copilot_ai_credit' ,
779+ 'GPT-5.2' ,
780+ '50' ,
781+ 'ai-credits' ,
782+ '0.01' ,
783+ '0.50' ,
784+ '0.20' ,
785+ '0.30' ,
786+ '3900' ,
787+ 'example-org' ,
788+ 'Cost Center A' ,
789+ '75' ,
790+ '0.75' ,
791+ ] ) ,
792+ header ,
793+ )
794+
795+ expect ( record ) . toMatchObject ( {
796+ quantity : 50 ,
797+ gross_amount : 0.5 ,
798+ discount_amount : 0.2 ,
799+ net_amount : 0.3 ,
800+ aic_quantity : 50 ,
801+ aic_gross_amount : 0.5 ,
802+ aic_net_amount : 0.3 ,
803+ has_aic_quantity : true ,
804+ has_aic_gross_amount : true ,
805+ } )
806+ } )
807+ } )
808+
679809describe ( 'validateHeader' , ( ) => {
680810 it ( 'accepts a header that contains all required columns' , ( ) => {
681811 const header = parseTokenUsageHeader ( FULL_HEADER )
0 commit comments