package service import ( "strings" "testing" ) func TestSanitizeSubjectField(t *testing.T) { long := strings.Repeat("a", 100) longRunes := strings.Repeat("深", 100) tests := []struct { name string in string want string }{ {"plain ascii", "Acme", "Acme"}, {"strips newline", "Acme\nEvil", "AcmeEvil"}, {"strips crlf header-style", "Acme\r\nBcc: evil@example.com", "AcmeBcc: evil@example.com"}, {"strips tab", "Acme\tTeam", "AcmeTeam"}, {"strips unicode control", "Acme\x07Beep", "AcmeBeep"}, {"preserves non-ascii", "深度学习工作区", "深度学习工作区"}, {"preserves emoji", "Team 🚀", "Team 🚀"}, {"truncates long ascii", long, strings.Repeat("a", maxSubjectFieldRunes-1) + "…"}, {"truncates rune-aware", longRunes, strings.Repeat("深", maxSubjectFieldRunes-1) + "…"}, {"empty stays empty", "", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := sanitizeSubjectField(tt.in) if got != tt.want { t.Errorf("sanitizeSubjectField(%q) = %q, want %q", tt.in, got, tt.want) } }) } } func TestBuildInvitationParams_EscapesHTMLInBody(t *testing.T) { tests := []struct { name string inviter string workspace string wantInBody []string wantNotInBody []string }{ { name: "escapes script tag in inviter", inviter: "", workspace: "Acme", wantInBody: []string{ "<script>alert(1)</script>", }, wantNotInBody: []string{ "", }, }, { name: "escapes attribute-break payload in inviter", inviter: `Alice" onclick="evil()`, workspace: "Acme", wantNotInBody: []string{ `Alice" onclick="evil()`, }, }, { name: "escapes anchor tag in workspace", inviter: "Alice", workspace: `Click`, wantInBody: []string{ "<a href=", ">Click</a>", }, wantNotInBody: []string{ `Click`, }, }, { name: "benign text unchanged", inviter: "Alice", workspace: "Acme", wantInBody: []string{ "Alice", "Acme", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := buildInvitationParams( "noreply@multica.ai", "invitee@example.com", tt.inviter, tt.workspace, "https://app.multica.ai/invite/abc-123", ) for _, needle := range tt.wantInBody { if !strings.Contains(p.Html, needle) { t.Errorf("body missing %q\nbody: %s", needle, p.Html) } } for _, needle := range tt.wantNotInBody { if strings.Contains(p.Html, needle) { t.Errorf("body should not contain raw %q\nbody: %s", needle, p.Html) } } }) } } func TestBuildInvitationParams_SubjectStripsControls(t *testing.T) { p := buildInvitationParams( "noreply@multica.ai", "invitee@example.com", "Alice\r\n", "Acme\t", "https://app.multica.ai/invite/abc", ) if strings.ContainsAny(p.Subject, "\r\n\t") { t.Errorf("subject still contains control characters: %q", p.Subject) } if p.Subject != "Alice invited you to Acme on Multica" { t.Errorf("unexpected subject: %q", p.Subject) } } func TestBuildInvitationParams_SubjectNotHTMLEscaped(t *testing.T) { // Subject is not HTML-rendered; entities would render literally in inboxes. p := buildInvitationParams( "noreply@multica.ai", "invitee@example.com", "Alice", "Acme & Co.", "https://app.multica.ai/invite/abc", ) if strings.Contains(p.Subject, "&") { t.Errorf("subject should not be HTML-escaped, got %q", p.Subject) } if !strings.Contains(p.Subject, "Acme & Co.") { t.Errorf("subject missing literal ampersand: %q", p.Subject) } } func TestBuildInvitationParams_SubjectTruncated(t *testing.T) { longWorkspace := strings.Repeat("A", 200) p := buildInvitationParams( "noreply@multica.ai", "invitee@example.com", "Alice", longWorkspace, "https://app.multica.ai/invite/abc", ) // Template: "Alice invited you to on Multica" // ws is capped at maxSubjectFieldRunes; overall subject should also be bounded. maxExpected := len("Alice invited you to on Multica") + maxSubjectFieldRunes if runes := len([]rune(p.Subject)); runes > maxExpected { t.Errorf("subject not bounded: %d runes, max %d: %q", runes, maxExpected, p.Subject) } if !strings.Contains(p.Subject, "…") { t.Errorf("truncated subject should contain ellipsis marker: %q", p.Subject) } } func TestBuildInvitationParams_ToAndFromPassedThrough(t *testing.T) { p := buildInvitationParams( "noreply@multica.ai", "invitee@example.com", "Alice", "Acme", "https://app.multica.ai/invite/abc", ) if p.From != "noreply@multica.ai" { t.Errorf("From = %q", p.From) } if len(p.To) != 1 || p.To[0] != "invitee@example.com" { t.Errorf("To = %v", p.To) } if !strings.Contains(p.Html, "https://app.multica.ai/invite/abc") { t.Errorf("body missing invite URL: %s", p.Html) } }