Skip to content

Commit 72bd7a7

Browse files
test(gmail): add unit tests for signature helpers and alias path
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a4db6b0 commit 72bd7a7

1 file changed

Lines changed: 157 additions & 0 deletions

File tree

internal/cmd/gmail_send_signature_test.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,163 @@ func TestSignatureFileContentType_HTML(t *testing.T) {
453453
}
454454
}
455455

456+
func TestAppendSignaturePlain(t *testing.T) {
457+
tests := []struct {
458+
name string
459+
body string
460+
signature string
461+
want string
462+
}{
463+
{name: "normal case", body: "Hello", signature: "-- John", want: "Hello\n\n--\n-- John"},
464+
{name: "empty signature", body: "Hello", signature: "", want: "Hello"},
465+
{name: "whitespace-only signature", body: "Hello", signature: " ", want: "Hello"},
466+
{name: "empty body with signature", body: "", signature: "Sig", want: "\n\n--\nSig"},
467+
}
468+
for _, tt := range tests {
469+
t.Run(tt.name, func(t *testing.T) {
470+
got := appendSignaturePlain(tt.body, tt.signature)
471+
if got != tt.want {
472+
t.Errorf("appendSignaturePlain(%q, %q)\ngot: %q\nwant: %q", tt.body, tt.signature, got, tt.want)
473+
}
474+
})
475+
}
476+
}
477+
478+
func TestAppendSignatureHTML(t *testing.T) {
479+
tests := []struct {
480+
name string
481+
body string
482+
signature string
483+
want string
484+
}{
485+
{
486+
name: "normal case",
487+
body: "<p>Hi</p>",
488+
signature: "<div>Sig</div>",
489+
want: "<p>Hi</p>\n\n" + `<div class="gmail_signature"><div>Sig</div></div>`,
490+
},
491+
{name: "empty signature", body: "<p>Hi</p>", signature: "", want: "<p>Hi</p>"},
492+
{name: "whitespace-only signature", body: "<p>Hi</p>", signature: " ", want: "<p>Hi</p>"},
493+
}
494+
for _, tt := range tests {
495+
t.Run(tt.name, func(t *testing.T) {
496+
got := appendSignatureHTML(tt.body, tt.signature)
497+
if got != tt.want {
498+
t.Errorf("appendSignatureHTML(%q, %q)\ngot: %q\nwant: %q", tt.body, tt.signature, got, tt.want)
499+
}
500+
})
501+
}
502+
}
503+
504+
func TestValidateSignatureFlags(t *testing.T) {
505+
tests := []struct {
506+
name string
507+
signature bool
508+
signatureName string
509+
signatureFile string
510+
wantErr bool
511+
}{
512+
{name: "no flags set", signature: false, signatureName: "", signatureFile: "", wantErr: false},
513+
{name: "only --signature", signature: true, signatureName: "", signatureFile: "", wantErr: false},
514+
{name: "only --signature-name", signature: false, signatureName: "alias@example.com", signatureFile: "", wantErr: false},
515+
{name: "only --signature-file", signature: false, signatureName: "", signatureFile: "/tmp/sig.html", wantErr: false},
516+
{name: "--signature + --signature-name", signature: true, signatureName: "alias@example.com", signatureFile: "", wantErr: true},
517+
{name: "--signature + --signature-file", signature: true, signatureName: "", signatureFile: "/tmp/sig.html", wantErr: true},
518+
{name: "--signature-name + --signature-file", signature: false, signatureName: "alias@example.com", signatureFile: "/tmp/sig.html", wantErr: true},
519+
{name: "all three", signature: true, signatureName: "alias@example.com", signatureFile: "/tmp/sig.html", wantErr: true},
520+
}
521+
for _, tt := range tests {
522+
t.Run(tt.name, func(t *testing.T) {
523+
err := validateSignatureFlags(tt.signature, tt.signatureName, tt.signatureFile)
524+
if (err != nil) != tt.wantErr {
525+
t.Errorf("validateSignatureFlags(%v, %q, %q) error = %v, wantErr = %v",
526+
tt.signature, tt.signatureName, tt.signatureFile, err, tt.wantErr)
527+
}
528+
})
529+
}
530+
}
531+
532+
func TestGmailSendCmd_SignatureNameAlias(t *testing.T) {
533+
origNew := newGmailService
534+
t.Cleanup(func() { newGmailService = origNew })
535+
536+
var gotRaw string
537+
var gotSendAsGetPath string
538+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
539+
path := strings.TrimPrefix(r.URL.Path, "/gmail/v1")
540+
switch {
541+
case r.Method == http.MethodGet && strings.HasPrefix(path, "/users/me/settings/sendAs/"):
542+
gotSendAsGetPath = path
543+
w.Header().Set("Content-Type", "application/json")
544+
_ = json.NewEncoder(w).Encode(map[string]any{
545+
"sendAsEmail": "alias@example.com",
546+
"signature": "<div>Alias Sig</div>",
547+
"verificationStatus": "accepted",
548+
})
549+
return
550+
case r.Method == http.MethodPost && path == "/users/me/messages/send":
551+
var payload struct {
552+
Raw string `json:"raw"`
553+
}
554+
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
555+
t.Fatalf("decode payload: %v", err)
556+
}
557+
decoded, err := base64.RawURLEncoding.DecodeString(payload.Raw)
558+
if err != nil {
559+
t.Fatalf("decode raw: %v", err)
560+
}
561+
gotRaw = string(decoded)
562+
w.Header().Set("Content-Type", "application/json")
563+
_ = json.NewEncoder(w).Encode(map[string]any{"id": "m1"})
564+
return
565+
default:
566+
http.NotFound(w, r)
567+
return
568+
}
569+
}))
570+
defer srv.Close()
571+
572+
svc, err := gmail.NewService(context.Background(),
573+
option.WithoutAuthentication(),
574+
option.WithHTTPClient(srv.Client()),
575+
option.WithEndpoint(srv.URL+"/"),
576+
)
577+
if err != nil {
578+
t.Fatalf("NewService: %v", err)
579+
}
580+
newGmailService = func(context.Context, string) (*gmail.Service, error) { return svc, nil }
581+
582+
u, err := ui.New(ui.Options{Stdout: io.Discard, Stderr: io.Discard, Color: "never"})
583+
if err != nil {
584+
t.Fatalf("ui.New: %v", err)
585+
}
586+
ctx := ui.WithUI(context.Background(), u)
587+
588+
cmd := &GmailSendCmd{
589+
To: "recipient@example.com",
590+
Subject: "Hello",
591+
Body: "Body",
592+
SignatureName: "alias@example.com",
593+
}
594+
if err := cmd.Run(ctx, &RootFlags{Account: "a@b.com"}); err != nil {
595+
t.Fatalf("Run: %v", err)
596+
}
597+
598+
// Verify the SendAs.Get call used alias@example.com, not the account email a@b.com.
599+
wantPath := "/users/me/settings/sendAs/alias@example.com"
600+
if gotSendAsGetPath != wantPath {
601+
t.Errorf("expected SendAs.Get path %q, got %q", wantPath, gotSendAsGetPath)
602+
}
603+
604+
if gotRaw == "" {
605+
t.Fatal("expected raw message")
606+
}
607+
// The signature HTML should be converted to plain text "Alias Sig" and appended.
608+
if !strings.Contains(gotRaw, "Body\r\n\r\n--\r\nAlias Sig") {
609+
t.Errorf("expected alias signature in plain body, got: %q", gotRaw)
610+
}
611+
}
612+
456613
func TestSignatureHTMLToPlain(t *testing.T) {
457614
tests := []struct {
458615
name string

0 commit comments

Comments
 (0)