package main import ( "context" "strings" "testing" "time" ) // The review of pull request 155 (hq issue 345): a restart never loses an unsent message unasked, a // preference's value never carries an address, a log never a credential, the reminder test never // counts as the operator's Betterbird and leaves nothing behind, and a restart answers within the // runtime's call limit. const composerTree = ` 0x2a00003 "Inbox - Betterbird": ("Mail" "eu.betterbird.Betterbird") 2560x1388+0+0 +0+26 0x2a01234 "Write: hello - Betterbird": ("Msgcompose" "eu.betterbird.Betterbird") 900x700+10+10 +10+10 ` func TestARestartRefusesWhileAComposerIsOpenUnlessTheUnsentMayGo(t *testing.T) { f := newFake(t) f.desktopSession() f.betterbird() links(t, nil) f.declare("// none") f.answer = func(name string, args []string) Output { if name == "xwininfo" { return Output{Stdout: composerTree} } return Output{} } if _, err := f.Restart(false); err == nil || !strings.Contains(err.Error(), "composer") { t.Fatalf("refused while a message is being written: %v", err) } if len(f.signals) != 0 { t.Fatalf("nothing was ended: %v", f.signals) } f.onStart = func([]string) { f.proc(5000, 1000, "betterbird-bin", []string{"betterbird"}, "mesh-betterbird.service") } a, err := f.Restart(true) if err != nil || len(a.Ended) != 1 || a.Composers != 1 { t.Fatalf("with discard_unsent it goes ahead and says what was open: %+v %v", a, err) } } func TestARestartAnswersWithinTheCallLimitEvenWhenNothingCooperates(t *testing.T) { f := newFake(t) f.desktopSession() f.betterbird() links(t, nil) f.stubborn[4000] = true slept := time.Duration(0) f.Sleep = func(d time.Duration) { slept += d } a, err := f.Restart(false) if err != nil { t.Fatalf("a start that has not shown yet is said, not failed: %v", err) } if slept > 18*time.Second || len(a.Killed) != 1 || a.Note == "" { t.Fatalf("waited %v of the runtime's 30 s: %+v", slept, a) } } func TestAPreferencesValueCarriesNoAddressHostOrHome(t *testing.T) { f := newFake(t) f.betterbird() links(t, nil) f.write(profileDir+"/prefs.js", strings.Join([]string{ `user_pref("mail.last_msg_movecopy_target_uri", "imap://someone%40example.org@mail.example.org/INBOX");`, `user_pref("messenger.account.a1.server", "x");`, `user_pref("mail.cloud_files.accounts.a", "y");`, `user_pref("editor.lastFileLocation", "/home/operator/Documents/a.pdf");`, `user_pref("network.proxy.http", "proxy.corp.example.com");`, `user_pref("mail.compose.default_to", "imap://someone%40example.org@mail.example.org/x and bob@example.org");`, `user_pref("extensions.activeThemeID", "thunderbird-compact-dark@mozilla.org");`, `user_pref("extensions.ui.lastCategory", "addons://list/theme");`, `user_pref("font.size.variable.x-western", 12);`, }, "\n")+"\n") got, err := f.Prefs([]string{"mail.last_msg*", "messenger.account.", "mail.cloud_files.", "editor.", "network.proxy.", "mail.compose.default_to", "extensions.", "font.size."}) if err != nil { t.Fatal(err) } by := map[string]Pref{} for _, p := range got["prefs"].([]Pref) { by[p.Name] = p } for _, n := range []string{"mail.last_msg_movecopy_target_uri", "messenger.account.a1.server", "mail.cloud_files.accounts.a", "extensions.ui.lastCategory"} { if p := by[n]; p.Value != "" || p.Withheld == "" { t.Errorf("withheld: %s %+v", n, p) } } if v := by["editor.lastFileLocation"].Value; v != `"~/Documents/a.pdf"` { t.Errorf("home as ~: %s", v) } if v := by["network.proxy.http"].Value; strings.Contains(v, "example") { t.Errorf("a host: %s", v) } if v := by["mail.compose.default_to"].Value; strings.Contains(v, "example") || strings.Contains(v, "someone") { t.Errorf("an address: %s", v) } if v := by["extensions.activeThemeID"].Value; v != `"thunderbird-compact-dark@mozilla.org"` { t.Errorf("a theme's id is answered as it is: %s", v) } if v := by["font.size.variable.x-western"].Value; v != "12" { t.Errorf("%s", v) } for _, bad := range [][]string{{""}, {"*"}, {"."}, {" "}} { if _, err := f.Prefs(bad); err == nil { t.Errorf("%q is refused: it asks for everything", bad) } } } func TestALogLineKeepsNoBearerOrBasicCredential(t *testing.T) { for _, l := range []string{"Authorization: Bearer eyJhbGciOi.abc.def", "auth header basic dXNlcjpwYXNz sent", "token=abc123"} { got := mask(l) for _, secret := range []string{"eyJhbGciOi", "dXNlcjpwYXNz", "abc123"} { if strings.Contains(got, secret) { t.Errorf("%q → %q", l, got) } } } } func TestTheReminderTestIsNeverTheOperatorsBetterbirdAndCleansUpWhenItEnds(t *testing.T) { f := newFake(t) f.desktopSession() f.betterbird() links(t, nil) f.proc(6000, 1000, "betterbird-bin", []string{"/usr/bin/betterbird", "--no-remote", "--profile", "/run/user/1000/" + testUnit + "/profile"}, testUnit+".service") if ps := f.mainProcs(); len(ps) != 1 || ps[0].PID != 4000 { t.Fatalf("only the operator's: %+v", ps) } f.declare("// none") if _, err := f.ReminderTest(40*time.Second, nil, false); err != nil { t.Fatal(err) } if !f.called("systemd-run --user --collect --quiet --unit=" + testUnit + " -p RuntimeMaxSec=600 -p ExecStopPost=/usr/bin/rm -rf /run/user/1000/" + testUnit) { t.Fatalf("the unit removes its profile when it ends: %v", f.calls) } } func TestAComposerIsKnownByItsInstanceWhateverItsClass(t *testing.T) { f := newFake(t) f.desktopSession() f.betterbird() links(t, nil) f.answer = func(name string, args []string) Output { if name == "xwininfo" { return Output{Stdout: ` 0x2a01234 "Write: x": ("Msgcompose" "Betterbird") 900x700+10+10 +10+10` + "\n"} } return Output{} } if _, err := f.Restart(false); err == nil || !strings.Contains(err.Error(), "composer") { t.Fatalf("a composer of another class is still a composer: %v", err) } } func TestARestartsWorstCaseStaysUnderTheRuntimesThirtySeconds(t *testing.T) { f := newFake(t) f.desktopSession() f.betterbird() links(t, nil) f.stubborn[4000] = true total := time.Duration(0) run := f.Run f.Run = func(ctx context.Context, env []string, name string, args ...string) Output { // Every command takes its whole timeout: the worst case. if d, ok := ctx.Deadline(); ok { total += time.Until(d).Round(time.Second) } return run(ctx, env, name, args...) } f.Sleep = func(d time.Duration) { total += d } if _, err := f.Restart(true); err != nil { t.Fatal(err) } if total > 26*time.Second { t.Fatalf("the worst case is %v, over the runtime's 30 s less a margin", total) } }