Files
beads/cmd/bd/import_multipart_id_test.go
Steve Yegge 3d924f88f3 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>
2025-12-23 16:20:04 -08:00

224 lines
5.6 KiB
Go

package main
import (
"context"
"path/filepath"
"testing"
"github.com/steveyegge/beads/internal/types"
)
// TestImportMultiPartIDs tests that issue IDs with hyphens in the suffix
// (like "vc-baseline-test") are correctly recognized as having prefix "vc"
// and not treated as having different prefixes like "vc-baseline-"
func TestImportMultiPartIDs(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
// Create database with "vc" prefix
st := newTestStoreWithPrefix(t, dbPath, "vc")
ctx := context.Background()
// Create issues with multi-part IDs
issues := []*types.Issue{
{
ID: "vc-baseline-test",
Title: "Baseline test issue",
Description: "Issue with hyphenated suffix",
Status: "open",
Priority: 1,
IssueType: "task",
},
{
ID: "vc-92cl-gate-test",
Title: "Gate test issue",
Description: "Another issue with hyphenated suffix",
Status: "open",
Priority: 1,
IssueType: "task",
},
{
ID: "vc-test",
Title: "Simple test issue",
Description: "Issue without hyphenated suffix",
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)
}
// Should not detect prefix mismatch
if result.PrefixMismatch {
t.Errorf("Import incorrectly detected prefix mismatch")
t.Logf("Mismatched prefixes: %v", result.MismatchPrefixes)
}
// All issues should be created
if result.Created != 3 {
t.Errorf("Expected 3 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)
}
}
}
// 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) {
tests := []struct {
name string
issues []*types.Issue
expected string
}{
{
name: "simple IDs",
issues: []*types.Issue{
{ID: "bd-1"},
{ID: "bd-2"},
{ID: "bd-3"},
},
expected: "bd",
},
{
name: "multi-part IDs",
issues: []*types.Issue{
{ID: "vc-baseline-test"},
{ID: "vc-92cl-gate-test"},
{ID: "vc-test"},
},
expected: "vc",
},
{
name: "mixed multi-part IDs",
issues: []*types.Issue{
{ID: "prefix-a-b-c"},
{ID: "prefix-x-y-z"},
{ID: "prefix-simple"},
},
expected: "prefix",
},
{
name: "empty list",
issues: []*types.Issue{},
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := detectPrefixFromIssues(tt.issues)
if result != tt.expected {
t.Errorf("detectPrefixFromIssues() = %q, want %q", result, tt.expected)
}
})
}
}