package web import ( "context" "encoding/json" "errors" "strings" "testing" ) // 1262/AC2: mail-store answers accounts a and b; status answers that it serves, and is granted a and b. func TestStatusAnswersItServesAndTheGrantedAccounts(t *testing.T) { b := &Bundle{Accounts: func(context.Context) (json.RawMessage, error) { return json.Marshal(map[string]any{"accounts": []map[string]any{ {"account": "a", "kind": "imap"}, {"account": "b", "kind": "imap"}, }}) }} out, err := b.statusTool(nil) if err != nil { t.Fatal(err) } m := out.(map[string]any) if m["serves"] != true { t.Fatalf("serves: %v", m["serves"]) } got, _ := m["accounts"].([]string) if len(got) != 2 || got[0] != "a" || got[1] != "b" { t.Fatalf("accounts: %v", m["accounts"]) } } // 1262/AC3: mail-store does not answer (or refuses); status answers that it does not serve, and why, and does // not panic. func TestStatusAnswersItDoesNotServeWhenMailStoreRefuses(t *testing.T) { b := &Bundle{Accounts: func(context.Context) (json.RawMessage, error) { return nil, errors.New("refused: mesh-mail-web is not granted any account") }} out, err := b.statusTool(nil) if err != nil { t.Fatal(err) } m := out.(map[string]any) if m["serves"] != false { t.Fatalf("serves: %v", m["serves"]) } why, _ := m["why"].(string) if why == "" { t.Fatalf("no reason given: %v", m) } } // Same AC3, the other half: mail-store answers something this module cannot read. func TestStatusAnswersItDoesNotServeWhenMailStoresAnswerCannotBeRead(t *testing.T) { b := &Bundle{Accounts: func(context.Context) (json.RawMessage, error) { return json.RawMessage(`{"accounts": "not a list"}`), nil }} out, err := b.statusTool(nil) if err != nil { t.Fatal(err) } m := out.(map[string]any) if m["serves"] != false || m["why"] == "" { t.Fatalf("%v", m) } } // review round 1 finding 4: mail-webclient.status and mail_web_health both say the HTTP endpoint's own state // (main.go's serveHTTP) rather than going on claiming to serve and to be healthy regardless — one test per // state (setting up sign-in, listening, ended). func TestStatusAndHealthSayTheHTTPEndpointsState(t *testing.T) { accountsOK := func(context.Context) (json.RawMessage, error) { return json.Marshal(map[string]any{"accounts": []any{}}) } operatorsOK := operatorsFile(t, "alice") cases := []struct { name string phase string err error }{ {"setting up sign-in", HTTPSettingUpSignIn, errors.New("the issuer could not be reached")}, {"ended", HTTPEnded, errors.New("listen tcp :8080: bind: address already in use")}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { h := NewHTTPStatus() h.set(c.phase, c.err) b := &Bundle{HTTP: h, Accounts: accountsOK, Env: Env{Operators: operatorsOK}} statusOut, err := b.statusTool(nil) if err != nil { t.Fatal(err) } sm := statusOut.(map[string]any) if sm["serves"] != false { t.Fatalf("status serves: %v, want false while %s", sm["serves"], c.phase) } if why, _ := sm["why"].(string); !strings.Contains(why, c.phase) { t.Fatalf("status why %q does not name the phase %q", why, c.phase) } healthOut, err := b.healthTool(nil) if err != nil { t.Fatal(err) } hm := healthOut.(map[string]any) if hm["healthy"] != false { t.Fatalf("health healthy: %v, want false while %s", hm["healthy"], c.phase) } if why, _ := hm["why"].(string); !strings.Contains(why, c.phase) { t.Fatalf("health why %q does not name the phase %q", why, c.phase) } }) } // HTTPListening: status and health go back to their own, ordinary checks (mail-store's own accounts // answer, the operators setting), neither blocked by the HTTP endpoint's own state any more. t.Run("listening", func(t *testing.T) { h := NewHTTPStatus() h.set(HTTPListening, nil) b := &Bundle{HTTP: h, Accounts: accountsOK, Env: Env{Operators: operatorsOK}} statusOut, err := b.statusTool(nil) if err != nil { t.Fatal(err) } if sm := statusOut.(map[string]any); sm["serves"] != true { t.Fatalf("status serves: %v, want true once listening", sm["serves"]) } healthOut, err := b.healthTool(nil) if err != nil { t.Fatal(err) } if hm := healthOut.(map[string]any); hm["healthy"] != true { t.Fatalf("health healthy: %v, want true once listening: %v", hm["healthy"], hm) } }) }