mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; 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 superseded: a newer head of the same pull request
A calendar reminder showed as a transparent, empty window on shanks and no tool could say which window it was or which preferences Betterbird ran with. The module owns one file, the user.js its setting prefs fills, which its tools place whole into the profile Betterbird uses and never over an operator's own; it reads named preferences without credentials or accounts, restarts Betterbird under the user manager so its output reaches the journal, checks what it promises, and shows a reminder on demand from a throw-away profile so a fix can be proved without the operator's calendar. No package: betterbird-bin is the AUR's.
111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// betterbird's shape (novox/hq issue 345, ADR 0208): no package (Betterbird comes from the AUR, and the
|
|
// node-engine installs from the official repositories only), no seat, no start of its own (the operator
|
|
// starts it from the launcher); the X display on its own machine; one directory and one file of its own,
|
|
// the user.js the setting prefs fills; and the Go bundle serving exactly the listed betterbird_ tools.
|
|
|
|
type manifest struct {
|
|
Module string `json:"module"`
|
|
Version string `json:"version"`
|
|
Requires []string `json:"requires"`
|
|
Settings map[string]any `json:"settings"`
|
|
Tools []string `json:"tools"`
|
|
Resources []map[string]any `json:"resources"`
|
|
Build struct {
|
|
Artifacts []map[string]any `json:"artifacts"`
|
|
} `json:"build"`
|
|
}
|
|
|
|
func readManifest(t *testing.T) (manifest, string) {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dec := json.NewDecoder(strings.NewReader(string(raw)))
|
|
dec.DisallowUnknownFields()
|
|
var m manifest
|
|
if err := dec.Decode(&m); err != nil {
|
|
t.Fatalf("module.json: %v", err)
|
|
}
|
|
return m, string(raw)
|
|
}
|
|
|
|
func TestItOwnsTheUserJSAndNothingOfTheProfile(t *testing.T) {
|
|
m, raw := readManifest(t)
|
|
if m.Module != "betterbird" || !reflect.DeepEqual(m.Requires, []string{"x11-display"}) {
|
|
t.Fatalf("%+v", m)
|
|
}
|
|
var ids []string
|
|
for _, r := range m.Resources {
|
|
ids = append(ids, r["id"].(string))
|
|
if r["type"] == "package" {
|
|
t.Errorf("betterbird-bin is the AUR's: a package resource asks the node-engine for what it cannot install")
|
|
}
|
|
if p, _ := r["path"].(string); !strings.HasPrefix(p, "${machine:account-home}/.config/betterbird") {
|
|
t.Errorf("a path outside the module's directory: %s", p)
|
|
}
|
|
}
|
|
if strings.Join(ids, ",") != "configuration-dir,user-js" {
|
|
t.Fatalf("%v", ids)
|
|
}
|
|
for _, never := range []string{".thunderbird", "prefs.js", "logins.json", "key4.db"} {
|
|
if strings.Contains(raw, never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
file := m.Resources[1]
|
|
content := file["content"].(string)
|
|
if !strings.HasPrefix(content, mark) || !strings.Contains(content, "${setting:prefs}") || file["trusted"] != nil {
|
|
t.Fatalf("the user.js begins with the mark, holds the setting, and stays trusted (only the terminal sets it): %v", file)
|
|
}
|
|
// The default is a comment, so a machine without the setting places a valid, empty user.js.
|
|
def := m.Settings["prefs"].(map[string]any)["default"].(string)
|
|
if err := validUserJS(strings.ReplaceAll(content, "${setting:prefs}", def)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestTheToolsAgreeWithTheManifest(t *testing.T) {
|
|
m, raw := readManifest(t)
|
|
served := map[string]bool{}
|
|
for _, tool := range tools() {
|
|
served[tool.Name] = true
|
|
if !strings.HasPrefix(tool.Name, "betterbird_") || strings.TrimSpace(tool.Description) == "" {
|
|
t.Errorf("%s: prefixed betterbird_ and described", tool.Name)
|
|
}
|
|
}
|
|
for _, name := range m.Tools {
|
|
if !served[name] {
|
|
t.Errorf("module.json lists %s, which the bundle does not serve", name)
|
|
}
|
|
delete(served, name)
|
|
}
|
|
for name := range served {
|
|
t.Errorf("the bundle serves %s, which module.json does not list", name)
|
|
}
|
|
if len(m.Build.Artifacts) != 1 {
|
|
t.Fatalf("%v", m.Build.Artifacts)
|
|
}
|
|
b := m.Build.Artifacts[0]
|
|
if b["kind"] != "bundle" || b["language"] != "go" || b["from"] != "cmd/betterbird-tools" || b["binary"] != "betterbird-tools" {
|
|
t.Errorf("the Go tools bundle: %v", b)
|
|
}
|
|
s := strings.ToLower(raw)
|
|
for _, never := range []string{"/home/", "jochen", "g14", "shanks", "novox.be", "http"} {
|
|
if strings.Contains(s, never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
}
|