package bnwasm import ( "encoding/json" "reflect" "testing" abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1" "github.com/google/uuid" ) // TestToDbValueTextArrayNilVsEmpty pins the guest-side arg encoding for []string: // a nil slice marshals to SQL NULL (matching []byte/json.RawMessage), while an // empty-but-non-nil slice stays a non-NULL empty text[]. WO-WZ-007's host must // mirror this nil→NULL convention. func TestToDbValueTextArrayNilVsEmpty(t *testing.T) { t.Run("nil", func(t *testing.T) { dv, err := toDbValue([]string(nil)) if err != nil { t.Fatal(err) } if _, ok := dv.GetKind().(*abiv1.DbValue_Null); !ok { t.Fatalf("nil []string: want DbValue_Null, got %T", dv.GetKind()) } }) t.Run("empty", func(t *testing.T) { dv, err := toDbValue([]string{}) if err != nil { t.Fatal(err) } ta, ok := dv.GetKind().(*abiv1.DbValue_TextArrayValue) if !ok { t.Fatalf("empty []string: want DbValue_TextArrayValue, got %T", dv.GetKind()) } if got := ta.TextArrayValue.GetValues(); len(got) != 0 { t.Fatalf("empty []string: want zero-length text[], got %#v", got) } }) t.Run("values", func(t *testing.T) { dv, err := toDbValue([]string{"a", "b"}) if err != nil { t.Fatal(err) } ta, ok := dv.GetKind().(*abiv1.DbValue_TextArrayValue) if !ok { t.Fatalf("want DbValue_TextArrayValue, got %T", dv.GetKind()) } if got := ta.TextArrayValue.GetValues(); !reflect.DeepEqual(got, []string{"a", "b"}) { t.Fatalf("want [a b], got %#v", got) } }) } // TestToDbValueUuidArray pins the guest-side arg encoding for []uuid.UUID: a nil // slice marshals to SQL NULL, an empty-but-non-nil slice stays a non-NULL empty // uuid[], and values marshal to canonical strings in order. The host binds this // as a native uuid[] parameter (no text[]-cast workaround). func TestToDbValueUuidArray(t *testing.T) { t.Run("nil", func(t *testing.T) { dv, err := toDbValue([]uuid.UUID(nil)) if err != nil { t.Fatal(err) } if _, ok := dv.GetKind().(*abiv1.DbValue_Null); !ok { t.Fatalf("nil []uuid.UUID: want DbValue_Null, got %T", dv.GetKind()) } }) t.Run("empty", func(t *testing.T) { dv, err := toDbValue([]uuid.UUID{}) if err != nil { t.Fatal(err) } ua, ok := dv.GetKind().(*abiv1.DbValue_UuidArrayValue) if !ok { t.Fatalf("empty []uuid.UUID: want DbValue_UuidArrayValue, got %T", dv.GetKind()) } if got := ua.UuidArrayValue.GetValues(); len(got) != 0 { t.Fatalf("empty []uuid.UUID: want zero-length uuid[], got %#v", got) } }) t.Run("values", func(t *testing.T) { dv, err := toDbValue(FixtureUUIDs) if err != nil { t.Fatal(err) } ua, ok := dv.GetKind().(*abiv1.DbValue_UuidArrayValue) if !ok { t.Fatalf("want DbValue_UuidArrayValue, got %T", dv.GetKind()) } want := []string{FixtureUUIDs[0].String(), FixtureUUIDs[1].String(), FixtureUUIDs[2].String()} if got := ua.UuidArrayValue.GetValues(); !reflect.DeepEqual(got, want) { t.Fatalf("want %v, got %#v", want, got) } }) } // TestToDbValueNilConvention locks the nil→NULL convention across the reference // types so []string stays consistent with []byte and json.RawMessage. func TestToDbValueNilConvention(t *testing.T) { cases := []struct { name string in any }{ {"bytes", []byte(nil)}, {"json", json.RawMessage(nil)}, {"text_array", []string(nil)}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { dv, err := toDbValue(c.in) if err != nil { t.Fatal(err) } if _, ok := dv.GetKind().(*abiv1.DbValue_Null); !ok { t.Fatalf("nil %s: want DbValue_Null, got %T", c.name, dv.GetKind()) } }) } }