test: add ephemeral flag tests for spawnMolecule (bd-phin)

- Add TestSpawnMoleculeEphemeralFlag: verifies DB-based spawn
- Add TestSpawnMoleculeFromFormulaEphemeral: verifies formula-based spawn
- Both tests confirm Ephemeral flag is set correctly
- Tests also verify ephemeral issues excluded from ready work

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-27 14:48:37 -08:00
parent 26785f9b46
commit 3d927c8895

View File

@@ -2364,3 +2364,166 @@ func TestCalculateBlockingDepths(t *testing.T) {
t.Errorf("step3 depth = %d, want 3", depths["step3"])
}
}
// TestSpawnMoleculeEphemeralFlag verifies that spawnMolecule with ephemeral=true
// creates issues with the Ephemeral flag set (bd-phin)
func TestSpawnMoleculeEphemeralFlag(t *testing.T) {
ctx := context.Background()
dbPath := t.TempDir() + "/test.db"
s, err := sqlite.New(ctx, dbPath)
if err != nil {
t.Fatalf("Failed to create store: %v", err)
}
defer s.Close()
if err := s.SetConfig(ctx, "issue_prefix", "test"); err != nil {
t.Fatalf("Failed to set config: %v", err)
}
// Create a template with a child (IDs will be auto-generated)
root := &types.Issue{
Title: "Template Epic",
Status: types.StatusOpen,
Priority: 2,
IssueType: types.TypeEpic,
Labels: []string{MoleculeLabel}, // Required for loadTemplateSubgraph
}
child := &types.Issue{
Title: "Template Task",
Status: types.StatusOpen,
Priority: 2,
IssueType: types.TypeTask,
}
if err := s.CreateIssue(ctx, root, "test"); err != nil {
t.Fatalf("Failed to create template root: %v", err)
}
if err := s.CreateIssue(ctx, child, "test"); err != nil {
t.Fatalf("Failed to create template child: %v", err)
}
// Add parent-child dependency
if err := s.AddDependency(ctx, &types.Dependency{
IssueID: child.ID,
DependsOnID: root.ID,
Type: types.DepParentChild,
}, "test"); err != nil {
t.Fatalf("Failed to add parent-child dependency: %v", err)
}
// Load subgraph
subgraph, err := loadTemplateSubgraph(ctx, s, root.ID)
if err != nil {
t.Fatalf("Failed to load subgraph: %v", err)
}
// Spawn with ephemeral=true
result, err := spawnMolecule(ctx, s, subgraph, nil, "", "test", true, "eph")
if err != nil {
t.Fatalf("spawnMolecule failed: %v", err)
}
// Verify all spawned issues have Ephemeral=true
for oldID, newID := range result.IDMapping {
spawned, err := s.GetIssue(ctx, newID)
if err != nil {
t.Fatalf("Failed to get spawned issue %s: %v", newID, err)
}
if !spawned.Ephemeral {
t.Errorf("Spawned issue %s (from %s) should have Ephemeral=true, got false", newID, oldID)
}
}
// Verify spawned issues have the correct prefix
for _, newID := range result.IDMapping {
if !strings.HasPrefix(newID, "test-eph-") {
t.Errorf("Spawned issue ID %s should have prefix 'test-eph-'", newID)
}
}
}
// TestSpawnMoleculeFromFormulaEphemeral verifies that spawning from a cooked formula
// with ephemeral=true creates issues with the Ephemeral flag set (bd-phin)
func TestSpawnMoleculeFromFormulaEphemeral(t *testing.T) {
ctx := context.Background()
dbPath := t.TempDir() + "/test.db"
s, err := sqlite.New(ctx, dbPath)
if err != nil {
t.Fatalf("Failed to create store: %v", err)
}
defer s.Close()
if err := s.SetConfig(ctx, "issue_prefix", "test"); err != nil {
t.Fatalf("Failed to set config: %v", err)
}
// Create a minimal in-memory subgraph (simulating cookFormulaToSubgraph output)
root := &types.Issue{
ID: "test-formula",
Title: "Test Formula",
Status: types.StatusOpen,
Priority: 2,
IssueType: types.TypeEpic,
IsTemplate: true,
}
step := &types.Issue{
ID: "test-formula.step1",
Title: "Step 1",
Status: types.StatusOpen,
Priority: 2,
IssueType: types.TypeTask,
IsTemplate: true,
}
subgraph := &TemplateSubgraph{
Root: root,
Issues: []*types.Issue{root, step},
Dependencies: []*types.Dependency{
{
IssueID: step.ID,
DependsOnID: root.ID,
Type: types.DepParentChild,
},
},
IssueMap: map[string]*types.Issue{
root.ID: root,
step.ID: step,
},
}
// Spawn with ephemeral=true (simulating bd mol wisp <formula>)
result, err := spawnMolecule(ctx, s, subgraph, nil, "", "test", true, "eph")
if err != nil {
t.Fatalf("spawnMolecule failed: %v", err)
}
// Verify all spawned issues have Ephemeral=true
for oldID, newID := range result.IDMapping {
spawned, err := s.GetIssue(ctx, newID)
if err != nil {
t.Fatalf("Failed to get spawned issue %s: %v", newID, err)
}
if !spawned.Ephemeral {
t.Errorf("Spawned issue %s (from %s) should have Ephemeral=true, got false", newID, oldID)
}
t.Logf("Issue %s: Ephemeral=%v", newID, spawned.Ephemeral)
}
// Verify they have the correct prefix
for _, newID := range result.IDMapping {
if !strings.HasPrefix(newID, "test-eph-") {
t.Errorf("Spawned issue ID %s should have prefix 'test-eph-'", newID)
}
}
// Verify ephemeral issues are excluded from ready work
readyWork, err := s.GetReadyWork(ctx, types.WorkFilter{})
if err != nil {
t.Fatalf("GetReadyWork failed: %v", err)
}
for _, issue := range readyWork {
for _, spawnedID := range result.IDMapping {
if issue.ID == spawnedID {
t.Errorf("Ephemeral issue %s should not appear in ready work", spawnedID)
}
}
}
}