190 lines
5.6 KiB
Go
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
|
|
}
|