Files
zeekay 7d727874d5 chore: rebrand Formance Payments -> Hanzo Payments (hanzo-fi)
- go.mod module path + all self-imports: github.com/formancehq/payments -> github.com/hanzo-fi/payments
  (1590 .go files / 3152 refs; 20 modules: root, pkg/client, pkg/domain, 18 ce/plugins).
  External dep kept: formancehq/go-libs.
- plugins: each ce/plugins/* consumed a *published* pkg/domain; added local
  'replace .../pkg/domain => ../../../pkg/domain' (consistent with root) so every
  standalone plugin module builds in-monorepo. compile-plugin.sh MOD var rebranded too.
- branding: README, LICENSE (dual MIT+EE; upstream copyright retained + Hanzo added),
  goreleaser (release images -> registry.hanzo.ai/hanzo-fi, OCI labels, homepage),
  proto go_package, renovate, speakeasy gen.yaml, connector-template *.gotpl.
- CI: removed Numary/Formance workflows + composite action; added canonical hanzo.yml +
  cicd.yml (hanzoai/ci, gated HANZO_CI_ENABLED) + secretless ci.yml (go build/vet).
- kept as data/compat keys: wire capability enums ALLOW_FORMANCE_*, generated SDK type
  client.Formance, domain field FormanceRedirectURL, OTEL_SERVICE_NAME, upstream base image.
- verified: go build ./... (default + -tags ee) + all 20 nested modules green.
2026-07-05 13:08:55 -07:00

118 lines
3.5 KiB
Go

package mappers
import (
"testing"
"github.com/hanzo-fi/payments/ee/plugins/routable/client"
)
// TestPayableMetadata_AliasKeys locks in the correlation contract
// documented in MAPPINGS.md §5.5: every synced Payment must carry the
// Routable payable UUID under MetadataKeyRoutablePayableID, and the
// originating PI reference under MetadataKeyPaymentInitiationReference
// (when external_id is present).
func TestPayableMetadata_AliasKeys(t *testing.T) {
cases := []struct {
name string
payable client.Payable
wantPIRef string // expected value for payment_initiation_reference (empty = absent)
wantPayable string // expected value for payable_id
}{
{
name: "with external_id (Formance-initiated)",
payable: client.Payable{
ID: "pa_abc123",
ExternalID: "pi_xyz",
Type: "ach",
},
wantPIRef: "pi_xyz",
wantPayable: "pa_abc123",
},
{
name: "without external_id (Routable UI-initiated)",
payable: client.Payable{
ID: "pa_def456",
Type: "ach",
},
wantPIRef: "", // absent — stripEmpty drops empty values
wantPayable: "pa_def456",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
m := PayableMetadata(tc.payable)
assertAliasMetadata(t, m, tc.wantPIRef, tc.wantPayable)
})
}
}
// TestReceivableMetadata_AliasKeys mirrors the payable contract for the
// inbound path so the correlation works regardless of payment direction.
func TestReceivableMetadata_AliasKeys(t *testing.T) {
cases := []struct {
name string
receivable client.Receivable
wantPIRef string
wantPayable string
}{
{
name: "with external_id",
receivable: client.Receivable{
ID: "re_abc",
ExternalID: "pi_inbound",
Type: "ach",
},
wantPIRef: "pi_inbound",
wantPayable: "re_abc",
},
{
name: "without external_id",
receivable: client.Receivable{
ID: "re_def",
Type: "ach",
},
wantPIRef: "",
wantPayable: "re_def",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
m := ReceivableMetadata(tc.receivable)
assertAliasMetadata(t, m, tc.wantPIRef, tc.wantPayable)
})
}
}
// assertAliasMetadata pins the correlation contract precisely:
// - payable_id MUST be present and equal to wantPayable.
// - When wantPIRef != "", payment_initiation_reference and external_id
// MUST both be present and equal to wantPIRef.
// - When wantPIRef == "" (Routable-UI-initiated row), both alias and
// legacy keys MUST be ABSENT (not present-with-empty-string), since
// stripEmpty drops empty values. CodeRabbit flagged that comparing
// value-only would let a missing key and an empty-string value both
// pass — this helper closes the gap.
func assertAliasMetadata(t *testing.T, m map[string]string, wantPIRef, wantPayable string) {
t.Helper()
got, ok := m[MetadataKeyRoutablePayableID]
if !ok {
t.Fatalf("payable_id alias missing entirely from metadata: %v", m)
}
if got != wantPayable {
t.Errorf("payable_id = %q, want %q", got, wantPayable)
}
for _, key := range []string{MetadataKeyPaymentInitiationReference, MetadataKeyExternalID} {
got, present := m[key]
switch {
case wantPIRef == "" && present:
t.Errorf("%s should be ABSENT when external_id is empty (stripEmpty contract); got %q", key, got)
case wantPIRef != "" && !present:
t.Errorf("%s should be present when external_id is set (want %q); not found in %v", key, wantPIRef, m)
case wantPIRef != "" && got != wantPIRef:
t.Errorf("%s = %q, want %q", key, got, wantPIRef)
}
}
}