11package main
22
33import (
4- "os "
4+ "context "
55 "path/filepath"
66 "testing"
77
@@ -11,9 +11,7 @@ import (
1111
1212func loadCSVContent (t * testing.T , content string ) error {
1313 t .Helper ()
14- csvPath := filepath .Join (t .TempDir (), "recs.csv" )
15- require .NoError (t , os .WriteFile (csvPath , []byte (content ), 0o600 ))
16- _ , err := loadRecommendationsFromCSV (csvPath )
14+ _ , err := loadRecommendationsFromCSV (writeTestRecommendationsCSV (t , content ))
1715 return err
1816}
1917
@@ -46,8 +44,7 @@ func TestLoadRecommendationsFromCSV_StrictCount(t *testing.T) {
4644}
4745
4846func TestLoadRecommendationsFromCSV_CountTrimmed (t * testing.T ) {
49- csvPath := filepath .Join (t .TempDir (), "recs.csv" )
50- require .NoError (t , os .WriteFile (csvPath , []byte ("Service,Region,ResourceType,Count\n rds,us-east-1,db.t3.micro,\" 3 \" \n " ), 0o600 ))
47+ csvPath := writeTestRecommendationsCSV (t , "Service,Region,ResourceType,Count\n rds,us-east-1,db.t3.micro,\" 3 \" \n " )
5148 recs , err := loadRecommendationsFromCSV (csvPath )
5249 require .NoError (t , err )
5350 require .Len (t , recs , 1 )
@@ -86,10 +83,42 @@ func TestLoadRecommendationsFromCSV_StrictEstimatedSavings(t *testing.T) {
8683// A blank EstimatedSavings cell stays absent-as-zero: requireRankingSignal
8784// relies on it to refuse a binding --max-instances cap.
8885func TestLoadRecommendationsFromCSV_BlankSavingsStillZero (t * testing.T ) {
89- csvPath := filepath .Join (t .TempDir (), "recs.csv" )
90- require .NoError (t , os .WriteFile (csvPath , []byte ("Service,Region,ResourceType,Count,EstimatedSavings\n rds,us-east-1,db.t3.micro,2, \n " ), 0o600 ))
86+ csvPath := writeTestRecommendationsCSV (t , "Service,Region,ResourceType,Count,EstimatedSavings\n rds,us-east-1,db.t3.micro,2, \n " )
9187 recs , err := loadRecommendationsFromCSV (csvPath )
9288 require .NoError (t , err )
9389 require .Len (t , recs , 1 )
9490 assert .Zero (t , recs [0 ].EstimatedSavings )
9591}
92+
93+ func TestRunToolFromCSV_RejectsMalformedNumbers (t * testing.T ) {
94+ isolateAWSEnv (t )
95+ tests := []struct {
96+ name string
97+ count string
98+ savings string
99+ column string
100+ }{
101+ {"fractional count" , "3.7" , "100" , `column 4 "Count"` },
102+ {"count suffix" , "12 units" , "100" , `column 4 "Count"` },
103+ {"negative count" , "-5" , "100" , `column 4 "Count"` },
104+ {"count overflow" , "99999999999999999999" , "100" , `column 4 "Count"` },
105+ {"savings suffix" , "2" , "1000 USD" , `column 5 "EstimatedSavings"` },
106+ }
107+ for _ , tt := range tests {
108+ t .Run (tt .name , func (t * testing.T ) {
109+ cfg := Config {
110+ CSVInput : writeTestRecommendationsCSV (t , "Service,Region,ResourceType,Count,EstimatedSavings\n " +
111+ "rds,us-east-1,db.t3.micro," + tt .count + "," + tt .savings + "\n " ),
112+ CSVOutput : filepath .Join (t .TempDir (), "report.csv" ),
113+ AuditLog : filepath .Join (t .TempDir (), "audit.jsonl" ),
114+ ActualPurchase : false ,
115+ Coverage : 100 ,
116+ }
117+ err := runToolFromCSV (context .Background (), cfg )
118+ require .Error (t , err )
119+ assert .Contains (t , err .Error (), "failed to read CSV file: CSV line 2:" )
120+ assert .Contains (t , err .Error (), tt .column )
121+ assert .NoFileExists (t , cfg .CSVOutput )
122+ })
123+ }
124+ }
0 commit comments