The mesh noticed 48 core failures in six days and told nobody (ADR 0227). messenger holds the operator-channel seat: it consumes the controller's condition events and sends them to Telegram and the desktop notifier, deduplicated by key, reminded once, edited on clear, capped at 20 an hour with the rest folded, and refusing anything carrying an address, a path or a secret. mesh-watcher, on a machine other than the control node, sends to Telegram directly when the self-check heartbeat or the bus goes silent.
69 lines
2.2 KiB
Go
69 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The manifest says what the code does: it consumes the heartbeat and nothing else, calls its own
|
|
// ping and the controller's seats verb, holds no seat (it must not be the controller's), keeps its
|
|
// Telegram token as its own secret, lists its own tools — and names nothing of one installation.
|
|
|
|
func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m struct {
|
|
Module string `json:"module"`
|
|
Consumes []string `json:"consumes"`
|
|
Invokes []string `json:"invokes"`
|
|
Claims []any `json:"claims"`
|
|
Own map[string]string `json:"own-secrets"`
|
|
Tools []string `json:"tools"`
|
|
Build struct {
|
|
Artifacts []map[string]any `json:"artifacts"`
|
|
} `json:"build"`
|
|
}
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(m.Consumes, []string{ControllerSeat + "." + HeartbeatEvent}) {
|
|
t.Fatalf("consumes %v", m.Consumes)
|
|
}
|
|
if !reflect.DeepEqual(m.Invokes, []string{m.Module + ".watcher_ping", "seat:mesh-controller.seats"}) {
|
|
t.Fatalf("invokes %v", m.Invokes)
|
|
}
|
|
if len(m.Claims) != 0 {
|
|
t.Fatalf("a watcher holds no seat: %v", m.Claims)
|
|
}
|
|
env, _ := m.Build.Artifacts[0]["env"].(map[string]any)
|
|
if m.Own["telegram-token"] == "" || env["MESH_WATCHER_TELEGRAM_TOKEN_FILE"] != m.Own["telegram-token"] {
|
|
t.Fatalf("token: own %v, env %v", m.Own, env)
|
|
}
|
|
var served []string
|
|
for _, tool := range tools(NewWatcher(&fakeTelegram{}, time.Now, t.Logf, ""), &listening{}) {
|
|
served = append(served, tool.Name)
|
|
if strings.TrimSpace(tool.Description) == "" {
|
|
t.Errorf("%s has no description", tool.Name)
|
|
}
|
|
}
|
|
listed := append([]string(nil), m.Tools...)
|
|
sort.Strings(served)
|
|
sort.Strings(listed)
|
|
if !reflect.DeepEqual(served, listed) {
|
|
t.Fatalf("serves %v, lists %v", served, listed)
|
|
}
|
|
for _, never := range []string{"/home/", "jochen", "g14", "shanks", "novox", "zurag", "api.telegram"} {
|
|
if strings.Contains(strings.ToLower(string(raw)), never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
}
|