Files
larksuite-cli/shortcuts/mail/draft/patch_recipient_test.go
tuxedomm e64d24580a refactor: migrate mail shortcuts to FileIO (#356)
* refactor: migrate mail shortcuts to FileIO

- DraftSnapshot.FIO: inject FileIO into draft snapshot for patch ops
  (addAttachment, loadAndAttachInline, replaceInline)
- emlbuilder.Builder.fio: inject via WithFileIO(), readFile uses FileIO.Open
- mail_draft_edit: loadPatchFile uses runtime.FileIO().Open
- helpers: checkAttachmentSizeLimit takes fio param, uses FileIO.Stat
- validateComposeInlineAndAttachments: pass fio through to size check
- All mail entry points (send/reply/reply_all/forward/draft_create):
  pass runtime.FileIO() to builder and size limit checks
2026-04-09 17:40:30 +08:00

293 lines
7.1 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package draft
import (
"strings"
"testing"
)
// ---------------------------------------------------------------------------
// add_recipient — adding individual recipients
// ---------------------------------------------------------------------------
func TestAddRecipientAppendsNewAddress(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "add_recipient",
Field: "to",
Name: "Carol",
Address: "carol@example.com",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.To) != 2 {
t.Fatalf("To len = %d, want 2", len(snapshot.To))
}
if snapshot.To[1].Address != "carol@example.com" {
t.Fatalf("To[1] = %#v", snapshot.To[1])
}
}
func TestAddRecipientDeduplicatesExistingAddress(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "add_recipient",
Field: "to",
Name: "Bobby",
Address: "BOB@example.com", // case-insensitive match
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.To) != 1 {
t.Fatalf("To len = %d, want 1 (dedup)", len(snapshot.To))
}
}
func TestAddRecipientToCcField(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "add_recipient",
Field: "cc",
Name: "Carol",
Address: "carol@example.com",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.Cc) != 1 || snapshot.Cc[0].Address != "carol@example.com" {
t.Fatalf("Cc = %#v", snapshot.Cc)
}
}
func TestAddRecipientToBccField(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "add_recipient",
Field: "bcc",
Name: "Hidden",
Address: "hidden@example.com",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.Bcc) != 1 || snapshot.Bcc[0].Address != "hidden@example.com" {
t.Fatalf("Bcc = %#v", snapshot.Bcc)
}
}
func TestAddRecipientRejectsEmptyAddress(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "add_recipient",
Field: "to",
Name: "Nobody",
Address: " ",
}},
})
if err == nil {
t.Fatalf("expected error for empty address")
}
}
// ---------------------------------------------------------------------------
// remove_recipient — removing individual recipients
// ---------------------------------------------------------------------------
func TestRemoveRecipientByAddress(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>, Carol <carol@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "remove_recipient",
Field: "to",
Address: "bob@example.com",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.To) != 1 {
t.Fatalf("To len = %d, want 1", len(snapshot.To))
}
if snapshot.To[0].Address != "carol@example.com" {
t.Fatalf("To[0] = %#v", snapshot.To[0])
}
}
func TestRemoveRecipientCaseInsensitive(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>, Carol <carol@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "remove_recipient",
Field: "to",
Address: "BOB@EXAMPLE.COM",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.To) != 1 {
t.Fatalf("To len = %d, want 1", len(snapshot.To))
}
}
func TestRemoveRecipientNotFoundReturnsError(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "remove_recipient",
Field: "to",
Address: "nonexistent@example.com",
}},
})
if err == nil || !strings.Contains(err.Error(), "not found") {
t.Fatalf("error = %v, want not found error", err)
}
}
func TestRemoveRecipientFromEmptyFieldReturnsError(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "remove_recipient",
Field: "cc",
Address: "bob@example.com",
}},
})
if err == nil || !strings.Contains(err.Error(), "empty") {
t.Fatalf("error = %v, want empty header error", err)
}
}
func TestRemoveLastRecipientClearsHeader(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
Cc: Carol <carol@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "remove_recipient",
Field: "cc",
Address: "carol@example.com",
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.Cc) != 0 {
t.Fatalf("Cc = %#v, want empty", snapshot.Cc)
}
if got := headerValue(snapshot.Headers, "Cc"); got != "" {
t.Fatalf("Cc header still present: %q", got)
}
}
// ---------------------------------------------------------------------------
// set_recipients to empty list clears header
// ---------------------------------------------------------------------------
func TestSetRecipientsEmptyListClearsHeader(t *testing.T) {
snapshot := mustParseFixtureDraft(t, `Subject: Test
From: Alice <alice@example.com>
To: Bob <bob@example.com>
Cc: Carol <carol@example.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
hello
`)
err := Apply(&DraftCtx{FIO: testFIO}, snapshot, Patch{
Ops: []PatchOp{{
Op: "set_recipients",
Field: "cc",
Addresses: []Address{},
}},
})
if err != nil {
t.Fatalf("Apply() error = %v", err)
}
if len(snapshot.Cc) != 0 {
t.Fatalf("Cc = %#v, want empty", snapshot.Cc)
}
}