mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
139 lines
4.5 KiB
Go
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)
|
|
}
|
|
}
|