1+ import { describe , it , expect } from 'vitest' ;
2+ import {
3+ getModelUsageSummary ,
4+ CopilotUsageData ,
5+ COPILOT_PLANS ,
6+ PLAN_MONTHLY_LIMITS ,
7+ MODEL_MULTIPLIERS ,
8+ DEFAULT_MODELS ,
9+ EXCESS_REQUEST_COST
10+ } from '../lib/utils' ;
11+
12+ describe ( 'Model Info and Limits Feature' , ( ) => {
13+ const mockData : CopilotUsageData [ ] = [
14+ {
15+ timestamp : new Date ( '2025-01-01T10:00:00Z' ) ,
16+ user : 'user1' ,
17+ model : 'gpt-4o-2024-11-20' ,
18+ requestsUsed : 100 ,
19+ exceedsQuota : false ,
20+ totalMonthlyQuota : '500'
21+ } ,
22+ {
23+ timestamp : new Date ( '2025-01-01T11:00:00Z' ) ,
24+ user : 'user2' ,
25+ model : 'gpt-4.1-2025-04-14' ,
26+ requestsUsed : 50 ,
27+ exceedsQuota : true ,
28+ totalMonthlyQuota : '500'
29+ } ,
30+ {
31+ timestamp : new Date ( '2025-01-01T12:00:00Z' ) ,
32+ user : 'user3' ,
33+ model : 'gpt-4.1-vision' ,
34+ requestsUsed : 25 ,
35+ exceedsQuota : false ,
36+ totalMonthlyQuota : '500'
37+ } ,
38+ {
39+ timestamp : new Date ( '2025-01-01T13:00:00Z' ) ,
40+ user : 'user4' ,
41+ model : 'o3-mini-2025-01-31' ,
42+ requestsUsed : 10 ,
43+ exceedsQuota : true ,
44+ totalMonthlyQuota : '500'
45+ }
46+ ] ;
47+
48+ it ( 'should calculate plan limits based on model multipliers' , ( ) => {
49+ const result = getModelUsageSummary ( mockData ) ;
50+
51+ const defaultGroup = result . find ( item => item . model === 'Default (GPT-4o, GPT-4.1)' ) ;
52+ expect ( defaultGroup ) . toBeDefined ( ) ;
53+
54+ if ( defaultGroup ) {
55+ expect ( defaultGroup . multiplier ) . toBe ( 1 ) ;
56+ expect ( defaultGroup . individualPlanLimit ) . toBe ( 150 ) ; // 150 / 1
57+ expect ( defaultGroup . businessPlanLimit ) . toBe ( 500 ) ; // 500 / 1
58+ expect ( defaultGroup . enterprisePlanLimit ) . toBe ( 500 ) ; // 500 / 1
59+ }
60+ } ) ;
61+
62+ it ( 'should group default models (GPT-4o and GPT-4.1) together' , ( ) => {
63+ const result = getModelUsageSummary ( mockData ) ;
64+
65+ const defaultGroup = result . find ( item => item . model === 'Default (GPT-4o, GPT-4.1)' ) ;
66+ expect ( defaultGroup ) . toBeDefined ( ) ;
67+
68+ if ( defaultGroup ) {
69+ // Should combine requests from both gpt-4o-2024-11-20 (100) and gpt-4.1-2025-04-14 (50)
70+ expect ( defaultGroup . totalRequests ) . toBe ( 150 ) ;
71+ expect ( defaultGroup . compliantRequests ) . toBe ( 100 ) ;
72+ expect ( defaultGroup . exceedingRequests ) . toBe ( 50 ) ;
73+ }
74+
75+ // Should not have individual entries for default models
76+ const gpt4oEntry = result . find ( item => item . model === 'gpt-4o-2024-11-20' ) ;
77+ const gpt41Entry = result . find ( item => item . model === 'gpt-4.1-2025-04-14' ) ;
78+ expect ( gpt4oEntry ) . toBeUndefined ( ) ;
79+ expect ( gpt41Entry ) . toBeUndefined ( ) ;
80+ } ) ;
81+
82+ it ( 'should keep non-default models separate' , ( ) => {
83+ const result = getModelUsageSummary ( mockData ) ;
84+
85+ const visionModel = result . find ( item => item . model === 'gpt-4.1-vision' ) ;
86+ expect ( visionModel ) . toBeDefined ( ) ;
87+ expect ( visionModel ?. totalRequests ) . toBe ( 25 ) ;
88+
89+ const o3Model = result . find ( item => item . model === 'o3-mini-2025-01-31' ) ;
90+ expect ( o3Model ) . toBeDefined ( ) ;
91+ expect ( o3Model ?. totalRequests ) . toBe ( 10 ) ;
92+ } ) ;
93+
94+ it ( 'should calculate excess costs correctly' , ( ) => {
95+ const result = getModelUsageSummary ( mockData ) ;
96+
97+ const defaultGroup = result . find ( item => item . model === 'Default (GPT-4o, GPT-4.1)' ) ;
98+ expect ( defaultGroup ) . toBeDefined ( ) ;
99+
100+ if ( defaultGroup ) {
101+ // 50 exceeding requests * 1x multiplier * $0.15 = $7.50
102+ expect ( defaultGroup . excessCost ) . toBe ( 50 * 1 * EXCESS_REQUEST_COST ) ;
103+ }
104+
105+ const o3Model = result . find ( item => item . model === 'o3-mini-2025-01-31' ) ;
106+ if ( o3Model ) {
107+ // 10 exceeding requests * 1x multiplier * $0.15 = $1.50
108+ expect ( o3Model . excessCost ) . toBe ( 10 * 1 * EXCESS_REQUEST_COST ) ;
109+ }
110+ } ) ;
111+
112+ it ( 'should include all required fields in ModelUsageSummary' , ( ) => {
113+ const result = getModelUsageSummary ( mockData ) ;
114+
115+ expect ( result . length ) . toBeGreaterThan ( 0 ) ;
116+
117+ result . forEach ( item => {
118+ expect ( item ) . toHaveProperty ( 'model' ) ;
119+ expect ( item ) . toHaveProperty ( 'displayName' ) ;
120+ expect ( item ) . toHaveProperty ( 'totalRequests' ) ;
121+ expect ( item ) . toHaveProperty ( 'compliantRequests' ) ;
122+ expect ( item ) . toHaveProperty ( 'exceedingRequests' ) ;
123+ expect ( item ) . toHaveProperty ( 'multiplier' ) ;
124+ expect ( item ) . toHaveProperty ( 'individualPlanLimit' ) ;
125+ expect ( item ) . toHaveProperty ( 'businessPlanLimit' ) ;
126+ expect ( item ) . toHaveProperty ( 'enterprisePlanLimit' ) ;
127+ expect ( item ) . toHaveProperty ( 'excessCost' ) ;
128+
129+ // Validate types
130+ expect ( typeof item . model ) . toBe ( 'string' ) ;
131+ expect ( typeof item . displayName ) . toBe ( 'string' ) ;
132+ expect ( typeof item . totalRequests ) . toBe ( 'number' ) ;
133+ expect ( typeof item . compliantRequests ) . toBe ( 'number' ) ;
134+ expect ( typeof item . exceedingRequests ) . toBe ( 'number' ) ;
135+ expect ( typeof item . multiplier ) . toBe ( 'number' ) ;
136+ expect ( typeof item . individualPlanLimit ) . toBe ( 'number' ) ;
137+ expect ( typeof item . businessPlanLimit ) . toBe ( 'number' ) ;
138+ expect ( typeof item . enterprisePlanLimit ) . toBe ( 'number' ) ;
139+ expect ( typeof item . excessCost ) . toBe ( 'number' ) ;
140+ } ) ;
141+ } ) ;
142+
143+ it ( 'should handle unknown models with default multiplier' , ( ) => {
144+ const unknownModelData : CopilotUsageData [ ] = [
145+ {
146+ timestamp : new Date ( '2025-01-01T10:00:00Z' ) ,
147+ user : 'user1' ,
148+ model : 'unknown-model-2025' ,
149+ requestsUsed : 20 ,
150+ exceedsQuota : false ,
151+ totalMonthlyQuota : '500'
152+ }
153+ ] ;
154+
155+ const result = getModelUsageSummary ( unknownModelData ) ;
156+
157+ expect ( result ) . toHaveLength ( 1 ) ;
158+ expect ( result [ 0 ] . model ) . toBe ( 'unknown-model-2025' ) ;
159+ expect ( result [ 0 ] . multiplier ) . toBe ( 1 ) ; // Default multiplier
160+ expect ( result [ 0 ] . individualPlanLimit ) . toBe ( 150 ) ; // 150 / 1
161+ expect ( result [ 0 ] . businessPlanLimit ) . toBe ( 500 ) ; // 500 / 1
162+ } ) ;
163+
164+ it ( 'should sort results by total requests descending' , ( ) => {
165+ const result = getModelUsageSummary ( mockData ) ;
166+
167+ for ( let i = 0 ; i < result . length - 1 ; i ++ ) {
168+ expect ( result [ i ] . totalRequests ) . toBeGreaterThanOrEqual ( result [ i + 1 ] . totalRequests ) ;
169+ }
170+ } ) ;
171+ } ) ;
0 commit comments