changelog generator & diff tool stormlightlabs.github.io/git-storm/
changelog changeset markdown golang git
at main 774 lines 20 kB view raw
1package changeset 2 3import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/goccy/go-yaml" 12 "github.com/stormlightlabs/git-storm/internal/testutils" 13) 14 15func TestWrite(t *testing.T) { 16 tmpDir := t.TempDir() 17 tests := []struct { 18 name string 19 entry Entry 20 wantType string 21 wantScope string 22 wantSummary string 23 }{ 24 { 25 name: "basic entry", 26 entry: Entry{ 27 Type: "added", 28 Scope: "cli", 29 Summary: "Add changelog command", 30 Breaking: false, 31 }, 32 wantType: "added", 33 wantScope: "cli", 34 wantSummary: "Add changelog command", 35 }, 36 { 37 name: "entry without scope", 38 entry: Entry{ 39 Type: "fixed", 40 Scope: "", 41 Summary: "Fix bug in parser", 42 Breaking: false, 43 }, 44 wantType: "fixed", 45 wantScope: "", 46 wantSummary: "Fix bug in parser", 47 }, 48 { 49 name: "breaking change", 50 entry: Entry{ 51 Type: "changed", 52 Scope: "api", 53 Summary: "Remove legacy endpoints", 54 Breaking: true, 55 }, 56 wantType: "changed", 57 wantScope: "api", 58 wantSummary: "Remove legacy endpoints", 59 }, 60 } 61 62 for _, tt := range tests { 63 t.Run(tt.name, func(t *testing.T) { 64 filePath, err := Write(tmpDir, tt.entry) 65 if err != nil { 66 t.Fatalf("Write() error = %v", err) 67 } 68 69 if _, err := os.Stat(filePath); os.IsNotExist(err) { 70 t.Errorf("File was not created: %s", filePath) 71 } 72 73 content, err := os.ReadFile(filePath) 74 if err != nil { 75 t.Fatalf("Failed to read file: %v", err) 76 } 77 78 contentStr := string(content) 79 if !strings.HasPrefix(contentStr, "---\n") { 80 t.Errorf("File should start with YAML frontmatter delimiter") 81 } 82 83 parts := strings.SplitN(contentStr, "---\n", 3) 84 if len(parts) < 3 { 85 t.Fatalf("Invalid YAML frontmatter format") 86 } 87 88 var parsed Entry 89 if err := yaml.Unmarshal([]byte(parts[1]), &parsed); err != nil { 90 t.Fatalf("Failed to parse YAML: %v", err) 91 } 92 93 if parsed.Type != tt.wantType { 94 t.Errorf("Type = %v, want %v", parsed.Type, tt.wantType) 95 } 96 if parsed.Scope != tt.wantScope { 97 t.Errorf("Scope = %v, want %v", parsed.Scope, tt.wantScope) 98 } 99 if parsed.Summary != tt.wantSummary { 100 t.Errorf("Summary = %v, want %v", parsed.Summary, tt.wantSummary) 101 } 102 if parsed.Breaking != tt.entry.Breaking { 103 t.Errorf("Breaking = %v, want %v", parsed.Breaking, tt.entry.Breaking) 104 } 105 }) 106 } 107} 108 109func TestWrite_CollisionHandling(t *testing.T) { 110 tmpDir := t.TempDir() 111 112 entry := Entry{ 113 Type: "added", 114 Scope: "test", 115 Summary: "Test collision handling", 116 } 117 118 path1, err := Write(tmpDir, entry) 119 if err != nil { 120 t.Fatalf("First Write() error = %v", err) 121 } 122 123 path2, err := Write(tmpDir, entry) 124 if err != nil { 125 t.Fatalf("Second Write() error = %v", err) 126 } 127 128 if path1 == path2 { 129 t.Errorf("Expected different file paths for collision, got same path: %s", path1) 130 } 131 132 if _, err := os.Stat(path1); os.IsNotExist(err) { 133 t.Errorf("First file was not created: %s", path1) 134 } 135 if _, err := os.Stat(path2); os.IsNotExist(err) { 136 t.Errorf("Second file was not created: %s", path2) 137 } 138} 139 140func TestSlugify(t *testing.T) { 141 tests := []struct { 142 name string 143 input string 144 want string 145 }{ 146 { 147 name: "simple text", 148 input: "Add new feature", 149 want: "add-new-feature", 150 }, 151 { 152 name: "text with special chars", 153 input: "Fix: bug in parser!", 154 want: "fix-bug-in-parser", 155 }, 156 { 157 name: "text with numbers", 158 input: "Update version 1.2.3", 159 want: "update-version-1-2-3", 160 }, 161 { 162 name: "text with underscores", 163 input: "Add user_profile field", 164 want: "add-user-profile-field", 165 }, 166 { 167 name: "long text gets truncated", 168 input: "This is a very long summary that should be truncated to fifty characters maximum", 169 want: "this-is-a-very-long-summary-that-should-be-truncat", 170 }, 171 } 172 173 for _, tt := range tests { 174 t.Run(tt.name, func(t *testing.T) { 175 got := slugify(tt.input) 176 if got != tt.want { 177 t.Errorf("slugify() = %v, want %v", got, tt.want) 178 } 179 }) 180 } 181} 182 183func TestWrite_DirectoryCreation(t *testing.T) { 184 tmpDir := t.TempDir() 185 changesDir := filepath.Join(tmpDir, "nested", "changes") 186 187 entry := Entry{ 188 Type: "added", 189 Summary: "Test directory creation", 190 } 191 192 filePath, err := Write(changesDir, entry) 193 if err != nil { 194 t.Fatalf("Write() error = %v", err) 195 } 196 197 if _, err := os.Stat(changesDir); os.IsNotExist(err) { 198 t.Errorf("Directory was not created: %s", changesDir) 199 } 200 201 if _, err := os.Stat(filePath); os.IsNotExist(err) { 202 t.Errorf("File was not created: %s", filePath) 203 } 204} 205 206func TestComputeDiffHash_Stability(t *testing.T) { 207 repo := testutils.SetupTestRepo(t) 208 commits := testutils.GetCommitHistory(t, repo) 209 210 if len(commits) == 0 { 211 t.Fatal("Expected at least one commit in test repo") 212 } 213 214 commit := commits[0] 215 hash1, err := ComputeDiffHash(commit) 216 if err != nil { 217 t.Fatalf("ComputeDiffHash() error = %v", err) 218 } 219 220 hash2, err := ComputeDiffHash(commit) 221 if err != nil { 222 t.Fatalf("ComputeDiffHash() second call error = %v", err) 223 } 224 225 testutils.Expect.Equal(t, hash1, hash2, "Diff hash should be stable across multiple calls") 226 testutils.Expect.Equal(t, len(hash1), 64, "Diff hash should be 64 characters (SHA256 hex)") 227} 228 229func TestComputeDiffHash_DifferentCommits(t *testing.T) { 230 repo := testutils.SetupTestRepo(t) 231 232 testutils.AddCommit(t, repo, "file1.txt", "content1", "Add file1") 233 testutils.AddCommit(t, repo, "file2.txt", "content2", "Add file2") 234 235 commits := testutils.GetCommitHistory(t, repo) 236 if len(commits) < 2 { 237 t.Fatal("Expected at least 2 commits") 238 } 239 240 hash1, err := ComputeDiffHash(commits[0]) 241 if err != nil { 242 t.Fatalf("ComputeDiffHash() for commit 1 error = %v", err) 243 } 244 245 hash2, err := ComputeDiffHash(commits[1]) 246 if err != nil { 247 t.Fatalf("ComputeDiffHash() for commit 2 error = %v", err) 248 } 249 250 testutils.Expect.NotEqual(t, hash1, hash2, "Different commits should have different diff hashes") 251} 252 253func TestWriteWithMetadata(t *testing.T) { 254 tmpDir := t.TempDir() 255 256 meta := Metadata{ 257 CommitHash: "abc123def456", 258 DiffHash: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 259 Type: "added", 260 Scope: "cli", 261 Summary: "Add new feature", 262 Breaking: false, 263 Author: "Test User", 264 Date: time.Now(), 265 Filename: "", 266 } 267 268 filePath, err := WriteWithMetadata(tmpDir, meta) 269 if err != nil { 270 t.Fatalf("WriteWithMetadata() error = %v", err) 271 } 272 273 testutils.Expect.True(t, strings.HasSuffix(filePath, ".md"), "File path should have .md extension") 274 if _, err := os.Stat(filePath); os.IsNotExist(err) { 275 t.Errorf("Markdown file was not created: %s", filePath) 276 } 277 278 filename := filepath.Base(filePath) 279 testutils.Expect.True(t, strings.HasPrefix(filename, meta.DiffHash[:7]), "Filename should start with first 7 chars of diff hash") 280 281 content, err := os.ReadFile(filePath) 282 if err != nil { 283 t.Fatalf("Failed to read markdown file: %v", err) 284 } 285 286 var parsedEntry Entry 287 parts := strings.SplitN(string(content), "---\n", 3) 288 if len(parts) < 3 { 289 t.Fatal("Invalid YAML frontmatter format") 290 } 291 292 if err := yaml.Unmarshal([]byte(parts[1]), &parsedEntry); err != nil { 293 t.Fatalf("Failed to parse YAML: %v", err) 294 } 295 296 testutils.Expect.Equal(t, parsedEntry.Type, meta.Type) 297 testutils.Expect.Equal(t, parsedEntry.Summary, meta.Summary) 298 testutils.Expect.Equal(t, parsedEntry.CommitHash, meta.CommitHash) 299 testutils.Expect.Equal(t, parsedEntry.DiffHash, meta.DiffHash) 300 301 jsonPath := filepath.Join(tmpDir, "data", meta.DiffHash+".json") 302 if _, err := os.Stat(jsonPath); os.IsNotExist(err) { 303 t.Errorf("JSON metadata file was not created: %s", jsonPath) 304 } 305 306 jsonContent, err := os.ReadFile(jsonPath) 307 if err != nil { 308 t.Fatalf("Failed to read JSON metadata: %v", err) 309 } 310 311 var parsedMeta Metadata 312 if err := json.Unmarshal(jsonContent, &parsedMeta); err != nil { 313 t.Fatalf("Failed to parse JSON metadata: %v", err) 314 } 315 316 testutils.Expect.Equal(t, parsedMeta.CommitHash, meta.CommitHash) 317 testutils.Expect.Equal(t, parsedMeta.DiffHash, meta.DiffHash) 318 testutils.Expect.Equal(t, parsedMeta.Type, meta.Type) 319 testutils.Expect.Equal(t, parsedMeta.Summary, meta.Summary) 320} 321 322func TestLoadExistingMetadata(t *testing.T) { 323 tmpDir := t.TempDir() 324 325 meta1 := Metadata{ 326 CommitHash: "abc123", 327 DiffHash: "hash1111111111111111111111111111111111111111111111111111111111111", 328 Type: "added", 329 Summary: "Feature 1", 330 Author: "User1", 331 Date: time.Now(), 332 } 333 334 meta2 := Metadata{ 335 CommitHash: "def456", 336 DiffHash: "hash2222222222222222222222222222222222222222222222222222222222222", 337 Type: "fixed", 338 Summary: "Fix 1", 339 Author: "User2", 340 Date: time.Now(), 341 } 342 343 _, err := WriteWithMetadata(tmpDir, meta1) 344 if err != nil { 345 t.Fatalf("Failed to write meta1: %v", err) 346 } 347 348 _, err = WriteWithMetadata(tmpDir, meta2) 349 if err != nil { 350 t.Fatalf("Failed to write meta2: %v", err) 351 } 352 353 loaded, err := LoadExistingMetadata(tmpDir) 354 if err != nil { 355 t.Fatalf("LoadExistingMetadata() error = %v", err) 356 } 357 358 testutils.Expect.Equal(t, len(loaded), 2, "Should load 2 metadata entries") 359 360 if m, exists := loaded[meta1.DiffHash]; exists { 361 testutils.Expect.Equal(t, m.CommitHash, meta1.CommitHash) 362 testutils.Expect.Equal(t, m.Type, meta1.Type) 363 } else { 364 t.Errorf("meta1 not found in loaded metadata") 365 } 366 367 if m, exists := loaded[meta2.DiffHash]; exists { 368 testutils.Expect.Equal(t, m.CommitHash, meta2.CommitHash) 369 testutils.Expect.Equal(t, m.Type, meta2.Type) 370 } else { 371 t.Errorf("meta2 not found in loaded metadata") 372 } 373} 374 375func TestLoadExistingMetadata_EmptyDirectory(t *testing.T) { 376 tmpDir := t.TempDir() 377 378 loaded, err := LoadExistingMetadata(tmpDir) 379 if err != nil { 380 t.Fatalf("LoadExistingMetadata() error = %v", err) 381 } 382 383 testutils.Expect.Equal(t, len(loaded), 0, "Should return empty map for non-existent data directory") 384} 385 386func TestUpdateMetadata(t *testing.T) { 387 tmpDir := t.TempDir() 388 389 meta := Metadata{ 390 CommitHash: "original123", 391 DiffHash: "diffhash111111111111111111111111111111111111111111111111111111111", 392 Type: "added", 393 Summary: "Feature", 394 Author: "User", 395 Date: time.Now(), 396 } 397 398 _, err := WriteWithMetadata(tmpDir, meta) 399 if err != nil { 400 t.Fatalf("Failed to write metadata: %v", err) 401 } 402 403 newCommitHash := "rebased456" 404 err = UpdateMetadata(tmpDir, meta.DiffHash, newCommitHash) 405 if err != nil { 406 t.Fatalf("UpdateMetadata() error = %v", err) 407 } 408 409 loaded, err := LoadExistingMetadata(tmpDir) 410 if err != nil { 411 t.Fatalf("LoadExistingMetadata() error = %v", err) 412 } 413 414 updated, exists := loaded[meta.DiffHash] 415 if !exists { 416 t.Fatal("Updated metadata not found") 417 } 418 419 testutils.Expect.Equal(t, updated.CommitHash, newCommitHash, "CommitHash should be updated") 420 testutils.Expect.Equal(t, updated.Type, meta.Type, "Other fields should remain unchanged") 421 testutils.Expect.Equal(t, updated.Summary, meta.Summary, "Other fields should remain unchanged") 422} 423 424func TestDeduplication_SameCommit(t *testing.T) { 425 tmpDir := t.TempDir() 426 repo := testutils.SetupTestRepo(t) 427 commits := testutils.GetCommitHistory(t, repo) 428 if len(commits) == 0 { 429 t.Fatal("Expected at least one commit") 430 } 431 432 commit := commits[0] 433 diffHash, err := ComputeDiffHash(commit) 434 if err != nil { 435 t.Fatalf("ComputeDiffHash() error = %v", err) 436 } 437 438 meta := Metadata{ 439 CommitHash: commit.Hash.String(), 440 DiffHash: diffHash, 441 Type: "added", 442 Summary: "Test feature", 443 Author: commit.Author.Name, 444 Date: commit.Author.When, 445 } 446 447 _, err = WriteWithMetadata(tmpDir, meta) 448 if err != nil { 449 t.Fatalf("First WriteWithMetadata() error = %v", err) 450 } 451 452 existing, err := LoadExistingMetadata(tmpDir) 453 if err != nil { 454 t.Fatalf("LoadExistingMetadata() error = %v", err) 455 } 456 457 if existingMeta, exists := existing[diffHash]; exists { 458 testutils.Expect.Equal(t, existingMeta.CommitHash, commit.Hash.String(), "Should detect exact duplicate") 459 } else { 460 t.Error("Metadata should exist in loaded entries") 461 } 462} 463 464func TestDeduplication_RebasedCommit(t *testing.T) { 465 tmpDir := t.TempDir() 466 repo := testutils.SetupTestRepo(t) 467 468 commits := testutils.GetCommitHistory(t, repo) 469 if len(commits) == 0 { 470 t.Fatal("Expected at least one commit") 471 } 472 473 commit := commits[0] 474 diffHash, err := ComputeDiffHash(commit) 475 if err != nil { 476 t.Fatalf("ComputeDiffHash() error = %v", err) 477 } 478 479 originalMeta := Metadata{ 480 CommitHash: "original_commit_hash_123", 481 DiffHash: diffHash, 482 Type: "added", 483 Summary: "Test feature", 484 Author: commit.Author.Name, 485 Date: commit.Author.When, 486 } 487 488 _, err = WriteWithMetadata(tmpDir, originalMeta) 489 if err != nil { 490 t.Fatalf("WriteWithMetadata() error = %v", err) 491 } 492 493 existing, err := LoadExistingMetadata(tmpDir) 494 if err != nil { 495 t.Fatalf("LoadExistingMetadata() error = %v", err) 496 } 497 498 if existingMeta, exists := existing[diffHash]; exists { 499 if existingMeta.CommitHash != commit.Hash.String() { 500 err = UpdateMetadata(tmpDir, diffHash, commit.Hash.String()) 501 if err != nil { 502 t.Fatalf("UpdateMetadata() error = %v", err) 503 } 504 505 updated, err := LoadExistingMetadata(tmpDir) 506 if err != nil { 507 t.Fatalf("LoadExistingMetadata() after update error = %v", err) 508 } 509 510 updatedMeta := updated[diffHash] 511 testutils.Expect.Equal(t, updatedMeta.CommitHash, commit.Hash.String(), "CommitHash should be updated for rebased commit") 512 } 513 } 514} 515 516func TestWritePartial(t *testing.T) { 517 tmpDir := t.TempDir() 518 519 tests := []struct { 520 name string 521 filename string 522 entry Entry 523 wantErr bool 524 wantType string 525 wantSummary string 526 }{ 527 { 528 name: "basic partial entry", 529 filename: "abc1234.added.md", 530 entry: Entry{ 531 Type: "added", 532 Scope: "cli", 533 Summary: "Add feature", 534 CommitHash: "abc123def456", 535 }, 536 wantErr: false, 537 wantType: "added", 538 wantSummary: "Add feature", 539 }, 540 { 541 name: "partial with different type", 542 filename: "def5678.fixed.md", 543 entry: Entry{ 544 Type: "fixed", 545 Summary: "Fix bug", 546 CommitHash: "def5678abc", 547 }, 548 wantErr: false, 549 wantType: "fixed", 550 wantSummary: "Fix bug", 551 }, 552 } 553 554 for _, tt := range tests { 555 t.Run(tt.name, func(t *testing.T) { 556 filePath, err := WritePartial(tmpDir, tt.filename, tt.entry) 557 if (err != nil) != tt.wantErr { 558 t.Fatalf("WritePartial() error = %v, wantErr %v", err, tt.wantErr) 559 } 560 561 if tt.wantErr { 562 return 563 } 564 565 expectedPath := filepath.Join(tmpDir, tt.filename) 566 testutils.Expect.Equal(t, filePath, expectedPath, "File path should match expected") 567 568 if _, err := os.Stat(filePath); os.IsNotExist(err) { 569 t.Errorf("File was not created: %s", filePath) 570 } 571 572 content, err := os.ReadFile(filePath) 573 if err != nil { 574 t.Fatalf("Failed to read file: %v", err) 575 } 576 577 parts := strings.SplitN(string(content), "---\n", 3) 578 if len(parts) < 3 { 579 t.Fatal("Invalid YAML frontmatter format") 580 } 581 582 var parsed Entry 583 if err := yaml.Unmarshal([]byte(parts[1]), &parsed); err != nil { 584 t.Fatalf("Failed to parse YAML: %v", err) 585 } 586 587 testutils.Expect.Equal(t, parsed.Type, tt.wantType) 588 testutils.Expect.Equal(t, parsed.Summary, tt.wantSummary) 589 testutils.Expect.Equal(t, parsed.CommitHash, tt.entry.CommitHash) 590 }) 591 } 592} 593 594func TestWritePartial_DuplicateFilename(t *testing.T) { 595 tmpDir := t.TempDir() 596 597 filename := "abc1234.added.md" 598 entry := Entry{ 599 Type: "added", 600 Summary: "Test feature", 601 CommitHash: "abc1234", 602 } 603 604 _, err := WritePartial(tmpDir, filename, entry) 605 if err != nil { 606 t.Fatalf("First WritePartial() error = %v", err) 607 } 608 609 _, err = WritePartial(tmpDir, filename, entry) 610 if err == nil { 611 t.Error("Expected error when writing duplicate filename, got nil") 612 } 613 614 if !strings.Contains(err.Error(), "already exists") { 615 t.Errorf("Expected 'already exists' error, got: %v", err) 616 } 617} 618 619func TestDelete(t *testing.T) { 620 tmpDir := t.TempDir() 621 622 entry := Entry{ 623 Type: "added", 624 Scope: "test", 625 Summary: "Test deletion", 626 } 627 628 filePath, err := Write(tmpDir, entry) 629 if err != nil { 630 t.Fatalf("Write() error = %v", err) 631 } 632 633 filename := filepath.Base(filePath) 634 635 if _, err := os.Stat(filePath); os.IsNotExist(err) { 636 t.Fatalf("File should exist before deletion: %s", filePath) 637 } 638 639 err = Delete(tmpDir, filename) 640 if err != nil { 641 t.Fatalf("Delete() error = %v", err) 642 } 643 644 if _, err := os.Stat(filePath); !os.IsNotExist(err) { 645 t.Errorf("File should not exist after deletion: %s", filePath) 646 } 647} 648 649func TestDelete_NonExistentFile(t *testing.T) { 650 tmpDir := t.TempDir() 651 652 err := Delete(tmpDir, "nonexistent.md") 653 if err == nil { 654 t.Error("Expected error when deleting non-existent file, got nil") 655 } 656 657 if !strings.Contains(err.Error(), "does not exist") { 658 t.Errorf("Expected 'does not exist' error, got: %v", err) 659 } 660} 661 662func TestUpdate(t *testing.T) { 663 tmpDir := t.TempDir() 664 665 originalEntry := Entry{ 666 Type: "added", 667 Scope: "cli", 668 Summary: "Original summary", 669 } 670 671 filePath, err := Write(tmpDir, originalEntry) 672 if err != nil { 673 t.Fatalf("Write() error = %v", err) 674 } 675 676 filename := filepath.Base(filePath) 677 678 updatedEntry := Entry{ 679 Type: "changed", 680 Scope: "api", 681 Summary: "Updated summary", 682 } 683 684 err = Update(tmpDir, filename, updatedEntry) 685 if err != nil { 686 t.Fatalf("Update() error = %v", err) 687 } 688 689 content, err := os.ReadFile(filePath) 690 if err != nil { 691 t.Fatalf("Failed to read updated file: %v", err) 692 } 693 694 parts := strings.SplitN(string(content), "---\n", 3) 695 if len(parts) < 3 { 696 t.Fatal("Invalid YAML frontmatter format") 697 } 698 699 var parsed Entry 700 if err := yaml.Unmarshal([]byte(parts[1]), &parsed); err != nil { 701 t.Fatalf("Failed to parse YAML: %v", err) 702 } 703 704 testutils.Expect.Equal(t, parsed.Type, updatedEntry.Type, "Type should be updated") 705 testutils.Expect.Equal(t, parsed.Scope, updatedEntry.Scope, "Scope should be updated") 706 testutils.Expect.Equal(t, parsed.Summary, updatedEntry.Summary, "Summary should be updated") 707} 708 709func TestUpdate_NonExistentFile(t *testing.T) { 710 tmpDir := t.TempDir() 711 712 entry := Entry{ 713 Type: "added", 714 Summary: "Test", 715 } 716 717 err := Update(tmpDir, "nonexistent.md", entry) 718 if err == nil { 719 t.Error("Expected error when updating non-existent file, got nil") 720 } 721 722 if !strings.Contains(err.Error(), "does not exist") { 723 t.Errorf("Expected 'does not exist' error, got: %v", err) 724 } 725} 726 727func TestUpdate_PreserveMetadata(t *testing.T) { 728 tmpDir := t.TempDir() 729 730 originalEntry := Entry{ 731 Type: "added", 732 Scope: "cli", 733 Summary: "Original", 734 Breaking: false, 735 CommitHash: "abc123", 736 DiffHash: "def456", 737 } 738 739 filePath, err := Write(tmpDir, originalEntry) 740 if err != nil { 741 t.Fatalf("Write() error = %v", err) 742 } 743 744 filename := filepath.Base(filePath) 745 746 updatedEntry := Entry{ 747 Type: "changed", 748 Scope: "api", 749 Summary: "Updated", 750 Breaking: true, 751 CommitHash: "abc123", 752 DiffHash: "def456", 753 } 754 755 err = Update(tmpDir, filename, updatedEntry) 756 if err != nil { 757 t.Fatalf("Update() error = %v", err) 758 } 759 760 content, err := os.ReadFile(filePath) 761 if err != nil { 762 t.Fatalf("Failed to read updated file: %v", err) 763 } 764 765 parts := strings.SplitN(string(content), "---\n", 3) 766 var parsed Entry 767 if err := yaml.Unmarshal([]byte(parts[1]), &parsed); err != nil { 768 t.Fatalf("Failed to parse YAML: %v", err) 769 } 770 771 testutils.Expect.Equal(t, parsed.CommitHash, updatedEntry.CommitHash, "CommitHash should be preserved") 772 testutils.Expect.Equal(t, parsed.DiffHash, updatedEntry.DiffHash, "DiffHash should be preserved") 773 testutils.Expect.Equal(t, parsed.Breaking, updatedEntry.Breaking, "Breaking should be updated") 774}