Files
mesh-catalog/modules/mesh-mail-web/cmd/mesh-mail-web/tools_test.go
T

140 lines
4.3 KiB
Go

package main
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)
}
})
}