Files
gnoma/internal/message/content_test.go
vikingowl 85c643fdca feat: add foundation types, streaming, and provider interface
internal/message/ — Content discriminated union, Message, Usage,
StopReason, Response. 22 tests.

internal/stream/ — Stream pull-based iterator interface, Event types,
Accumulator (assembles Response from events). 8 tests.

internal/provider/ — Provider interface, Request, ToolDefinition,
Registry with factory pattern, ProviderError with HTTP status
classification. errors.AsType[E] for Go 1.26. 13 tests.

43 tests total, all passing.
2026-04-03 10:57:54 +02:00

175 lines
4.2 KiB
Go

package message
import (
"encoding/json"
"testing"
)
func TestNewTextContent(t *testing.T) {
c := NewTextContent("hello world")
if c.Type != ContentText {
t.Errorf("Type = %v, want %v", c.Type, ContentText)
}
if c.Text != "hello world" {
t.Errorf("Text = %q, want %q", c.Text, "hello world")
}
if c.ToolCall != nil {
t.Error("ToolCall should be nil for text content")
}
if c.ToolResult != nil {
t.Error("ToolResult should be nil for text content")
}
if c.Thinking != nil {
t.Error("Thinking should be nil for text content")
}
}
func TestNewToolCallContent(t *testing.T) {
args := json.RawMessage(`{"command":"ls -la"}`)
tc := ToolCall{
ID: "tc_001",
Name: "bash",
Arguments: args,
}
c := NewToolCallContent(tc)
if c.Type != ContentToolCall {
t.Errorf("Type = %v, want %v", c.Type, ContentToolCall)
}
if c.ToolCall == nil {
t.Fatal("ToolCall should not be nil")
}
if c.ToolCall.ID != "tc_001" {
t.Errorf("ToolCall.ID = %q, want %q", c.ToolCall.ID, "tc_001")
}
if c.ToolCall.Name != "bash" {
t.Errorf("ToolCall.Name = %q, want %q", c.ToolCall.Name, "bash")
}
if string(c.ToolCall.Arguments) != `{"command":"ls -la"}` {
t.Errorf("ToolCall.Arguments = %s, want %s", c.ToolCall.Arguments, args)
}
if c.Text != "" {
t.Error("Text should be empty for tool call content")
}
}
func TestNewToolResultContent(t *testing.T) {
tr := ToolResult{
ToolCallID: "tc_001",
Content: "file1.go\nfile2.go",
IsError: false,
}
c := NewToolResultContent(tr)
if c.Type != ContentToolResult {
t.Errorf("Type = %v, want %v", c.Type, ContentToolResult)
}
if c.ToolResult == nil {
t.Fatal("ToolResult should not be nil")
}
if c.ToolResult.ToolCallID != "tc_001" {
t.Errorf("ToolResult.ToolCallID = %q, want %q", c.ToolResult.ToolCallID, "tc_001")
}
if c.ToolResult.IsError {
t.Error("ToolResult.IsError should be false")
}
}
func TestNewToolResultContent_Error(t *testing.T) {
tr := ToolResult{
ToolCallID: "tc_002",
Content: "permission denied",
IsError: true,
}
c := NewToolResultContent(tr)
if !c.ToolResult.IsError {
t.Error("ToolResult.IsError should be true")
}
if c.ToolResult.Content != "permission denied" {
t.Errorf("ToolResult.Content = %q, want %q", c.ToolResult.Content, "permission denied")
}
}
func TestNewThinkingContent(t *testing.T) {
th := Thinking{
Text: "Let me think about this...",
Signature: "sig_abc123",
}
c := NewThinkingContent(th)
if c.Type != ContentThinking {
t.Errorf("Type = %v, want %v", c.Type, ContentThinking)
}
if c.Thinking == nil {
t.Fatal("Thinking should not be nil")
}
if c.Thinking.Text != "Let me think about this..." {
t.Errorf("Thinking.Text = %q", c.Thinking.Text)
}
if c.Thinking.Signature != "sig_abc123" {
t.Errorf("Thinking.Signature = %q", c.Thinking.Signature)
}
if c.Thinking.Redacted {
t.Error("Thinking.Redacted should be false")
}
}
func TestNewRedactedThinkingContent(t *testing.T) {
th := Thinking{
Redacted: true,
}
c := NewThinkingContent(th)
if !c.Thinking.Redacted {
t.Error("Thinking.Redacted should be true")
}
}
func TestContentType_String(t *testing.T) {
tests := []struct {
ct ContentType
want string
}{
{ContentText, "text"},
{ContentToolCall, "tool_call"},
{ContentToolResult, "tool_result"},
{ContentThinking, "thinking"},
{ContentType(99), "unknown(99)"},
}
for _, tt := range tests {
if got := tt.ct.String(); got != tt.want {
t.Errorf("ContentType(%d).String() = %q, want %q", tt.ct, got, tt.want)
}
}
}
func TestToolCall_JSON_RoundTrip(t *testing.T) {
original := ToolCall{
ID: "tc_100",
Name: "fs.read",
Arguments: json.RawMessage(`{"path":"/tmp/test.go","offset":0}`),
}
data, err := json.Marshal(original)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
var decoded ToolCall
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if decoded.ID != original.ID {
t.Errorf("ID = %q, want %q", decoded.ID, original.ID)
}
if decoded.Name != original.Name {
t.Errorf("Name = %q, want %q", decoded.Name, original.Name)
}
if string(decoded.Arguments) != string(original.Arguments) {
t.Errorf("Arguments = %s, want %s", decoded.Arguments, original.Arguments)
}
}