Deploy to K8s / deploy (push) Successful in 20s
Go (go/) becomes the primary implementation. The original Python version moves to legacy/ and is kept runnable; new features are still built in both (see the Dual implementation rule in CLAUDE.md). - git mv app.py, scripts/, templates/, tests/, pyproject.toml, uv.lock and build/ into legacy/ - Makefile: Python targets point at legacy/, venv in legacy/.venv, CREDENTIALS_PATH exported for the root .secret/ file - CI and make image build the Python image with legacy/ as context; the Makefile is no longer copied into the image - legacy/.dockerignore moved to the context root so it takes effect - CLAUDE.md, docs/README.md, Go comments, fixture README, capture script and VS Code launch config updated to the new layout Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
3.7 KiB
Go
120 lines
3.7 KiB
Go
package synch
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// All expected digests verified against the live Python implementation on 2026-05-06:
|
|
//
|
|
// PYTHONPATH=legacy/scripts:legacy python -c '
|
|
// from sync_fio_to_sheets import generate_sync_id
|
|
// cases = [
|
|
// {"date":"2026-01-15","amount":500.0,"currency":"CZK","sender":"Jan Novak","vs":"123","message":"clenske 1/2026","bank_id":"abc123"},
|
|
// {"date":"2026-01-15","amount":500.0,"sender":"Jan Novak","vs":"123","message":"clenske 1/2026","bank_id":"abc123"},
|
|
// {"date":"2026-02-10","amount":1234.56,"currency":"CZK","sender":"ABC SRO","vs":"","message":"FAKTURA 42","bank_id":"xyz"},
|
|
// {"date":"2026-03-01","amount":-500.0,"currency":"CZK","sender":"refund","vs":"","message":"","bank_id":""},
|
|
// {"date":"2026-04-01","amount":0.0,"currency":"CZK","sender":"","vs":"","message":"","bank_id":""},
|
|
// {"date":"","amount":0.0,"currency":"CZK","sender":"","vs":"","message":"","bank_id":""},
|
|
// ]
|
|
// for c in cases: print(generate_sync_id(c))
|
|
// '
|
|
func TestGenerateSyncID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
name string
|
|
tx Transaction
|
|
want string
|
|
}{
|
|
{
|
|
name: "all fields set",
|
|
tx: Transaction{
|
|
Date: "2026-01-15", Amount: 500.0, Currency: "CZK",
|
|
Sender: "Jan Novak", VS: "123", Message: "clenske 1/2026", BankID: "abc123",
|
|
},
|
|
want: "4ac26598b6f23965380690172156a438a7e97a97dcedf222e5afe1afbe2c1bc4",
|
|
},
|
|
{
|
|
name: "currency empty defaults to CZK",
|
|
tx: Transaction{
|
|
Date: "2026-01-15", Amount: 500.0, Currency: "",
|
|
Sender: "Jan Novak", VS: "123", Message: "clenske 1/2026", BankID: "abc123",
|
|
},
|
|
want: "4ac26598b6f23965380690172156a438a7e97a97dcedf222e5afe1afbe2c1bc4",
|
|
},
|
|
{
|
|
name: "mixed-case fields lowercased before hashing",
|
|
tx: Transaction{
|
|
Date: "2026-02-10", Amount: 1234.56, Currency: "CZK",
|
|
Sender: "ABC SRO", VS: "", Message: "FAKTURA 42", BankID: "xyz",
|
|
},
|
|
want: "d40fa224d4fa572ffcd58e308e5c6508c4d5ca087b24ef6ff9284528fc128250",
|
|
},
|
|
{
|
|
name: "negative amount",
|
|
tx: Transaction{
|
|
Date: "2026-03-01", Amount: -500.0, Currency: "CZK",
|
|
Sender: "refund", VS: "", Message: "", BankID: "",
|
|
},
|
|
want: "0c630a407160367c396a2beec08efb94c319b4d84a8b90cc2be89e6ea10c391f",
|
|
},
|
|
{
|
|
name: "zero amount",
|
|
tx: Transaction{
|
|
Date: "2026-04-01", Amount: 0.0, Currency: "CZK",
|
|
Sender: "", VS: "", Message: "", BankID: "",
|
|
},
|
|
want: "6a23ce53717cd539064d550d2c2ec5de2e9bf81016d16852820ca9b8e259331f",
|
|
},
|
|
{
|
|
// Python equivalent: {"date":"","amount":0.0,"currency":"CZK","sender":"","vs":"","message":"","bank_id":""}
|
|
// Note: Python generate_sync_id({}) hashes "" for missing amount, not "0.0".
|
|
name: "zero-value Transaction",
|
|
tx: Transaction{},
|
|
want: "d33d7e391f5a43f0192bb5a34c0ec15715139125678ecef8e1324af7d943b21d",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := GenerateSyncID(tc.tx)
|
|
if got != tc.want {
|
|
t.Errorf("GenerateSyncID(%+v) = %q, want %q", tc.tx, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// All expected strings verified against the live Python implementation on 2026-05-06:
|
|
//
|
|
// PYTHONPATH=legacy/scripts:legacy python -c '
|
|
// for v in [0.0, 500.0, -500.0, 0.1, 1234.56, 99999.99, 1500000.0, 1e16, 1e-5]:
|
|
// print(repr(v), "->", repr(str(v)))
|
|
// '
|
|
func TestFormatAmount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
in float64
|
|
want string
|
|
}{
|
|
{0.0, "0.0"},
|
|
{500.0, "500.0"},
|
|
{-500.0, "-500.0"},
|
|
{0.1, "0.1"},
|
|
{1234.56, "1234.56"},
|
|
{99999.99, "99999.99"},
|
|
{1500000.0, "1500000.0"},
|
|
{1e16, "1e+16"},
|
|
{1e-5, "1e-05"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := formatAmount(tc.in)
|
|
if got != tc.want {
|
|
t.Errorf("formatAmount(%v) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|