Files
go-llm/v2/internal/schema/schema_test.go
Steve Dudenhoeffer a4cb4baab5 Add go-llm v2: redesigned API for simpler LLM abstraction
v2 is a new Go module (v2/) with a dramatically simpler API:
- Unified Message type (no more Input marker interface)
- Define[T] for ergonomic tool creation with standard context.Context
- Chat session with automatic tool-call loop (agent loop)
- Streaming via pull-based StreamReader
- MCP one-call connect (MCPStdioServer, MCPHTTPServer, MCPSSEServer)
- Middleware support (logging, retry, timeout, usage tracking)
- Decoupled JSON Schema (map[string]any, no provider coupling)
- Sample tools: WebSearch, Browser, Exec, ReadFile, WriteFile, HTTP
- Providers: OpenAI, Anthropic, Google (all with streaming)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 20:00:08 -05:00

182 lines
4.4 KiB
Go

package schema
import (
"encoding/json"
"testing"
)
type SimpleParams struct {
Name string `json:"name" description:"The name"`
Age int `json:"age" description:"The age"`
}
type OptionalParams struct {
Required string `json:"required" description:"A required field"`
Optional *string `json:"optional,omitempty" description:"An optional field"`
}
type EnumParams struct {
Color string `json:"color" description:"The color" enum:"red,green,blue"`
}
type NestedParams struct {
Inner SimpleParams `json:"inner" description:"Nested object"`
}
type ArrayParams struct {
Items []string `json:"items" description:"A list of items"`
}
type EmbeddedBase struct {
ID string `json:"id" description:"The ID"`
}
type EmbeddedParams struct {
EmbeddedBase
Name string `json:"name" description:"The name"`
}
func TestFromStruct_Simple(t *testing.T) {
s := FromStruct(SimpleParams{})
if s["type"] != "object" {
t.Errorf("expected type=object, got %v", s["type"])
}
props, ok := s["properties"].(map[string]any)
if !ok {
t.Fatal("expected properties to be map[string]any")
}
if len(props) != 2 {
t.Errorf("expected 2 properties, got %d", len(props))
}
nameSchema, ok := props["name"].(map[string]any)
if !ok {
t.Fatal("expected name property to be map[string]any")
}
if nameSchema["type"] != "string" {
t.Errorf("expected name type=string, got %v", nameSchema["type"])
}
if nameSchema["description"] != "The name" {
t.Errorf("expected name description='The name', got %v", nameSchema["description"])
}
ageSchema, ok := props["age"].(map[string]any)
if !ok {
t.Fatal("expected age property to be map[string]any")
}
if ageSchema["type"] != "integer" {
t.Errorf("expected age type=integer, got %v", ageSchema["type"])
}
required, ok := s["required"].([]string)
if !ok {
t.Fatal("expected required to be []string")
}
if len(required) != 2 {
t.Errorf("expected 2 required fields, got %d", len(required))
}
}
func TestFromStruct_Optional(t *testing.T) {
s := FromStruct(OptionalParams{})
required, ok := s["required"].([]string)
if !ok {
t.Fatal("expected required to be []string")
}
// Only "required" field should be required, not "optional"
if len(required) != 1 {
t.Errorf("expected 1 required field, got %d: %v", len(required), required)
}
if required[0] != "required" {
t.Errorf("expected required field 'required', got %v", required[0])
}
}
func TestFromStruct_Enum(t *testing.T) {
s := FromStruct(EnumParams{})
props := s["properties"].(map[string]any)
colorSchema := props["color"].(map[string]any)
if colorSchema["type"] != "string" {
t.Errorf("expected enum type=string, got %v", colorSchema["type"])
}
enums, ok := colorSchema["enum"].([]string)
if !ok {
t.Fatal("expected enum to be []string")
}
if len(enums) != 3 {
t.Errorf("expected 3 enum values, got %d", len(enums))
}
}
func TestFromStruct_Nested(t *testing.T) {
s := FromStruct(NestedParams{})
props := s["properties"].(map[string]any)
innerSchema := props["inner"].(map[string]any)
if innerSchema["type"] != "object" {
t.Errorf("expected nested type=object, got %v", innerSchema["type"])
}
innerProps := innerSchema["properties"].(map[string]any)
if len(innerProps) != 2 {
t.Errorf("expected 2 inner properties, got %d", len(innerProps))
}
}
func TestFromStruct_Array(t *testing.T) {
s := FromStruct(ArrayParams{})
props := s["properties"].(map[string]any)
itemsSchema := props["items"].(map[string]any)
if itemsSchema["type"] != "array" {
t.Errorf("expected array type=array, got %v", itemsSchema["type"])
}
items := itemsSchema["items"].(map[string]any)
if items["type"] != "string" {
t.Errorf("expected items type=string, got %v", items["type"])
}
}
func TestFromStruct_Embedded(t *testing.T) {
s := FromStruct(EmbeddedParams{})
props := s["properties"].(map[string]any)
// Should have both ID from embedded and Name
if len(props) != 2 {
t.Errorf("expected 2 properties (flattened), got %d", len(props))
}
if _, ok := props["id"]; !ok {
t.Error("expected 'id' property from embedded struct")
}
if _, ok := props["name"]; !ok {
t.Error("expected 'name' property")
}
}
func TestFromStruct_ValidJSON(t *testing.T) {
s := FromStruct(SimpleParams{})
data, err := json.Marshal(s)
if err != nil {
t.Fatalf("schema should be valid JSON: %v", err)
}
var parsed map[string]any
if err := json.Unmarshal(data, &parsed); err != nil {
t.Fatalf("schema should round-trip through JSON: %v", err)
}
}