fix: handle multi-hyphen prefixes correctly (GH#422)
The prefix mismatch detection was using ExtractIssuePrefix() which tries to guess the prefix by analyzing suffix patterns. This failed for multi-hyphen prefixes like "asianops-audit-" when issue IDs had word-like suffixes (e.g., "asianops-audit-test") - the heuristic would fall back to the first hyphen and report "asianops-" as the prefix. Fixed by checking directly if the issue ID starts with the configured prefix using strings.HasPrefix(). This is more reliable than guessing since we know the expected prefix from the database config. Added test case TestImportMultiHyphenPrefix to prevent regression. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,92 @@ func TestImportMultiPartIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImportMultiHyphenPrefix tests GH#422: importing with multi-hyphen prefixes
|
||||
// like "asianops-audit-" should not cause false positive prefix mismatch errors.
|
||||
func TestImportMultiHyphenPrefix(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
// Create database with multi-hyphen prefix "asianops-audit"
|
||||
st := newTestStoreWithPrefix(t, dbPath, "asianops-audit")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create issues with hash-like suffixes that could be mistaken for words
|
||||
// The key is that "test", "task", "demo" look like English words (4+ chars, no digits)
|
||||
// which previously caused ExtractIssuePrefix to fall back to first hyphen
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "asianops-audit-sa0",
|
||||
Title: "Issue with short hash suffix",
|
||||
Description: "Short hash suffix should work",
|
||||
Status: "open",
|
||||
Priority: 1,
|
||||
IssueType: "task",
|
||||
},
|
||||
{
|
||||
ID: "asianops-audit-test",
|
||||
Title: "Issue with word-like suffix",
|
||||
Description: "Word-like suffix 'test' was causing false positive",
|
||||
Status: "open",
|
||||
Priority: 1,
|
||||
IssueType: "task",
|
||||
},
|
||||
{
|
||||
ID: "asianops-audit-task",
|
||||
Title: "Another word-like suffix",
|
||||
Description: "Word-like suffix 'task' was also problematic",
|
||||
Status: "open",
|
||||
Priority: 1,
|
||||
IssueType: "task",
|
||||
},
|
||||
{
|
||||
ID: "asianops-audit-demo",
|
||||
Title: "Demo issue",
|
||||
Description: "Word-like suffix 'demo'",
|
||||
Status: "open",
|
||||
Priority: 1,
|
||||
IssueType: "task",
|
||||
},
|
||||
}
|
||||
|
||||
// Import should succeed without prefix mismatch errors
|
||||
opts := ImportOptions{
|
||||
DryRun: false,
|
||||
SkipUpdate: false,
|
||||
Strict: false,
|
||||
}
|
||||
|
||||
result, err := importIssuesCore(ctx, dbPath, st, issues, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Import failed: %v", err)
|
||||
}
|
||||
|
||||
// GH#422: Should NOT detect prefix mismatch
|
||||
if result.PrefixMismatch {
|
||||
t.Errorf("Import incorrectly detected prefix mismatch for multi-hyphen prefix")
|
||||
t.Logf("Expected prefix: asianops-audit")
|
||||
t.Logf("Mismatched prefixes detected: %v", result.MismatchPrefixes)
|
||||
}
|
||||
|
||||
// All issues should be created
|
||||
if result.Created != 4 {
|
||||
t.Errorf("Expected 4 issues created, got %d", result.Created)
|
||||
}
|
||||
|
||||
// Verify issues exist in database
|
||||
for _, issue := range issues {
|
||||
dbIssue, err := st.GetIssue(ctx, issue.ID)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get issue %s: %v", issue.ID, err)
|
||||
continue
|
||||
}
|
||||
if dbIssue.Title != issue.Title {
|
||||
t.Errorf("Issue %s title mismatch: got %q, want %q", issue.ID, dbIssue.Title, issue.Title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDetectPrefixFromIssues tests the detectPrefixFromIssues function
|
||||
// with multi-part IDs
|
||||
func TestDetectPrefixFromIssues(t *testing.T) {
|
||||
|
||||
@@ -231,8 +231,13 @@ func handlePrefixMismatch(ctx context.Context, sqliteStore *sqlite.SQLiteStorage
|
||||
var tombstonesToRemove []string
|
||||
|
||||
for _, issue := range issues {
|
||||
prefix := utils.ExtractIssuePrefix(issue.ID)
|
||||
if !allowedPrefixes[prefix] {
|
||||
// GH#422: Check if issue ID starts with configured prefix directly
|
||||
// rather than extracting/guessing. This handles multi-hyphen prefixes
|
||||
// like "asianops-audit-" correctly.
|
||||
prefixMatches := strings.HasPrefix(issue.ID, configuredPrefix+"-")
|
||||
if !prefixMatches {
|
||||
// Extract prefix for error reporting (best effort)
|
||||
prefix := utils.ExtractIssuePrefix(issue.ID)
|
||||
if issue.IsTombstone() {
|
||||
tombstoneMismatchPrefixes[prefix]++
|
||||
tombstonesToRemove = append(tombstonesToRemove, issue.ID)
|
||||
|
||||
Reference in New Issue
Block a user