Files
larksuite-cli/shortcuts/mail/draft/parse_extra_test.go
梁硕 83dfb068ad feat: open-source lark-cli — the official CLI for Lark/Feishu
Change-Id: I113d9cdb5403cec347efe4595415e34a18b7decf
2026-03-28 10:36:25 +08:00

227 lines
7.1 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package draft
import (
"strings"
"testing"
)
// ---------------------------------------------------------------------------
// Parse — empty/invalid raw EML
// ---------------------------------------------------------------------------
func TestParseEmptyRawEML(t *testing.T) {
_, err := Parse(DraftRaw{DraftID: "d-1", RawEML: ""})
if err == nil || !strings.Contains(err.Error(), "empty") {
t.Fatalf("error = %v, want empty error", err)
}
}
func TestParseInvalidBase64(t *testing.T) {
_, err := Parse(DraftRaw{DraftID: "d-1", RawEML: "!!!not-base64!!!"})
if err == nil || !strings.Contains(err.Error(), "not valid base64") {
t.Fatalf("error = %v, want base64 error", err)
}
}
func TestParseMissingHeaderBodySeparator(t *testing.T) {
_, err := Parse(DraftRaw{
DraftID: "d-1",
RawEML: encodeFixtureEML("Subject: Test\nno body separator here"),
})
if err == nil || !strings.Contains(err.Error(), "missing header/body separator") {
t.Fatalf("error = %v, want separator error", err)
}
}
// ---------------------------------------------------------------------------
// Parse — no Content-Type header defaults to text/plain
// ---------------------------------------------------------------------------
func TestParseNoContentTypeDefaultsToPlainText(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-noct",
RawEML: encodeFixtureEML("Subject: Bare\nFrom: alice@example.com\n\nhello world\n"),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if snapshot.Body.MediaType != "text/plain" {
t.Fatalf("MediaType = %q, want text/plain", snapshot.Body.MediaType)
}
if got := string(snapshot.Body.Body); got != "hello world\n" {
t.Fatalf("body = %q", got)
}
}
// ---------------------------------------------------------------------------
// Parse — multipart/mixed with missing boundary param
// ---------------------------------------------------------------------------
func TestParseMultipartMissingBoundaryReturnsError(t *testing.T) {
_, err := Parse(DraftRaw{
DraftID: "d-nobnd",
RawEML: encodeFixtureEML("Subject: Test\nContent-Type: multipart/mixed\n\nsome body\n"),
})
if err == nil || !strings.Contains(err.Error(), "missing boundary") {
t.Fatalf("error = %v, want missing boundary error", err)
}
}
// ---------------------------------------------------------------------------
// Parse — charset decoding of non-UTF-8 charset
// ---------------------------------------------------------------------------
func TestParseMalformedCharsetGracefulDegradation(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-badcharset",
RawEML: encodeFixtureEML("Subject: Bad charset\nContent-Type: text/plain; charset=X-UNKNOWN-CHARSET-999\n\nhello\n"),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if !snapshot.Body.EncodingProblem {
t.Fatalf("EncodingProblem should be true for unknown charset")
}
// Body should still have content (raw bytes fallback)
if len(snapshot.Body.Body) == 0 {
t.Fatalf("body should not be empty")
}
}
// ---------------------------------------------------------------------------
// Parse — message/rfc822 part is preserved as opaque
// ---------------------------------------------------------------------------
func TestParseMessageRFC822Preserved(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-rfc822",
RawEML: mustReadFixtureEML(t, "testdata/message_rfc822_draft.eml"),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// Should find an attachment part with message/rfc822
found := false
for _, child := range snapshot.Body.Children {
if child != nil && child.MediaType == "message/rfc822" {
found = true
}
}
if !found {
t.Fatalf("expected message/rfc822 child part")
}
}
// ---------------------------------------------------------------------------
// Parse — unknown transfer encoding preserves raw bytes
// ---------------------------------------------------------------------------
func TestParseUnknownTransferEncodingPreservesRawBytes(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-unknowncte",
RawEML: encodeFixtureEML(`Subject: Unknown CTE
From: alice@example.com
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: x-custom-encoding
raw body content here
`),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if got := string(snapshot.Body.Body); got != "raw body content here\n" {
t.Fatalf("body = %q, want raw bytes preserved", got)
}
}
// ---------------------------------------------------------------------------
// Parse — text/plain with disposition=attachment is not primary body
// ---------------------------------------------------------------------------
func TestParseTextPlainAttachmentNotSelectedAsPrimaryBody(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-txtattach",
RawEML: encodeFixtureEML(`Subject: Text attachment
From: alice@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=mix
--mix
Content-Type: text/html; charset=UTF-8
<p>real body</p>
--mix
Content-Type: text/plain; charset=UTF-8
Content-Disposition: attachment; filename=notes.txt
This is an attached text file, not the body.
--mix--
`),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// The text/plain with disposition=attachment should NOT be the primary text part
if snapshot.PrimaryTextPartID != "" {
textPart := findPart(snapshot.Body, snapshot.PrimaryTextPartID)
if textPart != nil && strings.EqualFold(textPart.ContentDisposition, "attachment") {
t.Fatalf("attachment text/plain should not be selected as primary body, got part %q", snapshot.PrimaryTextPartID)
}
}
// The text/html should be the primary HTML part
if snapshot.PrimaryHTMLPartID == "" {
t.Fatalf("PrimaryHTMLPartID should not be empty")
}
htmlPart := findPart(snapshot.Body, snapshot.PrimaryHTMLPartID)
if got := string(htmlPart.Body); got != "<p>real body</p>" {
t.Fatalf("html body = %q", got)
}
}
// ---------------------------------------------------------------------------
// Parse — inline image disposition is not selected as primary body
// ---------------------------------------------------------------------------
func TestParseInlineImageNotSelectedAsPrimaryBody(t *testing.T) {
snapshot, err := Parse(DraftRaw{
DraftID: "d-inlineskip",
RawEML: encodeFixtureEML(`Subject: Inline skip
From: alice@example.com
MIME-Version: 1.0
Content-Type: multipart/related; boundary=rel
--rel
Content-Type: text/html; charset=UTF-8
<p>body <img src="cid:logo"></p>
--rel
Content-Type: text/plain
Content-Disposition: inline; filename=logo.txt
Content-ID: <logo>
logo-placeholder
--rel--
`),
})
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// The inline text/plain with CID should NOT be the primary text part
if snapshot.PrimaryTextPartID != "" {
t.Fatalf("PrimaryTextPartID = %q, want empty (inline parts skipped)", snapshot.PrimaryTextPartID)
}
if snapshot.PrimaryHTMLPartID == "" {
t.Fatalf("PrimaryHTMLPartID should not be empty")
}
}
func mustReadFixtureEML(t *testing.T, path string) string {
t.Helper()
return encodeFixtureEML(mustReadFixture(t, path))
}