Files
beads/cmd/bd/external_ref_test.go
Steve Yegge e7f09660c0 feat: add cross-project dependency support - config and external: prefix (bd-66w1, bd-om4a)
Config (bd-66w1):
- Add external_projects config for mapping project names to paths
- Add GetExternalProjects() and ResolveExternalProjectPath() functions
- Add config documentation and tests

External deps (bd-om4a):
- bd dep add accepts external:project:capability syntax
- External refs stored as-is in dependencies table
- GetBlockedIssues includes external deps in blocked_by list
- blocked_issues_cache includes external dependencies
- Add validation and parsing helpers for external refs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 23:08:00 -08:00

111 lines
2.4 KiB
Go

package main
import (
"testing"
)
func TestValidateExternalRef(t *testing.T) {
tests := []struct {
name string
ref string
wantErr bool
}{
{
name: "valid external ref",
ref: "external:beads:mol-run-assignee",
wantErr: false,
},
{
name: "valid with complex capability",
ref: "external:gastown:cross-project-deps",
wantErr: false,
},
{
name: "missing external prefix",
ref: "beads:mol-run",
wantErr: true,
},
{
name: "missing capability",
ref: "external:beads:",
wantErr: true,
},
{
name: "missing project",
ref: "external::capability",
wantErr: true,
},
{
name: "only external prefix",
ref: "external:",
wantErr: true,
},
{
name: "too few parts",
ref: "external:beads",
wantErr: true,
},
{
name: "local issue ID",
ref: "bd-xyz",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateExternalRef(tt.ref)
if (err != nil) != tt.wantErr {
t.Errorf("validateExternalRef(%q) error = %v, wantErr %v", tt.ref, err, tt.wantErr)
}
})
}
}
func TestIsExternalRef(t *testing.T) {
tests := []struct {
ref string
want bool
}{
{"external:beads:capability", true},
{"external:", true}, // prefix matches even if invalid
{"bd-xyz", false},
{"", false},
{"External:beads:cap", false}, // case-sensitive
}
for _, tt := range tests {
t.Run(tt.ref, func(t *testing.T) {
if got := IsExternalRef(tt.ref); got != tt.want {
t.Errorf("IsExternalRef(%q) = %v, want %v", tt.ref, got, tt.want)
}
})
}
}
func TestParseExternalRef(t *testing.T) {
tests := []struct {
ref string
wantProject string
wantCapability string
}{
{"external:beads:mol-run-assignee", "beads", "mol-run-assignee"},
{"external:gastown:cross-project", "gastown", "cross-project"},
{"external:a:b", "a", "b"},
{"bd-xyz", "", ""}, // not external
{"external:", "", ""}, // invalid format
{"external:proj", "", ""}, // missing capability
{"", "", ""}, // empty
}
for _, tt := range tests {
t.Run(tt.ref, func(t *testing.T) {
gotProj, gotCap := ParseExternalRef(tt.ref)
if gotProj != tt.wantProject || gotCap != tt.wantCapability {
t.Errorf("ParseExternalRef(%q) = (%q, %q), want (%q, %q)",
tt.ref, gotProj, gotCap, tt.wantProject, tt.wantCapability)
}
})
}
}