Files
mailgoserver/internal/jmap/mailbox_test.go
T
2026-08-22 06:45:05 +01:00

190 lines
5.6 KiB
Go

package jmap_test
import (
"encoding/json"
"testing"
"mailgoserver/internal/jmap"
)
func TestJMAPMailboxGetSynthesizesStandardFolders(t *testing.T) {
srv, _, _, email, password, _ := newTestJMAPServer(t)
resp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{
{Name: "Mailbox/get", Args: json.RawMessage(`{}`), ID: "c1"},
},
})
var result struct {
List []struct {
ID string `json:"id"`
Name string `json:"name"`
Role *string `json:"role"`
} `json:"list"`
}
if err := json.Unmarshal(resp.MethodResponses[0].Args, &result); err != nil {
t.Fatal(err)
}
wantNames := map[string]bool{"INBOX": true, "Junk": true, "Sent": true, "Drafts": true, "Trash": true}
seen := map[string]bool{}
for _, m := range result.List {
if m.ID == "" {
t.Errorf("folder %q got no stable id", m.Name)
}
seen[m.Name] = true
}
for name := range wantNames {
if !seen[name] {
t.Errorf("expected standard folder %q in a fresh mailbox's Mailbox/get, got %+v", name, result.List)
}
}
}
func TestJMAPMailboxQueryAndChanges(t *testing.T) {
srv, database, _, email, password, mailboxID := newTestJMAPServer(t)
state1 := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{Name: "Mailbox/get", Args: json.RawMessage(`{"ids":[]}`), ID: "c1"}},
})
var getResult struct {
State string `json:"state"`
}
if err := json.Unmarshal(state1.MethodResponses[0].Args, &getResult); err != nil {
t.Fatal(err)
}
if err := database.CreateMailboxFolderUnder(mailboxID, "Projects", "INBOX"); err != nil {
t.Fatal(err)
}
queryResp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{Name: "Mailbox/query", Args: json.RawMessage(`{"filter":{"name":"Projects"}}`), ID: "c2"}},
})
var queryResult struct {
IDs []string `json:"ids"`
}
if err := json.Unmarshal(queryResp.MethodResponses[0].Args, &queryResult); err != nil {
t.Fatal(err)
}
if len(queryResult.IDs) != 1 {
t.Fatalf("expected Mailbox/query to find the new 'Projects' folder, got %+v", queryResult.IDs)
}
changesResp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{Name: "Mailbox/changes", Args: json.RawMessage(`{"sinceState":"` + getResult.State + `"}`), ID: "c3"}},
})
var changesResult struct {
Created []string `json:"created"`
}
if err := json.Unmarshal(changesResp.MethodResponses[0].Args, &changesResult); err != nil {
t.Fatal(err)
}
found := false
for _, id := range changesResult.Created {
if id == queryResult.IDs[0] {
found = true
}
}
if !found {
t.Fatalf("expected Mailbox/changes to report the new folder %q as created, got %+v", queryResult.IDs[0], changesResult.Created)
}
}
func TestJMAPMailboxSetCreateRenameDestroy(t *testing.T) {
srv, database, _, email, password, mailboxID := newTestJMAPServer(t)
createResp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{
Name: "Mailbox/set",
Args: json.RawMessage(`{"create":{"c1":{"name":"Projects"}}}`),
ID: "c1",
}},
})
var createResult struct {
Created map[string]struct{ ID string } `json:"created"`
NotCreated map[string]any `json:"notCreated"`
}
if err := json.Unmarshal(createResp.MethodResponses[0].Args, &createResult); err != nil {
t.Fatal(err)
}
if len(createResult.NotCreated) != 0 {
t.Fatalf("expected no notCreated entries, got %+v", createResult.NotCreated)
}
folderID, ok := createResult.Created["c1"]
if !ok || folderID.ID == "" {
t.Fatalf("expected Mailbox/set to create 'Projects', got %+v", createResult.Created)
}
folders, err := database.AllFoldersForMailbox(mailboxID)
if err != nil {
t.Fatal(err)
}
found := false
for _, f := range folders {
if f == "Projects" {
found = true
}
}
if !found {
t.Fatalf("expected 'Projects' to exist after Mailbox/set create, got %+v", folders)
}
renameResp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{
Name: "Mailbox/set",
Args: json.RawMessage(`{"update":{"` + folderID.ID + `":{"name":"Work"}}}`),
ID: "c1",
}},
})
var renameResult struct {
Updated map[string]any `json:"updated"`
NotUpdated map[string]any `json:"notUpdated"`
}
if err := json.Unmarshal(renameResp.MethodResponses[0].Args, &renameResult); err != nil {
t.Fatal(err)
}
if len(renameResult.NotUpdated) != 0 {
t.Fatalf("expected the rename to succeed, got notUpdated %+v", renameResult.NotUpdated)
}
folders, err = database.AllFoldersForMailbox(mailboxID)
if err != nil {
t.Fatal(err)
}
if !contains(folders, "Work") || contains(folders, "Projects") {
t.Fatalf("expected 'Projects' renamed to 'Work', got %+v", folders)
}
destroyResp := doJMAP(t, srv, email, password, jmap.Request{
MethodCalls: []jmap.Invocation{{
Name: "Mailbox/set",
Args: json.RawMessage(`{"destroy":["` + folderID.ID + `"]}`),
ID: "c1",
}},
})
var destroyResult struct {
Destroyed []string `json:"destroyed"`
NotDestroyed map[string]any `json:"notDestroyed"`
}
if err := json.Unmarshal(destroyResp.MethodResponses[0].Args, &destroyResult); err != nil {
t.Fatal(err)
}
if len(destroyResult.NotDestroyed) != 0 || len(destroyResult.Destroyed) != 1 {
t.Fatalf("expected the destroy to succeed, got %+v / notDestroyed %+v", destroyResult.Destroyed, destroyResult.NotDestroyed)
}
root, err := database.FolderRoot(mailboxID, "Work")
if err != nil {
t.Fatal(err)
}
if root != "Trash" {
t.Fatalf("expected 'Work' to have been soft-deleted under Trash, got root %q", root)
}
}
func contains(list []string, s string) bool {
for _, v := range list {
if v == s {
return true
}
}
return false
}