1+ /**
2+ * @jest -environment node
3+ */
4+ import { estimateETFGain , analyzeMultipleIndustries } from "@/lib/finance/finance-agent"
5+ import { ModelName } from "@/lib/utils/modelUtils"
6+
7+ describe ( "Finance Agent tests" , ( ) => {
8+ jest . setTimeout ( 60000 ) // 1-minute timeout
9+
10+ it ( "estimates ETF gain for Biotechnology industry with different models" , async ( ) => {
11+ const industryName = "Biotechnology"
12+ const models : ModelName [ ] = [
13+ 'gemini-1.5-pro' ,
14+ 'claude-3-5-sonnet-20240620'
15+ ]
16+
17+ for ( const modelName of models ) {
18+ console . log ( `\nTesting with model: ${ modelName } ` )
19+ const result = await estimateETFGain ( industryName , modelName )
20+
21+ // Check the structure of the response
22+ expect ( result ) . toHaveProperty ( "industryName" )
23+ expect ( result ) . toHaveProperty ( "parameters" )
24+ expect ( result ) . toHaveProperty ( "results" )
25+ expect ( result ) . toHaveProperty ( "tokenUsage" )
26+
27+ // Check parameters
28+ expect ( result . parameters ) . toHaveProperty ( "regulatoryCost" )
29+ expect ( result . parameters ) . toHaveProperty ( "percentageOfRevenue" )
30+ expect ( result . parameters ) . toHaveProperty ( "potentialDecrease" )
31+ expect ( result . parameters ) . toHaveProperty ( "netProfitMargin" )
32+
33+ // Validate parameter ranges
34+ expect ( result . parameters . regulatoryCost ) . toBeGreaterThan ( 0 )
35+ expect ( result . parameters . percentageOfRevenue ) . toBeGreaterThan ( 0 )
36+ expect ( result . parameters . percentageOfRevenue ) . toBeLessThan ( 100 )
37+ expect ( result . parameters . potentialDecrease ) . toBeGreaterThan ( 0 )
38+ expect ( result . parameters . potentialDecrease ) . toBeLessThan ( 100 )
39+ expect ( result . parameters . netProfitMargin ) . toBeGreaterThan ( 0 )
40+ expect ( result . parameters . netProfitMargin ) . toBeLessThan ( 100 )
41+
42+ // Check results
43+ expect ( result . results ) . toHaveProperty ( "industryRevenue" )
44+ expect ( result . results ) . toHaveProperty ( "currentNetIncome" )
45+ expect ( result . results ) . toHaveProperty ( "deltaC" )
46+ expect ( result . results ) . toHaveProperty ( "percentIncreaseNetIncome" )
47+ expect ( result . results ) . toHaveProperty ( "estimatedETFIncrease" )
48+
49+ // Validate calculations
50+ expect ( result . results . industryRevenue ) . toBeGreaterThan ( 0 )
51+ expect ( result . results . currentNetIncome ) . toBeGreaterThan ( 0 )
52+ expect ( result . results . deltaC ) . toBeGreaterThan ( 0 )
53+ expect ( result . results . percentIncreaseNetIncome ) . toBeGreaterThan ( 0 )
54+ expect ( result . results . estimatedETFIncrease ) . toBeGreaterThan ( 0 )
55+
56+ // Optional: Log the result for manual inspection
57+ console . log ( `${ modelName } Analysis:` , JSON . stringify ( result , null , 2 ) )
58+ }
59+ } )
60+
61+ it ( "estimates ETF gain for Financial Services industry" , async ( ) => {
62+ const industryName = "Financial Services"
63+ const result = await estimateETFGain ( industryName )
64+
65+ // Basic structure checks
66+ expect ( result ) . toBeDefined ( )
67+ expect ( result . industryName ) . toBe ( industryName )
68+
69+ // Check token usage
70+ expect ( result . tokenUsage ) . toBeDefined ( )
71+ expect ( result . tokenUsage ?. totalTokens ) . toBeGreaterThan ( 0 )
72+
73+ // Validate that calculations are consistent
74+ const { parameters, results } = result
75+
76+ // Calculate industry revenue manually to verify
77+ const calculatedRevenue = parameters . regulatoryCost / ( parameters . percentageOfRevenue / 100 )
78+ expect ( results . industryRevenue ) . toBeCloseTo ( calculatedRevenue , 5 )
79+
80+ // Calculate net income manually to verify
81+ const calculatedNetIncome = results . industryRevenue * ( parameters . netProfitMargin / 100 )
82+ expect ( results . currentNetIncome ) . toBeCloseTo ( calculatedNetIncome , 5 )
83+
84+ // Calculate regulatory cost savings manually to verify
85+ const calculatedSavings = parameters . regulatoryCost * ( parameters . potentialDecrease / 100 )
86+ expect ( results . deltaC ) . toBeCloseTo ( calculatedSavings , 5 )
87+
88+ console . log ( "Financial Services Analysis:" , JSON . stringify ( result , null , 2 ) )
89+ } )
90+
91+ it ( "provides consistent results for the same industry" , async ( ) => {
92+ const industryName = "Healthcare"
93+
94+ // Make two separate calls
95+ const result1 = await estimateETFGain ( industryName )
96+ const result2 = await estimateETFGain ( industryName )
97+
98+ // Results should be similar but not identical due to AI variation
99+ // We'll check if they're within reasonable ranges of each other
100+ const marginOfError = 0.5 // 50% margin to account for AI variation
101+
102+ expect (
103+ Math . abs ( result1 . parameters . regulatoryCost - result2 . parameters . regulatoryCost ) /
104+ result1 . parameters . regulatoryCost
105+ ) . toBeLessThan ( marginOfError )
106+
107+ expect (
108+ Math . abs ( result1 . parameters . percentageOfRevenue - result2 . parameters . percentageOfRevenue ) /
109+ result1 . parameters . percentageOfRevenue
110+ ) . toBeLessThan ( marginOfError )
111+
112+ console . log ( "Consistency Test Results:" , {
113+ firstCall : result1 ,
114+ secondCall : result2
115+ } )
116+ } )
117+
118+ it ( "analyzes multiple industries with different models" , async ( ) => {
119+ const industries = [
120+ "Biotechnology" ,
121+ "Financial Services" ,
122+ "Healthcare"
123+ ] ;
124+
125+ const models : ModelName [ ] = [
126+ 'gemini-1.5-pro' ,
127+ 'claude-3-5-sonnet-20240620'
128+ ]
129+
130+ for ( const modelName of models ) {
131+ console . log ( `\n=== Testing with model: ${ modelName } ===` )
132+
133+ const results = await analyzeMultipleIndustries ( industries , modelName ) ;
134+
135+ // Check we got results for all industries
136+ expect ( results . length ) . toBe ( industries . length ) ;
137+
138+ // Check results are sorted by ETF increase
139+ for ( let i = 0 ; i < results . length - 1 ; i ++ ) {
140+ expect ( results [ i ] . results . estimatedETFIncrease )
141+ . toBeGreaterThanOrEqual ( results [ i + 1 ] . results . estimatedETFIncrease ) ;
142+ }
143+
144+ // Verify structure of each result
145+ results . forEach ( result => {
146+ // Check basic structure
147+ expect ( result ) . toHaveProperty ( "industryName" ) ;
148+ expect ( result ) . toHaveProperty ( "parameters" ) ;
149+ expect ( result ) . toHaveProperty ( "results" ) ;
150+
151+ // Check parameters are within reasonable ranges
152+ expect ( result . parameters . regulatoryCost ) . toBeGreaterThan ( 0 ) ;
153+ expect ( result . parameters . percentageOfRevenue ) . toBeLessThan ( 100 ) ;
154+ expect ( result . parameters . potentialDecrease ) . toBeLessThan ( 100 ) ;
155+ expect ( result . parameters . netProfitMargin ) . toBeLessThan ( 100 ) ;
156+
157+ // Verify calculations for each industry
158+ const calculatedRevenue = result . parameters . regulatoryCost /
159+ ( result . parameters . percentageOfRevenue / 100 ) ;
160+ expect ( result . results . industryRevenue ) . toBeCloseTo ( calculatedRevenue , 5 ) ;
161+ } ) ;
162+
163+ // Check that industries are unique
164+ const uniqueIndustries = new Set ( results . map ( r => r . industryName ) ) ;
165+ expect ( uniqueIndustries . size ) . toBe ( industries . length ) ;
166+
167+ console . log ( `\n${ modelName } Multi-Industry Analysis:` ,
168+ JSON . stringify ( results , null , 2 ) ) ;
169+ }
170+ } , 480000 ) ; // 8-minute timeout for multiple models and industries
171+
172+ it ( "compares results between different models" , async ( ) => {
173+ const industryName = "Healthcare" ;
174+ const models : ModelName [ ] = [
175+ 'gemini-1.5-pro' ,
176+ 'claude-3-5-sonnet-20240620'
177+ ] ;
178+
179+ const results = await Promise . all (
180+ models . map ( model => estimateETFGain ( industryName , model ) )
181+ ) ;
182+
183+ // Compare results between models
184+ results . forEach ( ( result , i ) => {
185+ console . log ( `\n${ models [ i ] } results:` )
186+ console . log ( `ETF Increase: ${ result . results . estimatedETFIncrease . toFixed ( 2 ) } %` )
187+ console . log ( `Regulatory Cost: $${ result . parameters . regulatoryCost . toFixed ( 2 ) } B` )
188+ console . log ( `Token Usage: ${ result . tokenUsage ?. totalTokens || 'N/A' } ` )
189+ } ) ;
190+
191+ // Check that results are within reasonable ranges of each other
192+ const marginOfError = 0.5 ; // 50% margin
193+ for ( let i = 0 ; i < results . length - 1 ; i ++ ) {
194+ const ratio = Math . abs (
195+ results [ i ] . results . estimatedETFIncrease -
196+ results [ i + 1 ] . results . estimatedETFIncrease
197+ ) / results [ i ] . results . estimatedETFIncrease ;
198+
199+ expect ( ratio ) . toBeLessThan ( marginOfError ) ;
200+ }
201+ } , 120000 ) ; // 2-minute timeout for model comparison
202+ } )
0 commit comments