|
5 | 5 | "path/filepath" |
6 | 6 | "strings" |
7 | 7 | "testing" |
| 8 | + |
| 9 | + "github.com/kitproj/coding-context-cli/pkg/codingcontext/taskparser" |
8 | 10 | ) |
9 | 11 |
|
10 | 12 | func TestParseMarkdownFile(t *testing.T) { |
@@ -272,3 +274,99 @@ This task has no frontmatter. |
272 | 274 | }) |
273 | 275 | } |
274 | 276 | } |
| 277 | + |
| 278 | +func TestParseMarkdownFile_MultipleNewlinesAfterFrontmatter(t *testing.T) { |
| 279 | + // This test verifies that multiple newlines after the frontmatter |
| 280 | + // closing delimiter are handled correctly. |
| 281 | + // The parser should: |
| 282 | + // 1. Preserve multiple newlines between frontmatter and content |
| 283 | + // 2. Strip a single newline (treating it as just a separator) |
| 284 | + // 3. Allow the task parser to successfully parse content that starts with newlines |
| 285 | + tests := []struct { |
| 286 | + name string |
| 287 | + content string |
| 288 | + wantContent string |
| 289 | + }{ |
| 290 | + { |
| 291 | + name: "multiple newlines after frontmatter", |
| 292 | + content: `--- |
| 293 | +{} |
| 294 | +--- |
| 295 | +
|
| 296 | +Start of context |
| 297 | +`, |
| 298 | + wantContent: "\nStart of context\n", // Content copied as-is after frontmatter |
| 299 | + }, |
| 300 | + { |
| 301 | + name: "single newline after frontmatter (baseline)", |
| 302 | + content: `--- |
| 303 | +{} |
| 304 | +--- |
| 305 | +Start of context |
| 306 | +`, |
| 307 | + wantContent: "Start of context\n", // Content copied as-is after frontmatter (newline after --- is preserved) |
| 308 | + }, |
| 309 | + { |
| 310 | + name: "three newlines after frontmatter", |
| 311 | + content: `--- |
| 312 | +{} |
| 313 | +--- |
| 314 | +
|
| 315 | +
|
| 316 | +Start of context |
| 317 | +`, |
| 318 | + wantContent: "\n\nStart of context\n", // Content copied as-is after frontmatter |
| 319 | + }, |
| 320 | + { |
| 321 | + name: "mixed whitespace after frontmatter", |
| 322 | + content: `--- |
| 323 | +{} |
| 324 | +--- |
| 325 | + |
| 326 | + |
| 327 | +
|
| 328 | +Start of context |
| 329 | +`, |
| 330 | + wantContent: " \n\t \n\nStart of context\n", // Content copied as-is, preserving whitespace (newline after --- is preserved) |
| 331 | + }, |
| 332 | + } |
| 333 | + |
| 334 | + for _, tt := range tests { |
| 335 | + t.Run(tt.name, func(t *testing.T) { |
| 336 | + // Create a temporary file |
| 337 | + tmpDir := t.TempDir() |
| 338 | + tmpFile := filepath.Join(tmpDir, "test.md") |
| 339 | + if err := os.WriteFile(tmpFile, []byte(tt.content), 0o644); err != nil { |
| 340 | + t.Fatalf("failed to create temp file: %v", err) |
| 341 | + } |
| 342 | + |
| 343 | + // Parse the file |
| 344 | + var frontmatter BaseFrontMatter |
| 345 | + md, err := ParseMarkdownFile(tmpFile, &frontmatter) |
| 346 | + if err != nil { |
| 347 | + t.Fatalf("ParseMarkdownFile() error = %v", err) |
| 348 | + } |
| 349 | + |
| 350 | + // Check content |
| 351 | + if md.Content != tt.wantContent { |
| 352 | + t.Errorf("ParseMarkdownFile() content = %q, want %q", md.Content, tt.wantContent) |
| 353 | + } |
| 354 | + |
| 355 | + // Verify that the content can be parsed as a task |
| 356 | + // This is the actual use case - content is parsed as a task after frontmatter extraction |
| 357 | + task, err := taskparser.ParseTask(md.Content) |
| 358 | + if err != nil { |
| 359 | + t.Fatalf("ParseTask() failed: %v, content = %q", err, md.Content) |
| 360 | + } |
| 361 | + if len(task) == 0 && strings.TrimSpace(md.Content) != "" { |
| 362 | + t.Errorf("ParseTask() returned empty task for non-empty content: %q", md.Content) |
| 363 | + } |
| 364 | + // Verify that the parsed task content matches the original exactly |
| 365 | + // The parser preserves all content including leading newlines |
| 366 | + taskContent := task.String() |
| 367 | + if taskContent != md.Content { |
| 368 | + t.Errorf("ParseTask() then String() = %q, want %q", taskContent, md.Content) |
| 369 | + } |
| 370 | + }) |
| 371 | + } |
| 372 | +} |
0 commit comments