mesh/merge-gate head 481e44cdaa29 merged into trunk 23ea0f431faf — pass: builds betterbird, sent nowhere; no bus step; 1 wait(s) for a person; every machin…
mesh/repo-check head 481e44cdaa29 merged into trunk 23ea0f431faf — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Betterbird's own default asks at every start with a window to become the default for mail, news, feeds and calendar files. The policy locks mail.shell.checkDefaultClient off, which wins over a profile's own value, and betterbird_check says when a node would still ask.
232 lines
9.1 KiB
Go
232 lines
9.1 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// What the mesh installs, as the tools say it (novox/hq ADR 0316 §16): the version against the pin and the
|
|
// newest published build, the policy, Owl against its pin; and the reminder test, visibly a test.
|
|
|
|
func (f *fake) xpi(path, version string) {
|
|
f.t.Helper()
|
|
full := filepath.Join(f.Root, path)
|
|
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
|
f.t.Fatal(err)
|
|
}
|
|
out, err := os.Create(full)
|
|
if err != nil {
|
|
f.t.Fatal(err)
|
|
}
|
|
w := zip.NewWriter(out)
|
|
m, _ := w.Create("manifest.json")
|
|
m.Write([]byte(`{"version": "` + version + `", "browser_specific_settings": {"gecko": {"id": "owl@beonex.com"}}}`))
|
|
w.Close()
|
|
out.Close()
|
|
}
|
|
|
|
// placedByTheMesh is a node after the switch: the release, the policy and Owl's xpi in /opt/betterbird, and
|
|
// the profile running Owl at the pin.
|
|
func (f *fake) placedByTheMesh() {
|
|
f.noPackage()
|
|
f.write(appIni, "[App]\nVendor=Betterbird Project\nVersion=153.4.0\nBuildID=20260929222610\n")
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "install", "distribution", "policies.json"))
|
|
if err != nil {
|
|
f.t.Fatal(err)
|
|
}
|
|
f.write(policyFile, string(raw))
|
|
f.xpi(owlFile, "1.5.6.1")
|
|
f.write(profileDir+"/extensions.json", `{"addons":[{"id":"owl@beonex.com","version":"1.5.6.1","active":true,"location":"app-profile"}]}`)
|
|
}
|
|
|
|
func TestTheStatusSaysTheInstallationAgainstThePinTheNewestBuildThePolicyAndOwl(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
links(t, nil)
|
|
publishedBuilds = func() (string, error) {
|
|
return "blocklist,{}\nLinux,153,20261101000000\nLinux,140,20260829111240\n", nil
|
|
}
|
|
t.Cleanup(func() { publishedBuilds = func() (string, error) { return "", os.ErrDeadlineExceeded } })
|
|
s, err := f.Status()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
in := s.Installation
|
|
if in.From != "the mesh" || in.Version != "153.4.0" || in.Newest != "20261101000000" || !in.Behind ||
|
|
!strings.HasPrefix(in.Policy, "placed:") || in.Owl == nil || in.Owl.Pinned != "1.5.6.1" || !in.Owl.AtThePin || !in.Owl.Active {
|
|
t.Fatalf("%+v %+v", in, in.Owl)
|
|
}
|
|
c, err := f.Check()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, x := range c.Findings {
|
|
found = found || strings.Contains(x.What, "behind the newest published build 20261101000000")
|
|
}
|
|
if !found {
|
|
t.Fatalf("a release left behind is said: %+v", c.Findings)
|
|
}
|
|
}
|
|
|
|
func TestTheCommunityPackageAPolicyThatIsNotTheModulesAndAnOwlOffItsPinAreSaid(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
links(t, nil)
|
|
f.write(policyFile, `{"policies": {"ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed",
|
|
"install_url": "https://vendor.example/owl.xpi", "updates_disabled": true}}}}`)
|
|
f.write(profileDir+"/extensions.json", `{"addons":[{"id":"owl@beonex.com","version":"1.5.7","active":true,"location":"app-profile"}]}`)
|
|
in := f.installation(filepath.Join(f.Root, profileDir))
|
|
if !strings.Contains(in.Policy, "DisableAppUpdate") || in.Owl.AtThePin || in.Owl.InProfile != "1.5.7" {
|
|
t.Fatalf("%+v %+v", in, in.Owl)
|
|
}
|
|
// A newer Owl than the pin is a fact with the real remedy, never a finding to remove it.
|
|
if !strings.Contains(in.Owl.Note, "move the module's pin") {
|
|
t.Fatalf("%+v", in.Owl)
|
|
}
|
|
f.write(policyFile, `{"policies": {"DisableAppUpdate": true, "ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed",
|
|
"install_url": "file:///opt/betterbird/owl/owl.xpi", "updates_disabled": true}}}}`)
|
|
c, _ := f.Check()
|
|
for _, x := range c.Findings {
|
|
if strings.Contains(x.What, "Owl") {
|
|
t.Errorf("a newer Owl is not a finding: %+v", x)
|
|
}
|
|
}
|
|
f.write(profileDir+"/extensions.json", `{"addons":[{"id":"owl@beonex.com","version":"1.5.5","active":true,"location":"app-profile"}]}`)
|
|
c, _ = f.Check()
|
|
found := false
|
|
for _, x := range c.Findings {
|
|
found = found || (strings.Contains(x.What, "older than the mesh's pin") && strings.Contains(x.Do, "move the module's Owl pin"))
|
|
}
|
|
if !found {
|
|
t.Fatalf("an older Owl is a finding whose remedy is the pin: %+v", c.Findings)
|
|
}
|
|
f.write(policyFile, `{"policies": {"DisableAppUpdate": true, "ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed",
|
|
"install_url": "https://vendor.example/owl.xpi", "updates_disabled": true}}}}`)
|
|
if in := f.installation(""); !strings.Contains(in.Policy, "not from the mesh's xpi") {
|
|
t.Fatalf("%s", in.Policy)
|
|
}
|
|
f.answer = func(name string, args []string) Output {
|
|
if name == "pacman" && args[0] == "-Q" {
|
|
return Output{Stdout: "betterbird-bin 153.1.0esr-1\n"}
|
|
}
|
|
return Output{}
|
|
}
|
|
in = f.installation("")
|
|
if !strings.HasPrefix(in.From, "the community package betterbird-bin 153.1.0esr-1") || !strings.Contains(in.From, "operator") {
|
|
t.Fatalf("%s", in.From)
|
|
}
|
|
raw, _ := json.Marshal(in)
|
|
if strings.Contains(string(raw), "example.org") {
|
|
t.Fatalf("%s", raw)
|
|
}
|
|
}
|
|
|
|
func TestTheReminderTestIsVisiblyATestAndSaysItOpensAWindow(t *testing.T) {
|
|
f := newFake(t)
|
|
f.desktopSession()
|
|
f.betterbird()
|
|
links(t, nil)
|
|
f.declare("// none")
|
|
f.declareAccounts(personal)
|
|
a, err := f.ReminderTest(40*time.Second, nil, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dir := filepath.Join("/run/user/1000", testUnit)
|
|
css := f.read(dir + "/profile/chrome/userChrome.css")
|
|
user := f.read(dir + "/profile/user.js")
|
|
cal := f.read(dir + "/test.ics")
|
|
if !strings.Contains(css, "MESH TEST") || !strings.Contains(css, `:root:not([windowtype="Calendar:AlarmWindow"])`) {
|
|
t.Fatalf("the test's windows but the reminder carry a MESH TEST band: %s", css)
|
|
}
|
|
if !strings.Contains(user, `user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);`) ||
|
|
strings.Contains(user, "mesh_personal") || strings.Contains(user, "example.org") {
|
|
t.Fatalf("the test profile reads its userChrome.css and holds no declared account: %s", user)
|
|
}
|
|
if !strings.Contains(cal, "SUMMARY:MESH TEST reminder") || !strings.Contains(a.OnScreen, "operator's screen") {
|
|
t.Fatalf("the event and the answer say it is a test on the operator's screen: %s %+v", cal, a)
|
|
}
|
|
if !f.called("systemd-run --user --collect --quiet --unit="+testUnit) || !strings.Contains(strings.Join(f.calls, "\n"), startBin+" --no-remote") {
|
|
t.Fatalf("the test starts Betterbird itself, never the module's start: %v", f.calls)
|
|
}
|
|
for _, tool := range tools() {
|
|
if tool.Name == "betterbird_reminder_test" && !strings.HasPrefix(tool.Description, "OPENS WINDOWS ON THE OPERATOR'S SCREEN") {
|
|
t.Fatalf("the description says first that a window opens on the operator's screen: %s", tool.Description)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheUserJSDescriptionSaysAdoption(t *testing.T) {
|
|
for _, tool := range tools() {
|
|
if tool.Name == "betterbird_user_js" && (!strings.Contains(tool.Description, "adopted under its own keys") ||
|
|
strings.Contains(tool.Description, "refuses the apply")) {
|
|
t.Fatalf("%s", tool.Description)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Betterbird never asks at its start to be the default mail client: the module's policy locks
|
|
// mail.shell.checkDefaultClient off, and the check says so when it is on (novox/hq issue 345).
|
|
func TestTheDefaultClientAskIsLockedOffAndTheCheckSaysWhenItIsOn(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
links(t, nil)
|
|
asks := func() string {
|
|
t.Helper()
|
|
c, err := f.Check()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, x := range c.Findings {
|
|
if strings.Contains(x.What, "asks at its start") {
|
|
return x.What + " | " + x.Do
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
if got := asks(); got != "" {
|
|
t.Fatalf("the module's policy locks the ask off, yet: %s", got)
|
|
}
|
|
unlocked := `{"policies": {"DisableAppUpdate": true, "ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed",
|
|
"install_url": "file:///opt/betterbird/owl/owl.xpi", "updates_disabled": true}}}}`
|
|
if why := policyProblem([]byte(unlocked)); !strings.Contains(why, defaultClientPref) {
|
|
t.Fatalf("a policy that leaves the ask on is said: %q", why)
|
|
}
|
|
f.write(policyFile, unlocked)
|
|
if got := asks(); !strings.Contains(got, "default mail client") || !strings.Contains(got, "push the module") {
|
|
t.Fatalf("the ask on is a finding with its remedy: %q", got)
|
|
}
|
|
f.write(policyFile, `{"policies": {"DisableAppUpdate": true, "Preferences": {"mail.shell.checkDefaultClient": {"Value": false, "Status": "default"}},
|
|
"ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed", "install_url": "file:///opt/betterbird/owl/owl.xpi", "updates_disabled": true}}}}`)
|
|
if why := policyProblem(mustRead(t, f, policyFile)); !strings.Contains(why, "locked") {
|
|
t.Fatalf("a default the profile can override is not a lock: %q", why)
|
|
}
|
|
f.write(profileDir+"/prefs.js", `user_pref("mail.shell.checkDefaultClient", true);`+"\n")
|
|
if got := asks(); got == "" {
|
|
t.Fatal("a profile that turned the ask back on over an unlocked default is a finding")
|
|
}
|
|
f.write(policyFile, unlocked)
|
|
f.write(profileDir+"/prefs.js", `user_pref("mail.shell.checkDefaultClient", false);`+"\n")
|
|
if got := asks(); got != "" {
|
|
t.Fatalf("a profile with the ask off does not ask: %s", got)
|
|
}
|
|
}
|
|
|
|
func mustRead(t *testing.T, f *fake, p string) []byte {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join(f.Root, p))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|