Why: Downstream products must extend the base through a public package and host API instead of editing internal code. Evidence: go test ./... passed, including plugin v2 assets, sidecar startup, migration v13, pending confirms, HITL timeout, hash embedder, command pairs, and the extension surface snapshot. Impact: apiVersion 2.0-experimental rejects empty network allowlists for declarative HTTP. Hash embeddings stay opt-in via models.embedding.provider=hash. Plugin secrets resolve per owner at execution time. Refs: docs/proposals/2026-10-01-plugin-contract-v2.md Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
1.5 KiB
Go
70 lines
1.5 KiB
Go
package extension
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSurfaceSnapshot(t *testing.T) {
|
|
fset := token.NewFileSet()
|
|
pkgs, err := parser.ParseDir(fset, ".", func(info os.FileInfo) bool {
|
|
return !strings.HasSuffix(info.Name(), "_test.go")
|
|
}, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pkg := pkgs["extension"]
|
|
if pkg == nil {
|
|
t.Fatal("package extension not parsed")
|
|
}
|
|
var names []string
|
|
for _, f := range pkg.Files {
|
|
for _, d := range f.Decls {
|
|
switch n := d.(type) {
|
|
case *ast.FuncDecl:
|
|
if n.Recv == nil && n.Name.IsExported() {
|
|
names = append(names, n.Name.Name)
|
|
}
|
|
case *ast.GenDecl:
|
|
for _, spec := range n.Specs {
|
|
switch s := spec.(type) {
|
|
case *ast.TypeSpec:
|
|
if s.Name.IsExported() {
|
|
names = append(names, s.Name.Name)
|
|
}
|
|
case *ast.ValueSpec:
|
|
for _, name := range s.Names {
|
|
if name.IsExported() {
|
|
names = append(names, name.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(names)
|
|
got := strings.Join(names, "\n") + "\n"
|
|
path := filepath.Join("surface.txt")
|
|
want, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != string(want) {
|
|
t.Fatalf("public surface drifted; update surface.txt if the change is intentional\n got:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMainWithoutLink(t *testing.T) {
|
|
SetRunner(nil)
|
|
if err := Main(Options{}); err != ErrNotLinked {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|