152 lines
4.8 KiB
Go
152 lines
4.8 KiB
Go
package scenes
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestLoadResponseSchemaForScenes(t *testing.T) {
|
|
schema, err := loadResponseSchema()
|
|
if err != nil {
|
|
t.Fatalf("loadResponseSchema() error = %v, want nil", err)
|
|
}
|
|
if schema.Key != ResponseSchemaKey {
|
|
t.Fatalf("schema.Key = %q, want %q", schema.Key, ResponseSchemaKey)
|
|
}
|
|
if schema.ID != ResponseSchemaID {
|
|
t.Fatalf("schema.ID = %q, want %q", schema.ID, ResponseSchemaID)
|
|
}
|
|
if schema.Version != ResponseSchemaVersion {
|
|
t.Fatalf("schema.Version = %q, want %q", schema.Version, ResponseSchemaVersion)
|
|
}
|
|
if schema.Name != ResponseSchemaName {
|
|
t.Fatalf("schema.Name = %q, want %q", schema.Name, ResponseSchemaName)
|
|
}
|
|
if !strings.HasPrefix(schema.SHA256, "sha256:") {
|
|
t.Fatalf("schema.SHA256 = %q, want sha256 prefix", schema.SHA256)
|
|
}
|
|
if !json.Valid(schema.JSONSchema) {
|
|
t.Fatalf("schema.JSONSchema is invalid JSON: %s", schema.JSONSchema)
|
|
}
|
|
}
|
|
|
|
func TestResponseSchemaShapeUsesSourceUnitBoundaries(t *testing.T) {
|
|
schema, err := loadResponseSchema()
|
|
if err != nil {
|
|
t.Fatalf("loadResponseSchema() error = %v, want nil", err)
|
|
}
|
|
|
|
var decoded map[string]any
|
|
if err := json.Unmarshal(schema.JSONSchema, &decoded); err != nil {
|
|
t.Fatalf("Unmarshal() error = %v, want nil", err)
|
|
}
|
|
if decoded["$id"] != ResponseSchemaID {
|
|
t.Fatalf("$id = %#v, want %q", decoded["$id"], ResponseSchemaID)
|
|
}
|
|
if decoded["additionalProperties"] != false {
|
|
t.Fatalf("additionalProperties = %#v, want false", decoded["additionalProperties"])
|
|
}
|
|
|
|
properties := decoded["properties"].(map[string]any)
|
|
if _, ok := properties["artifact_type"]; ok {
|
|
t.Fatal("schema includes artifact_type, want only scene response fields")
|
|
}
|
|
if _, ok := properties["session_scope"]; ok {
|
|
t.Fatal("schema includes session_scope, want no session wrapper")
|
|
}
|
|
|
|
sceneProperties := properties["scenes"].(map[string]any)["items"].(map[string]any)["properties"].(map[string]any)
|
|
for _, field := range []string{"scene_id", "start_segment_id", "end_segment_id"} {
|
|
if _, ok := sceneProperties[field]; ok {
|
|
t.Fatalf("scene schema includes old field %q", field)
|
|
}
|
|
}
|
|
for _, field := range []string{"start_unit_id", "end_unit_id"} {
|
|
property := sceneProperties[field].(map[string]any)
|
|
if property["type"] != "integer" {
|
|
t.Fatalf("%s type = %#v, want integer", field, property["type"])
|
|
}
|
|
if property["minimum"] != float64(1) {
|
|
t.Fatalf("%s minimum = %#v, want 1", field, property["minimum"])
|
|
}
|
|
}
|
|
|
|
modeEnum := sceneProperties["primary_mode"].(map[string]any)["enum"].([]any)
|
|
if !sameStrings(modeEnum, []string{"Recap", "Discussion", "Combat", "Narrative"}) {
|
|
t.Fatalf("primary_mode enum = %#v, want Recap/Discussion/Combat/Narrative", modeEnum)
|
|
}
|
|
confidenceEnum := sceneProperties["boundary_confidence"].(map[string]any)["enum"].([]any)
|
|
if !sameStrings(confidenceEnum, []string{"High", "Medium", "Low"}) {
|
|
t.Fatalf("boundary_confidence enum = %#v, want High/Medium/Low", confidenceEnum)
|
|
}
|
|
|
|
boundaryCaveatItems := decoded["properties"].(map[string]any)["boundary_caveats"].(map[string]any)["items"].(map[string]any)
|
|
if boundaryCaveatItems["type"] != "string" {
|
|
t.Fatalf("boundary_caveats.items.type = %#v, want string", boundaryCaveatItems["type"])
|
|
}
|
|
if boundaryCaveatItems["minLength"] != float64(1) {
|
|
t.Fatalf("boundary_caveats.items.minLength = %#v, want 1", boundaryCaveatItems["minLength"])
|
|
}
|
|
}
|
|
|
|
func TestResponseStructAcceptsIntegerBoundaries(t *testing.T) {
|
|
raw := []byte(`{
|
|
"scenes": [
|
|
{
|
|
"start_unit_id": 1,
|
|
"end_unit_id": 3,
|
|
"short_title": "Ambush",
|
|
"primary_mode": "Combat",
|
|
"main_participants": ["Aria"],
|
|
"summary": "The party fights.",
|
|
"boundary_note": "Combat starts and resolves.",
|
|
"boundary_confidence": "High"
|
|
}
|
|
],
|
|
"boundary_caveats": []
|
|
}`)
|
|
|
|
var response chunkResponse
|
|
if err := json.Unmarshal(raw, &response); err != nil {
|
|
t.Fatalf("Unmarshal() error = %v, want nil", err)
|
|
}
|
|
if got := response.Scenes[0].StartUnitID.String(); got != "1" {
|
|
t.Fatalf("StartUnitID = %q, want 1", got)
|
|
}
|
|
if got := response.Scenes[0].EndUnitID.String(); got != "3" {
|
|
t.Fatalf("EndUnitID = %q, want 3", got)
|
|
}
|
|
}
|
|
|
|
func TestResponseSchemaJSONIsMutationSafe(t *testing.T) {
|
|
first, err := loadResponseSchema()
|
|
if err != nil {
|
|
t.Fatalf("loadResponseSchema() error = %v, want nil", err)
|
|
}
|
|
first.JSONSchema[0] = '['
|
|
|
|
second, err := loadResponseSchema()
|
|
if err != nil {
|
|
t.Fatalf("loadResponseSchema() error = %v, want nil", err)
|
|
}
|
|
if !json.Valid(second.JSONSchema) {
|
|
t.Fatalf("schema JSON was mutated: %s", second.JSONSchema)
|
|
}
|
|
if len(second.JSONSchema) > 0 && second.JSONSchema[0] == '[' {
|
|
t.Fatalf("schema JSON did not use defensive copy")
|
|
}
|
|
}
|
|
|
|
func sameStrings(got []any, want []string) bool {
|
|
if len(got) != len(want) {
|
|
return false
|
|
}
|
|
for i := range want {
|
|
if got[i] != want[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|