161 lines
6.0 KiB
Go
161 lines
6.0 KiB
Go
package main
|
|
|
|
// The package view over the bus (novox/hq ADR 0507 §6, §7; acceptance 1840/AC6): two nodes' `installed` keys and
|
|
// the node-engine's `packages`, answered without asking any machine.
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// viewStates is two nodes' packages on the bus: node-a current and declared, node-b over the cap since a while,
|
|
// with a foreign package, and no declaration the node-engine published.
|
|
func viewStates(t *testing.T) (*fakeStates, *Machine) {
|
|
t.Helper()
|
|
states := &fakeStates{}
|
|
inst := states.store(StateInstalled)
|
|
inst.Put("node-a", InstalledValue{At: t0, Packages: []InstalledPackage{
|
|
{"glibc", "2.42-1", false, "core"}, {"tmux", "3.5-1", true, "extra"}, {"vim", "9.1-1", true, "extra"},
|
|
{"zsh", "5.9-2", true, "extra"},
|
|
}})
|
|
inst.Put("node-b", InstalledValue{At: t0.Add(-time.Hour), Packages: []InstalledPackage{
|
|
{"glibc", "2.42-1", false, "core"}, {"yay", "12.0-1", true, RepoForeign}, {"zsh", "5.9-1", false, "extra"},
|
|
}})
|
|
inst.Put("node-b.not-current", NotCurrent{Bytes: 300000, Since: t0.Add(-30 * time.Minute)})
|
|
decl := states.store(DeclaredPackagesState)
|
|
var d declaredValue
|
|
json.Unmarshal([]byte(`{"node":"node-a","packages":[{"package":"vim","modules":["editor"],"absent":false},`+
|
|
`{"package":"tmux","modules":["shell"],"absent":true},{"package":"zsh","modules":["login","shell"],"absent":false}]}`), &d)
|
|
decl.Put("node-a", d)
|
|
m := machine(func(_ context.Context, name string, args ...string) Ran {
|
|
t.Errorf("a machine was asked: %s %v", name, args)
|
|
return Ran{Status: 99}
|
|
}, 1000)
|
|
m.Node, m.States = "node-a", readerOf(states)
|
|
return states, m
|
|
}
|
|
|
|
func TestOnePackageOnEveryNodeWithWhoDeclaresIt(t *testing.T) {
|
|
_, m := viewStates(t)
|
|
r, err := m.Packages("zsh", 500)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
on := r["nodes"].([]PackageOnNode)
|
|
if len(on) != 2 {
|
|
t.Fatalf("%+v", on)
|
|
}
|
|
a, b := on[0], on[1]
|
|
if a.Node != "node-a" || !a.Installed || a.Version != "5.9-2" || !a.Explicit || a.Repository != "extra" ||
|
|
strings.Join(a.DeclaredBy, ",") != "login,shell" || !a.Current {
|
|
t.Errorf("node-a: %+v", a)
|
|
}
|
|
if b.Node != "node-b" || b.Version != "5.9-1" || b.Explicit || len(b.DeclaredBy) != 0 || b.Current {
|
|
t.Errorf("node-b: %+v", b)
|
|
}
|
|
r, _ = m.Packages("yay", 500)
|
|
on = r["nodes"].([]PackageOnNode)
|
|
if on[0].Installed || !on[1].Installed || !on[1].Foreign || on[1].Repository != "" {
|
|
t.Errorf("yay: %+v", on)
|
|
}
|
|
}
|
|
|
|
func TestTheViewSaysWhatDiffersWhatIsOnSomeNodesWhatNoModuleDeclaresAndTheForeignApart(t *testing.T) {
|
|
_, m := viewStates(t)
|
|
r, err := m.Packages("", 500)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nodes := r["nodes"].([]NodeView)
|
|
if len(nodes) != 2 {
|
|
t.Fatalf("%+v", nodes)
|
|
}
|
|
a, b := nodes[0], nodes[1]
|
|
if a.Node != "node-a" || a.Packages != 4 || a.Explicit != 3 || !a.Current || fmt.Sprint(a.Undeclared) != "[{tmux 3.5-1}]" ||
|
|
len(a.Foreign) != 0 || !a.At.Equal(t0) {
|
|
t.Errorf("node-a: %+v", a)
|
|
}
|
|
if b.Current || b.NotCurrent == nil || b.NotCurrent.Bytes != 300000 || b.Undeclared != nil ||
|
|
fmt.Sprint(b.Foreign) != "[{yay 12.0-1}]" || !b.At.Equal(t0.Add(-time.Hour)) {
|
|
t.Errorf("node-b (not current, no declaration, a foreign package): %+v", b)
|
|
}
|
|
if d := r["differing"].([]Spread); len(d) != 1 || d[0].Name != "zsh" || d[0].Versions["node-a"] != "5.9-2" || d[0].Versions["node-b"] != "5.9-1" {
|
|
t.Errorf("differing: %+v", d)
|
|
}
|
|
some := r["some_nodes"].([]Spread)
|
|
var names []string
|
|
for _, s := range some {
|
|
names = append(names, s.Name)
|
|
}
|
|
if strings.Join(names, ",") != "tmux,vim" {
|
|
t.Errorf("on some nodes only, the foreign apart: %v", names)
|
|
}
|
|
r, _ = m.Packages("", 1)
|
|
if r["some_nodes_count"] != 2 || r["some_nodes_truncated"] != true || len(r["some_nodes"].([]Spread)) != 1 {
|
|
t.Errorf("bounded: %v", r)
|
|
}
|
|
}
|
|
|
|
func TestInstalledAndForeignAnswerFromTheBus(t *testing.T) {
|
|
_, m := viewStates(t)
|
|
r, err := m.InstalledFrom("", "", "", false, 10)
|
|
if err != nil || r["node"] != "node-a" || r["count"] != 4 || r["current"] != true {
|
|
t.Fatalf("this node's: %v %v", r, err)
|
|
}
|
|
pk := r["packages"].([]Package)
|
|
if pk[0].Reason != "dependency" || pk[0].Repository != "core" || pk[1].Reason != "explicit" {
|
|
t.Errorf("%+v", pk)
|
|
}
|
|
r, err = m.InstalledFrom("node-b", "", "", true, 10)
|
|
if err != nil || r["count"] != 1 || r["current"] != false || r["not_current"] == nil {
|
|
t.Fatalf("node-b's foreign: %v %v", r, err)
|
|
}
|
|
if tot := r["totals"].(map[string]int); tot["explicit"] != 1 || tot["dependency"] != 2 || tot["foreign"] != 1 {
|
|
t.Errorf("totals: %v", tot)
|
|
}
|
|
f, err := m.ForeignFrom("node-b")
|
|
if err != nil || f["count"] != 1 || fmt.Sprint(f["packages"]) != "[{yay 12.0-1}]" {
|
|
t.Errorf("%v %v", f, err)
|
|
}
|
|
if _, err := m.InstalledFrom("node-c", "", "", false, 10); err == nil || !strings.Contains(err.Error(), "node-c") {
|
|
t.Errorf("a node with no packages on the bus: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpdatesAnswerEveryNodeAndWhenItsListLastChanged(t *testing.T) {
|
|
states, m := viewStates(t)
|
|
up := states.store(StateUpdates)
|
|
up.now = func() time.Time { return t0.Add(-2 * time.Hour) }
|
|
up.Put("node-a", UpdatesValue{Reachable: true, Packages: []VersionChange{{"zsh", "5.9-2", "5.9-3"}}})
|
|
up.now = func() time.Time { return t0.Add(-3 * time.Hour) }
|
|
up.Put("node-b", UpdatesValue{Reachable: false, Packages: []VersionChange{}})
|
|
r, err := m.UpdatesFor("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v := r["nodes"].([]UpdatesView)
|
|
if len(v) != 2 || v[0].Count != 1 || !v[0].Reachable || v[0].Changed == nil || !v[0].Changed.Equal(t0.Add(-2*time.Hour)) ||
|
|
v[1].Reachable || !v[1].Changed.Equal(t0.Add(-3*time.Hour)) {
|
|
t.Fatalf("%+v", v)
|
|
}
|
|
r, _ = m.UpdatesFor("node-b")
|
|
if v := r["nodes"].([]UpdatesView); len(v) != 1 || v[0].Node != "node-b" {
|
|
t.Errorf("one node: %+v", v)
|
|
}
|
|
}
|
|
|
|
func TestANodeArgumentIsOneKeyToken(t *testing.T) {
|
|
for _, bad := range []string{"node-a.not-current", "node-a.*", "a b"} {
|
|
if _, err := nodeOf(map[string]any{"node": bad}); err == nil {
|
|
t.Errorf("%q was taken as a node", bad)
|
|
}
|
|
}
|
|
if n, err := nodeOf(map[string]any{"node": "node-a"}); err != nil || n != "node-a" {
|
|
t.Errorf("%q %v", n, err)
|
|
}
|
|
}
|