Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/TransactionAuthorizationProtocol/tap-go

go 1.26.2
go 1.26.6

require (
github.com/google/uuid v1.6.0
Expand Down
14 changes: 13 additions & 1 deletion types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Agent struct {
ID string `json:"@id"`
Type string `json:"@type,omitempty"`
Role string `json:"role,omitempty"`
For ForField `json:"for,omitempty"`
For ForField `json:"for,omitzero"`
Name string `json:"name,omitempty"`
NameHash string `json:"nameHash,omitempty"`
LEICode string `json:"lei:leiCode,omitempty"`
Expand Down Expand Up @@ -66,6 +66,11 @@ func (f ForField) IsEmpty() bool {
return len(f.values) == 0
}

// IsZero reports whether the ForField has no values; omitzero reads it.
func (f ForField) IsZero() bool {
return len(f.values) == 0
}

// MarshalJSON marshals the ForField as a string (single value) or array (multiple values).
func (f ForField) MarshalJSON() ([]byte, error) {
if len(f.values) == 0 {
Expand All @@ -79,6 +84,13 @@ func (f ForField) MarshalJSON() ([]byte, error) {

// UnmarshalJSON unmarshals the ForField from a string or array of strings.
func (f *ForField) UnmarshalJSON(data []byte) error {
// null into a non-pointer is a no-op returning no error, so the branch
// below would take it and produce [""].
if string(data) == "null" {
f.values = nil
return nil
}

// Try single string first
var single string
if err := json.Unmarshal(data, &single); err == nil {
Expand Down
51 changes: 51 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,54 @@ func TestInvoice_JSONRoundTrip(t *testing.T) {
t.Errorf("LineItems mismatch: got %v", got.LineItems)
}
}

func TestAgent_OwnerlessOmitsForKey(t *testing.T) {
data, err := json.Marshal(Agent{ID: "did:pkh:eip155:1:0xabc", Role: "SettlementAddress"})
if err != nil {
t.Fatalf("marshal: %v", err)
}
if bytes.Contains(data, []byte(`"for"`)) {
t.Errorf("ownerless agent must omit the for key, got %s", data)
}

var got Agent
if err := json.Unmarshal(data, &got); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if !got.For.IsEmpty() {
t.Errorf("round-tripped ownerless agent declares an owner: %v", got.For.Values())
}
}

func TestForField_UnmarshalNull(t *testing.T) {
var f ForField
if err := json.Unmarshal([]byte("null"), &f); err != nil {
t.Fatalf("unmarshal null: %v", err)
}
if !f.IsEmpty() {
t.Errorf("null must decode to no values, got %v", f.Values())
}
if f.String() != "" {
t.Errorf("null must decode to the empty string, got %q", f.String())
}
}

func TestAgent_ForNullDecodesToNoOwner(t *testing.T) {
var got Agent
if err := json.Unmarshal([]byte(`{"@id":"did:pkh:eip155:1:0xabc","for":null}`), &got); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if !got.For.IsEmpty() {
t.Errorf(`"for": null must decode to no owner, got %v`, got.For.Values())
}
}

func TestForField_EmptySliceOmitsKey(t *testing.T) {
data, err := json.Marshal(Agent{ID: "did:eg:agent", For: NewForField()})
if err != nil {
t.Fatalf("marshal: %v", err)
}
if bytes.Contains(data, []byte(`"for"`)) {
t.Errorf("empty ForField must omit the for key, got %s", data)
}
}
Loading