* fix(sling_test): update test for cook dir change
The cook command no longer needs database context and runs from cwd,
not the target rig directory. Update test to match this behavior
change from bd2a5ab5.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(tests): skip tests requiring missing binaries, handle --allow-stale
- Add skipIfAgentBinaryMissing helper to skip tests when codex/gemini
binaries aren't available in the test environment
- Update rig manager test stub to handle --allow-stale flag
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: julianknutsen <julianknutsen@users.noreply.github>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
693 lines
18 KiB
Go
693 lines
18 KiB
Go
package rig
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/steveyegge/gastown/internal/config"
|
|
"github.com/steveyegge/gastown/internal/git"
|
|
)
|
|
|
|
func setupTestTown(t *testing.T) (string, *config.RigsConfig) {
|
|
t.Helper()
|
|
root := t.TempDir()
|
|
|
|
rigsConfig := &config.RigsConfig{
|
|
Version: 1,
|
|
Rigs: make(map[string]config.RigEntry),
|
|
}
|
|
|
|
return root, rigsConfig
|
|
}
|
|
|
|
func writeFakeBD(t *testing.T, script string) string {
|
|
t.Helper()
|
|
binDir := t.TempDir()
|
|
scriptPath := filepath.Join(binDir, "bd")
|
|
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
|
t.Fatalf("write fake bd: %v", err)
|
|
}
|
|
return binDir
|
|
}
|
|
|
|
func assertBeadsDirLog(t *testing.T, logPath, want string) {
|
|
t.Helper()
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
t.Fatalf("reading beads dir log: %v", err)
|
|
}
|
|
lines := strings.Split(strings.TrimSpace(string(data)), "\n")
|
|
if len(lines) == 0 || (len(lines) == 1 && lines[0] == "") {
|
|
t.Fatalf("expected beads dir log entries, got none")
|
|
}
|
|
for _, line := range lines {
|
|
if line != want {
|
|
t.Fatalf("BEADS_DIR = %q, want %q", line, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createTestRig(t *testing.T, root, name string) {
|
|
t.Helper()
|
|
|
|
rigPath := filepath.Join(root, name)
|
|
if err := os.MkdirAll(rigPath, 0755); err != nil {
|
|
t.Fatalf("mkdir rig: %v", err)
|
|
}
|
|
|
|
// Create agent dirs (witness, refinery, mayor)
|
|
for _, dir := range AgentDirs {
|
|
dirPath := filepath.Join(rigPath, dir)
|
|
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", dir, err)
|
|
}
|
|
}
|
|
|
|
// Create some polecats
|
|
polecatsDir := filepath.Join(rigPath, "polecats")
|
|
for _, polecat := range []string{"Toast", "Cheedo"} {
|
|
if err := os.MkdirAll(filepath.Join(polecatsDir, polecat), 0755); err != nil {
|
|
t.Fatalf("mkdir polecat: %v", err)
|
|
}
|
|
}
|
|
// Create a shared support dir that should not be treated as a polecat worktree.
|
|
if err := os.MkdirAll(filepath.Join(polecatsDir, ".claude"), 0755); err != nil {
|
|
t.Fatalf("mkdir polecats/.claude: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDiscoverRigs(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
|
|
// Create test rig
|
|
createTestRig(t, root, "gastown")
|
|
rigsConfig.Rigs["gastown"] = config.RigEntry{
|
|
GitURL: "git@github.com:test/gastown.git",
|
|
}
|
|
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
rigs, err := manager.DiscoverRigs()
|
|
if err != nil {
|
|
t.Fatalf("DiscoverRigs: %v", err)
|
|
}
|
|
|
|
if len(rigs) != 1 {
|
|
t.Errorf("rigs count = %d, want 1", len(rigs))
|
|
}
|
|
|
|
rig := rigs[0]
|
|
if rig.Name != "gastown" {
|
|
t.Errorf("Name = %q, want gastown", rig.Name)
|
|
}
|
|
if len(rig.Polecats) != 2 {
|
|
t.Errorf("Polecats count = %d, want 2", len(rig.Polecats))
|
|
}
|
|
if slices.Contains(rig.Polecats, ".claude") {
|
|
t.Errorf("expected polecats/.claude to be ignored, got %v", rig.Polecats)
|
|
}
|
|
if !rig.HasWitness {
|
|
t.Error("expected HasWitness = true")
|
|
}
|
|
if !rig.HasRefinery {
|
|
t.Error("expected HasRefinery = true")
|
|
}
|
|
}
|
|
|
|
func TestGetRig(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
|
|
createTestRig(t, root, "test-rig")
|
|
rigsConfig.Rigs["test-rig"] = config.RigEntry{
|
|
GitURL: "git@github.com:test/test-rig.git",
|
|
}
|
|
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
rig, err := manager.GetRig("test-rig")
|
|
if err != nil {
|
|
t.Fatalf("GetRig: %v", err)
|
|
}
|
|
|
|
if rig.Name != "test-rig" {
|
|
t.Errorf("Name = %q, want test-rig", rig.Name)
|
|
}
|
|
}
|
|
|
|
func TestGetRigNotFound(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
_, err := manager.GetRig("nonexistent")
|
|
if err != ErrRigNotFound {
|
|
t.Errorf("GetRig = %v, want ErrRigNotFound", err)
|
|
}
|
|
}
|
|
|
|
func TestRigExists(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
rigsConfig.Rigs["exists"] = config.RigEntry{}
|
|
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
if !manager.RigExists("exists") {
|
|
t.Error("expected RigExists = true for existing rig")
|
|
}
|
|
if manager.RigExists("nonexistent") {
|
|
t.Error("expected RigExists = false for nonexistent rig")
|
|
}
|
|
}
|
|
|
|
func TestRemoveRig(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
rigsConfig.Rigs["to-remove"] = config.RigEntry{}
|
|
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
if err := manager.RemoveRig("to-remove"); err != nil {
|
|
t.Fatalf("RemoveRig: %v", err)
|
|
}
|
|
|
|
if manager.RigExists("to-remove") {
|
|
t.Error("rig should not exist after removal")
|
|
}
|
|
}
|
|
|
|
func TestRemoveRigNotFound(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
err := manager.RemoveRig("nonexistent")
|
|
if err != ErrRigNotFound {
|
|
t.Errorf("RemoveRig = %v, want ErrRigNotFound", err)
|
|
}
|
|
}
|
|
|
|
func TestAddRig_RejectsInvalidNames(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
tests := []struct {
|
|
name string
|
|
wantError string
|
|
}{
|
|
{"op-baby", `rig name "op-baby" contains invalid characters`},
|
|
{"my.rig", `rig name "my.rig" contains invalid characters`},
|
|
{"my rig", `rig name "my rig" contains invalid characters`},
|
|
{"op-baby-test", `rig name "op-baby-test" contains invalid characters`},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := manager.AddRig(AddRigOptions{
|
|
Name: tt.name,
|
|
GitURL: "git@github.com:test/test.git",
|
|
})
|
|
if err == nil {
|
|
t.Errorf("AddRig(%q) succeeded, want error containing %q", tt.name, tt.wantError)
|
|
return
|
|
}
|
|
if !strings.Contains(err.Error(), tt.wantError) {
|
|
t.Errorf("AddRig(%q) error = %q, want error containing %q", tt.name, err.Error(), tt.wantError)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListRigNames(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
rigsConfig.Rigs["rig1"] = config.RigEntry{}
|
|
rigsConfig.Rigs["rig2"] = config.RigEntry{}
|
|
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
names := manager.ListRigNames()
|
|
if len(names) != 2 {
|
|
t.Errorf("names count = %d, want 2", len(names))
|
|
}
|
|
}
|
|
|
|
func TestRigSummary(t *testing.T) {
|
|
rig := &Rig{
|
|
Name: "test",
|
|
Polecats: []string{"a", "b", "c"},
|
|
HasWitness: true,
|
|
HasRefinery: false,
|
|
}
|
|
|
|
summary := rig.Summary()
|
|
|
|
if summary.Name != "test" {
|
|
t.Errorf("Name = %q, want test", summary.Name)
|
|
}
|
|
if summary.PolecatCount != 3 {
|
|
t.Errorf("PolecatCount = %d, want 3", summary.PolecatCount)
|
|
}
|
|
if !summary.HasWitness {
|
|
t.Error("expected HasWitness = true")
|
|
}
|
|
if summary.HasRefinery {
|
|
t.Error("expected HasRefinery = false")
|
|
}
|
|
}
|
|
|
|
func TestEnsureGitignoreEntry_AddsEntry(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
gitignorePath := filepath.Join(root, ".gitignore")
|
|
|
|
if err := manager.ensureGitignoreEntry(gitignorePath, ".test-entry/"); err != nil {
|
|
t.Fatalf("ensureGitignoreEntry: %v", err)
|
|
}
|
|
|
|
content, _ := os.ReadFile(gitignorePath)
|
|
if string(content) != ".test-entry/\n" {
|
|
t.Errorf("content = %q, want .test-entry/", string(content))
|
|
}
|
|
}
|
|
|
|
func TestEnsureGitignoreEntry_DoesNotDuplicate(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
gitignorePath := filepath.Join(root, ".gitignore")
|
|
|
|
// Pre-populate with the entry
|
|
if err := os.WriteFile(gitignorePath, []byte(".test-entry/\n"), 0644); err != nil {
|
|
t.Fatalf("writing .gitignore: %v", err)
|
|
}
|
|
|
|
if err := manager.ensureGitignoreEntry(gitignorePath, ".test-entry/"); err != nil {
|
|
t.Fatalf("ensureGitignoreEntry: %v", err)
|
|
}
|
|
|
|
content, _ := os.ReadFile(gitignorePath)
|
|
if string(content) != ".test-entry/\n" {
|
|
t.Errorf("content = %q, want single .test-entry/", string(content))
|
|
}
|
|
}
|
|
|
|
func TestEnsureGitignoreEntry_AppendsToExisting(t *testing.T) {
|
|
root, rigsConfig := setupTestTown(t)
|
|
manager := NewManager(root, rigsConfig, git.NewGit(root))
|
|
|
|
gitignorePath := filepath.Join(root, ".gitignore")
|
|
|
|
// Pre-populate with existing entries
|
|
if err := os.WriteFile(gitignorePath, []byte("node_modules/\n*.log\n"), 0644); err != nil {
|
|
t.Fatalf("writing .gitignore: %v", err)
|
|
}
|
|
|
|
if err := manager.ensureGitignoreEntry(gitignorePath, ".test-entry/"); err != nil {
|
|
t.Fatalf("ensureGitignoreEntry: %v", err)
|
|
}
|
|
|
|
content, _ := os.ReadFile(gitignorePath)
|
|
expected := "node_modules/\n*.log\n.test-entry/\n"
|
|
if string(content) != expected {
|
|
t.Errorf("content = %q, want %q", string(content), expected)
|
|
}
|
|
}
|
|
|
|
func TestInitBeads_TrackedBeads_CreatesRedirect(t *testing.T) {
|
|
t.Parallel()
|
|
// When the cloned repo has tracked beads (mayor/rig/.beads exists),
|
|
// initBeads should create a redirect file at <rig>/.beads/redirect
|
|
// pointing to mayor/rig/.beads instead of creating a local database.
|
|
rigPath := t.TempDir()
|
|
|
|
// Simulate tracked beads in the cloned repo
|
|
mayorBeadsDir := filepath.Join(rigPath, "mayor", "rig", ".beads")
|
|
if err := os.MkdirAll(mayorBeadsDir, 0755); err != nil {
|
|
t.Fatalf("mkdir mayor beads: %v", err)
|
|
}
|
|
// Create a config file to simulate a real beads directory
|
|
if err := os.WriteFile(filepath.Join(mayorBeadsDir, "config.yaml"), []byte("prefix: gt\n"), 0644); err != nil {
|
|
t.Fatalf("write mayor config: %v", err)
|
|
}
|
|
|
|
manager := &Manager{}
|
|
if err := manager.initBeads(rigPath, "gt"); err != nil {
|
|
t.Fatalf("initBeads: %v", err)
|
|
}
|
|
|
|
// Verify redirect file was created
|
|
redirectPath := filepath.Join(rigPath, ".beads", "redirect")
|
|
content, err := os.ReadFile(redirectPath)
|
|
if err != nil {
|
|
t.Fatalf("reading redirect file: %v", err)
|
|
}
|
|
|
|
expected := "mayor/rig/.beads\n"
|
|
if string(content) != expected {
|
|
t.Errorf("redirect content = %q, want %q", string(content), expected)
|
|
}
|
|
|
|
// Verify no local database was created (no config.yaml at rig level)
|
|
rigConfigPath := filepath.Join(rigPath, ".beads", "config.yaml")
|
|
if _, err := os.Stat(rigConfigPath); !os.IsNotExist(err) {
|
|
t.Errorf("expected no config.yaml at rig level when using redirect, but it exists")
|
|
}
|
|
}
|
|
|
|
func TestInitBeads_LocalBeads_CreatesDatabase(t *testing.T) {
|
|
// Cannot use t.Parallel() due to t.Setenv
|
|
// When the cloned repo does NOT have tracked beads (no mayor/rig/.beads),
|
|
// initBeads should create a local database at <rig>/.beads/
|
|
rigPath := t.TempDir()
|
|
|
|
// Create mayor/rig directory but WITHOUT .beads (no tracked beads)
|
|
mayorRigDir := filepath.Join(rigPath, "mayor", "rig")
|
|
if err := os.MkdirAll(mayorRigDir, 0755); err != nil {
|
|
t.Fatalf("mkdir mayor/rig: %v", err)
|
|
}
|
|
|
|
// Use fake bd that succeeds
|
|
script := `#!/usr/bin/env bash
|
|
set -e
|
|
if [[ "$1" == "init" ]]; then
|
|
# Simulate successful bd init
|
|
exit 0
|
|
fi
|
|
exit 0
|
|
`
|
|
binDir := writeFakeBD(t, script)
|
|
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
|
|
manager := &Manager{}
|
|
if err := manager.initBeads(rigPath, "gt"); err != nil {
|
|
t.Fatalf("initBeads: %v", err)
|
|
}
|
|
|
|
// Verify NO redirect file was created
|
|
redirectPath := filepath.Join(rigPath, ".beads", "redirect")
|
|
if _, err := os.Stat(redirectPath); !os.IsNotExist(err) {
|
|
t.Errorf("expected no redirect file for local beads, but it exists")
|
|
}
|
|
|
|
// Verify .beads directory was created
|
|
beadsDir := filepath.Join(rigPath, ".beads")
|
|
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
|
|
t.Errorf("expected .beads directory to be created")
|
|
}
|
|
}
|
|
|
|
func TestInitBeadsWritesConfigOnFailure(t *testing.T) {
|
|
rigPath := t.TempDir()
|
|
beadsDir := filepath.Join(rigPath, ".beads")
|
|
|
|
script := `#!/usr/bin/env bash
|
|
set -e
|
|
if [[ -n "$BEADS_DIR_LOG" ]]; then
|
|
echo "${BEADS_DIR:-<unset>}" >> "$BEADS_DIR_LOG"
|
|
fi
|
|
cmd="$1"
|
|
shift
|
|
if [[ "$cmd" == "init" ]]; then
|
|
echo "bd init failed" >&2
|
|
exit 1
|
|
fi
|
|
echo "unexpected command: $cmd" >&2
|
|
exit 1
|
|
`
|
|
|
|
binDir := writeFakeBD(t, script)
|
|
beadsDirLog := filepath.Join(t.TempDir(), "beads-dir.log")
|
|
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
t.Setenv("BEADS_DIR_LOG", beadsDirLog)
|
|
|
|
manager := &Manager{}
|
|
if err := manager.initBeads(rigPath, "gt"); err != nil {
|
|
t.Fatalf("initBeads: %v", err)
|
|
}
|
|
|
|
configPath := filepath.Join(beadsDir, "config.yaml")
|
|
config, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
t.Fatalf("reading config.yaml: %v", err)
|
|
}
|
|
if string(config) != "prefix: gt\n" {
|
|
t.Fatalf("config.yaml = %q, want %q", string(config), "prefix: gt\n")
|
|
}
|
|
assertBeadsDirLog(t, beadsDirLog, beadsDir)
|
|
}
|
|
|
|
func TestInitAgentBeadsUsesRigBeadsDir(t *testing.T) {
|
|
// Rig-level agent beads (witness, refinery) are stored in rig beads.
|
|
// Town-level agents (mayor, deacon) are created by gt install in town beads.
|
|
// This test verifies that rig agent beads are created in the rig directory,
|
|
// using the resolved rig beads directory for BEADS_DIR.
|
|
townRoot := t.TempDir()
|
|
rigPath := filepath.Join(townRoot, "testrip")
|
|
rigBeadsDir := filepath.Join(rigPath, ".beads")
|
|
|
|
if err := os.MkdirAll(rigBeadsDir, 0755); err != nil {
|
|
t.Fatalf("mkdir rig beads dir: %v", err)
|
|
}
|
|
|
|
// Track which agent IDs were created
|
|
var createdAgents []string
|
|
|
|
script := `#!/usr/bin/env bash
|
|
set -e
|
|
if [[ -n "$BEADS_DIR_LOG" ]]; then
|
|
echo "${BEADS_DIR:-<unset>}" >> "$BEADS_DIR_LOG"
|
|
fi
|
|
if [[ "$1" == "--no-daemon" ]]; then
|
|
shift
|
|
fi
|
|
if [[ "$1" == "--allow-stale" ]]; then
|
|
shift
|
|
fi
|
|
cmd="$1"
|
|
shift
|
|
case "$cmd" in
|
|
show)
|
|
# Return empty to indicate agent doesn't exist yet
|
|
echo "[]"
|
|
;;
|
|
create)
|
|
id=""
|
|
title=""
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--id=*) id="${arg#--id=}" ;;
|
|
--title=*) title="${arg#--title=}" ;;
|
|
esac
|
|
done
|
|
# Log the created agent ID for verification
|
|
echo "$id" >> "$AGENT_LOG"
|
|
printf '{"id":"%s","title":"%s","description":"","issue_type":"agent"}' "$id" "$title"
|
|
;;
|
|
slot)
|
|
# Accept slot commands
|
|
;;
|
|
*)
|
|
echo "unexpected command: $cmd" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
`
|
|
|
|
binDir := writeFakeBD(t, script)
|
|
agentLog := filepath.Join(t.TempDir(), "agents.log")
|
|
beadsDirLog := filepath.Join(t.TempDir(), "beads-dir.log")
|
|
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
t.Setenv("AGENT_LOG", agentLog)
|
|
t.Setenv("BEADS_DIR_LOG", beadsDirLog)
|
|
t.Setenv("BEADS_DIR", "") // Clear any existing BEADS_DIR
|
|
|
|
manager := &Manager{townRoot: townRoot}
|
|
if err := manager.initAgentBeads(rigPath, "demo", "gt"); err != nil {
|
|
t.Fatalf("initAgentBeads: %v", err)
|
|
}
|
|
|
|
// Verify the expected rig-level agents were created
|
|
data, err := os.ReadFile(agentLog)
|
|
if err != nil {
|
|
t.Fatalf("reading agent log: %v", err)
|
|
}
|
|
createdAgents = strings.Split(strings.TrimSpace(string(data)), "\n")
|
|
|
|
// Should create witness and refinery for the rig
|
|
expectedAgents := map[string]bool{
|
|
"gt-demo-witness": false,
|
|
"gt-demo-refinery": false,
|
|
}
|
|
|
|
for _, id := range createdAgents {
|
|
if _, ok := expectedAgents[id]; ok {
|
|
expectedAgents[id] = true
|
|
}
|
|
}
|
|
|
|
for id, found := range expectedAgents {
|
|
if !found {
|
|
t.Errorf("expected agent %s was not created", id)
|
|
}
|
|
}
|
|
assertBeadsDirLog(t, beadsDirLog, rigBeadsDir)
|
|
}
|
|
|
|
func TestIsValidBeadsPrefix(t *testing.T) {
|
|
tests := []struct {
|
|
prefix string
|
|
want bool
|
|
}{
|
|
// Valid prefixes
|
|
{"gt", true},
|
|
{"bd", true},
|
|
{"hq", true},
|
|
{"gastown", true},
|
|
{"myProject", true},
|
|
{"my-project", true},
|
|
{"a", true},
|
|
{"A", true},
|
|
{"test123", true},
|
|
{"a1b2c3", true},
|
|
{"a-b-c", true},
|
|
|
|
// Invalid prefixes
|
|
{"", false}, // empty
|
|
{"1abc", false}, // starts with number
|
|
{"-abc", false}, // starts with hyphen
|
|
{"abc def", false}, // contains space
|
|
{"abc;ls", false}, // shell injection attempt
|
|
{"$(whoami)", false}, // command substitution
|
|
{"`id`", false}, // backtick command
|
|
{"abc|cat", false}, // pipe
|
|
{"../etc/passwd", false}, // path traversal
|
|
{"aaaaaaaaaaaaaaaaaaaaa", false}, // too long (21 chars, >20 limit)
|
|
{"valid-but-with-$var", false}, // variable reference
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.prefix, func(t *testing.T) {
|
|
got := isValidBeadsPrefix(tt.prefix)
|
|
if got != tt.want {
|
|
t.Errorf("isValidBeadsPrefix(%q) = %v, want %v", tt.prefix, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitBeadsRejectsInvalidPrefix(t *testing.T) {
|
|
rigPath := t.TempDir()
|
|
manager := &Manager{}
|
|
|
|
tests := []string{
|
|
"",
|
|
"$(whoami)",
|
|
"abc;rm -rf /",
|
|
"../etc",
|
|
"123",
|
|
}
|
|
|
|
for _, prefix := range tests {
|
|
t.Run(prefix, func(t *testing.T) {
|
|
err := manager.initBeads(rigPath, prefix)
|
|
if err == nil {
|
|
t.Errorf("initBeads(%q) should have failed", prefix)
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid beads prefix") {
|
|
t.Errorf("initBeads(%q) error = %q, want error containing 'invalid beads prefix'", prefix, err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeriveBeadsPrefix(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
want string
|
|
}{
|
|
// Compound words with common suffixes should split
|
|
{"gastown", "gt"}, // gas + town
|
|
{"nashville", "nv"}, // nash + ville
|
|
{"bridgeport", "bp"}, // bridge + port
|
|
{"someplace", "sp"}, // some + place
|
|
{"greenland", "gl"}, // green + land
|
|
{"springfield", "sf"}, // spring + field
|
|
{"hollywood", "hw"}, // holly + wood
|
|
{"oxford", "of"}, // ox + ford
|
|
|
|
// Hyphenated names
|
|
{"my-project", "mp"},
|
|
{"gas-town", "gt"},
|
|
{"some-long-name", "sln"},
|
|
|
|
// Underscored names
|
|
{"my_project", "mp"},
|
|
|
|
// Short single words (use the whole name)
|
|
{"foo", "foo"},
|
|
{"bar", "bar"},
|
|
{"ab", "ab"},
|
|
|
|
// Longer single words without known suffixes (first 2 chars)
|
|
{"myrig", "my"},
|
|
{"awesome", "aw"},
|
|
{"coolrig", "co"},
|
|
|
|
// With language suffixes stripped
|
|
{"myproject-py", "my"},
|
|
{"myproject-go", "my"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := deriveBeadsPrefix(tt.name)
|
|
if got != tt.want {
|
|
t.Errorf("deriveBeadsPrefix(%q) = %q, want %q", tt.name, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSplitCompoundWord(t *testing.T) {
|
|
tests := []struct {
|
|
word string
|
|
want []string
|
|
}{
|
|
// Known suffixes
|
|
{"gastown", []string{"gas", "town"}},
|
|
{"nashville", []string{"nash", "ville"}},
|
|
{"bridgeport", []string{"bridge", "port"}},
|
|
{"someplace", []string{"some", "place"}},
|
|
{"greenland", []string{"green", "land"}},
|
|
{"springfield", []string{"spring", "field"}},
|
|
{"hollywood", []string{"holly", "wood"}},
|
|
{"oxford", []string{"ox", "ford"}},
|
|
|
|
// Just the suffix (should not split)
|
|
{"town", []string{"town"}},
|
|
{"ville", []string{"ville"}},
|
|
|
|
// No known suffix
|
|
{"myrig", []string{"myrig"}},
|
|
{"awesome", []string{"awesome"}},
|
|
|
|
// Empty prefix would result (should not split)
|
|
// Note: "town" itself shouldn't split to ["", "town"]
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.word, func(t *testing.T) {
|
|
got := splitCompoundWord(tt.word)
|
|
if len(got) != len(tt.want) {
|
|
t.Errorf("splitCompoundWord(%q) = %v, want %v", tt.word, got, tt.want)
|
|
return
|
|
}
|
|
for i := range got {
|
|
if got[i] != tt.want[i] {
|
|
t.Errorf("splitCompoundWord(%q)[%d] = %q, want %q", tt.word, i, got[i], tt.want[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|