Files
lti-api/cmd/seed-house-depreciation-standards/main_test.go
T

139 lines
4.5 KiB
Go

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)
}
}