An absent bundle is an empty list, not a list holding null

The bundle carries the native coin's USD price. A chain with no wrapped
native has no anchor to publish, so the store reports a miss — and the
resolver wrapped that miss in a one-element list anyway, so `bundles`
answered [null].

Every other collection here answers [] when it holds nothing, and a test
already pins that as the wire signature separating a deployed-and-idle
subgraph from a broken one. Bundles is the one singleton collection, and it
was the exception.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-05 19:35:45 -07:00
co-authored by Hanzo Dev
parent fd6c0ff95a
commit 2476031987
2 changed files with 39 additions and 0 deletions
+7
View File
@@ -90,6 +90,13 @@ func (e *Engine) resolveBundles(ctx context.Context, s *storage.Store, args map[
if err != nil {
return nil, err
}
// No bundle is an empty list, not a list holding nothing. A chain with
// no wrapped native has no price anchor to publish, and a client
// iterating the collection should find no element rather than one it
// has to test for null.
if b == nil {
return []interface{}{}, nil
}
return []interface{}{b}, nil
}
+32
View File
@@ -135,3 +135,35 @@ func TestAmmSchema_DoesNotResolveMarkets(t *testing.T) {
t.Fatalf("amm schema must resolve `pools`, got: %+v", resp.Errors)
}
}
// TestAmmSchema_BundlesIsEmptyNotNull extends the empty-is-[] rule to the amm
// schema's one singleton collection.
//
// The bundle carries the native coin's USD price. A chain with no wrapped
// native has no anchor to publish, so there is no bundle — and `bundles` used
// to answer [null], a list holding nothing, because the resolver wrapped the
// store's miss instead of reporting it. A client iterating the collection then
// has to test each element for null, which no other collection here requires.
func TestAmmSchema_BundlesIsEmptyNotNull(t *testing.T) {
s, err := storage.New(t.TempDir())
if err != nil {
t.Fatal(err)
}
if err := s.Init(nil); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { s.Close() })
eng := New(s, &Config{MaxQueryDepth: 10, MaxResultSize: 1 << 20, QueryTimeoutMs: 30000})
if err := eng.LoadBuiltin("amm"); err != nil {
t.Fatalf("LoadBuiltin(amm): %v", err)
}
resp := eng.Execute(context.Background(), &Request{Query: "{ bundles { id ethPriceUSD } }"})
if len(resp.Errors) != 0 {
t.Fatalf("unexpected errors: %+v", resp.Errors)
}
raw, _ := json.Marshal(resp.Data)
if strings.Contains(string(raw), "null") {
t.Errorf("bundles answered %s; an absent bundle must be an empty list, not a list holding null", raw)
}
}