467 lines
7.7 KiB
Go
467 lines
7.7 KiB
Go
package singlerequesttemplate_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"iop/packages/go/singlerequesttemplate"
|
|
)
|
|
|
|
func TestValidatePlanTemplate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
tmpl string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "default plan template is valid",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "custom plan template with extra static text",
|
|
tmpl: `# Plan
|
|
|
|
Custom header notes.
|
|
|
|
## Goal
|
|
{{goal}}
|
|
|
|
## Steps
|
|
{{steps}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
|
|
Footer notes.
|
|
`,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty template",
|
|
tmpl: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "oversized template 8193 bytes",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate + strings.Repeat(" ", 8193-len(singlerequesttemplate.DefaultPlanTemplate)),
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing {{goal}}",
|
|
tmpl: `# Plan
|
|
|
|
## Goal
|
|
|
|
## Steps
|
|
{{steps}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "duplicate {{goal}}",
|
|
tmpl: `# Plan
|
|
|
|
## Goal
|
|
{{goal}} {{goal}}
|
|
|
|
## Steps
|
|
{{steps}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unknown token",
|
|
tmpl: `# Plan
|
|
|
|
## Goal
|
|
{{goal}} {{foo}}
|
|
|
|
## Steps
|
|
{{steps}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "wrong token order",
|
|
tmpl: `# Plan
|
|
|
|
## Goal
|
|
{{steps}}
|
|
|
|
## Steps
|
|
{{goal}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing required heading # Plan",
|
|
tmpl: `## Goal
|
|
{{goal}}
|
|
|
|
## Steps
|
|
{{steps}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := singlerequesttemplate.ValidatePlanTemplate(tt.tmpl)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ValidatePlanTemplate() err = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParsePlan(t *testing.T) {
|
|
validOutput := `# Plan
|
|
|
|
## Goal
|
|
Fix single-request template handling bug.
|
|
|
|
## Steps
|
|
- Inspect template file resolution.
|
|
- Verify template validation logic.
|
|
|
|
## Verification
|
|
- Run go test on singlerequesttemplate package.
|
|
`
|
|
|
|
tests := []struct {
|
|
name string
|
|
tmpl string
|
|
raw string
|
|
maxOutputBytes int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid plan output default template",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: validOutput,
|
|
maxOutputBytes: 1024,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "boundary steps = 2, verif = 1",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: validOutput,
|
|
maxOutputBytes: 1024,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "boundary steps = 6, verif = 3",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Implement feature end to end.
|
|
|
|
## Steps
|
|
- Step one
|
|
- Step two
|
|
- Step three
|
|
- Step four
|
|
- Step five
|
|
- Step six
|
|
|
|
## Verification
|
|
- Verify 1
|
|
- Verify 2
|
|
- Verify 3
|
|
`,
|
|
maxOutputBytes: 2048,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid step count = 1 (too few)",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Implement feature.
|
|
|
|
## Steps
|
|
- Step one
|
|
|
|
## Verification
|
|
- Verify 1
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid step count = 7 (too many)",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Implement feature.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
- Step 3
|
|
- Step 4
|
|
- Step 5
|
|
- Step 6
|
|
- Step 7
|
|
|
|
## Verification
|
|
- Verify 1
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid verif count = 0 (too few)",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Implement feature.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
|
|
## Verification
|
|
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid verif count = 4 (too many)",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Implement feature.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
|
|
## Verification
|
|
- Verify 1
|
|
- Verify 2
|
|
- Verify 3
|
|
- Verify 4
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "multiline goal",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
First line of goal.
|
|
Second line of goal.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
|
|
## Verification
|
|
- Verify 1
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "altered heading static text",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan Mismatch
|
|
|
|
## Goal
|
|
Fix bug.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
|
|
## Verification
|
|
- Verify 1
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unresolved token in output",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: `# Plan
|
|
|
|
## Goal
|
|
Fix {{goal}} bug.
|
|
|
|
## Steps
|
|
- Step 1
|
|
- Step 2
|
|
|
|
## Verification
|
|
- Verify 1
|
|
`,
|
|
maxOutputBytes: 1024,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "output exceeds maxOutputBytes",
|
|
tmpl: singlerequesttemplate.DefaultPlanTemplate,
|
|
raw: validOutput,
|
|
maxOutputBytes: 10,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := singlerequesttemplate.ParsePlan(tt.tmpl, tt.raw, tt.maxOutputBytes)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ParsePlan() err = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
if !tt.wantErr && string(got) != tt.raw {
|
|
t.Errorf("ParsePlan() got = %q, want %q", string(got), tt.raw)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateReviewTemplate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
tmpl string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "default review template is valid",
|
|
tmpl: singlerequesttemplate.DefaultReviewTemplate,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty template",
|
|
tmpl: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing PASS",
|
|
tmpl: `# Review
|
|
|
|
## Result
|
|
FAIL
|
|
|
|
## Checks
|
|
{{checks}}
|
|
|
|
## Verification
|
|
{{verification}}
|
|
|
|
## Summary
|
|
{{summary}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "wrong order",
|
|
tmpl: `# Review
|
|
|
|
## Result
|
|
PASS
|
|
|
|
## Verification
|
|
{{verification}}
|
|
|
|
## Checks
|
|
{{checks}}
|
|
|
|
## Summary
|
|
{{summary}}
|
|
`,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := singlerequesttemplate.ValidateReviewTemplate(tt.tmpl)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ValidateReviewTemplate() err = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRenderReview(t *testing.T) {
|
|
fields := singlerequesttemplate.ReviewFields{
|
|
Checks: "- Checked file permissions\n- Verified build pass",
|
|
Verification: "- Executed unit test suite",
|
|
Summary: "All requirements met successfully.",
|
|
}
|
|
|
|
got, err := singlerequesttemplate.RenderReview(singlerequesttemplate.DefaultReviewTemplate, fields, 1024)
|
|
if err != nil {
|
|
t.Fatalf("RenderReview() unexpected err = %v", err)
|
|
}
|
|
|
|
want := `# Review
|
|
|
|
## Result
|
|
PASS
|
|
|
|
## Checks
|
|
- Checked file permissions
|
|
- Verified build pass
|
|
|
|
## Verification
|
|
- Executed unit test suite
|
|
|
|
## Summary
|
|
All requirements met successfully.
|
|
`
|
|
if string(got) != want {
|
|
t.Errorf("RenderReview() got:\n%s\nwant:\n%s", string(got), want)
|
|
}
|
|
|
|
// Missing field test
|
|
badFields := fields
|
|
badFields.Summary = ""
|
|
_, err = singlerequesttemplate.RenderReview(singlerequesttemplate.DefaultReviewTemplate, badFields, 1024)
|
|
if err == nil {
|
|
t.Errorf("RenderReview() expected error for empty Summary, got nil")
|
|
}
|
|
}
|
|
|
|
func TestDigest(t *testing.T) {
|
|
d1 := singlerequesttemplate.Digest(singlerequesttemplate.DefaultPlanTemplate)
|
|
d2 := singlerequesttemplate.Digest(singlerequesttemplate.DefaultPlanTemplate)
|
|
d3 := singlerequesttemplate.Digest("other content")
|
|
|
|
if d1 == "" {
|
|
t.Errorf("Digest() returned empty string")
|
|
}
|
|
if d1 != d2 {
|
|
t.Errorf("Digest() not deterministic: %q != %q", d1, d2)
|
|
}
|
|
if d1 == d3 {
|
|
t.Errorf("Digest() collision: %q == %q", d1, d3)
|
|
}
|
|
}
|