fix(tombstone): clear closed_at when converting closed issue to tombstone
When CreateTombstone was called on a closed issue, the CHECK constraint (status = closed) = (closed_at IS NOT NULL) was violated because closed_at was not cleared. Now setting closed_at = NULL in the UPDATE. Added regression test for creating tombstone from closed issue. Fixes: bd-fi05 Generated with Claude Code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -873,9 +873,12 @@ func (s *SQLiteStorage) CreateTombstone(ctx context.Context, id string, actor st
|
||||
originalType := string(issue.IssueType)
|
||||
|
||||
// Convert issue to tombstone
|
||||
// Note: closed_at must be set to NULL because of CHECK constraint:
|
||||
// (status = 'closed') = (closed_at IS NOT NULL)
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
UPDATE issues
|
||||
SET status = ?,
|
||||
closed_at = NULL,
|
||||
deleted_at = ?,
|
||||
deleted_by = ?,
|
||||
delete_reason = ?,
|
||||
|
||||
@@ -54,6 +54,58 @@ func TestCreateTombstone(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create tombstone for closed issue", func(t *testing.T) {
|
||||
// Regression test: closed issues have closed_at set, which must be
|
||||
// cleared when creating tombstone due to CHECK constraint:
|
||||
// (status = 'closed') = (closed_at IS NOT NULL)
|
||||
issue := &types.Issue{
|
||||
ID: "bd-closed-1",
|
||||
Title: "Closed Issue",
|
||||
Status: types.StatusOpen, // Create as open first
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
}
|
||||
|
||||
if err := store.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatalf("Failed to create issue: %v", err)
|
||||
}
|
||||
|
||||
// Close the issue to set closed_at
|
||||
if err := store.CloseIssue(ctx, "bd-closed-1", "closing for test", "tester"); err != nil {
|
||||
t.Fatalf("Failed to close issue: %v", err)
|
||||
}
|
||||
|
||||
// Verify closed_at is set
|
||||
closedIssue, err := store.GetIssue(ctx, "bd-closed-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get closed issue: %v", err)
|
||||
}
|
||||
if closedIssue.ClosedAt == nil {
|
||||
t.Fatal("closed_at should be set for closed issue")
|
||||
}
|
||||
|
||||
// Create tombstone - this should work without constraint violation
|
||||
if err := store.CreateTombstone(ctx, "bd-closed-1", "tester", "testing tombstone from closed"); err != nil {
|
||||
t.Fatalf("CreateTombstone from closed issue failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify tombstone was created correctly
|
||||
tombstone, err := store.GetIssue(ctx, "bd-closed-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get tombstone: %v", err)
|
||||
}
|
||||
if tombstone.Status != types.StatusTombstone {
|
||||
t.Errorf("Expected status=tombstone, got %s", tombstone.Status)
|
||||
}
|
||||
// closed_at should be nil for tombstone
|
||||
if tombstone.ClosedAt != nil {
|
||||
t.Error("closed_at should be nil for tombstone")
|
||||
}
|
||||
if tombstone.DeletedAt == nil {
|
||||
t.Error("deleted_at should be set for tombstone")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("create tombstone for non-existent issue", func(t *testing.T) {
|
||||
err := store.CreateTombstone(ctx, "bd-999", "tester", "testing")
|
||||
if err == nil {
|
||||
|
||||
Reference in New Issue
Block a user