test(export): add test for tombstone inclusion in JSONL export (bd-yk8w)
Verify that tombstones are properly included when exporting issues to JSONL format, including all tombstone-specific fields like DeletedAt, DeletedBy, DeleteReason, and OriginalType. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -260,3 +260,103 @@ func TestRoundTrip(t *testing.T) {
|
||||
t.Errorf("EstimatedMinutes = %v, want %v", decoded.EstimatedMinutes, original.EstimatedMinutes)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExportIncludesTombstones verifies that tombstones are included in JSONL export (bd-yk8w)
|
||||
func TestExportIncludesTombstones(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, "test.db")
|
||||
store := newTestStoreWithPrefix(t, dbPath, "test")
|
||||
|
||||
// Create a regular issue
|
||||
regularIssue := &types.Issue{
|
||||
ID: "test-abc",
|
||||
Title: "Regular issue",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := store.CreateIssue(ctx, regularIssue, "test"); err != nil {
|
||||
t.Fatalf("Failed to create regular issue: %v", err)
|
||||
}
|
||||
|
||||
// Create a tombstone issue
|
||||
deletedAt := time.Now().Add(-time.Hour)
|
||||
tombstone := &types.Issue{
|
||||
ID: "test-def",
|
||||
Title: "(deleted)",
|
||||
Status: types.StatusTombstone,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-48 * time.Hour),
|
||||
UpdatedAt: deletedAt,
|
||||
DeletedAt: &deletedAt,
|
||||
DeletedBy: "alice",
|
||||
DeleteReason: "duplicate issue",
|
||||
OriginalType: "bug",
|
||||
}
|
||||
if err := store.CreateIssue(ctx, tombstone, "test"); err != nil {
|
||||
t.Fatalf("Failed to create tombstone: %v", err)
|
||||
}
|
||||
|
||||
// Export all issues (including tombstones)
|
||||
allIssues, err := store.SearchIssues(ctx, "", types.IssueFilter{IncludeTombstones: true})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
// Verify we got both issues
|
||||
if len(allIssues) != 2 {
|
||||
t.Fatalf("Expected 2 issues (1 regular + 1 tombstone), got %d", len(allIssues))
|
||||
}
|
||||
|
||||
// Encode to JSONL
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
for _, issue := range allIssues {
|
||||
if err := encoder.Encode(issue); err != nil {
|
||||
t.Fatalf("Failed to encode issue: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify JSONL contains both issues
|
||||
lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
|
||||
if len(lines) != 2 {
|
||||
t.Fatalf("Expected 2 JSONL lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
// Parse and verify tombstone fields are present
|
||||
foundTombstone := false
|
||||
for _, line := range lines {
|
||||
var issue types.Issue
|
||||
if err := json.Unmarshal([]byte(line), &issue); err != nil {
|
||||
t.Fatalf("Failed to parse JSONL line: %v", err)
|
||||
}
|
||||
|
||||
if issue.ID == "test-def" {
|
||||
foundTombstone = true
|
||||
if issue.Status != types.StatusTombstone {
|
||||
t.Errorf("Expected tombstone status, got %q", issue.Status)
|
||||
}
|
||||
if issue.DeletedBy != "alice" {
|
||||
t.Errorf("Expected DeletedBy 'alice', got %q", issue.DeletedBy)
|
||||
}
|
||||
if issue.DeleteReason != "duplicate issue" {
|
||||
t.Errorf("Expected DeleteReason 'duplicate issue', got %q", issue.DeleteReason)
|
||||
}
|
||||
if issue.OriginalType != "bug" {
|
||||
t.Errorf("Expected OriginalType 'bug', got %q", issue.OriginalType)
|
||||
}
|
||||
if issue.DeletedAt == nil {
|
||||
t.Error("Expected DeletedAt to be set")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !foundTombstone {
|
||||
t.Error("Tombstone not found in JSONL output")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user