knowledge-base.progress takes since: only what changed in a window, read from the tracker's and mesh-delivery's history, so a five-minute report says what moved (the operator, 2026-10-28)
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -393,3 +394,134 @@ func TestProgressOfAnIssueAndARecord(t *testing.T) {
|
||||
t.Error("an issue the tracker does not hold was answered")
|
||||
}
|
||||
}
|
||||
|
||||
// sinceWorld plays the tracker and the delivery seat from what they answered on 2026-10-28 (testdata/progress/since):
|
||||
// a get or a show it holds no answer for is refused, as a seat that cannot answer.
|
||||
func sinceWorld(t *testing.T) func(string, any) (json.RawMessage, error) {
|
||||
t.Helper()
|
||||
read := func(name string) json.RawMessage { return readFixture(t, filepath.Join("since", name)) }
|
||||
var rows []json.RawMessage
|
||||
if err := json.Unmarshal(read("rows.json"), &rows); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
byNumber := map[int]json.RawMessage{}
|
||||
for _, r := range rows {
|
||||
var n struct{ Number int }
|
||||
_ = json.Unmarshal(r, &n)
|
||||
byNumber[n.Number] = r
|
||||
}
|
||||
shows := map[string]string{"novox/mesh-catalog@470687ea4cce": "show-mesh-catalog-539.json", "novox/mesh-catalog@f5d4b2f86ed4": "show-mesh-catalog-537.json",
|
||||
"novox/mesh-delivery@2b801f5bd49a": "show-mesh-delivery-94.json", "novox/de-spiegel@fba6dbe3259e": "show-de-spiegel-6.json"}
|
||||
return func(key string, body any) (json.RawMessage, error) {
|
||||
raw, _ := json.Marshal(body)
|
||||
var a struct {
|
||||
Numbers []int `json:"numbers"`
|
||||
Number int `json:"number"`
|
||||
ID string `json:"id"`
|
||||
One string `json:"one"`
|
||||
}
|
||||
_ = json.Unmarshal(raw, &a)
|
||||
switch key {
|
||||
case MilestonesVerb:
|
||||
return read("milestone-47-6.json"), nil
|
||||
case "seat:issue-tracker.list":
|
||||
var out []json.RawMessage
|
||||
for _, n := range a.Numbers {
|
||||
if r, ok := byNumber[n]; ok {
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
return json.Marshal(map[string]any{"rows": out, "matched": len(out)})
|
||||
case "seat:issue-tracker.get":
|
||||
if a.Number == 1422 || a.Number == 1956 {
|
||||
return read(fmt.Sprintf("get-%d.json", a.Number)), nil
|
||||
}
|
||||
case "seat:mesh-delivery.deliveries":
|
||||
return read("deliveries.json"), nil
|
||||
case "seat:mesh-delivery.groups":
|
||||
return json.RawMessage(`[]`), nil
|
||||
case "seat:mesh-delivery.show":
|
||||
if f, ok := shows[a.ID]; ok {
|
||||
return read(f), nil
|
||||
}
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
}
|
||||
|
||||
// The operator, 2026-10-28: "can the progress tool also report the progress made since the last call, or maybe better,
|
||||
// the last 60 minutes". With since, an answer lists only what changed in that window under of, read from the
|
||||
// tracker's notes, links, workers and closes, its milestones' history and mesh-delivery's transitions and checks.
|
||||
func TestProgressSinceAWindowSaysOnlyWhatChanged(t *testing.T) {
|
||||
at := func(s string) *time.Time { v, _ := time.Parse(time.RFC3339, s); return &v }
|
||||
k := &KB{Records: progressRecords(), Seats: sinceWorld(t), Now: func() time.Time { return *at("2026-10-28T11:13:30Z") }}
|
||||
p, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lines := strings.Split(p.Tree, "\n")
|
||||
if lines[0] != "17 changes in the last 120 min under issue 1422 (since "+when(at("2026-10-28T09:13:30Z"))+")" || p.Changes != 17 || p.Since != "2h0m0s" {
|
||||
t.Fatalf("%d changes, since %q:\n%s", p.Changes, p.Since, p.Tree)
|
||||
}
|
||||
for _, want := range []string{
|
||||
"▶ 1422 " + clip("The catch-up reads the trunk 'main' of repositories the catalogue follows by another branch or by a commit, and the forge holds none", 80) + " — → fixing by fix-trunk-unknown@shanks " + when(at("2026-10-28T09:38:37Z")) +
|
||||
"; 9 notes, the last " + when(at("2026-10-28T11:05:21Z")) + " by shanks.node-tools",
|
||||
"✓ mesh-catalog#537 (1422) opened " + when(at("2026-10-28T10:20:56Z")) + " · check pass " + when(at("2026-10-28T10:30:45Z")) + " · merged " + when(at("2026-10-28T10:34:49Z")) + ", on the machines",
|
||||
"✓ mesh-delivery#94 (1126, 1422) opened ",
|
||||
"▶ mesh-catalog#539 (1422, 1956) opened " + when(at("2026-10-28T11:07:42Z")) + ", now ready, waits for its merge",
|
||||
} {
|
||||
if !strings.Contains(p.Tree, want) {
|
||||
t.Errorf("no %q in\n%s", want, p.Tree)
|
||||
}
|
||||
}
|
||||
if strings.Contains(p.Tree, "de-spiegel") || strings.Contains(p.Tree, "triage") {
|
||||
t.Errorf("a change outside issue 1422 or the window is said:\n%s", p.Tree)
|
||||
}
|
||||
w := strings.Join(p.Warnings, "\n")
|
||||
for _, want := range []string{"keeps no history of an issue's moves", "keeps no time a merge reached its machines"} {
|
||||
if !strings.Contains(w, want) {
|
||||
t.Errorf("no %q in the warnings: %v", want, p.Warnings)
|
||||
}
|
||||
}
|
||||
d, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h", Detail: DetailDetailed})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"│ ├── noted " + when(at("2026-10-28T09:38:09Z")) + " by shanks.node-tools: lead-47@shanks 2026-10-28 09:40Z.",
|
||||
" └── merged " + when(at("2026-10-28T10:34:49Z")) + ": on the trunk its modules follow: merged there"} {
|
||||
if !strings.Contains(d.Tree, want) {
|
||||
t.Errorf("detailed: no %q in\n%s", want, d.Tree)
|
||||
}
|
||||
}
|
||||
// A milestone's issue the tracker's get cannot answer falls back to its row's moved time, said as such.
|
||||
m, err := k.Progress(ProgressAsk{Of: "milestone 47/L2", Since: "60m"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"6 changes in the last 60 min under milestone 47/L2", "▶ 1126 " + clip("About 49,800 controller code lines stay after the delivery model, against a first limit of 25,000", 80) + " — moved " +
|
||||
when(at("2026-10-28T11:13:28Z")) + ", now fixing (lead-47@shanks)", "✓ de-spiegel#6 (1126) check pass ", "✓ mesh-delivery#94 (1126, 1422) opened "} {
|
||||
if !strings.Contains(m.Tree, want) {
|
||||
t.Errorf("milestone: no %q in\n%s", want, m.Tree)
|
||||
}
|
||||
}
|
||||
if w := strings.Join(m.Warnings, "\n"); !strings.Contains(w, "issue-tracker.get 1126") {
|
||||
t.Errorf("the fallback is not said: %v", m.Warnings)
|
||||
}
|
||||
if small, _ := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h", Detail: DetailDetailed, Bound: LeastBound}); small == nil || size(small) > LeastBound || !strings.Contains(small.Cut, "raise bound") {
|
||||
t.Errorf("a window's answer is not kept within its bound: %+v", small)
|
||||
}
|
||||
// A milestone's move, from its history: what it was before, what it is now, who moved it and when.
|
||||
k.Now = func() time.Time { return *at("2026-10-26T18:40:00Z") }
|
||||
ds, err := k.Progress(ProgressAsk{Of: "design 47", Since: "60m"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ds.Tree != "1 change in the last 60 min under design 47 (since "+when(at("2026-10-26T17:40:00Z"))+")\n└── ✓ milestone 47/6 open → closed: folded into R4g · by shanks.node-tools "+when(at("2026-10-26T18:10:10Z")) {
|
||||
t.Errorf("design:\n%s", ds.Tree)
|
||||
}
|
||||
for _, bad := range []string{"0m", "-5m", "25h", "a day", "5"} {
|
||||
if _, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: bad}); err == nil || !strings.Contains(err.Error(), "since") {
|
||||
t.Errorf("since %q: %v", bad, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user