diff --git a/internal/commands/checkins_test.go b/internal/commands/checkins_test.go index 7194e7a2..73125a31 100644 --- a/internal/commands/checkins_test.go +++ b/internal/commands/checkins_test.go @@ -84,7 +84,7 @@ func TestCheckinsAnswerCreateDefaultsDateToToday(t *testing.T) { require.NoError(t, err) require.NotNil(t, transport.recordedBody) assert.Equal(t, "/99999/questions/456/answers.json", transport.recordedPath) - assert.Equal(t, "

hello world

", transport.recordedBody["content"]) + assert.Equal(t, "
hello world
", transport.recordedBody["content"]) assert.Equal(t, "2026-03-25", transport.recordedBody["group_on"]) } diff --git a/internal/richtext/richtext.go b/internal/richtext/richtext.go index cf7b4474..044b91c8 100644 --- a/internal/richtext/richtext.go +++ b/internal/richtext/richtext.go @@ -47,8 +47,15 @@ var ( // prefix (e.g.

vs

,  vs 
, vs , vs , // vs ", - expected: "

<script>alert(1)</script>

", + expected: "
<script>alert(1)</script>
", }, { name: "multiline script tag", input: "", - expected: "

<script> alert(1) </script>

", + expected: "
<script> alert(1) </script>
", }, } @@ -1332,8 +1332,15 @@ func TestRoundTrip(t *testing.T) { if strings.Contains(back, "\n\n") { t.Errorf("round-trip produced two paragraphs, want one\nhtml: %q\nback: %q", html, back) } - if !strings.Contains(back, "Line 1") || !strings.Contains(back, "Line 2") { - t.Errorf("round-trip lost content\nhtml: %q\nback: %q", html, back) + line1Idx := strings.Index(back, "Line 1") + line2Idx := strings.Index(back, "Line 2") + if line1Idx == -1 || line2Idx == -1 || line1Idx >= line2Idx { + t.Errorf("round-trip did not preserve line order/content\nhtml: %q\nback: %q", html, back) + return + } + between := back[line1Idx+len("Line 1") : line2Idx] + if !strings.Contains(between, "\n") { + t.Errorf("round-trip did not preserve a line break between lines\nhtml: %q\nback: %q", html, back) } }) } @@ -1535,23 +1542,23 @@ func TestResolveMentions(t *testing.T) { }{ { name: "single mention", - input: `

Hey @John, check this

`, - expected: `

Hey ` + MentionToHTML("sgid-john", "John Doe") + `, check this

`, + input: `
Hey @John, check this
`, + expected: `
Hey ` + MentionToHTML("sgid-john", "John Doe") + `, check this
`, }, { name: "first.last mention", - input: `

Hey @Igor.Logachev, check this

`, - expected: `

Hey ` + MentionToHTML("sgid-igor", "Igor Logachev") + `, check this

`, + input: `
Hey @Igor.Logachev, check this
`, + expected: `
Hey ` + MentionToHTML("sgid-igor", "Igor Logachev") + `, check this
`, }, { name: "multiple mentions", - input: `

@John and @Igor please review

`, - expected: `

` + MentionToHTML("sgid-john", "John Doe") + ` and ` + MentionToHTML("sgid-igor", "Igor Logachev") + ` please review

`, + input: `
@John and @Igor please review
`, + expected: `
` + MentionToHTML("sgid-john", "John Doe") + ` and ` + MentionToHTML("sgid-igor", "Igor Logachev") + ` please review
`, }, { name: "no mentions", - input: `

Hello world

`, - expected: `

Hello world

`, + input: `
Hello world
`, + expected: `
Hello world
`, }, { name: "mention at start of line", @@ -1560,12 +1567,12 @@ func TestResolveMentions(t *testing.T) { }, { name: "email not treated as mention", - input: `

Send to user@John.com

`, - expected: `

Send to user@John.com

`, + input: `
Send to user@John.com
`, + expected: `
Send to user@John.com
`, }, { name: "unresolved mention is error", - input: `

Hey @Unknown

`, + input: `
Hey @Unknown
`, wantErr: true, }, { @@ -1580,13 +1587,13 @@ func TestResolveMentions(t *testing.T) { }, { name: "unicode name mention", - input: `

Hey @José, check this

`, - expected: `

Hey ` + MentionToHTML("sgid-jose", "José García") + `, check this

`, + input: `
Hey @José, check this
`, + expected: `
Hey ` + MentionToHTML("sgid-jose", "José García") + `, check this
`, }, { name: "mention inside code block is skipped", - input: `

Use @John syntax

`, - expected: `

Use @John syntax

`, + input: `
Use @John syntax
`, + expected: `
Use @John syntax
`, }, { name: "mention inside pre block is skipped", @@ -1606,41 +1613,41 @@ func TestResolveMentions(t *testing.T) { // Expanded prefix tests { name: "mention after open paren", - input: `

(@John) check this

`, - expected: `

(` + MentionToHTML("sgid-john", "John Doe") + `) check this

`, + input: `
(@John) check this
`, + expected: `
(` + MentionToHTML("sgid-john", "John Doe") + `) check this
`, }, { name: "mention after open bracket", - input: `

[@John] check this

`, - expected: `

[` + MentionToHTML("sgid-john", "John Doe") + `] check this

`, + input: `
[@John] check this
`, + expected: `
[` + MentionToHTML("sgid-john", "John Doe") + `] check this
`, }, { name: "mention after double quote", - input: `

"@John" check this

`, - expected: `

"` + MentionToHTML("sgid-john", "John Doe") + `" check this

`, + input: `
"@John" check this
`, + expected: `
"` + MentionToHTML("sgid-john", "John Doe") + `" check this
`, }, { name: "mention after single quote", - input: `

'@John' check this

`, - expected: `

'` + MentionToHTML("sgid-john", "John Doe") + `' check this

`, + input: `
'@John' check this
`, + expected: `
'` + MentionToHTML("sgid-john", "John Doe") + `' check this
`, }, // Trailing-character bailout tests { name: "hyphen bailout", - input: `

Hey @John-Doe

`, - expected: `

Hey @John-Doe

`, + input: `
Hey @John-Doe
`, + expected: `
Hey @John-Doe
`, wantErr: false, }, { name: "apostrophe letter bailout", - input: `

Hey @John's stuff

`, - expected: `

Hey @John's stuff

`, + input: `
Hey @John's stuff
`, + expected: `
Hey @John's stuff
`, wantErr: false, }, { name: "apostrophe then non-letter is not bailout", - input: `

'@John' said hi

`, - expected: `

'` + MentionToHTML("sgid-john", "John Doe") + `' said hi

`, + input: `
'@John' said hi
`, + expected: `
'` + MentionToHTML("sgid-john", "John Doe") + `' said hi
`, }, // Case-insensitive bc-attachment guard { @@ -1687,8 +1694,8 @@ func TestResolveMentions_MentionSGID(t *testing.T) { }, { name: "mention in paragraph", - input: `

Hey @Jane Smith, check this

`, - expected: `

Hey ` + MentionToHTML("BAh7CEkiCG", "Jane Smith") + `, check this

`, + input: `
Hey @Jane Smith, check this
`, + expected: `
Hey ` + MentionToHTML("BAh7CEkiCG", "Jane Smith") + `, check this
`, }, { name: "mention inside code block is skipped", @@ -1760,8 +1767,8 @@ func TestResolveMentions_PersonID(t *testing.T) { }, { name: "person scheme in paragraph", - input: `

Hey @Jane, check this

`, - expected: `

Hey ` + MentionToHTML("sgid-jane", "Jane Smith") + `, check this

`, + input: `
Hey @Jane, check this
`, + expected: `
Hey ` + MentionToHTML("sgid-jane", "Jane Smith") + `, check this
`, }, { name: "person scheme — not pingable", @@ -1802,8 +1809,8 @@ func TestResolveMentions_SGIDInline(t *testing.T) { }{ { name: "sgid inline — direct embed", - input: `

Hey @sgid:BAh7CEkiCG, check this

`, - expected: `

Hey ` + MentionToHTML("BAh7CEkiCG", "BAh7CEkiCG") + `, check this

`, + input: `
Hey @sgid:BAh7CEkiCG, check this
`, + expected: `
Hey ` + MentionToHTML("BAh7CEkiCG", "BAh7CEkiCG") + `, check this
`, }, { name: "sgid at start of line", @@ -1812,8 +1819,8 @@ func TestResolveMentions_SGIDInline(t *testing.T) { }, { name: "sgid with base64 chars", - input: `

Hey @sgid:BAh7+CG/k=, check

`, - expected: `

Hey ` + MentionToHTML("BAh7+CG/k=", "BAh7+CG/k=") + `, check

`, + input: `
Hey @sgid:BAh7+CG/k=, check
`, + expected: `
Hey ` + MentionToHTML("BAh7+CG/k=", "BAh7+CG/k=") + `, check
`, }, { name: "sgid inside code is skipped",