test: add contract verification for php-devops wishlist

18 tests verify the full workspace contract is implemented:
- repos.yaml: loads, required fields, valid types, deps exist,
  topological order, foundations, defaults, meta clone rules, domains
- workspace.yaml: loads, active package in registry
- .core/ folder: exists, has spec doc
- Scripts: setup.sh exists+executable, install scripts exist
- Plugins: marketplace.json exists, all plugins have manifests

Enables archival of core/php-devops and core/go-agent.

Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-03-06 15:30:30 +00:00
parent e90a84eaa0
commit c7c181ccf8

View file

@ -0,0 +1,270 @@
// Package workspace verifies the workspace contract defined by the original
// php-devops wishlist is fully implemented. This test loads the real repos.yaml
// shipped with core/agent and validates every aspect of the specification.
package workspace
import (
"os"
"path/filepath"
"testing"
"forge.lthn.ai/core/go-io"
"forge.lthn.ai/core/go-scm/repos"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
// repoRoot returns the absolute path to the core/agent repo root.
func repoRoot(t *testing.T) string {
t.Helper()
// Walk up from this test file to find repos.yaml.
dir, err := os.Getwd()
require.NoError(t, err)
for {
if _, err := os.Stat(filepath.Join(dir, "repos.yaml")); err == nil {
return dir
}
parent := filepath.Dir(dir)
require.NotEqual(t, parent, dir, "repos.yaml not found")
dir = parent
}
}
// ── repos.yaml contract ────────────────────────────────────────────
func TestContract_ReposYAML_Loads(t *testing.T) {
root := repoRoot(t)
path := filepath.Join(root, "repos.yaml")
reg, err := repos.LoadRegistry(io.Local, path)
require.NoError(t, err)
require.NotNil(t, reg)
assert.Equal(t, 1, reg.Version, "repos.yaml must declare version: 1")
assert.NotEmpty(t, reg.Org, "repos.yaml must declare an org")
assert.NotEmpty(t, reg.BasePath, "repos.yaml must declare base_path")
}
func TestContract_ReposYAML_HasRequiredFields(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
require.NotEmpty(t, reg.Repos, "repos.yaml must define at least one repo")
for name, repo := range reg.Repos {
assert.NotEmpty(t, repo.Type, "%s: must have a type", name)
assert.NotEmpty(t, repo.Description, "%s: must have a description", name)
}
}
func TestContract_ReposYAML_ValidTypes(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
validTypes := map[string]bool{
"foundation": true,
"module": true,
"product": true,
"template": true,
"meta": true,
}
for name, repo := range reg.Repos {
assert.True(t, validTypes[repo.Type], "%s: invalid type %q", name, repo.Type)
}
}
func TestContract_ReposYAML_DependenciesExist(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
for name, repo := range reg.Repos {
for _, dep := range repo.DependsOn {
_, ok := reg.Get(dep)
assert.True(t, ok, "%s: depends on %q which is not in repos.yaml", name, dep)
}
}
}
func TestContract_ReposYAML_TopologicalOrder(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
order, err := reg.TopologicalOrder()
require.NoError(t, err, "dependency graph must be acyclic")
assert.Equal(t, len(reg.Repos), len(order), "topological order must include all repos")
// Verify ordering: every dependency appears before its dependant.
seen := map[string]bool{}
for _, repo := range order {
for _, dep := range repo.DependsOn {
assert.True(t, seen[dep], "%s appears before its dependency %s", repo.Name, dep)
}
seen[repo.Name] = true
}
}
func TestContract_ReposYAML_HasFoundation(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
foundations := reg.ByType("foundation")
assert.NotEmpty(t, foundations, "repos.yaml must have at least one foundation package")
}
func TestContract_ReposYAML_Defaults(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
assert.NotEmpty(t, reg.Defaults.Branch, "defaults must specify a branch")
assert.NotEmpty(t, reg.Defaults.License, "defaults must specify a licence")
}
func TestContract_ReposYAML_MetaDoesNotCloneSelf(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
for name, repo := range reg.Repos {
if repo.Type == "meta" && repo.Clone != nil && !*repo.Clone {
// Meta repos with clone: false are correct.
continue
}
if repo.Type == "meta" {
t.Logf("%s: meta repo should set clone: false", name)
}
}
}
func TestContract_ReposYAML_ProductsHaveDomain(t *testing.T) {
root := repoRoot(t)
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
for name, repo := range reg.Repos {
if repo.Type == "product" && repo.Domain != "" {
// Products with domains are properly configured.
assert.Contains(t, repo.Domain, ".", "%s: domain should be a valid hostname", name)
}
}
}
// ── workspace.yaml contract ────────────────────────────────────────
type workspaceConfig struct {
Version int `yaml:"version"`
Active string `yaml:"active"`
DefaultOnly []string `yaml:"default_only"`
PackagesDir string `yaml:"packages_dir"`
Settings map[string]any `yaml:"settings"`
}
func TestContract_WorkspaceYAML_Loads(t *testing.T) {
root := repoRoot(t)
path := filepath.Join(root, ".core", "workspace.yaml")
data, err := os.ReadFile(path)
require.NoError(t, err, ".core/workspace.yaml must exist")
var ws workspaceConfig
require.NoError(t, yaml.Unmarshal(data, &ws))
assert.Equal(t, 1, ws.Version, "workspace.yaml must declare version: 1")
assert.NotEmpty(t, ws.Active, "workspace.yaml must declare an active package")
assert.NotEmpty(t, ws.PackagesDir, "workspace.yaml must declare packages_dir")
}
func TestContract_WorkspaceYAML_ActiveInRegistry(t *testing.T) {
root := repoRoot(t)
// Load workspace config.
data, err := os.ReadFile(filepath.Join(root, ".core", "workspace.yaml"))
require.NoError(t, err)
var ws workspaceConfig
require.NoError(t, yaml.Unmarshal(data, &ws))
// Load repos registry.
reg, err := repos.LoadRegistry(io.Local, filepath.Join(root, "repos.yaml"))
require.NoError(t, err)
_, ok := reg.Get(ws.Active)
assert.True(t, ok, "workspace.yaml active package %q must exist in repos.yaml", ws.Active)
}
// ── .core/ folder spec contract ────────────────────────────────────
func TestContract_CoreFolder_Exists(t *testing.T) {
root := repoRoot(t)
info, err := os.Stat(filepath.Join(root, ".core"))
require.NoError(t, err, ".core/ directory must exist")
assert.True(t, info.IsDir())
}
func TestContract_CoreFolder_HasSpec(t *testing.T) {
root := repoRoot(t)
_, err := os.Stat(filepath.Join(root, ".core", "docs", "core-folder-spec.md"))
assert.NoError(t, err, ".core/docs/core-folder-spec.md must exist")
}
// ── Setup scripts contract ─────────────────────────────────────────
func TestContract_SetupScript_Exists(t *testing.T) {
root := repoRoot(t)
_, err := os.Stat(filepath.Join(root, "setup.sh"))
assert.NoError(t, err, "setup.sh must exist at repo root")
}
func TestContract_SetupScript_Executable(t *testing.T) {
root := repoRoot(t)
info, err := os.Stat(filepath.Join(root, "setup.sh"))
if err != nil {
t.Skip("setup.sh not found")
}
assert.NotZero(t, info.Mode()&0111, "setup.sh must be executable")
}
func TestContract_InstallScripts_Exist(t *testing.T) {
root := repoRoot(t)
scripts := []string{
"scripts/install-deps.sh",
"scripts/install-core.sh",
}
for _, s := range scripts {
_, err := os.Stat(filepath.Join(root, s))
assert.NoError(t, err, "%s must exist", s)
}
}
// ── Claude plugins contract ────────────────────────────────────────
func TestContract_Marketplace_Exists(t *testing.T) {
root := repoRoot(t)
_, err := os.Stat(filepath.Join(root, ".claude-plugin", "marketplace.json"))
assert.NoError(t, err, ".claude-plugin/marketplace.json must exist for plugin distribution")
}
func TestContract_Plugins_HaveManifests(t *testing.T) {
root := repoRoot(t)
pluginDir := filepath.Join(root, "claude")
entries, err := os.ReadDir(pluginDir)
if err != nil {
t.Skip("claude/ directory not found")
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
manifest := filepath.Join(pluginDir, entry.Name(), ".claude-plugin", "plugin.json")
_, err := os.Stat(manifest)
assert.NoError(t, err, "claude/%s must have .claude-plugin/plugin.json", entry.Name())
}
}