mesh/merge-gate head c4b4bac757c5 merged into trunk 0863ec2a059d — pass: builds betterbird, sent nowhere; no bus step; 1 wait(s) for a person; every machin…
mesh/repo-check head c4b4bac757c5 merged into trunk 0863ec2a059d — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Betterbird keeps the active theme in its add-on database, and extensions.activeThemeID only mirrors it. So clearing the preference changed nothing, and the drift report then hid that. The theme is now read from extensions.json against default-theme@mozilla.org. Another theme is drift, and its remedy is the add-on manager: config_apply never clears it and keep refuses it. A credential's, an account's or a server's name in the defaults file is refused by code: it is never answered or cleared, and the check names it. Apply also refuses while the profile's lock is there, and syncs prefs.js before the rename. A saved default of a built-in preference is no longer drift.
521 lines
22 KiB
Go
521 lines
22 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The configuration the module ships (novox/hq issue 345): the policy's locks, the module's defaults file, and the
|
|
// tools that show the layers, flag drift, clear it and propose a node layer.
|
|
|
|
// shippedDefaultsWant is the DEFAULT layer the operator approved on 2026-10-12: the laptop's setup, with
|
|
// reminders missed while asleep shown at the next start and chat off.
|
|
var shippedDefaultsWant = map[string]string{
|
|
"mail.uidensity": "0",
|
|
"mail.uifontsize": "14",
|
|
"mail.dark-reader.enabled": "false",
|
|
"mail.pane_config.dynamic": "2",
|
|
"mail.pane_config.multiline_all": "true",
|
|
"mail.threadpane.listview": "0",
|
|
"mailnews.default_view_flags": "1",
|
|
"mail.warn_on_shift_delete": "false",
|
|
"mail.biff.show_alert": "false",
|
|
"mail.biff.play_sound": "false",
|
|
"mail.biff.show_tray_icon": "false",
|
|
"mail.biff.use_system_alert": "true",
|
|
"calendar.view.daystarthour": "9",
|
|
"calendar.view.dayendhour": "20",
|
|
"calendar.item.promptDelete": "false",
|
|
"calendar.timezone.useSystemTimezone": "true",
|
|
"calendar.alarms.show": "true",
|
|
"calendar.alarms.showmissed": "true",
|
|
"mail.chat.enabled": "false",
|
|
"mailnews.message_display.disable_remote_image": "true",
|
|
"privacy.donottrackheader.enabled": "true",
|
|
}
|
|
|
|
var shippedKeepsWant = []string{"extensions.activeThemeID", "browser.display.use_system_colors",
|
|
"layout.css.always_underline_links", "mail.spam.manualMark", "mail.compose.show_attachment_pane"}
|
|
|
|
func repoFile(t *testing.T, rel string) []byte {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", rel))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// withDefaults places the module's defaults file as the push does.
|
|
func (f *fake) withDefaults() {
|
|
f.t.Helper()
|
|
f.write(meshDefaults, string(repoFile(f.t, "install/defaults-pref/mesh-defaults.js")))
|
|
}
|
|
|
|
// omni places a packed application holding the given files.
|
|
func (f *fake) omni(files map[string]string) {
|
|
f.t.Helper()
|
|
full := filepath.Join(f.Root, omniJa)
|
|
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)
|
|
for name, body := range files {
|
|
e, _ := w.Create(name)
|
|
e.Write([]byte(body))
|
|
}
|
|
w.Close()
|
|
out.Close()
|
|
}
|
|
|
|
// betterbirdsOwn is a small omni.ja: two files that contradict each other, as all-thunderbird.js and
|
|
// mailnews.js do in the pinned release.
|
|
func (f *fake) betterbirdsOwn() {
|
|
f.omni(map[string]string{
|
|
"greprefs.js": `pref("gfx.webrender.software", false);` + "\n",
|
|
"defaults/pref/all-thunderbird.js": `pref("offline.autoDetect", true);` + "\n" +
|
|
`pref("mail.threadpane.listview", 1);` + "\n" + `pref("mail.uidensity", 1);` + "\n" +
|
|
`pref("mail.shell.checkDefaultClient", true);` + "\n",
|
|
"defaults/pref/mailnews.js": `pref("offline.autoDetect", false);` + "\n" +
|
|
`pref("mail.biff.show_alert", true); // a comment after it` + "\n" + `pref("mail.spam.manualMark", false);` + "\n" +
|
|
`pref("mailnews.start_page.enabled", true);` + "\n",
|
|
"defaults/pref/calendar.js": `pref("calendar.alarms.showmissed", false);` + "\n",
|
|
"chrome/other.js": `pref("not.a.default", 1);` + "\n",
|
|
})
|
|
}
|
|
|
|
func stopBetterbird(t *testing.T, f *fake) {
|
|
t.Helper()
|
|
for _, pid := range []string{"4000", "4001"} {
|
|
if err := os.RemoveAll(filepath.Join(f.Root, "proc", pid)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheModuleShipsTheApprovedLocksAndDefaults(t *testing.T) {
|
|
m, _ := readManifest(t)
|
|
if a := artifact(t, m, "defaults"); a["from"] != "install/defaults-pref" {
|
|
t.Fatalf("the defaults file ships through the defaults artifact: %v", a)
|
|
}
|
|
if r := resource(t, m, "spelling"); r["path"] != prefDir || r["artifact"] != "defaults" {
|
|
t.Fatalf("placed in Betterbird's own defaults directory: %v", r)
|
|
}
|
|
raw := repoFile(t, "install/distribution/policies.json")
|
|
if why := policyProblem(raw); why != "" {
|
|
t.Fatal(why)
|
|
}
|
|
var p struct {
|
|
Policies map[string]json.RawMessage `json:"policies"`
|
|
}
|
|
if err := json.Unmarshal(raw, &p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for k, want := range map[string]string{"DisableAppUpdate": "true", "DisableTelemetry": "true", "ExtensionUpdate": "false"} {
|
|
if string(p.Policies[k]) != want {
|
|
t.Errorf("%s is %s, not %s", k, p.Policies[k], want)
|
|
}
|
|
}
|
|
prefs := policyPrefsOf(raw)
|
|
for _, n := range []string{"mail.shell.checkDefaultClient", "mail.inappnotifications.enabled", "mailnews.start_page.enabled"} {
|
|
if prefs[n] != (policyPref{Value: "false", Locked: true}) {
|
|
t.Errorf("%s is locked off: %+v", n, prefs[n])
|
|
}
|
|
}
|
|
// Betterbird's Preferences policy accepts only these prefixes (Policies.sys.mjs); privacy. and toolkit. are not.
|
|
allowed := []string{"accessibility.", "app.update.", "browser.", "calendar.", "chat.", "datareporting.policy.", "dom.",
|
|
"extensions.", "general.autoScroll", "general.smoothScroll", "geo.", "gfx.", "intl.", "layers.", "layout.", "mail.",
|
|
"mailnews.", "media.", "network.", "pdfjs.", "places.", "print.", "signon.", "spellchecker.", "ui.", "widget."}
|
|
for n := range prefs {
|
|
ok := false
|
|
for _, a := range allowed {
|
|
ok = ok || strings.HasPrefix(n, a)
|
|
}
|
|
if !ok {
|
|
t.Errorf("the policy cannot lock %s: its prefix is not one Betterbird accepts", n)
|
|
}
|
|
}
|
|
|
|
text := string(repoFile(t, "install/defaults-pref/mesh-defaults.js"))
|
|
sh := parseShipped([]byte(text))
|
|
if !reflect.DeepEqual(sh.values, shippedDefaultsWant) {
|
|
t.Errorf("the defaults are the approved ones:\n got %v\nwant %v", sh.values, shippedDefaultsWant)
|
|
}
|
|
var keeps []string
|
|
for _, n := range sh.order {
|
|
if sh.keeps[n] {
|
|
keeps = append(keeps, n)
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(keeps, shippedKeepsWant) {
|
|
t.Errorf("the preferences kept at Betterbird's default: %v", keeps)
|
|
}
|
|
lines := strings.Split(text, "\n")
|
|
for i, l := range lines {
|
|
if defaultLine.MatchString(l) && (i == 0 || !strings.HasPrefix(lines[i-1], "// ")) {
|
|
t.Errorf("line %d has no comment saying why: %s", i+1, l)
|
|
}
|
|
if t2 := strings.TrimSpace(l); t2 != "" && !strings.HasPrefix(t2, "//") && !defaultLine.MatchString(l) {
|
|
t.Errorf("line %d is neither a comment nor a pref(…) line: %s", i+1, l)
|
|
}
|
|
}
|
|
for n := range sh.values {
|
|
if withheld(n) != "" || passwordSecret.MatchString(n) {
|
|
t.Errorf("%s is an account's or a credential's preference, never a default of the module", n)
|
|
}
|
|
if _, locked := prefs[n]; locked {
|
|
t.Errorf("%s is both locked and a default", n)
|
|
}
|
|
}
|
|
}
|
|
|
|
// T2: Betterbird's own defaults are values read from omni.ja, in Gecko's order, and the module's beside them.
|
|
func TestPrefsSayTheDefaultAndTheFileThatGivesIt(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
f.withDefaults()
|
|
f.betterbirdsOwn()
|
|
links(t, nil)
|
|
got, err := f.Prefs([]string{"offline.autoDetect", "mail.threadpane.listview", "mail.uidensity", "gfx.webrender.software",
|
|
"mailnews.start_page.enabled", "layout.css.always_underline_links", "not.a.default"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
by := map[string]Pref{}
|
|
for _, p := range got["prefs"].([]Pref) {
|
|
by[p.Name] = p
|
|
}
|
|
if p := by["offline.autoDetect"]; p.Value != "true" || p.Default != "true" || !strings.Contains(p.DefaultFrom, "all-thunderbird.js") {
|
|
t.Errorf("of two files, the one first by name holds, as Gecko reads them: %+v", p)
|
|
}
|
|
if p := by["mail.threadpane.listview"]; p.Value != "0" || !strings.Contains(p.From, "the module's default") {
|
|
t.Errorf("the module's default is over Betterbird's: %+v", p)
|
|
}
|
|
if p := by["gfx.webrender.software"]; p.Value != "false" || !strings.Contains(p.From, "greprefs.js") {
|
|
t.Errorf("%+v", p)
|
|
}
|
|
if p := by["mailnews.start_page.enabled"]; p.Value != "false" || p.From != "locked by the module's policy" || p.Default != "true" {
|
|
t.Errorf("a locked preference says its lock and Betterbird's default under it: %+v", p)
|
|
}
|
|
if p := by["layout.css.always_underline_links"]; p.Value != "" || !strings.Contains(p.From, "built-in") {
|
|
t.Errorf("a preference in no file is said as such: %+v", p)
|
|
}
|
|
if p := by["not.a.default"]; p.Default != "" {
|
|
t.Errorf("only defaults/pref and greprefs.js are defaults: %+v", p)
|
|
}
|
|
}
|
|
|
|
// T4: of mailnews.* only what names an account, a server or the operator's tags is withheld.
|
|
func TestMailnewsIsWithheldOnlyWhereItNamesAnAccount(t *testing.T) {
|
|
for name, held := range map[string]bool{
|
|
"mailnews.start_page.enabled": false,
|
|
"mailnews.default_view_flags": false,
|
|
"mailnews.message_display.disable_remote_image": false,
|
|
"mailnews.tags.$label1.tag": true,
|
|
"mailnews.smtp.loglevel": true,
|
|
"mailnews.reply_to_self_check_all_ident": true,
|
|
"mailnews.auto_config.fetchFromISP.sendEmailAddress": true,
|
|
"mailnews.oauth.useSchemeRedirect": true,
|
|
"mail.server.server1.hostname": true,
|
|
} {
|
|
if got := withheld(name) != ""; got != held {
|
|
t.Errorf("%s: withheld %v, want %v", name, got, held)
|
|
}
|
|
}
|
|
}
|
|
|
|
// T1: every managed preference through the layers, and drift where the profile's own value beats the module's
|
|
// default without a node layer saying so.
|
|
func TestTheConfigShowsTheLayersAndTheDrift(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
f.withDefaults()
|
|
f.betterbirdsOwn()
|
|
links(t, nil)
|
|
f.declare(`user_pref("mail.uifontsize", 16);`)
|
|
f.write(profileDir+"/prefs.js", "// Mozilla User Preferences\n"+
|
|
`user_pref("extensions.activeThemeID", "thunderbird-compact-dark@mozilla.org");`+"\n"+
|
|
`user_pref("mail.uifontsize", 16);`+"\n"+
|
|
`user_pref("mail.uidensity", 0);`+"\n"+
|
|
`user_pref("mail.biff.show_alert", true);`+"\n"+
|
|
`user_pref("mail.spam.manualMark", false);`+"\n"+
|
|
`user_pref("mailnews.start_page.enabled", true);`+"\n")
|
|
c, err := f.Config(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
by := map[string]ConfigPref{}
|
|
for _, p := range c.Prefs {
|
|
by[p.Name] = p
|
|
}
|
|
if len(c.Prefs) != 3+len(shippedDefaultsWant)+len(shippedKeepsWant) || !strings.HasPrefix(c.Defaults, "placed") {
|
|
t.Fatalf("every lock, default and kept default: %d, %s", len(c.Prefs), c.Defaults)
|
|
}
|
|
if !reflect.DeepEqual(c.Drifting, []string{"extensions.activeThemeID", "mail.biff.show_alert"}) {
|
|
t.Fatalf("drift: %v", c.Drifting)
|
|
}
|
|
if p := by["mail.biff.show_alert"]; p.Layer != "default" || p.ModuleDefault != "false" || p.BetterbirdDefault != "true" ||
|
|
p.Profile != "true" || p.Effective != "true" || p.From != "the profile's prefs.js" || !strings.Contains(p.Drift, "beats the module's false") {
|
|
t.Errorf("%+v", p)
|
|
}
|
|
if p := by["extensions.activeThemeID"]; p.Layer != "Betterbird's default" || !strings.Contains(p.Drift, `"default-theme@mozilla.org"`) ||
|
|
!strings.Contains(p.Remedy, "add-on manager") || p.Profile != `"thunderbird-compact-dark@mozilla.org"` {
|
|
t.Errorf("another theme is drift whose remedy is the add-on manager, its id answered whole: %+v", p)
|
|
}
|
|
if p := by["mail.spam.manualMark"]; p.Drift != "" || p.BetterbirdDefault != "false" {
|
|
t.Errorf("a saved value equal to Betterbird's default is not drift: %+v", p)
|
|
}
|
|
if p := by["mail.uifontsize"]; p.Drift != "" || p.NodeLayer != "16" {
|
|
t.Errorf("a node layer that says so is not drift: %+v", p)
|
|
}
|
|
if p := by["mailnews.start_page.enabled"]; p.Layer != "locked" || p.Effective != "false" || p.Drift != "" {
|
|
t.Errorf("a lock wins over the profile: %+v", p)
|
|
}
|
|
if p := by["calendar.alarms.showmissed"]; p.Effective != "true" || p.From != "the module's default" {
|
|
t.Errorf("%+v", p)
|
|
}
|
|
// T5: the check says the drift, with both remedies.
|
|
ch, err := f.Check()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, x := range ch.Findings {
|
|
found = found || (strings.Contains(x.What, "1 managed preference(s) drift") && strings.Contains(x.What, "mail.biff.show_alert") &&
|
|
strings.Contains(x.Do, "betterbird_config_apply") && strings.Contains(x.Do, "keep"))
|
|
}
|
|
theme := false
|
|
for _, x := range ch.Findings {
|
|
theme = theme || (strings.HasPrefix(x.What, "extensions.activeThemeID drifts") && strings.Contains(x.Do, "add-on manager") &&
|
|
!strings.Contains(x.Do, "betterbird_config_apply"))
|
|
}
|
|
if !found || !theme {
|
|
t.Fatalf("the check reports the drift: %+v", ch.Findings)
|
|
}
|
|
os.Remove(filepath.Join(f.Root, meshDefaults))
|
|
ch, _ = f.Check()
|
|
found = false
|
|
for _, x := range ch.Findings {
|
|
found = found || strings.Contains(x.What, "the module's defaults are not placed")
|
|
}
|
|
if !found {
|
|
t.Fatalf("defaults missing on a node of the mesh's release is a finding: %+v", ch.Findings)
|
|
}
|
|
}
|
|
|
|
// T3: the drifting values are cleared from prefs.js only while Betterbird is not running, and nothing else.
|
|
func TestConfigApplyClearsTheDriftOnlyWhileBetterbirdIsQuit(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
f.withDefaults()
|
|
f.betterbirdsOwn()
|
|
links(t, nil)
|
|
f.declare("// none")
|
|
prefs := "// Mozilla User Preferences\n" +
|
|
`user_pref("extensions.activeThemeID", "thunderbird-compact-dark@mozilla.org");` + "\n" +
|
|
`user_pref("mail.identity.id1.useremail", "someone@example.org");` + "\n" +
|
|
`user_pref("mail.biff.show_alert", true);` + "\n" +
|
|
`user_pref("mail.uidensity", 0);` + "\n"
|
|
f.write(profileDir+"/prefs.js", prefs)
|
|
dry, err := f.ConfigApply(false)
|
|
if err != nil || dry.Applied || len(dry.Clears) != 1 || len(dry.NotCleared) != 1 {
|
|
t.Fatalf("a dry run lists and changes nothing: %+v %v", dry, err)
|
|
}
|
|
if f.read(profileDir+"/prefs.js") != prefs {
|
|
t.Fatal("a dry run wrote prefs.js")
|
|
}
|
|
if _, err := f.ConfigApply(true); err == nil || !strings.Contains(err.Error(), "running") {
|
|
t.Fatalf("refused while Betterbird runs: %v", err)
|
|
}
|
|
if f.read(profileDir+"/prefs.js") != prefs {
|
|
t.Fatal("prefs.js changed while Betterbird runs")
|
|
}
|
|
stopBetterbird(t, f)
|
|
f.write(profileDir+"/lock", "")
|
|
if _, err := f.ConfigApply(true); err == nil || !strings.Contains(err.Error(), "locked") {
|
|
t.Fatalf("refused while the profile's own lock is there: %v", err)
|
|
}
|
|
if err := os.Remove(filepath.Join(f.Root, profileDir, "lock")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a, err := f.ConfigApply(true)
|
|
if err != nil || !a.Applied {
|
|
t.Fatalf("%+v %v", a, err)
|
|
}
|
|
want := "// Mozilla User Preferences\n" +
|
|
`user_pref("extensions.activeThemeID", "thunderbird-compact-dark@mozilla.org");` + "\n" +
|
|
`user_pref("mail.identity.id1.useremail", "someone@example.org");` + "\n" +
|
|
`user_pref("mail.uidensity", 0);` + "\n"
|
|
if got := f.read(profileDir + "/prefs.js"); got != want {
|
|
t.Fatalf("only the drifting lines go:\n%s", got)
|
|
}
|
|
said := map[string]Cleared{}
|
|
for _, c := range a.Clears {
|
|
said[c.Name] = c
|
|
}
|
|
if c := said["mail.biff.show_alert"]; c.Was != "true" || c.Now != "false" {
|
|
t.Errorf("it says what it cleared and what holds now: %+v", c)
|
|
}
|
|
if _, cleared := said["extensions.activeThemeID"]; cleared || len(a.NotCleared) != 1 || !strings.Contains(a.NotCleared[0], "add-on manager") {
|
|
t.Errorf("the theme is never cleared, and said with its remedy: %+v", a)
|
|
}
|
|
raw, _ := json.Marshal(a)
|
|
if strings.Contains(string(raw), "example.org") {
|
|
t.Fatalf("no account value leaves: %s", raw)
|
|
}
|
|
if c, _ := f.Config(nil); !reflect.DeepEqual(c.Drifting, []string{"extensions.activeThemeID"}) {
|
|
t.Fatalf("once applied, only the theme drifts, until the add-on manager changes it: %v", c.Drifting)
|
|
}
|
|
os.Remove(filepath.Join(f.Root, meshDefaults))
|
|
if _, err := f.ConfigApply(true); err == nil || !strings.Contains(err.Error(), "not placed") {
|
|
t.Fatalf("without the module's defaults there is nothing to come back to: %v", err)
|
|
}
|
|
}
|
|
|
|
// T6: keeping a value on one node is a node layer proposed through the controller, never a write to user.js.
|
|
func TestKeepProposesANodeLayerThroughTheController(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
f.withDefaults()
|
|
links(t, nil)
|
|
f.declare(`user_pref("calendar.alarms.playsound", false); user_pref("mail.uifontsize", 12);`)
|
|
f.write(profileDir+"/prefs.js", `user_pref("mail.uifontsize", 16);`+"\n"+`user_pref("mail.biff.show_alert", true);`+"\n")
|
|
oldNode, oldAsk := nodeName, asker
|
|
t.Cleanup(func() { nodeName, asker = oldNode, oldAsk })
|
|
nodeName = func() string { return "shanks" }
|
|
var verb string
|
|
var args map[string]any
|
|
asker = func(v string, a any) (json.RawMessage, error) {
|
|
verb, args = v, a.(map[string]any)
|
|
return json.RawMessage(`{"ok": true, "output": "proposed: p-1"}`), nil
|
|
}
|
|
before := f.read(testHome + "/.config/betterbird/user.js")
|
|
c, err := f.Config([]string{"mail.uifontsize", "mail.biff.show_alert"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if verb != "seat:mesh-controller.settings" || args["node"] != "shanks" || args["propose"] != "true" || args["module"] != "betterbird" {
|
|
t.Fatalf("%s %v", verb, args)
|
|
}
|
|
var values map[string]string
|
|
if err := json.Unmarshal([]byte(args["values"].(string)), &values); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := `user_pref("calendar.alarms.playsound", false); user_pref("mail.uifontsize", 16); user_pref("mail.biff.show_alert", true);`
|
|
if values["prefs"] != want {
|
|
t.Fatalf("the layer keeps what the setting says and the kept values:\n got %s\nwant %s", values["prefs"], want)
|
|
}
|
|
if c.Proposed != "proposed: p-1" || f.read(testHome+"/.config/betterbird/user.js") != before {
|
|
t.Fatalf("proposed, and user.js untouched: %+v", c)
|
|
}
|
|
for _, bad := range [][]string{{"mailnews.start_page.enabled"}, {"some.other.pref"}, {"mail.uidensity"}} {
|
|
verb = ""
|
|
if _, err := f.Config(bad); err == nil || verb != "" {
|
|
t.Errorf("%v: a lock, an unmanaged name or a value the profile did not save is refused: %v", bad, err)
|
|
}
|
|
}
|
|
nodeName = func() string { return "" }
|
|
if _, err := f.Config([]string{"mail.uifontsize"}); err == nil || !strings.Contains(err.Error(), "MESH_NODE") {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
}
|
|
|
|
// The review of pull request 253: whether Betterbird asks to be the default client is decided from the policy's
|
|
// own Preferences entry, never from what else the policy lacks.
|
|
func TestTheDefaultClientAskIsDecidedFromThePreferencesEntry(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
links(t, nil)
|
|
f.write(policyFile, `{"policies": {"ExtensionSettings": {}}}`)
|
|
if why := f.defaultClientAsk(filepath.Join(f.Root, profileDir)); why == "" {
|
|
t.Fatal("a policy lacking the updater and the lock still asks")
|
|
}
|
|
f.write(policyFile, `{"policies": {"Preferences": {"mail.shell.checkDefaultClient": {"Value": false, "Status": "locked"}}}}`)
|
|
if why := f.defaultClientAsk(filepath.Join(f.Root, profileDir)); why != "" {
|
|
t.Fatalf("the lock holds whatever else the policy lacks: %s", why)
|
|
}
|
|
}
|
|
|
|
// The review of pull request 256: the theme is the add-on database's, and a saved default is not drift.
|
|
func TestTheThemeIsTheAddOnDatabasesAndASavedDefaultIsNotDrift(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
f.withDefaults()
|
|
links(t, nil)
|
|
f.write(profileDir+"/prefs.js", `user_pref("browser.display.use_system_colors", false);`+"\n"+
|
|
`user_pref("layout.css.always_underline_links", false);`+"\n")
|
|
f.write(profileDir+"/extensions.json", `{"addons":[{"id":"thunderbird-compact-dark@mozilla.org","type":"theme","active":true,"location":"app-builtin"},`+
|
|
`{"id":"default-theme@mozilla.org","type":"theme","active":false,"location":"app-builtin"}]}`)
|
|
c, err := f.Config(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(c.Drifting, []string{"extensions.activeThemeID"}) {
|
|
t.Fatalf("the active theme from the add-on database, with no preference saved, is drift; a saved default is not: %v", c.Drifting)
|
|
}
|
|
f.write(profileDir+"/prefs.js", `user_pref("extensions.activeThemeID", "default-theme@mozilla.org");`+"\n")
|
|
f.write(profileDir+"/extensions.json", `{"addons":[{"id":"default-theme@mozilla.org","type":"theme","active":true,"location":"app-builtin"}]}`)
|
|
if c, _ := f.Config(nil); len(c.Drifting) != 0 {
|
|
t.Fatalf("a profile back on the default theme does not drift: %v", c.Drifting)
|
|
}
|
|
if _, err := f.Config([]string{"extensions.activeThemeID"}); err == nil || !strings.Contains(err.Error(), "add-on manager") {
|
|
t.Fatalf("a node layer cannot choose the theme: %v", err)
|
|
}
|
|
}
|
|
|
|
// The review of pull request 256: a credential's or an account's name in the defaults file is never managed.
|
|
func TestAnAccountsNameInTheDefaultsIsNeverManaged(t *testing.T) {
|
|
f := newFake(t)
|
|
f.betterbird()
|
|
f.placedByTheMesh()
|
|
links(t, nil)
|
|
f.write(meshDefaults, string(repoFile(t, "install/defaults-pref/mesh-defaults.js"))+
|
|
`pref("mail.identity.id1.useremail", "x");`+"\n"+"// keeps Betterbird's default: mail.server.server1.hostname\n"+
|
|
`pref("mail.smtpservers", "");`+"\n")
|
|
prefs := `user_pref("mail.identity.id1.useremail", "someone@example.org");` + "\n" +
|
|
`user_pref("mail.server.server1.hostname", "imap.example.org");` + "\n" +
|
|
`user_pref("mail.smtpservers", "smtp1");` + "\n"
|
|
f.write(profileDir+"/prefs.js", prefs)
|
|
c, err := f.Config(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(c.Refused) != 3 || len(c.Drifting) != 0 {
|
|
t.Fatalf("refused, never managed: %+v", c)
|
|
}
|
|
raw, _ := json.Marshal(c)
|
|
if strings.Contains(string(raw), "example.org") {
|
|
t.Fatalf("no account value is answered: %s", raw)
|
|
}
|
|
stopBetterbird(t, f)
|
|
if _, err := f.ConfigApply(true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if f.read(profileDir+"/prefs.js") != prefs {
|
|
t.Fatal("prefs.js keeps every account's value")
|
|
}
|
|
ch, _ := f.Check()
|
|
found := false
|
|
for _, x := range ch.Findings {
|
|
found = found || (strings.Contains(x.What, "3 preference(s) the tools never manage") && strings.Contains(x.Do, "mesh-defaults.js"))
|
|
}
|
|
if !found {
|
|
t.Fatalf("the check says so: %+v", ch.Findings)
|
|
}
|
|
}
|