test: add gate field parsing tests (bd-4bt1)
Add tests for parsing the Gate field in formula steps: - TestParse_GateField: JSON parsing with all gate fields - TestParse_GateFieldTOML: TOML parsing - TestParse_GateFieldMinimal: minimal gate with only type - TestParse_GateFieldWithAllTypes: all gate types (gh:run, gh:pr, timer, human, bead, mail) - TestParse_GateInChildStep: gate in nested child steps The Gate struct and Step.Gate field already existed. These tests verify TOML and JSON parsing works correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package formula
|
package formula
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1102,3 +1103,207 @@ func TestValidate_OnComplete_InChildren(t *testing.T) {
|
|||||||
t.Errorf("Validate failed for on_complete in child: %v", err)
|
t.Errorf("Validate failed for on_complete in child: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bd-4bt1: Tests for gate field parsing
|
||||||
|
|
||||||
|
func TestParse_GateField(t *testing.T) {
|
||||||
|
jsonData := `{
|
||||||
|
"formula": "mol-release",
|
||||||
|
"version": 1,
|
||||||
|
"type": "workflow",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"id": "run-tests",
|
||||||
|
"title": "Run CI tests",
|
||||||
|
"gate": {
|
||||||
|
"type": "gh:run",
|
||||||
|
"id": "ci-tests",
|
||||||
|
"timeout": "1h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"id": "deploy", "title": "Deploy to prod", "depends_on": ["run-tests"]}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
p := NewParser()
|
||||||
|
formula, err := p.Parse([]byte(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate parsed formula
|
||||||
|
if err := formula.Validate(); err != nil {
|
||||||
|
t.Errorf("Validate failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check gate field
|
||||||
|
gate := formula.Steps[0].Gate
|
||||||
|
if gate == nil {
|
||||||
|
t.Fatal("Steps[0].Gate is nil")
|
||||||
|
}
|
||||||
|
if gate.Type != "gh:run" {
|
||||||
|
t.Errorf("Gate.Type = %q, want 'gh:run'", gate.Type)
|
||||||
|
}
|
||||||
|
if gate.ID != "ci-tests" {
|
||||||
|
t.Errorf("Gate.ID = %q, want 'ci-tests'", gate.ID)
|
||||||
|
}
|
||||||
|
if gate.Timeout != "1h" {
|
||||||
|
t.Errorf("Gate.Timeout = %q, want '1h'", gate.Timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse_GateFieldTOML(t *testing.T) {
|
||||||
|
tomlData := `
|
||||||
|
formula = "mol-release"
|
||||||
|
version = 1
|
||||||
|
type = "workflow"
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "wait-for-approval"
|
||||||
|
title = "Wait for human approval"
|
||||||
|
[steps.gate]
|
||||||
|
type = "human"
|
||||||
|
timeout = "24h"
|
||||||
|
|
||||||
|
[[steps]]
|
||||||
|
id = "proceed"
|
||||||
|
title = "Proceed after approval"
|
||||||
|
depends_on = ["wait-for-approval"]
|
||||||
|
`
|
||||||
|
p := NewParser()
|
||||||
|
formula, err := p.ParseTOML([]byte(tomlData))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseTOML failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate parsed formula
|
||||||
|
if err := formula.Validate(); err != nil {
|
||||||
|
t.Errorf("Validate failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check gate field
|
||||||
|
gate := formula.Steps[0].Gate
|
||||||
|
if gate == nil {
|
||||||
|
t.Fatal("Steps[0].Gate is nil")
|
||||||
|
}
|
||||||
|
if gate.Type != "human" {
|
||||||
|
t.Errorf("Gate.Type = %q, want 'human'", gate.Type)
|
||||||
|
}
|
||||||
|
if gate.Timeout != "24h" {
|
||||||
|
t.Errorf("Gate.Timeout = %q, want '24h'", gate.Timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse_GateFieldMinimal(t *testing.T) {
|
||||||
|
// Test gate with only type (minimal valid gate)
|
||||||
|
jsonData := `{
|
||||||
|
"formula": "mol-timer",
|
||||||
|
"version": 1,
|
||||||
|
"type": "workflow",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"id": "wait",
|
||||||
|
"title": "Wait for timer",
|
||||||
|
"gate": {"type": "timer"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
p := NewParser()
|
||||||
|
formula, err := p.Parse([]byte(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gate := formula.Steps[0].Gate
|
||||||
|
if gate == nil {
|
||||||
|
t.Fatal("Steps[0].Gate is nil")
|
||||||
|
}
|
||||||
|
if gate.Type != "timer" {
|
||||||
|
t.Errorf("Gate.Type = %q, want 'timer'", gate.Type)
|
||||||
|
}
|
||||||
|
if gate.ID != "" {
|
||||||
|
t.Errorf("Gate.ID = %q, want empty", gate.ID)
|
||||||
|
}
|
||||||
|
if gate.Timeout != "" {
|
||||||
|
t.Errorf("Gate.Timeout = %q, want empty", gate.Timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse_GateFieldWithAllTypes(t *testing.T) {
|
||||||
|
// Test various gate types mentioned in the spec
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
gateType string
|
||||||
|
id string
|
||||||
|
}{
|
||||||
|
{"github_run", "gh:run", "test-workflow"},
|
||||||
|
{"github_pr", "gh:pr", "123"},
|
||||||
|
{"timer", "timer", ""},
|
||||||
|
{"human", "human", ""},
|
||||||
|
{"bead", "bead", "bd-xyz"},
|
||||||
|
{"mail", "mail", "from:witness"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
jsonData := fmt.Sprintf(`{
|
||||||
|
"formula": "mol-test",
|
||||||
|
"version": 1,
|
||||||
|
"type": "workflow",
|
||||||
|
"steps": [
|
||||||
|
{"id": "step1", "title": "Test step", "gate": {"type": "%s", "id": "%s"}}
|
||||||
|
]
|
||||||
|
}`, tt.gateType, tt.id)
|
||||||
|
|
||||||
|
p := NewParser()
|
||||||
|
formula, err := p.Parse([]byte(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gate := formula.Steps[0].Gate
|
||||||
|
if gate == nil {
|
||||||
|
t.Fatal("Gate is nil")
|
||||||
|
}
|
||||||
|
if gate.Type != tt.gateType {
|
||||||
|
t.Errorf("Gate.Type = %q, want %q", gate.Type, tt.gateType)
|
||||||
|
}
|
||||||
|
if gate.ID != tt.id {
|
||||||
|
t.Errorf("Gate.ID = %q, want %q", gate.ID, tt.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse_GateInChildStep(t *testing.T) {
|
||||||
|
jsonData := `{
|
||||||
|
"formula": "mol-nested",
|
||||||
|
"version": 1,
|
||||||
|
"type": "workflow",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"id": "epic",
|
||||||
|
"title": "Release Epic",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "child-gate",
|
||||||
|
"title": "Wait for CI",
|
||||||
|
"gate": {"type": "gh:run", "id": "ci", "timeout": "30m"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
p := NewParser()
|
||||||
|
formula, err := p.Parse([]byte(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
child := formula.Steps[0].Children[0]
|
||||||
|
if child.Gate == nil {
|
||||||
|
t.Fatal("Child gate is nil")
|
||||||
|
}
|
||||||
|
if child.Gate.Type != "gh:run" {
|
||||||
|
t.Errorf("Child Gate.Type = %q, want 'gh:run'", child.Gate.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user