diff --git a/modules/mesh-delivery-checker/internal/check/try.go b/modules/mesh-delivery-checker/internal/check/try.go index 16b64403..8109a525 100644 --- a/modules/mesh-delivery-checker/internal/check/try.go +++ b/modules/mesh-delivery-checker/internal/check/try.go @@ -795,7 +795,7 @@ func OpenTries(store TryStore) ([]tryevent.Open, error) { }); err != nil { return nil, err } - var out []tryevent.Open + out := []tryevent.Open{} // none open is an empty list, never null: a reader tells it from no answer (issue 1921) for _, o := range found { if o != nil { out = append(out, *o) diff --git a/modules/mesh-delivery-checker/internal/check/try_test.go b/modules/mesh-delivery-checker/internal/check/try_test.go index 74a4681f..d0863d93 100644 --- a/modules/mesh-delivery-checker/internal/check/try_test.go +++ b/modules/mesh-delivery-checker/internal/check/try_test.go @@ -223,6 +223,8 @@ func TestATryIsJudgedAsAGroupsCheckAndOwesTheForgeNothing(t *testing.T) { // ADR } if open, err := OpenTries(memTries{kv}); err != nil || len(open) != 0 { t.Errorf("open tries after the end = %v, %v", open, err) + } else if raw, _ := json.Marshal(open); string(raw) != "[]" { // issue 1921: none open is [] on the wire, never null + t.Errorf("none open is answered %s, want []", raw) } if got.State != TryPass || got.Took == "" || len(got.Members) != 2 || got.Members[0].Repository != repoA || got.Members[1].Head != head2 || got.Said == "" || got.Levels[delivery.LevelUnit].Run+got.Levels[delivery.LevelUnit].Kept != 4 { // the bench gives both members the same digests diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go index 56b8ff07..33651087 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named.go @@ -306,12 +306,20 @@ func openTriesVia(call toolCaller) func(ctx context.Context) ([]tryevent.Open, e if err != nil { return nil, err } - var o struct { - Tries *[]tryevent.Open `json:"tries"` - } - if err := json.Unmarshal(raw, &o); err != nil || o.Tries == nil { + // No try open is an answer: the checker's list is empty, or null in an older checker (issue 1921). An answer without the + // key is none. + var o map[string]json.RawMessage + if err := json.Unmarshal(raw, &o); err != nil { return nil, fmt.Errorf("the answer of %s is not {tries}: %v", openTriesTool, err) } - return *o.Tries, nil + list, ok := o["tries"] + if !ok { + return nil, fmt.Errorf("the answer of %s is not {tries}: no tries key", openTriesTool) + } + var tries []tryevent.Open + if err := json.Unmarshal(list, &tries); err != nil { + return nil, fmt.Errorf("the answer of %s is not {tries}: %v", openTriesTool, err) + } + return tries, nil } } diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go index 2c5cfb13..6f80ef0c 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/named_test.go @@ -120,3 +120,24 @@ func TestReadViaReadsManifestsAndLocks(t *testing.T) { t.Fatalf("read = %+v", got) } } + +func TestTheOpenTriesAnswerTakesNoneOpenAsAnAnswer(t *testing.T) { // issue 1921: {"tries": null} refused the recovery for hours + for _, row := range []struct { + name, answer string + want int + refused bool + }{ + {"one open", `{"tries":[{"id":"try-1","state":"running","whole":true,"members":[{"repository":"novox/a","head":"h","trunk":"t"}]}]}`, 1, false}, + {"an empty list", `{"tries":[]}`, 0, false}, + {"null, as an older checker answers none", `{"tries":null}`, 0, false}, + {"no tries key", `{}`, 0, true}, + {"not json", `nope`, 0, true}, + {"tries of another shape", `{"tries":{"a":1}}`, 0, true}, + } { + call := func(string, any) (json.RawMessage, error) { return json.RawMessage(row.answer), nil } + got, err := openTriesVia(call)(context.Background()) + if (err != nil) != row.refused || len(got) != row.want { + t.Errorf("%s: %d tries, error %v; want %d, refused %v", row.name, len(got), err, row.want, row.refused) + } + } +}