ajhahn.de
← eeco
Go 74 lines
package cockpit

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"
)

// loadAllSources reads every shipped playbook JSON source directly (no
// internal/playbooks import → no cycle), so the cockpit package can assert the
// safety invariant against the real library.
func loadAllSources(t *testing.T) []Playbook {
	t.Helper()
	dir := filepath.Join("..", "playbooks", "data")
	entries, err := os.ReadDir(dir)
	if err != nil {
		t.Fatalf("read playbook data dir: %v", err)
	}
	var out []Playbook
	for _, e := range entries {
		if e.IsDir() || filepath.Ext(e.Name()) != ".json" {
			continue
		}
		b, err := os.ReadFile(filepath.Join(dir, e.Name()))
		if err != nil {
			t.Fatalf("read %s: %v", e.Name(), err)
		}
		var pb Playbook
		if err := json.Unmarshal(b, &pb); err != nil {
			t.Fatalf("parse %s: %v", e.Name(), err)
		}
		out = append(out, pb)
	}
	if len(out) == 0 {
		t.Fatal("no playbook sources found")
	}
	return out
}

// TestAllSources_NoWriteGitVerb is the S1 trap-door test: every shipped
// playbook's composed allowlist must hold the safety invariant (zero
// write-git verbs), so a `git tag` slipping into a capability fails at test
// time, not at emit time.
func TestAllSources_NoWriteGitVerb(t *testing.T) {
	for _, pb := range loadAllSources(t) {
		if hits := ScanAllowlistForWriteGitVerbs(composeAllowedTools(pb), pb.Intent.forbiddenVerbs()); len(hits) > 0 {
			t.Errorf("playbook %q grants forbidden write-git verb(s): %v", pb.Name, hits)
		}
	}
}

// TestAllSources_RenderOnEveryTarget: every shipped playbook renders without
// error on the per-playbook targets, and the whole set renders on the
// aggregate targets.
func TestAllSources_RenderOnEveryTarget(t *testing.T) {
	set := loadAllSources(t)
	for _, target := range []string{"claude", "cursor"} {
		r, _ := rendererFor(target)
		for _, pb := range set {
			if _, err := r.Render(pb); err != nil {
				t.Errorf("%s render of %q: %v", target, pb.Name, err)
			}
		}
	}
	for _, target := range []string{"agents", "gemini"} {
		r, _ := rendererFor(target)
		agg, _ := isAggregate(r)
		if _, err := agg.RenderAll(set); err != nil {
			t.Errorf("%s RenderAll: %v", target, err)
		}
	}
}