mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
- 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.
154 lines
5.3 KiB
Go
154 lines
5.3 KiB
Go
package client_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
|
|
powensclient "github.com/hanzo-fi/payments/ce/plugins/powens/client"
|
|
)
|
|
|
|
var _ = Describe("Transaction JSON (marshal/unmarshal)", func() {
|
|
Context("marshal", func() {
|
|
It("formats date fields (Europe/Paris for some)", func() {
|
|
// Set instants in UTC. Expect Paris-local formatting on marshal for Date and LastUpdate.
|
|
// Pick a summer date (CEST UTC+2) and a winter date (CET UTC+1)
|
|
summerUTC := time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC)
|
|
winterUTC := time.Date(2025, 1, 15, 7, 30, 45, 0, time.UTC)
|
|
|
|
tr := powensclient.Transaction{
|
|
ID: 123,
|
|
AccountID: 456,
|
|
Date: summerUTC,
|
|
DateTime: winterUTC, // formatted using time.DateTime as-is (string), doc says UTC
|
|
Value: json.Number("12.34"),
|
|
Type: "credit",
|
|
LastUpdate: winterUTC, // Paris naive DateTime
|
|
}
|
|
|
|
b, err := json.Marshal(tr)
|
|
Expect(err).To(BeNil())
|
|
|
|
// Parse back as raw map to check string forms
|
|
var m map[string]any
|
|
Expect(json.Unmarshal(b, &m)).To(Succeed())
|
|
|
|
Expect(m["id"]).To(Equal(float64(123)))
|
|
Expect(m["id_account"]).To(Equal(float64(456)))
|
|
Expect(m["type"]).To(Equal("credit"))
|
|
Expect(m["value"]).To(Equal(12.34))
|
|
|
|
// Date is formatted without timezone conversion, using DateOnly layout
|
|
Expect(m["date"]).To(Equal("2025-07-01"))
|
|
|
|
// DateTime is formatted with time.DateTime layout; documentation suggests UTC
|
|
Expect(m["date_time"]).To(Equal("2025-01-15 07:30:45"))
|
|
|
|
// LastUpdate is Paris local naive DateTime
|
|
Expect(m["last_update"]).To(Equal("2025-01-15 08:30:45"))
|
|
})
|
|
})
|
|
|
|
Context("unmarshal", func() {
|
|
It("parses Powens strings and converts to UTC instants", func() {
|
|
// Provide JSON as Powens would send
|
|
payload := `{
|
|
"id": 321,
|
|
"id_account": 654,
|
|
"date": "2025-07-01",
|
|
"date_time": "2025-01-15 07:30:45",
|
|
"value": "-42.00",
|
|
"type": "debit",
|
|
"last_update": "2025-01-15 08:30:45"
|
|
}`
|
|
|
|
var tr powensclient.Transaction
|
|
Expect(json.Unmarshal([]byte(payload), &tr)).To(Succeed())
|
|
|
|
Expect(tr.ID).To(Equal(321))
|
|
Expect(tr.AccountID).To(Equal(654))
|
|
Expect(tr.Value.String()).To(Equal("-42.00"))
|
|
Expect(tr.Type).To(Equal("debit"))
|
|
|
|
// Note that we do not do timezone conversion here, doing it would change the day
|
|
expectedDate := time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC)
|
|
Expect(tr.Date).To(Equal(expectedDate))
|
|
|
|
// date_time is documented as UTC, so parse as UTC (no conversion); expect exact instant
|
|
expectedDateTime := time.Date(2025, 1, 15, 7, 30, 45, 0, time.UTC)
|
|
Expect(tr.DateTime).To(Equal(expectedDateTime))
|
|
|
|
// last_update is Paris naive -> UTC
|
|
expectedLastUpdate := time.Date(2025, 1, 15, 7, 30, 45, 0, time.UTC)
|
|
Expect(tr.LastUpdate).To(Equal(expectedLastUpdate))
|
|
})
|
|
})
|
|
|
|
Context("round trip", func() {
|
|
It("marshal then unmarshal yields identical Transaction", func() {
|
|
in := powensclient.Transaction{
|
|
ID: 999,
|
|
AccountID: 888,
|
|
Date: time.Date(2025, 3, 15, 0, 0, 0, 0, time.UTC), // CET period (UTC+1) midnight Paris corresponds to 23:00 UTC
|
|
DateTime: time.Date(2025, 3, 15, 11, 5, 6, 0, time.UTC), // treated as UTC string
|
|
Value: json.Number("0.01"),
|
|
Type: "credit",
|
|
LastUpdate: time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC), // CEST period (UTC+2)
|
|
}
|
|
|
|
b, err := json.Marshal(in)
|
|
Expect(err).To(BeNil())
|
|
|
|
var out powensclient.Transaction
|
|
Expect(json.Unmarshal(b, &out)).To(Succeed())
|
|
|
|
// Exact equality should hold on all fields except Date's time component, which is dropped to midnight when marshaling as date-only.
|
|
Expect(out.ID).To(Equal(in.ID))
|
|
Expect(out.AccountID).To(Equal(in.AccountID))
|
|
Expect(out.Date).To(Equal(in.Date))
|
|
Expect(out.DateTime).To(Equal(in.DateTime))
|
|
Expect(out.Value).To(Equal(in.Value))
|
|
Expect(out.Type).To(Equal(in.Type))
|
|
Expect(out.LastUpdate).To(Equal(in.LastUpdate))
|
|
})
|
|
|
|
It("unmarshal then marshal preserves the JSON date formats", func() {
|
|
payload := `{
|
|
"id": 10,
|
|
"id_account": 20,
|
|
"date": "2025-01-15",
|
|
"date_time": "2025-01-15 07:30:45",
|
|
"value": "100.00",
|
|
"type": "credit",
|
|
"last_update": "2025-01-15 08:30:45"
|
|
}`
|
|
|
|
var tr powensclient.Transaction
|
|
Expect(json.Unmarshal([]byte(payload), &tr)).To(Succeed())
|
|
b, err := json.Marshal(tr)
|
|
Expect(err).To(BeNil())
|
|
|
|
// Compare specific fields to ensure formats preserved
|
|
var m map[string]any
|
|
Expect(json.Unmarshal(b, &m)).To(Succeed())
|
|
Expect(m["date"]).To(Equal("2025-01-15"))
|
|
Expect(m["date_time"]).To(Equal("2025-01-15 07:30:45"))
|
|
Expect(m["last_update"]).To(Equal("2025-01-15 08:30:45"))
|
|
})
|
|
|
|
It("omits empty optional fields on marshal", func() {
|
|
in := powensclient.Transaction{ID: 1, AccountID: 2, Value: json.Number("0"), Type: "debit"}
|
|
// zero-value time.Time fields in struct may be formatted by helpers; ensure they are truly omitted by setting them to zero and expecting omission only if MarshalJSON respects omitempty when formatted strings are empty.
|
|
b, err := json.Marshal(in)
|
|
Expect(err).To(BeNil())
|
|
var m map[string]any
|
|
Expect(json.Unmarshal(b, &m)).To(Succeed())
|
|
Expect(m).To(Not(HaveKey("date")))
|
|
Expect(m).To(Not(HaveKey("date_time")))
|
|
Expect(m).To(Not(HaveKey("last_update")))
|
|
})
|
|
})
|
|
})
|