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>
224 lines
5.6 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|