122 lines
4.9 KiB
Go
122 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"mesh-delivery-checker/internal/check"
|
|
)
|
|
|
|
func TestTheToolsServedAreTheFiveReadTools(t *testing.T) {
|
|
var names []string
|
|
for _, tl := range Tools(check.Reader{}) {
|
|
names = append(names, tl.Name)
|
|
if tl.Description == "" || tl.Run == nil {
|
|
t.Errorf("%s has no description or no run", tl.Name)
|
|
}
|
|
}
|
|
want := []string{check.VerdictsReadTool, check.PullReadTool, check.KeptReadTool, check.PreviewsReadTool, check.StatusReadTool}
|
|
if !slices.Equal(names, want) {
|
|
t.Errorf("tools = %v, want %v", names, want)
|
|
}
|
|
// The recovery tool beside them asks a lost check again; with nothing to write it refuses.
|
|
recovery := RecoveryTools(check.Reader{}, nil)
|
|
if len(recovery) != 1 || recovery[0].Name != check.AskAgainTool || recovery[0].Description == "" {
|
|
t.Errorf("recovery tools = %+v", recovery)
|
|
}
|
|
if _, err := recovery[0].Run(map[string]any{"group": "feat/x", "why": "lost"}); err == nil {
|
|
t.Errorf("%s answered with no state to write", check.AskAgainTool)
|
|
}
|
|
// With nothing to read from a tool says so and changes nothing.
|
|
for _, tl := range Tools(check.Reader{}) {
|
|
if tl.Name == check.StatusReadTool {
|
|
continue // it answers from what it can read, and says what it could not
|
|
}
|
|
if _, err := tl.Run(map[string]any{"repository": "novox/x", "number": float64(1)}); err == nil {
|
|
t.Errorf("%s answered with no state to read", tl.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// memStore is a tries state in memory.
|
|
type memStore struct{ vals map[string]json.RawMessage }
|
|
|
|
func (m *memStore) Keys() ([]string, error) {
|
|
var out []string
|
|
for k := range m.vals {
|
|
out = append(out, k)
|
|
}
|
|
return out, nil
|
|
}
|
|
func (m *memStore) Get(key string) (json.RawMessage, error) { return m.vals[key], nil }
|
|
func (m *memStore) Create(key string, value []byte) error {
|
|
if _, ok := m.vals[key]; ok {
|
|
return errors.New("exists")
|
|
}
|
|
m.vals[key] = value
|
|
return nil
|
|
}
|
|
|
|
func (m *memStore) Entry(key string) (json.RawMessage, uint64, error) { return m.vals[key], 1, nil }
|
|
func (m *memStore) Update(key string, value []byte, revision uint64) error {
|
|
m.vals[key] = value
|
|
return nil
|
|
}
|
|
|
|
func TestTheTryToolsAskAndReadATry(t *testing.T) { // ADR 0501 §5, the agreed interface with mesh-controller's try
|
|
store := &memStore{vals: map[string]json.RawMessage{}}
|
|
tools := TryTools(Trying{Tries: store, Ender: store, Now: func() time.Time { return time.Unix(1, 5) },
|
|
Trunks: func() (map[string]string, error) {
|
|
return map[string]string{"novox/mesh-delivery": strings.Repeat("a", 40)}, nil
|
|
}})
|
|
if len(tools) != 3 || tools[0].Name != "checker_try" || tools[1].Name != "checker_try_read" || tools[2].Name != "checker_try_end" {
|
|
t.Fatalf("tools = %+v", tools)
|
|
}
|
|
out, err := tools[0].Run(map[string]any{"repository": "novox/mesh-delivery", "head": strings.Repeat("1", 40), "trunk": "main"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := out.(map[string]any)["id"].(string)
|
|
if id != "try-1000000005" {
|
|
t.Errorf("id = %q", id)
|
|
}
|
|
raw, err := tools[1].Run(map[string]any{"id": id})
|
|
if err != nil || !strings.Contains(string(raw.(json.RawMessage)), `"state":"queued"`) {
|
|
t.Errorf("read = %s, %v", raw, err)
|
|
}
|
|
// The seat's verbs answer as the tools do (ADR 0547 §10, issue 774): the same ask is the same try, and the read is alike.
|
|
verbs := SeatTryTools(Trying{Tries: store, Ender: store, Now: func() time.Time { return time.Unix(1, 5) },
|
|
Trunks: func() (map[string]string, error) {
|
|
return map[string]string{"novox/mesh-delivery": strings.Repeat("a", 40)}, nil
|
|
}})
|
|
if len(verbs) != 3 || verbs[0].Name != Seat+".try" || verbs[1].Name != Seat+".try-read" || verbs[2].Name != Seat+".try-end" {
|
|
t.Fatalf("verbs = %+v", verbs)
|
|
}
|
|
again, err := verbs[0].Run(map[string]any{"repository": "novox/mesh-delivery", "head": strings.Repeat("1", 40), "trunk": "main"})
|
|
if err != nil || again.(map[string]any)["id"] != id {
|
|
t.Errorf("verb try = %v, %v, want the waiting try %s", again, err, id)
|
|
}
|
|
if viaVerb, err := verbs[1].Run(map[string]any{"id": id}); err != nil || string(viaVerb.(json.RawMessage)) != string(raw.(json.RawMessage)) {
|
|
t.Errorf("verb try-read = %s, %v; tool = %s", viaVerb, err, raw)
|
|
}
|
|
if _, err := verbs[0].Run(map[string]any{"repository": "novox/hq", "head": strings.Repeat("1", 40)}); err == nil {
|
|
t.Error("the verb tried a repository nothing checks")
|
|
}
|
|
// Issue 734: a try whose ask was lost is ended by hand with why, and its head is asked anew.
|
|
ended, err := tools[2].Run(map[string]any{"id": id, "why": "its ask was lost"})
|
|
if err != nil || ended.(map[string]any)["state"] != "error" {
|
|
t.Errorf("end = %v, %v", ended, err)
|
|
}
|
|
if _, err := tools[2].Run(map[string]any{"id": id, "why": "again"}); err == nil {
|
|
t.Error("an ended try was ended again")
|
|
}
|
|
if _, err := tools[0].Run(map[string]any{"repository": "novox/hq", "head": strings.Repeat("1", 40)}); err == nil ||
|
|
err.Error() != "no level of novox/hq is checked by the checker" {
|
|
t.Errorf("hq: %v", err)
|
|
}
|
|
}
|