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) } }