mesh/merge-gate head 19e3eac74658 merged into trunk 89cfa9431d8f — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 19e3eac74658 merged into trunk 89cfa9431d8f — pass: its merge-check.sh passed
mesh/delivery delivered
113 lines
4.2 KiB
Go
113 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// aPresenceFake is the watchman's presence bucket as a reader sees it.
|
|
func aPresenceFake(entries map[string]presenceEntry, err error) presenceReader {
|
|
return func(_ context.Context, _ []string) (map[string]presenceEntry, error) { return entries, err }
|
|
}
|
|
|
|
// 1183/AC1: one function says when a machine was last heard: online is now, offline is the entry's time, and only where
|
|
// presence has no key, the last report.
|
|
func TestLastHeardIsPresenceThenTheReport(t *testing.T) { // 1183/AC1
|
|
now := time.Now()
|
|
report := now.Add(-time.Hour)
|
|
if got := lastHeard(report, nil, now); !got.Equal(report) {
|
|
t.Fatalf("no presence key: last heard is %v, want the report %v", got, report)
|
|
}
|
|
if got := lastHeard(report, &presenceEntry{Online: true, At: now.Add(-9 * time.Hour)}, now); !got.Equal(now) {
|
|
t.Fatalf("online: last heard is %v, want now", got)
|
|
}
|
|
offlineAt := now.Add(-4 * time.Minute)
|
|
if got := lastHeard(now.Add(-time.Minute), &presenceEntry{At: offlineAt}, now); !got.Equal(offlineAt) {
|
|
t.Fatalf("offline: last heard is %v, want the entry's time %v, not a report's", got, offlineAt)
|
|
}
|
|
if got := lastHeard(time.Time{}, nil, now); !got.IsZero() {
|
|
t.Fatalf("never heard and no key: %v", got)
|
|
}
|
|
}
|
|
|
|
// The machines facts, the nodes list, node and status all take that one function.
|
|
func TestEveryReaderOfLastHeardTakesPresence(t *testing.T) { // 1183/AC1
|
|
now := time.Now()
|
|
m := machineFacts{name: "laptop", lastHeard: now.Add(-time.Minute)}
|
|
applyPresence(&m, nil, &presenceEntry{At: now.Add(-4 * time.Minute)}, now)
|
|
if !m.lastHeard.Equal(now.Add(-4 * time.Minute)) {
|
|
t.Fatalf("machine facts take a report over the offline entry: %v", m.lastHeard)
|
|
}
|
|
}
|
|
|
|
func twoNodes(now time.Time) []inventory.Node {
|
|
return []inventory.Node{
|
|
{Name: "anchor", LastSeen: now.Add(-time.Hour)},
|
|
{Name: "laptop", LastSeen: now.Add(-30 * time.Second)},
|
|
{Name: "fresh"},
|
|
}
|
|
}
|
|
|
|
// 1183/AC2: a machine online by presence whose last report is an hour old reads here, is not quiet and is not silent; one
|
|
// offline for 4 minutes reads out of touch.
|
|
func TestOnlineByPresenceReadsHereWhateverTheReport(t *testing.T) { // 1183/AC2
|
|
now := time.Now()
|
|
read := aPresenceFake(map[string]presenceEntry{
|
|
"anchor": {Online: true, At: now.Add(-6 * time.Hour)},
|
|
"laptop": {Online: false, At: now.Add(-4 * time.Minute)},
|
|
}, nil)
|
|
nodes, err := nodesHeard(context.Background(), twoNodes(now), read, now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := heardFromAt(nodes[0], now); got != "here" {
|
|
t.Errorf("anchor online with an hour-old report reads %q", got)
|
|
}
|
|
if got := heardFromAt(nodes[1], now); got != "out of touch 4m" {
|
|
t.Errorf("laptop offline 4 minutes reads %q", got)
|
|
}
|
|
if got := heardFromAt(nodes[2], now); got != "never spoken" {
|
|
t.Errorf("a machine with no key and no report reads %q", got)
|
|
}
|
|
quiet := notHeardLately(nodes, now)
|
|
if len(quiet) != 1 || quiet[0].Name != "fresh" {
|
|
t.Errorf("quiet is %+v: an online machine is not quiet, and a machine offline 4 minutes is not quiet yet", quiet)
|
|
}
|
|
f := &machineFacts{name: "anchor", lastHeard: nodes[0].LastSeen}
|
|
if f.silent(now) {
|
|
t.Errorf("a machine online by presence is silent")
|
|
}
|
|
}
|
|
|
|
// Unreadable presence is said, and the report stands as it was.
|
|
func TestUnreadablePresenceLeavesTheReport(t *testing.T) { // 1183/AC1
|
|
now := time.Now()
|
|
nodes, err := nodesHeard(context.Background(), twoNodes(now), aPresenceFake(nil, errors.New("no bus")), now)
|
|
if err == nil || !strings.Contains(err.Error(), "no bus") {
|
|
t.Fatalf("an unreadable presence is not said: %v", err)
|
|
}
|
|
if !nodes[0].LastSeen.Equal(now.Add(-time.Hour)) {
|
|
t.Fatalf("the report was replaced: %v", nodes[0].LastSeen)
|
|
}
|
|
}
|
|
|
|
// 1183/AC4: where the watchman gives no verdict a machine is silent after a fixed three minutes, whatever interval a
|
|
// node-engine once said.
|
|
func TestTheSilentBoundIsFixed(t *testing.T) { // 1183/AC4
|
|
now := time.Now()
|
|
for _, c := range []struct {
|
|
ago time.Duration
|
|
want bool
|
|
}{{179 * time.Second, false}, {181 * time.Second, true}} {
|
|
m := machineFacts{name: "laptop", lastHeard: now.Add(-c.ago)}
|
|
if got := m.silent(now); got != c.want {
|
|
t.Errorf("heard %v ago: silent %v, want %v", c.ago, got, c.want)
|
|
}
|
|
}
|
|
}
|