using ClaudeDo.Worker.Runner; namespace ClaudeDo.Worker.Tests.Runner; public class CommitMessageBuilderTests { [Fact] public void Slug_FromListName() { Assert.Equal("lager-app", CommitMessageBuilder.ToSlug("Lager App")); } [Fact] public void Slug_SpecialCharsStripped() { Assert.Equal("my-list", CommitMessageBuilder.ToSlug("My! @List#")); } [Fact] public void Slug_CollapsesDashes() { Assert.Equal("a-b", CommitMessageBuilder.ToSlug("a -- b")); } [Fact] public void Slug_TrimsLeadingTrailingDashes() { Assert.Equal("abc", CommitMessageBuilder.ToSlug("--abc--")); } [Fact] public void Title_TruncatedTo60() { var longTitle = new string('x', 80); var msg = CommitMessageBuilder.Build("feat", "My List", longTitle, null, "task-id-123"); var header = msg.Split('\n')[0]; // Header format: feat(my-list): <60 chars> var titlePart = header.Split(": ", 2)[1]; Assert.Equal(60, titlePart.Length); } [Fact] public void NoDescription_TrailerStillPresent() { var msg = CommitMessageBuilder.Build("chore", "Test List", "do something", null, "abc-123"); var lines = msg.Split('\n'); // header \n \n trailer = 3 lines (no description block, just blank separator). Assert.Equal(3, lines.Length); Assert.Equal("chore(test-list): do something", lines[0]); Assert.Equal("", lines[1]); Assert.Equal("ClaudeDo-Task: abc-123", lines[2]); } [Fact] public void WithDescription_IncludedAboveTrailer() { var msg = CommitMessageBuilder.Build("feat", "Lager App", "add scan", "Detailed description here", "id-456"); var lines = msg.Split('\n'); Assert.Equal("feat(lager-app): add scan", lines[0]); Assert.Equal("", lines[1]); // blank after header Assert.Equal("Detailed description here", lines[2]); Assert.Equal("", lines[3]); // blank before trailer Assert.Equal("ClaudeDo-Task: id-456", lines[4]); } [Fact] public void Description_TruncatedTo400() { var longDesc = new string('d', 500); var msg = CommitMessageBuilder.Build("fix", "X", "title", longDesc, "id"); var lines = msg.Split('\n'); // lines[2] is the description line. Assert.Equal(400, lines[2].Length); } }