mesh/merge-gate pass: builds mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
mesh/delivery-group group fix/443-a-stalled-lines-times-are-local delivered: every member is delivered
67 lines
3.0 KiB
Go
67 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// **An unanswered line gives the checks it waits on as data** (novox/hq issue 443): each check's context, its state
|
|
// and, for a pending one, since when in RFC 3339 — so the controller says the time in the operator's own zone. A time
|
|
// inside a finished sentence cannot be said again in another zone, and the operator read "since
|
|
// 2026-10-11T00:11:39Z". The finished words stay beside the data, for a controller that does not read it yet.
|
|
func TestAnUnansweredLineGivesItsChecksAsData(t *testing.T) {
|
|
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), time.Date(2026, 10, 11, 0, 12, 5, 0, time.UTC))
|
|
w.forge.open = openPullsOf(t, strings.Replace(startedNeverAnswered, "2026-10-11T00:11:39Z", "2026-10-11T02:11:39+02:00", 1))
|
|
w.h.SeeOpenPulls()
|
|
w.later(Bounds[Proposed].For + time.Minute)
|
|
lines := w.h.Stalled()
|
|
if len(lines) != 1 {
|
|
t.Fatalf("the head is not said: %+v", lines)
|
|
}
|
|
want := []WaitingCheck{{Context: "mesh/merge-gate", State: CheckPending, Since: "2026-10-11T00:11:39Z"},
|
|
{Context: "mesh/repo-check", State: CheckNeverSet}}
|
|
if !slices.Equal(lines[0].Checks, want) {
|
|
t.Fatalf("the line's checks are %+v, not %+v", lines[0].Checks, want)
|
|
}
|
|
if words := []string{"mesh/merge-gate pending since 2026-10-11T00:11:39Z", "mesh/repo-check never set"}; !slices.Equal(lines[0].Waiting, words) {
|
|
t.Fatalf("the finished words a controller of before reads are gone: %q", lines[0].Waiting)
|
|
}
|
|
raw, _ := json.Marshal(lines[0])
|
|
t.Logf("the line as the controller reads it: %s", raw)
|
|
if !strings.Contains(string(raw), `"checks":[{"context":"mesh/merge-gate","state":"pending","since":"2026-10-11T00:11:39Z"},{"context":"mesh/repo-check","state":"never-set"}]`) {
|
|
t.Fatalf("the line's checks are not in the shape the controller reads: %s", raw)
|
|
}
|
|
}
|
|
|
|
// The data and the words never disagree: for every head the words are made from, the checks give the same contexts
|
|
// in the same order, and a pending time the forge did not say, or said unreadably, is given as no time at all.
|
|
func TestAnUnansweredLinesChecksAgreeWithItsWords(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name, at string
|
|
since string
|
|
}{{"a time in UTC", "2026-10-11T00:11:39Z", "2026-10-11T00:11:39Z"},
|
|
{"a time with an offset", "2026-10-11T02:11:39+02:00", "2026-10-11T00:11:39Z"},
|
|
{"no time", "", ""},
|
|
{"an unreadable time", "yesterday", ""}} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := openPullsOf(t, strings.Replace(startedNeverAnswered, "2026-10-11T00:11:39Z", tc.at, 1))[0]
|
|
words, _, _ := p.unanswered()
|
|
checks := p.waitingChecks()
|
|
if len(words) != len(checks) {
|
|
t.Fatalf("words %q, checks %+v", words, checks)
|
|
}
|
|
for i, c := range checks {
|
|
if !strings.HasPrefix(words[i], c.Context+" ") {
|
|
t.Fatalf("word %q is not of check %+v", words[i], c)
|
|
}
|
|
}
|
|
if checks[0].State != CheckPending || checks[0].Since != tc.since {
|
|
t.Fatalf("the pending check is %+v, not since %q", checks[0], tc.since)
|
|
}
|
|
})
|
|
}
|
|
}
|