package main import ( "strings" "testing" "time" ) func TestParseHeaderIndexes(t *testing.T) { t.Run("finds both columns regardless of order/case", func(t *testing.T) { dayIdx, multIdx, issues := parseHeaderIndexes([]string{"Multiplication_Percentage", "Day"}) if len(issues) != 0 { t.Fatalf("unexpected issues: %v", issues) } if dayIdx != 1 || multIdx != 0 { t.Fatalf("dayIdx=%d multIdx=%d", dayIdx, multIdx) } }) t.Run("missing headers reported", func(t *testing.T) { _, _, issues := parseHeaderIndexes([]string{"foo", "bar"}) if len(issues) != 2 { t.Fatalf("want 2 issues, got %v", issues) } }) } func TestParsePositiveInt(t *testing.T) { cases := map[string]bool{"1": true, "532": true, "12.0": true, "0": false, "-3": false, "1.5": false, "": false, "x": false} for raw, ok := range cases { _, err := parsePositiveInt(raw) if ok && err != nil { t.Errorf("%q: unexpected error %v", raw, err) } if !ok && err == nil { t.Errorf("%q: expected error", raw) } } } func TestParseMultiplication(t *testing.T) { cases := map[string]bool{"0.997742664": true, "1": true, "9.11e-12": true, "0": false, "-0.1": false, "1.0001": false, "": false, "abc": false} for raw, ok := range cases { _, err := parseMultiplication(raw) if ok && err != nil { t.Errorf("%q: unexpected error %v", raw, err) } if !ok && err == nil { t.Errorf("%q: expected error", raw) } } } func TestParseCurveRowDuplicateDay(t *testing.T) { seen := map[int]int{} if _, issues := parseCurveRow([]string{"1", "0.99"}, 2, 0, 1, seen); len(issues) != 0 { t.Fatalf("row 1 should be valid, got %v", issues) } _, issues := parseCurveRow([]string{"1", "0.98"}, 3, 0, 1, seen) if len(issues) != 1 || !strings.Contains(issues[0].Message, "duplicate day 1") { t.Fatalf("expected duplicate-day issue, got %v", issues) } } func TestBuildDayCoverageIssues(t *testing.T) { curve := []curveRow{{RowNumber: 2, Day: 1, Mult: 0.99}, {RowNumber: 3, Day: 999, Mult: 0.99}} global := map[int]bool{1: true} issues := buildDayCoverageIssues(curve, global, "close_house") if len(issues) != 1 || issues[0].Row != 3 { t.Fatalf("expected 1 issue for day 999, got %v", issues) } } func TestFormatValuesTuplesFirstNumericCast(t *testing.T) { out := formatValuesTuples([]curveRow{{Day: 1, Mult: 0.997742664}, {Day: 2, Mult: 1}}) if !strings.Contains(out, "(1, 0.997742664::numeric)") { t.Fatalf("first tuple must cast ::numeric: %s", out) } if strings.Contains(out, "(2, 1::numeric)") { t.Fatalf("only the first tuple should be cast: %s", out) } } func TestBuildUpSQL(t *testing.T) { opts := options{ProjectFlockID: 52, EffectiveDate: "2026-05-31"} curve := []curveRow{{Day: 1, Mult: 0.997742664}, {Day: 2, Mult: 0.5}} sql := buildUpSQL(opts, "close_house", curve) mustContain(t, sql, "INSERT INTO house_depreciation_standards") mustContain(t, sql, "52, g.house_type, g.day, DATE '2026-05-31'") mustContain(t, sql, "v.mult, (1 - v.mult) * 100, g.standard_week") mustContain(t, sql, "house_type = 'close_house'::house_type_enum") mustContain(t, sql, "(1, 0.997742664::numeric)") mustContain(t, sql, "JOIN LATERAL") mustContain(t, sql, "DELETE FROM farm_depreciation_snapshots WHERE project_flock_id = 52;") } func TestBuildDownSQL(t *testing.T) { sql := buildDownSQL(options{ProjectFlockID: 52, EffectiveDate: "2026-05-31"}) mustContain(t, sql, "DELETE FROM house_depreciation_standards") mustContain(t, sql, "WHERE project_flock_id = 52 AND effective_date = DATE '2026-05-31';") mustContain(t, sql, "DELETE FROM farm_depreciation_snapshots WHERE project_flock_id = 52;") } func TestResolveEffectiveDate(t *testing.T) { loc := time.UTC if _, err := resolveEffectiveDate("2026-05-31", loc); err != nil { t.Fatalf("valid date errored: %v", err) } if _, err := resolveEffectiveDate("31-05-2026", loc); err == nil { t.Fatalf("expected error for wrong format") } got, err := resolveEffectiveDate("", loc) if err != nil { t.Fatalf("default date errored: %v", err) } if got.Hour() != 0 || got.Minute() != 0 { t.Fatalf("default date should be midnight, got %v", got) } } func TestNormalizeHouseType(t *testing.T) { for _, ok := range []string{"open_house", "CLOSE_HOUSE", " close_house "} { if _, err := normalizeHouseType(ok); err != nil { t.Errorf("%q should be valid: %v", ok, err) } } if _, err := normalizeHouseType("barn"); err == nil { t.Errorf("barn should be invalid") } } func mustContain(t *testing.T, haystack, needle string) { t.Helper() if !strings.Contains(haystack, needle) { t.Fatalf("expected to find %q in:\n%s", needle, haystack) } }