package conversion import ( "encoding/json" "errors" "testing" "nex/backend/internal/conversion/canonical" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConversionError_WithProviderProtocol(t *testing.T) { err := NewConversionError(ErrorCodeInvalidInput, "test").WithProviderProtocol("anthropic") assert.Equal(t, "anthropic", err.ProviderProtocol) } func TestConversionError_WithInterfaceType(t *testing.T) { err := NewConversionError(ErrorCodeInvalidInput, "test").WithInterfaceType("CHAT") assert.Equal(t, "CHAT", err.InterfaceType) } func TestConversionError_FullBuilder(t *testing.T) { err := NewConversionError(ErrorCodeInvalidInput, "bad"). WithClientProtocol("openai"). WithProviderProtocol("anthropic"). WithInterfaceType("CHAT"). WithDetail("field", "model"). WithCause(errors.New("root")) assert.Equal(t, ErrorCodeInvalidInput, err.Code) assert.Equal(t, "openai", err.ClientProtocol) assert.Equal(t, "anthropic", err.ProviderProtocol) assert.Equal(t, "CHAT", err.InterfaceType) assert.Equal(t, "model", err.Details["field"]) assert.Equal(t, "root", err.Cause.Error()) } func TestEngine_Use(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) called := false engine.Use(&testMiddleware{fn: func(req *canonical.CanonicalRequest, cp, pp string, ctx *ConversionContext) (*canonical.CanonicalRequest, error) { called = true return req, nil }}) clientAdapter := newMockAdapter("client", false) clientAdapter.decodeReqFn = func(raw []byte) (*canonical.CanonicalRequest, error) { return &canonical.CanonicalRequest{Model: "test"}, nil } providerAdapter := newMockAdapter("provider", false) providerAdapter.encodeReqFn = func(req *canonical.CanonicalRequest, p *TargetProvider) ([]byte, error) { return json.Marshal(req) } _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) _, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/chat/completions", Method: "POST", Body: []byte(`{}`), }, "client", "provider", NewTargetProvider("https://example.com", "key", "model")) require.NoError(t, err) assert.True(t, called) } func TestConvertHttpRequest_DecodeError(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.decodeReqFn = func(raw []byte) (*canonical.CanonicalRequest, error) { return nil, errors.New("decode failed") } _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(newMockAdapter("provider", false)) _, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/chat/completions", Method: "POST", Body: []byte(`{}`), }, "client", "provider", NewTargetProvider("", "", "")) assert.Error(t, err) } func TestConvertHttpRequest_EncodeError(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) _ = engine.RegisterAdapter(newMockAdapter("client", false)) providerAdapter := newMockAdapter("provider", false) providerAdapter.encodeReqFn = func(req *canonical.CanonicalRequest, p *TargetProvider) ([]byte, error) { return nil, errors.New("encode failed") } _ = engine.RegisterAdapter(providerAdapter) _, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/chat/completions", Method: "POST", Body: []byte(`{}`), }, "client", "provider", NewTargetProvider("", "", "")) assert.Error(t, err) } func TestConvertHttpResponse_CrossProtocol(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.encodeRespFn = func(resp *canonical.CanonicalResponse) ([]byte, error) { return json.Marshal(map[string]string{"id": resp.ID}) } providerAdapter := newMockAdapter("provider", false) providerAdapter.decodeRespFn = func(raw []byte) (*canonical.CanonicalResponse, error) { return &canonical.CanonicalResponse{ID: "resp-1", Model: "test"}, nil } _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpResponse(HTTPResponseSpec{ StatusCode: 200, Body: []byte(`{"id":"resp-1"}`), }, "client", "provider", InterfaceTypeChat, "") require.NoError(t, err) assert.Equal(t, 200, result.StatusCode) assert.Contains(t, string(result.Body), "resp-1") } func TestConvertHttpResponse_DecodeError(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) providerAdapter := newMockAdapter("provider", false) providerAdapter.decodeRespFn = func(raw []byte) (*canonical.CanonicalResponse, error) { return nil, errors.New("decode error") } _ = engine.RegisterAdapter(providerAdapter) _ = engine.RegisterAdapter(newMockAdapter("client", false)) _, err := engine.ConvertHttpResponse(HTTPResponseSpec{Body: []byte(`{}`)}, "client", "provider", InterfaceTypeChat, "") assert.Error(t, err) } func TestConvertHttpRequest_EmbeddingInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.ifaceType = InterfaceTypeEmbeddings clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} clientAdapter.decodeReqFn = func(raw []byte) (*canonical.CanonicalRequest, error) { return &canonical.CanonicalRequest{Model: "test"}, nil } providerAdapter := newMockAdapter("provider", false) providerAdapter.ifaceType = InterfaceTypeEmbeddings providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/v1/embeddings", Method: "POST", Body: []byte(`{"model":"text-embedding","input":"hello"}`), }, "client", "provider", NewTargetProvider("https://example.com", "key", "model")) require.NoError(t, err) assert.NotNil(t, result) } func TestConvertHttpRequest_RerankInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.ifaceType = InterfaceTypeRerank clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.ifaceType = InterfaceTypeRerank providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/v1/rerank", Method: "POST", Body: []byte(`{"model":"rerank","query":"test","documents":["a"]}`), }, "client", "provider", NewTargetProvider("https://example.com", "key", "model")) require.NoError(t, err) assert.NotNil(t, result) } func TestConvertHttpResponse_EmbeddingInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpResponse(HTTPResponseSpec{ StatusCode: 200, Body: []byte(`{"object":"list","data":[],"model":"test"}`), }, "client", "provider", InterfaceTypeEmbeddings, "") require.NoError(t, err) assert.NotNil(t, result) } func TestConvertHttpResponse_RerankInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpResponse(HTTPResponseSpec{ StatusCode: 200, Body: []byte(`{"results":[],"model":"test"}`), }, "client", "provider", InterfaceTypeRerank, "") require.NoError(t, err) assert.NotNil(t, result) } func TestConvertHttpRequest_ModelsInterface_Passthrough(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.ifaceType = InterfaceTypeModels providerAdapter := newMockAdapter("provider", false) providerAdapter.ifaceType = InterfaceTypeModels _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) body := []byte(`{"object":"list","data":[]}`) result, err := engine.ConvertHttpRequest(HTTPRequestSpec{ URL: "/models", Method: "GET", Body: body, }, "client", "provider", NewTargetProvider("https://example.com", "key", "")) require.NoError(t, err) assert.Equal(t, body, result.Body) } func TestConvertHttpResponse_ModelsInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeModels: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeModels: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpResponse(HTTPResponseSpec{ StatusCode: 200, Body: []byte(`{"object":"list","data":[]}`), }, "client", "provider", InterfaceTypeModels, "") require.NoError(t, err) assert.NotNil(t, result) } func TestConvertHttpResponse_ModelInfoInterface(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeModelInfo: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeModelInfo: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) result, err := engine.ConvertHttpResponse(HTTPResponseSpec{ StatusCode: 200, Body: []byte(`{"id":"gpt-4","object":"model"}`), }, "client", "provider", InterfaceTypeModelInfo, "") require.NoError(t, err) assert.NotNil(t, result) } func TestRegistry_ListProtocols(t *testing.T) { registry := NewMemoryRegistry() _ = registry.Register(newMockAdapter("openai", true)) _ = registry.Register(newMockAdapter("anthropic", true)) protocols := registry.ListProtocols() assert.Len(t, protocols, 2) assert.Contains(t, protocols, "openai") assert.Contains(t, protocols, "anthropic") } func TestRegistry_ConcurrentAccess(t *testing.T) { registry := NewMemoryRegistry() done := make(chan bool, 2) go func() { for i := 0; i < 100; i++ { _ = registry.Register(newMockAdapter("proto-"+string(rune(i)), true)) } done <- true }() go func() { for i := 0; i < 100; i++ { _, _ = registry.Get("proto-" + string(rune(i))) } _ = registry.ListProtocols() done <- true }() <-done <-done } func TestNewConversionContext(t *testing.T) { ctx := NewConversionContext(InterfaceTypeChat) assert.NotEmpty(t, ctx.ConversionID) assert.Equal(t, InterfaceTypeChat, ctx.InterfaceType) assert.NotNil(t, ctx.Metadata) } type testMiddleware struct { fn func(req *canonical.CanonicalRequest, clientProtocol, providerProtocol string, ctx *ConversionContext) (*canonical.CanonicalRequest, error) } func (m *testMiddleware) Intercept(req *canonical.CanonicalRequest, clientProtocol, providerProtocol string, ctx *ConversionContext) (*canonical.CanonicalRequest, error) { if m.fn != nil { return m.fn(req, clientProtocol, providerProtocol, ctx) } return req, nil } func (m *testMiddleware) InterceptStreamEvent(event *canonical.CanonicalStreamEvent, clientProtocol, providerProtocol string, ctx *ConversionContext) (*canonical.CanonicalStreamEvent, error) { return event, nil } var _ = json.Marshal func TestConvertEmbeddingBody_DecodeError(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.decodeEmbeddingReqFn = func(raw []byte) (*canonical.CanonicalEmbeddingRequest, error) { return nil, errors.New("decode embedding failed") } clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeEmbeddings: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) body := []byte(`{"model":"text-embedding","input":"hello"}`) result, err := engine.convertEmbeddingBody(clientAdapter, providerAdapter, NewTargetProvider("", "", ""), body) require.NoError(t, err) assert.Equal(t, body, result) } func TestConvertRerankBody_DecodeError(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) clientAdapter.decodeRerankReqFn = func(raw []byte) (*canonical.CanonicalRerankRequest, error) { return nil, errors.New("decode rerank failed") } clientAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} providerAdapter := newMockAdapter("provider", false) providerAdapter.supportsIface = map[InterfaceType]bool{InterfaceTypeRerank: true} _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) body := []byte(`{"model":"rerank","query":"test","documents":["a"]}`) result, err := engine.convertRerankBody(clientAdapter, providerAdapter, NewTargetProvider("", "", ""), body) require.NoError(t, err) assert.Equal(t, body, result) } func TestConvertBody_UnknownInterfaceType(t *testing.T) { registry := NewMemoryRegistry() engine := NewConversionEngine(registry, nil) clientAdapter := newMockAdapter("client", false) providerAdapter := newMockAdapter("provider", false) _ = engine.RegisterAdapter(clientAdapter) _ = engine.RegisterAdapter(providerAdapter) body := []byte(`{"test":"data"}`) result, err := engine.convertBody(InterfaceType("UNKNOWN"), clientAdapter, providerAdapter, NewTargetProvider("", "", ""), body) require.NoError(t, err) assert.Equal(t, body, result) }