Files
go-sip/contracts/v1_bundle_test.go
T

211 lines
6.7 KiB
Go

package contracts_test
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/santhosh-tekuri/jsonschema/v6"
)
const v1Dir = "upstream/v1"
const v1Base = "https://go-sip.local/contracts/v1/"
type offlineLoader struct{}
func (offlineLoader) Load(url string) (any, error) {
return nil, fmt.Errorf("schema is not in the immutable local bundle: %s", url)
}
func compileV1(t *testing.T, name string) *jsonschema.Schema {
t.Helper()
compiler := jsonschema.NewCompiler()
compiler.AssertFormat()
compiler.UseLoader(offlineLoader{})
paths, err := filepath.Glob(filepath.Join(v1Dir, "*.schema.json"))
if err != nil || len(paths) == 0 {
t.Fatalf("contract bundle missing: %v", err)
}
for _, path := range paths {
var schema any
if err := json.Unmarshal(readV1(t, path), &schema); err != nil {
t.Fatal(err)
}
if err := compiler.AddResource(v1Base+filepath.Base(path), schema); err != nil {
t.Fatal(err)
}
}
schema, err := compiler.Compile(v1Base + name)
if err != nil {
t.Fatal(err)
}
return schema
}
func readV1(t *testing.T, path string) []byte {
t.Helper()
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return raw
}
func objectV1(t *testing.T, path string) map[string]any {
t.Helper()
var value map[string]any
if err := json.Unmarshal(readV1(t, path), &value); err != nil {
t.Fatal(err)
}
return value
}
func TestV1BundleFixtures(t *testing.T) {
for _, name := range []string{"mq.schema.json", "event-payloads.schema.json", "dispatcher-config.schema.json"} {
t.Run(name, func(t *testing.T) { compileV1(t, name) })
}
var cases []struct {
File string `json:"file"`
Schema string `json:"schema"`
Valid bool `json:"valid"`
}
if err := json.Unmarshal(readV1(t, filepath.Join(v1Dir, "fixtures.json")), &cases); err != nil {
t.Fatal(err)
}
if len(cases) < 16 {
t.Fatal("missing bounded positive and negative message/config fixtures")
}
for _, tc := range cases {
t.Run(tc.File, func(t *testing.T) {
schema := compileV1(t, tc.Schema)
value := objectV1(t, filepath.Join(v1Dir, tc.File))
err := schema.Validate(value)
if (err == nil) != tc.Valid {
t.Fatalf("valid=%v, validation=%v", tc.Valid, err)
}
})
}
}
func TestV1ExecuteIdentityAndTenantConstraints(t *testing.T) {
schema := compileV1(t, "mq.schema.json")
for _, tc := range []struct {
name string
edit func(map[string]any)
}{
{"missing dispatcher", func(m map[string]any) { delete(m, "dispatcher_id") }},
{"non UUID dispatcher", func(m map[string]any) { m["dispatcher_id"] = "dispatcher-a" }},
{"uppercase dispatcher", func(m map[string]any) { m["dispatcher_id"] = strings.ToUpper(m["dispatcher_id"].(string)) }},
{"wildcard tenant", func(m map[string]any) { m["tenant_key"] = "tenant.#" }},
{"old version", func(m map[string]any) { m["schema_version"] = "1.0" }},
{"extra root", func(m map[string]any) { m["metadata"] = map[string]any{} }},
{"extra payload", func(m map[string]any) { m["payload"].(map[string]any)["mode"] = "full_ai" }},
} {
t.Run(tc.name, func(t *testing.T) {
value := objectV1(t, filepath.Join(v1Dir, "examples/call-execute.json"))
tc.edit(value)
if err := schema.Validate(value); err == nil {
t.Fatal("invalid envelope accepted")
}
})
}
}
func TestV1ResponseCorrelationAndResultBranches(t *testing.T) {
schema := compileV1(t, "mq.schema.json")
for _, tc := range []struct {
name string
edit func(map[string]any)
}{
{"missing correlation", func(m map[string]any) { delete(m, "correlation_id") }},
{"wrong result", func(m map[string]any) { m["status"] = "rejected"; m["reason_code"] = "ok" }},
{"response deadline", func(m map[string]any) { m["not_after"] = "2026-09-21T00:00:30Z" }},
{"missing command ID", func(m map[string]any) { delete(m["payload"].(map[string]any), "command_id") }},
{"extra result", func(m map[string]any) { m["payload"].(map[string]any)["raw"] = map[string]any{} }},
} {
t.Run(tc.name, func(t *testing.T) {
value := objectV1(t, filepath.Join(v1Dir, "examples/command-query-result.json"))
tc.edit(value)
if err := schema.Validate(value); err == nil {
t.Fatal("invalid query response accepted")
}
})
}
}
func TestV1UploadIsAFactNotASaaSHandshake(t *testing.T) {
schema := compileV1(t, "mq.schema.json")
for _, field := range []string{"oss_id", "verified", "token", "upload_url", "access_key_secret"} {
t.Run(field, func(t *testing.T) {
value := objectV1(t, filepath.Join(v1Dir, "examples/event-recording-uploaded.json"))
value["payload"].(map[string]any)[field] = "not-a-real-value"
if err := schema.Validate(value); err == nil {
t.Fatal("uploaded fact accepted a credential or SaaS processing claim")
}
})
}
for _, kind := range []string{"recording.upload-session.request", "recording.upload-session.result", "recording.complete", "recording.verified"} {
if strings.Contains(string(readV1(t, filepath.Join(v1Dir, "mq.schema.json"))), `"`+kind+`"`) {
t.Errorf("removed upload handshake is still in the published schema: %s", kind)
}
}
}
func TestV1DispatcherConfigDoesNotAcceptSecretsOrTTLOverride(t *testing.T) {
schema := compileV1(t, "dispatcher-config.schema.json")
for _, field := range []string{"access_key_id", "access_key_secret", "grant_ttl_seconds", "token"} {
t.Run(field, func(t *testing.T) {
value := objectV1(t, filepath.Join(v1Dir, "examples/dispatcher-config.json"))
value["oss"].(map[string]any)[field] = "not-a-real-value"
if err := schema.Validate(value); err == nil {
t.Fatal("unexpected secret/override field accepted")
}
})
}
}
func TestV1ManifestPinsEveryBundleFile(t *testing.T) {
var manifest struct {
Version string `json:"version"`
Source string `json:"source"`
Files map[string]string `json:"files"`
}
if err := json.Unmarshal(readV1(t, filepath.Join(v1Dir, "manifest.json")), &manifest); err != nil {
t.Fatal(err)
}
if manifest.Version != "v1" || manifest.Source != "project-development-v1" || len(manifest.Files) == 0 {
t.Fatal("invalid project-local provenance")
}
err := filepath.WalkDir(v1Dir, func(path string, entry os.DirEntry, err error) error {
if err != nil || entry.IsDir() {
return err
}
rel, err := filepath.Rel(v1Dir, path)
if err != nil || rel == "manifest.json" {
return err
}
actual := sha256.Sum256(readV1(t, path))
if manifest.Files[filepath.ToSlash(rel)] != hex.EncodeToString(actual[:]) {
t.Errorf("missing/incorrect manifest hash: %s", rel)
}
return nil
})
if err != nil {
t.Fatal(err)
}
for rel := range manifest.Files {
if !filepath.IsLocal(rel) {
t.Errorf("non-local manifest entry: %s", rel)
continue
}
if _, err := os.Stat(filepath.Join(v1Dir, rel)); err != nil {
t.Error(err)
}
}
}