This commit is contained in:
2026-08-17 21:20:45 +02:00
parent 321f11e817
commit 6fcdc74843
157 changed files with 2895 additions and 7544 deletions
+160 -6
View File
@@ -415,25 +415,25 @@ func TestV1OpenAPIYAMLProcessDualIDsAndGatesDocumentsContracts(t *testing.T) {
}
}
// TestV1OpenAPIYAMLLegacyProcessItemEprelShape locks LegacyProcessItem.eprel
// TestV1OpenAPIYAMLProcessItemEprelShape locks ProcessItem.eprel
// to the live nested object from eprel.MergeInto / extractEPRELFromAttrs.
func TestV1OpenAPIYAMLLegacyProcessItemEprelShape(t *testing.T) {
func TestV1OpenAPIYAMLProcessItemEprelShape(t *testing.T) {
t.Parallel()
root := mustParseOpenAPIRoot(t, v1OpenAPIYAML)
comps, _ := root["components"].(map[string]any)
schemas, _ := comps["schemas"].(map[string]any)
item, _ := schemas["LegacyProcessItem"].(map[string]any)
item, _ := schemas["ProcessItem"].(map[string]any)
itemProps, _ := item["properties"].(map[string]any)
eprel, _ := itemProps["eprel"].(map[string]any)
eprelProps, _ := eprel["properties"].(map[string]any)
if eprelProps == nil {
t.Fatal("LegacyProcessItem.eprel.properties missing")
t.Fatal("ProcessItem.eprel.properties missing")
}
want := []string{"id", "label", "pdf", "energy_class", "energy_scale"}
for _, key := range want {
if _, ok := eprelProps[key]; !ok {
t.Fatalf("LegacyProcessItem.eprel missing property %q (live shape)", key)
t.Fatalf("ProcessItem.eprel missing property %q (live shape)", key)
}
}
if len(eprelProps) != len(want) {
@@ -441,7 +441,161 @@ func TestV1OpenAPIYAMLLegacyProcessItemEprelShape(t *testing.T) {
for k := range eprelProps {
keys = append(keys, k)
}
t.Fatalf("LegacyProcessItem.eprel properties = %v, want exactly %v", keys, want)
t.Fatalf("ProcessItem.eprel properties = %v, want exactly %v", keys, want)
}
}
func TestV1OpenAPIYAMLNoLegacyWording(t *testing.T) {
t.Parallel()
assertOpenAPIYAMLOmitsToken(t, "legacy")
}
func TestV1OpenAPIYAMLNoA1Wording(t *testing.T) {
t.Parallel()
// Case-sensitive: lowercase "a1" appears in UUIDs and must stay allowed.
assertOpenAPIYAMLOmitsToken(t, "A1")
}
func TestV1OpenAPIYAMLOmitsInternalPromptWording(t *testing.T) {
t.Parallel()
for _, token := range []string{
"role-section prompts",
"plain description, meta",
"after AI/manual edit",
} {
assertOpenAPIYAMLOmitsToken(t, token)
}
}
func TestV1OpenAPIYAMLProcessItemCompletedExamplesOmitLeakyShape(t *testing.T) {
t.Parallel()
root := mustParseOpenAPIRoot(t, v1OpenAPIYAML)
leaky := []string{"id", "title", "meta_title", "meta_description"}
assertProcessItems := func(where string, items []any) {
t.Helper()
if len(items) == 0 {
t.Fatalf("%s: expected completed ProcessItem example", where)
}
for i, raw := range items {
item, _ := raw.(map[string]any)
if item == nil {
t.Fatalf("%s[%d]: not an object", where, i)
}
if _, ok := item["ean"]; !ok {
t.Fatalf("%s[%d]: missing ean", where, i)
}
for _, k := range leaky {
if _, ok := item[k]; ok {
t.Fatalf("%s[%d] must omit %q (do not teach leaky ProcessItem shape)", where, i, k)
}
}
}
}
comps, _ := root["components"].(map[string]any)
examples, _ := comps["examples"].(map[string]any)
completed, _ := examples["ProcessCompletedExample"].(map[string]any)
val, _ := completed["value"].(map[string]any)
data, _ := val["data"].(map[string]any)
items, _ := data["items"].([]any)
assertProcessItems("ProcessCompletedExample", items)
paths, _ := root["paths"].(map[string]any)
productsProcess := openAPIJSONExamples(t, paths, "/products/process/{id}", "get", "200")
prodCompleted, _ := productsProcess["completed"].(map[string]any)
prodVal, _ := prodCompleted["value"].(map[string]any)
prodData, _ := prodVal["data"].(map[string]any)
prodItems, _ := prodData["items"].([]any)
assertProcessItems("GET /products/process/{id} completed", prodItems)
legacyProcess := openAPIJSONExamples(t, paths, "/process/{id}", "get", "200")
legacyCompleted, _ := legacyProcess["completed"].(map[string]any)
legacyVal, _ := legacyCompleted["value"].(map[string]any)
legacyItems, _ := legacyVal["items"].([]any)
assertProcessItems("GET /process/{id} completed", legacyItems)
}
func openAPIJSONExamples(t *testing.T, paths map[string]any, path, method, status string) map[string]any {
t.Helper()
p, _ := paths[path].(map[string]any)
op, _ := p[method].(map[string]any)
resps, _ := op["responses"].(map[string]any)
resp, _ := resps[status].(map[string]any)
content, _ := resp["content"].(map[string]any)
appJSON, _ := content["application/json"].(map[string]any)
examples, _ := appJSON["examples"].(map[string]any)
if examples == nil {
t.Fatalf("%s %s %s: missing application/json examples", method, path, status)
}
return examples
}
func assertOpenAPIYAMLOmitsToken(t *testing.T, token string) {
t.Helper()
doc := string(v1OpenAPIYAML)
if token == "legacy" {
doc = strings.ToLower(doc)
}
if i := strings.Index(doc, token); i >= 0 {
start := i - 40
if start < 0 {
start = 0
}
end := i + 40
if end > len(doc) {
end = len(doc)
}
t.Fatalf("public OpenAPI YAML must not mention %q (near %q)", token, doc[start:end])
}
}
func TestV1OpenAPIYAMLEmptySecurityOnlyOnPublicProbes(t *testing.T) {
t.Parallel()
root := mustParseOpenAPIRoot(t, v1OpenAPIYAML)
sec, _ := root["security"].([]any)
if len(sec) == 0 {
t.Fatal("document-level security must require API key schemes")
}
var hasBearer, hasAPIKey bool
for _, item := range sec {
m, _ := item.(map[string]any)
if _, ok := m["BearerAuth"]; ok {
hasBearer = true
}
if _, ok := m["ApiKeyAuth"]; ok {
hasAPIKey = true
}
}
if !hasBearer || !hasAPIKey {
t.Fatal("document-level security must include BearerAuth and ApiKeyAuth")
}
paths, _ := root["paths"].(map[string]any)
methods := []string{"get", "post", "put", "patch", "delete"}
var bad []string
for p, raw := range paths {
item, _ := raw.(map[string]any)
for _, m := range methods {
op, _ := item[m].(map[string]any)
if op == nil {
continue
}
rawSec, ok := op["security"]
if !ok {
continue
}
arr, _ := rawSec.([]any)
if len(arr) != 0 {
continue
}
if p != "/health" && p != "/openapi.yaml" {
bad = append(bad, m+" "+p)
}
}
}
if len(bad) > 0 {
t.Fatalf("empty security (no API key) on non-probe operations: %v", bad)
}
}