feat(go): fixture capture + characterization framework (M3)
All checks were successful
Deploy to K8s / deploy (push) Successful in 7s
All checks were successful
Deploy to K8s / deploy (push) Successful in 7s
Closes M3.1–M3.6. Parity safety net proving Go output matches Python
for every ported pure-domain function (M2.1–M2.9) and reconcile (M2.10).
Capture pipeline:
- scripts/capture_fixtures.py: calls each Python function with seeded
inputs, emits JSON fixtures to stdout (never writes files directly).
- scripts/scrub_fixtures.py: deterministic PII scrubber — SHA-256
pseudonyms for member names, digit-preserving hashes for VS/account/
bank_id, name-sweep in message text. Idempotent; no salt.
- scripts/_fixture_seeds.py: handcrafted seeds for all 11 functions;
synthetic names throughout (no real roster members).
- scripts/capture_all_fixtures.sh: convenience wrapper for full corpus
regeneration outside of make.
Fixture corpus (98 files, all PII-free):
- go/tests/fixtures/pure/<func>/<case>.json — 10 function directories.
- go/tests/fixtures/reconcile/<NN>_<case>.json — 10 branch-coverage
cases: greedy, overpayment credit, proportional remainder, even-split,
out-of-window, exception override, other: purpose, junior ?, multi-
person+month fan-out, unmatched.
Go parity tests (//go:build parity):
- go/tests/parity/parityio.go: generic LoadDir/RunAll helpers + typed
In/Out struct pairs for all 10 pure functions; Envelope decoder for
int/float/none disambiguation.
- 10 pure-function test packages + bespoke reconcile test with per-cell
float tolerance (math.Abs <= 0.01 for `paid` values).
Makefile: go-parity, go-test-all, capture-fixtures targets.
go/tests/fixtures/README.md: refresh workflow + PII audit guide.
Gate: make go-test green, make go-parity green (11/11 packages),
make go-lint clean (parity tag), make go-build clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
//go:build parity
|
||||
|
||||
package infer_transaction_details_parity_test
|
||||
|
||||
import (
|
||||
"fuj-management/go/internal/domain/matching"
|
||||
"fuj-management/go/tests/parity"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Verify expected values against live Python:
|
||||
//
|
||||
// PYTHONPATH=scripts:. python3 -c "
|
||||
// from match_payments import infer_transaction_details
|
||||
// print(infer_transaction_details({'sender':'Henrietta Ottová','message':'leden 2026','user_id':'','date':'2026-01-15'}, ['Henrietta Ottová']))"
|
||||
|
||||
func TestInferTransactionDetailsParity(t *testing.T) {
|
||||
t.Parallel()
|
||||
parity.RunAll(t, "../../../fixtures/pure/infer_transaction_details",
|
||||
func(in parity.InferTxDetailsIn) parity.InferTxDetailsOut {
|
||||
tx := matching.Transaction{
|
||||
Sender: in.Tx.Sender,
|
||||
Message: in.Tx.Message,
|
||||
UserID: in.Tx.UserID,
|
||||
Date: in.Tx.Date.AsAny(),
|
||||
}
|
||||
result := matching.InferTransactionDetails(tx, in.MemberNames, in.DefaultYear)
|
||||
|
||||
matches := make([]parity.MatchResult, len(result.Members))
|
||||
for i, m := range result.Members {
|
||||
matches[i] = parity.MatchResult{Name: m.Name, Confidence: string(m.Confidence)}
|
||||
}
|
||||
months := result.Months
|
||||
if months == nil {
|
||||
months = []string{}
|
||||
}
|
||||
return parity.InferTxDetailsOut{
|
||||
Matches: matches,
|
||||
Months: months,
|
||||
SearchText: result.SearchText,
|
||||
}
|
||||
},
|
||||
func(want, got parity.InferTxDetailsOut) bool {
|
||||
return reflect.DeepEqual(want, got)
|
||||
},
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user