242 lines
6.2 KiB
Go
242 lines
6.2 KiB
Go
package sandbox
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"go.starlark.net/starlark"
|
|
|
|
"git.gobha.me/xcaliber/chat-switchboard/providers"
|
|
)
|
|
|
|
// ─── ParseRequiresProvider ──────────────────
|
|
|
|
func TestParseRequiresProvider_Missing(t *testing.T) {
|
|
_, ok := ParseRequiresProvider(map[string]any{})
|
|
if ok {
|
|
t.Error("missing requires_provider should return false")
|
|
}
|
|
}
|
|
|
|
func TestParseRequiresProvider_BoolTrue(t *testing.T) {
|
|
cfg, ok := ParseRequiresProvider(map[string]any{"requires_provider": true})
|
|
if !ok {
|
|
t.Fatal("boolean true should return ok=true")
|
|
}
|
|
if cfg.ProviderConfigID != "" || cfg.DefaultModel != "" {
|
|
t.Errorf("boolean true should produce empty config, got %+v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestParseRequiresProvider_BoolFalse(t *testing.T) {
|
|
_, ok := ParseRequiresProvider(map[string]any{"requires_provider": false})
|
|
if ok {
|
|
t.Error("boolean false should return ok=false")
|
|
}
|
|
}
|
|
|
|
func TestParseRequiresProvider_ObjectFull(t *testing.T) {
|
|
cfg, ok := ParseRequiresProvider(map[string]any{
|
|
"requires_provider": map[string]any{
|
|
"provider_config_id": "cfg-uuid-123",
|
|
"model": "claude-3-haiku",
|
|
},
|
|
})
|
|
if !ok {
|
|
t.Fatal("object form should return ok=true")
|
|
}
|
|
if cfg.ProviderConfigID != "cfg-uuid-123" {
|
|
t.Errorf("provider_config_id = %q", cfg.ProviderConfigID)
|
|
}
|
|
if cfg.DefaultModel != "claude-3-haiku" {
|
|
t.Errorf("model = %q", cfg.DefaultModel)
|
|
}
|
|
}
|
|
|
|
func TestParseRequiresProvider_ObjectPartial(t *testing.T) {
|
|
cfg, ok := ParseRequiresProvider(map[string]any{
|
|
"requires_provider": map[string]any{
|
|
"model": "gpt-4o-mini",
|
|
},
|
|
})
|
|
if !ok {
|
|
t.Fatal("should return ok=true")
|
|
}
|
|
if cfg.ProviderConfigID != "" {
|
|
t.Errorf("provider_config_id should be empty, got %q", cfg.ProviderConfigID)
|
|
}
|
|
if cfg.DefaultModel != "gpt-4o-mini" {
|
|
t.Errorf("model = %q", cfg.DefaultModel)
|
|
}
|
|
}
|
|
|
|
func TestParseRequiresProvider_InvalidType(t *testing.T) {
|
|
_, ok := ParseRequiresProvider(map[string]any{"requires_provider": "yes"})
|
|
if ok {
|
|
t.Error("string value should return false")
|
|
}
|
|
}
|
|
|
|
// ─── starlarkToMessages ─────────────────────
|
|
|
|
func TestStarlarkToMessages_Valid(t *testing.T) {
|
|
list := starlark.NewList(nil)
|
|
|
|
msg1 := starlark.NewDict(2)
|
|
_ = msg1.SetKey(starlark.String("role"), starlark.String("system"))
|
|
_ = msg1.SetKey(starlark.String("content"), starlark.String("You are helpful."))
|
|
list.Append(msg1)
|
|
|
|
msg2 := starlark.NewDict(2)
|
|
_ = msg2.SetKey(starlark.String("role"), starlark.String("user"))
|
|
_ = msg2.SetKey(starlark.String("content"), starlark.String("Hello"))
|
|
list.Append(msg2)
|
|
|
|
msgs, err := starlarkToMessages(list)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(msgs) != 2 {
|
|
t.Fatalf("expected 2 messages, got %d", len(msgs))
|
|
}
|
|
if msgs[0].Role != "system" || msgs[0].Content != "You are helpful." {
|
|
t.Errorf("msg[0] = %+v", msgs[0])
|
|
}
|
|
if msgs[1].Role != "user" || msgs[1].Content != "Hello" {
|
|
t.Errorf("msg[1] = %+v", msgs[1])
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_Empty(t *testing.T) {
|
|
list := starlark.NewList(nil)
|
|
msgs, err := starlarkToMessages(list)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(msgs) != 0 {
|
|
t.Errorf("expected 0 messages, got %d", len(msgs))
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_Nil(t *testing.T) {
|
|
msgs, err := starlarkToMessages(nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if msgs != nil {
|
|
t.Errorf("expected nil, got %v", msgs)
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_MissingRole(t *testing.T) {
|
|
list := starlark.NewList(nil)
|
|
msg := starlark.NewDict(1)
|
|
_ = msg.SetKey(starlark.String("content"), starlark.String("hello"))
|
|
list.Append(msg)
|
|
|
|
_, err := starlarkToMessages(list)
|
|
if err == nil {
|
|
t.Error("expected error for missing role")
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_MissingContent(t *testing.T) {
|
|
list := starlark.NewList(nil)
|
|
msg := starlark.NewDict(1)
|
|
_ = msg.SetKey(starlark.String("role"), starlark.String("user"))
|
|
list.Append(msg)
|
|
|
|
_, err := starlarkToMessages(list)
|
|
if err == nil {
|
|
t.Error("expected error for missing content")
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_NonDictItem(t *testing.T) {
|
|
list := starlark.NewList([]starlark.Value{starlark.String("not a dict")})
|
|
|
|
_, err := starlarkToMessages(list)
|
|
if err == nil {
|
|
t.Error("expected error for non-dict item")
|
|
}
|
|
}
|
|
|
|
func TestStarlarkToMessages_NonStringRole(t *testing.T) {
|
|
list := starlark.NewList(nil)
|
|
msg := starlark.NewDict(2)
|
|
_ = msg.SetKey(starlark.String("role"), starlark.MakeInt(42))
|
|
_ = msg.SetKey(starlark.String("content"), starlark.String("hello"))
|
|
list.Append(msg)
|
|
|
|
_, err := starlarkToMessages(list)
|
|
if err == nil {
|
|
t.Error("expected error for non-string role")
|
|
}
|
|
}
|
|
|
|
// ─── completionToStarlark ───────────────────
|
|
|
|
func TestCompletionToStarlark(t *testing.T) {
|
|
resp := &providers.CompletionResponse{
|
|
Content: "Hello! How can I help?",
|
|
Model: "claude-3-haiku-20240307",
|
|
FinishReason: "stop",
|
|
InputTokens: 15,
|
|
OutputTokens: 8,
|
|
}
|
|
|
|
val, err := completionToStarlark(resp)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
d, ok := val.(*starlark.Dict)
|
|
if !ok {
|
|
t.Fatalf("expected dict, got %T", val)
|
|
}
|
|
|
|
// content
|
|
cv, found, _ := d.Get(starlark.String("content"))
|
|
if !found {
|
|
t.Fatal("missing 'content'")
|
|
}
|
|
if s, _ := starlark.AsString(cv); s != "Hello! How can I help?" {
|
|
t.Errorf("content = %q", s)
|
|
}
|
|
|
|
// model
|
|
mv, found, _ := d.Get(starlark.String("model"))
|
|
if !found {
|
|
t.Fatal("missing 'model'")
|
|
}
|
|
if s, _ := starlark.AsString(mv); s != "claude-3-haiku-20240307" {
|
|
t.Errorf("model = %q", s)
|
|
}
|
|
|
|
// finish_reason
|
|
fv, found, _ := d.Get(starlark.String("finish_reason"))
|
|
if !found {
|
|
t.Fatal("missing 'finish_reason'")
|
|
}
|
|
if s, _ := starlark.AsString(fv); s != "stop" {
|
|
t.Errorf("finish_reason = %q", s)
|
|
}
|
|
|
|
// input_tokens
|
|
iv, found, _ := d.Get(starlark.String("input_tokens"))
|
|
if !found {
|
|
t.Fatal("missing 'input_tokens'")
|
|
}
|
|
if i, err := starlark.AsInt32(iv); err != nil || i != 15 {
|
|
t.Errorf("input_tokens = %v (err=%v)", i, err)
|
|
}
|
|
|
|
// output_tokens
|
|
ov, found, _ := d.Get(starlark.String("output_tokens"))
|
|
if !found {
|
|
t.Fatal("missing 'output_tokens'")
|
|
}
|
|
if i, err := starlark.AsInt32(ov); err != nil || i != 8 {
|
|
t.Errorf("output_tokens = %v (err=%v)", i, err)
|
|
}
|
|
}
|