//go:build test && dev package payments import ( "os" "path/filepath" "regexp" "strconv" "testing" "github.com/stretchr/testify/require" ) // policyTSValue extracts the numeric value of a `NAME: value,` entry from the // frontend's POLICY object literal (policy.ts), tolerating the tab-indented // formatting the file uses. Returns "" when the name is absent. func policyTSValue(t *testing.T, src, name string) string { t.Helper() re := regexp.MustCompile(`(?m)^\s*` + regexp.QuoteMeta(name) + `:\s*(\d+(?:\.\d+)?),`) m := re.FindStringSubmatch(src) if m == nil { return "" } return m[1] } // TestPolicyTS_CrossCheck pins the frontend's single-source policy constants // (frontend/src/lib/constants/policy.ts) to THIS package's refund_policy.go // values by REACTING to drift: the test reads the .ts file and asserts each // entry equals the backend constant, so a one-sided change on either side fails // CI. The frontend's own vitest suite (policy.test.ts) pins the same values in // the other direction, closing the drift loop both ways. func TestPolicyTS_CrossCheck(t *testing.T) { path := filepath.Join("..", "..", "..", "frontend", "src", "lib", "constants", "policy.ts") data, err := os.ReadFile(path) require.NoError(t, err, "policy.ts not found at %s (tests run from the package dir; repo layout is /backend/handlers/payments + /frontend)", path) src := string(data) cases := []struct { name string want string }{ {"REQUIRED_DEPOSIT_PCT", "0.2"}, {"PROTECTED_DEPOSIT_MAX_PCT", "0.5"}, {"FULL_REFUND_THRESHOLD_HOURS", "72"}, {"PARTIAL_REFUND_THRESHOLD_HOURS", "24"}, {"NO_SHOW_THRESHOLD_HOURS", "24"}, {"DEPOSIT_ADVANCE_HOURS", "36"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := policyTSValue(t, src, tc.name) require.NotEmpty(t, got, "policy.ts no longer declares %q — the constant may have been renamed or removed", tc.name) require.Equal(t, tc.want, got, "%s drifted from backend/handlers/payments/refund_policy.go", tc.name) }) } // The backend has no named reschedule constants; the frontend values must // track the refund tiers they were derived from. for _, tc := range []struct { name string want string }{ {"RESCHEDULE_BLOCK_HOURS_WITH_PAYMENTS", strconv.Itoa(int(FullRefundThreshold.Hours()))}, {"RESCHEDULE_BLOCK_HOURS_NO_PAYMENTS", strconv.Itoa(int(PartialRefundThreshold.Hours()))}, } { t.Run(tc.name, func(t *testing.T) { got := policyTSValue(t, src, tc.name) require.NotEmpty(t, got, "policy.ts no longer declares %q", tc.name) require.Equal(t, tc.want, got, "%s must track the backend refund threshold", tc.name) }) } }