Move D&D spell assets into extractor module
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "notarius.dnd.spells",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["spell_casts"],
|
||||
"properties": {
|
||||
"spell_casts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"caster",
|
||||
"spell",
|
||||
"effect",
|
||||
"narrative_description",
|
||||
"source_refs"
|
||||
],
|
||||
"properties": {
|
||||
"caster": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"spell": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"narrative_description": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"source_refs": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["source_id", "start_unit_id", "end_unit_id"],
|
||||
"properties": {
|
||||
"source_id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"start_unit_id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"end_unit_id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@@ -17,13 +18,21 @@ var schemaAssets embed.FS
|
||||
type ResponseSchemaKey string
|
||||
|
||||
const (
|
||||
DNDSpellsSchemaKey ResponseSchemaKey = "dnd_spells"
|
||||
TestArtifactSchemaKey ResponseSchemaKey = "test_artifact"
|
||||
TestValidatorDecisionSchemaKey ResponseSchemaKey = "test_validator_decision"
|
||||
|
||||
schemaVersionV1 = "v1"
|
||||
)
|
||||
|
||||
// ResponseSchemaDefinition identifies a caller-owned structured response schema asset.
|
||||
type ResponseSchemaDefinition struct {
|
||||
Key ResponseSchemaKey
|
||||
ID string
|
||||
Version string
|
||||
Name string
|
||||
AssetPath string
|
||||
}
|
||||
|
||||
// ResponseSchema describes one registered structured response schema.
|
||||
type ResponseSchema struct {
|
||||
Key ResponseSchemaKey `json:"key"`
|
||||
@@ -35,27 +44,20 @@ type ResponseSchema struct {
|
||||
}
|
||||
|
||||
var responseSchemaRegistry = map[ResponseSchemaKey]ResponseSchema{
|
||||
DNDSpellsSchemaKey: mustLoadResponseSchema(
|
||||
DNDSpellsSchemaKey,
|
||||
"notarius.dnd.spells",
|
||||
schemaVersionV1,
|
||||
"notarius_dnd_spells_v1",
|
||||
"assets/schemas/dnd_spells.v1.json",
|
||||
),
|
||||
TestArtifactSchemaKey: mustLoadResponseSchema(
|
||||
TestArtifactSchemaKey,
|
||||
"notarius.test_artifact",
|
||||
schemaVersionV1,
|
||||
"notarius_test_artifact_v1",
|
||||
"assets/schemas/test_artifact.v1.json",
|
||||
),
|
||||
TestValidatorDecisionSchemaKey: mustLoadResponseSchema(
|
||||
TestValidatorDecisionSchemaKey,
|
||||
"notarius.test_validator_decision",
|
||||
schemaVersionV1,
|
||||
"notarius_test_validator_decision_v1",
|
||||
"assets/schemas/test_validator_decision.v1.json",
|
||||
),
|
||||
TestArtifactSchemaKey: mustLoadResponseSchema(schemaAssets, ResponseSchemaDefinition{
|
||||
Key: TestArtifactSchemaKey,
|
||||
ID: "notarius.test_artifact",
|
||||
Version: schemaVersionV1,
|
||||
Name: "notarius_test_artifact_v1",
|
||||
AssetPath: "assets/schemas/test_artifact.v1.json",
|
||||
}),
|
||||
TestValidatorDecisionSchemaKey: mustLoadResponseSchema(schemaAssets, ResponseSchemaDefinition{
|
||||
Key: TestValidatorDecisionSchemaKey,
|
||||
ID: "notarius.test_validator_decision",
|
||||
Version: schemaVersionV1,
|
||||
Name: "notarius_test_validator_decision_v1",
|
||||
AssetPath: "assets/schemas/test_validator_decision.v1.json",
|
||||
}),
|
||||
}
|
||||
|
||||
// RegisteredResponseSchemas returns all registered response schemas sorted by key.
|
||||
@@ -102,40 +104,35 @@ func (s ResponseSchema) DiagnosticsMap() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func mustLoadResponseSchema(
|
||||
key ResponseSchemaKey,
|
||||
id string,
|
||||
version string,
|
||||
name string,
|
||||
path string,
|
||||
) ResponseSchema {
|
||||
key = ResponseSchemaKey(strings.TrimSpace(string(key)))
|
||||
id = strings.TrimSpace(id)
|
||||
version = strings.TrimSpace(version)
|
||||
name = strings.TrimSpace(name)
|
||||
path = strings.TrimSpace(path)
|
||||
// LoadResponseSchema loads a structured response schema from a caller-owned filesystem.
|
||||
func LoadResponseSchema(fsys fs.FS, def ResponseSchemaDefinition) (ResponseSchema, error) {
|
||||
key := ResponseSchemaKey(strings.TrimSpace(string(def.Key)))
|
||||
id := strings.TrimSpace(def.ID)
|
||||
version := strings.TrimSpace(def.Version)
|
||||
name := strings.TrimSpace(def.Name)
|
||||
path := strings.TrimSpace(def.AssetPath)
|
||||
if key == "" {
|
||||
panic("response schema key must not be empty")
|
||||
return ResponseSchema{}, fmt.Errorf("response schema key must not be empty")
|
||||
}
|
||||
if id == "" {
|
||||
panic("response schema id must not be empty")
|
||||
return ResponseSchema{}, fmt.Errorf("response schema id must not be empty")
|
||||
}
|
||||
if version == "" {
|
||||
panic("response schema version must not be empty")
|
||||
return ResponseSchema{}, fmt.Errorf("response schema version must not be empty")
|
||||
}
|
||||
if name == "" {
|
||||
panic("response schema name must not be empty")
|
||||
return ResponseSchema{}, fmt.Errorf("response schema name must not be empty")
|
||||
}
|
||||
if path == "" {
|
||||
panic("response schema asset path must not be empty")
|
||||
return ResponseSchema{}, fmt.Errorf("response schema asset path must not be empty")
|
||||
}
|
||||
|
||||
rawSchema, err := schemaAssets.ReadFile(path)
|
||||
rawSchema, err := fs.ReadFile(fsys, path)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("read response schema %s: %v", path, err))
|
||||
return ResponseSchema{}, fmt.Errorf("read response schema %s: %w", path, err)
|
||||
}
|
||||
if !json.Valid(rawSchema) {
|
||||
panic(fmt.Sprintf("response schema %s is not valid JSON", path))
|
||||
return ResponseSchema{}, fmt.Errorf("response schema %s is not valid JSON", path)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(rawSchema)
|
||||
@@ -146,7 +143,15 @@ func mustLoadResponseSchema(
|
||||
Name: name,
|
||||
JSONSchema: append(json.RawMessage(nil), rawSchema...),
|
||||
SHA256: "sha256:" + hex.EncodeToString(hash[:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mustLoadResponseSchema(fsys fs.FS, def ResponseSchemaDefinition) ResponseSchema {
|
||||
schema, err := LoadResponseSchema(fsys, def)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func cloneResponseSchema(in ResponseSchema) ResponseSchema {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
func TestLookupResponseSchemaSucceedsForRegisteredSchemas(t *testing.T) {
|
||||
tests := []ResponseSchemaKey{
|
||||
DNDSpellsSchemaKey,
|
||||
TestArtifactSchemaKey,
|
||||
TestValidatorDecisionSchemaKey,
|
||||
}
|
||||
@@ -39,6 +38,12 @@ func TestLookupResponseSchemaUnknownReturnsFalse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupResponseSchemaDNDSpellsIsNotFrameworkRegistered(t *testing.T) {
|
||||
if schema, ok := LookupResponseSchema("dnd_spells"); ok {
|
||||
t.Fatalf("expected D&D spells schema lookup to fail in framework registry, got %+v", schema)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustLookupResponseSchemaPanicsForUnknownKey(t *testing.T) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
@@ -51,8 +56,8 @@ func TestMustLookupResponseSchemaPanicsForUnknownKey(t *testing.T) {
|
||||
|
||||
func TestRegisteredResponseSchemasSortedByKey(t *testing.T) {
|
||||
schemas := RegisteredResponseSchemas()
|
||||
if len(schemas) != 3 {
|
||||
t.Fatalf("expected three schemas, got %d", len(schemas))
|
||||
if len(schemas) != 2 {
|
||||
t.Fatalf("expected two schemas, got %d", len(schemas))
|
||||
}
|
||||
|
||||
keys := make([]string, len(schemas))
|
||||
@@ -64,8 +69,8 @@ func TestRegisteredResponseSchemasSortedByKey(t *testing.T) {
|
||||
if !sort.StringsAreSorted(keys) {
|
||||
t.Fatalf("expected sorted keys, got %v", keys)
|
||||
}
|
||||
if !seen[DNDSpellsSchemaKey] {
|
||||
t.Fatalf("registered schemas = %v, want %q", keys, DNDSpellsSchemaKey)
|
||||
if !seen[TestArtifactSchemaKey] || !seen[TestValidatorDecisionSchemaKey] {
|
||||
t.Fatalf("registered schemas = %v, want test schemas", keys)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +83,7 @@ func TestResponseSchemaContentIsValidJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
|
||||
for _, key := range []ResponseSchemaKey{DNDSpellsSchemaKey, TestArtifactSchemaKey} {
|
||||
for _, key := range []ResponseSchemaKey{TestArtifactSchemaKey, TestValidatorDecisionSchemaKey} {
|
||||
t.Run(string(key), func(t *testing.T) {
|
||||
first := MustLookupResponseSchema(key)
|
||||
first.JSONSchema[0] = '['
|
||||
|
||||
Reference in New Issue
Block a user