In-place upgrade tool for Stalwart Mail Server (0.15.5 -> latest) with checkpointed rollback and post-migration validation. Design stage; see ARCHITECTURE.md.
113 lines
3.9 KiB
Go
113 lines
3.9 KiB
Go
package stalwartapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestMailboxSnapshotImpersonatesAndFetchesCounts(t *testing.T) {
|
|
var apiURL string
|
|
var sessionAuthUser, sessionAuthPass string
|
|
var gotAccountID string
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.Method == http.MethodGet && r.URL.Path == "/.well-known/jmap":
|
|
sessionAuthUser, sessionAuthPass, _ = r.BasicAuth()
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"apiUrl": apiURL,
|
|
"primaryAccounts": map[string]string{jmapMailCapability: "mail-acct-1"},
|
|
})
|
|
case r.Method == http.MethodPost && r.URL.Path == "/jmap-api":
|
|
var body map[string]any
|
|
json.NewDecoder(r.Body).Decode(&body)
|
|
methodCalls := body["methodCalls"].([]any)
|
|
args := methodCalls[0].([]any)[1].(map[string]any)
|
|
gotAccountID, _ = args["accountId"].(string)
|
|
json.NewEncoder(w).Encode(jmapEnvelope{MethodResponses: []any{
|
|
[]any{"Mailbox/get", map[string]any{"list": []map[string]any{
|
|
{"name": "Inbox", "totalEmails": 42},
|
|
{"name": "Sent", "totalEmails": 7},
|
|
}}, "m"},
|
|
}})
|
|
default:
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
apiURL = srv.URL + "/jmap-api"
|
|
|
|
client := &Client{BaseURL: srv.URL, Username: "admin", Password: "hunter2"}
|
|
counts, err := client.MailboxSnapshot(context.Background(), "[email protected]")
|
|
if err != nil {
|
|
t.Fatalf("MailboxSnapshot: %v", err)
|
|
}
|
|
|
|
if sessionAuthUser != "[email protected]%admin" || sessionAuthPass != "hunter2" {
|
|
t.Errorf("session discovery auth = (%s, %s), want ([email protected]%%admin, hunter2)", sessionAuthUser, sessionAuthPass)
|
|
}
|
|
if gotAccountID != "mail-acct-1" {
|
|
t.Errorf("Mailbox/get accountId = %s, want mail-acct-1", gotAccountID)
|
|
}
|
|
if len(counts) != 2 || counts[0].Mailbox != "Inbox" || counts[0].Messages != 42 || counts[1].Mailbox != "Sent" || counts[1].Messages != 7 {
|
|
t.Errorf("counts = %+v, want [{Inbox 42} {Sent 7}]", counts)
|
|
}
|
|
}
|
|
|
|
func TestMailboxSnapshotFailsWhenImpersonationRejected(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := &Client{BaseURL: srv.URL, Username: "notasuperuser", Password: "x"}
|
|
_, err := client.MailboxSnapshot(context.Background(), "[email protected]")
|
|
if err == nil {
|
|
t.Fatal("MailboxSnapshot should error when session discovery (impersonation) is rejected")
|
|
}
|
|
}
|
|
|
|
func TestMailboxSnapshotFailsWhenSessionHasNoMailAccount(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"apiUrl": "http://unused/",
|
|
"primaryAccounts": map[string]string{},
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := &Client{BaseURL: srv.URL, Username: "admin", Password: "x"}
|
|
_, err := client.MailboxSnapshot(context.Background(), "[email protected]")
|
|
if err == nil {
|
|
t.Fatal("MailboxSnapshot should error when the session has no jmap:mail primary account")
|
|
}
|
|
}
|
|
|
|
func TestMailboxSnapshotPropagatesMailboxGetError(t *testing.T) {
|
|
var apiURL string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.URL.Path == "/.well-known/jmap":
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"apiUrl": apiURL,
|
|
"primaryAccounts": map[string]string{jmapMailCapability: "mail-acct-1"},
|
|
})
|
|
case r.URL.Path == "/jmap-api":
|
|
json.NewEncoder(w).Encode(jmapEnvelope{MethodResponses: []any{
|
|
[]any{"error", map[string]any{"type": "accountNotFound"}, "m"},
|
|
}})
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
apiURL = srv.URL + "/jmap-api"
|
|
|
|
client := &Client{BaseURL: srv.URL, Username: "admin", Password: "x"}
|
|
_, err := client.MailboxSnapshot(context.Background(), "[email protected]")
|
|
if err == nil {
|
|
t.Fatal("MailboxSnapshot should propagate a JMAP-level error from Mailbox/get")
|
|
}
|
|
}
|