613 lines · 23.8 KB · Go
RawDownload
package pluginhost import (	"context"	"errors"	"fmt"	"testing" 	coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"	coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"	"github.com/router-for-me/CLIProxyAPI/v7/sdk/pluginapi") func newRouteModelHostWithRecords(records ...capabilityRecord) *Host {	for i := range records {		caps := &records[i].plugin.Capabilities		if caps.Executor == nil {			continue		}		if len(caps.ExecutorInputFormats) == 0 {			caps.ExecutorInputFormats = []string{"openai"}		}		if len(caps.ExecutorOutputFormats) == 0 {			caps.ExecutorOutputFormats = []string{"openai"}		}	}	return newHostWithRecords(records...)} func TestHostRouteModelUsesHighestPriorityFirstMatch(t *testing.T) {	var lowCalled bool	host := newRouteModelHostWithRecords(		capabilityRecord{			id:       "low",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					lowCalled = true					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "high",			priority: 10,			meta:     pluginapi.Metadata{Name: "High Router"},			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					if req.Plugin.Name != "High Router" {						t.Fatalf("Plugin metadata = %#v, want High Router", req.Plugin)					}					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf, Reason: "match"}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !ok || !resp.Handled || resp.Target != "high" || resp.Reason != "match" {		t.Fatalf("RouteModel() = %#v, %v; want high executor handled", resp, ok)	}	if lowCalled {		t.Fatal("low priority router was called after high priority match")	}} func TestHostRouteModelContinuesAfterUnhandled(t *testing.T) {	var lowCalled bool	host := newRouteModelHostWithRecords(		capabilityRecord{			id:       "low",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					lowCalled = true					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "high",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{Handled: false}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !lowCalled {		t.Fatal("low priority router was not called after unhandled high priority router")	}	if !ok || resp.Target != "low" {		t.Fatalf("RouteModel() = %#v, %v; want low executor handled", resp, ok)	}} func TestHostRouteModelAllowsExplicitExecutorPluginTarget(t *testing.T) {	host := newRouteModelHostWithRecords(		capabilityRecord{			id: "executor",			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},			}},		},		capabilityRecord{			id:       "router",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					if req.PluginID != "router" {						t.Fatalf("PluginID = %q, want router", req.PluginID)					}					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetExecutor, Target: "executor"}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !ok || !resp.Handled || resp.Target != "executor" {		t.Fatalf("RouteModel() = %#v, %v; want executor target handled", resp, ok)	}} func TestHostExecutePluginExecutorByPluginIDPreservesModel(t *testing.T) {	var gotReq pluginapi.ExecutorRequest	executor := &fakeExecutor{		identifier: "plugin-provider",		execute: func(ctx context.Context, req pluginapi.ExecutorRequest) (pluginapi.ExecutorResponse, error) {			gotReq = req			return pluginapi.ExecutorResponse{Payload: []byte("plugin-ok")}, nil		},	}	host := newRouteModelHostWithRecords(capabilityRecord{		id: "executor",		plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{			Executor:              executor,			ExecutorInputFormats:  []string{"openai"},			ExecutorOutputFormats: []string{"openai"},		}},	}) 	resp, errExecute := host.ExecutePluginExecutor(context.Background(), "executor", coreexecutor.Request{Model: "client-model", Payload: []byte(`{"model":"client-model"}`)}, coreexecutor.Options{OriginalRequest: []byte(`{"model":"client-model"}`)})	if errExecute != nil {		t.Fatalf("ExecutePluginExecutor() error = %v", errExecute)	}	if string(resp.Payload) != "plugin-ok" {		t.Fatalf("payload = %q, want plugin-ok", resp.Payload)	}	if gotReq.AuthID != "" || gotReq.AuthProvider != "" {		t.Fatalf("auth fields = %q/%q, want empty static executor auth", gotReq.AuthID, gotReq.AuthProvider)	}	if gotReq.Model != "client-model" {		t.Fatalf("executor request model = %q, want client-model", gotReq.Model)	}} func TestHostRouteModelDefaultsHandledRouterToOwnExecutor(t *testing.T) {	host := newRouteModelHostWithRecords(capabilityRecord{		id: "router",		plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{			Executor: &fakeExecutor{identifier: "fake-provider"},			ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {				return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil			}),		}},	}) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !ok || resp.Target != "router" {		t.Fatalf("RouteModel() = %#v, %v; want router executor handled", resp, ok)	}} func TestHostRouteModelSkipsUnavailableExecutorTargets(t *testing.T) {	calls := 0	host := newRouteModelHostWithRecords(		capabilityRecord{			id:       "fallback",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					calls++					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "missing-target",			priority: 20,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					calls++					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetExecutor, Target: "missing"}, nil				}),			}},		},		capabilityRecord{			id:       "no-executor",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					calls++					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if calls != 3 {		t.Fatalf("router calls = %d, want all routers tried", calls)	}	if !ok || resp.Target != "fallback" {		t.Fatalf("RouteModel() = %#v, %v; want fallback executor handled", resp, ok)	}} func TestHostRouteModelErrorAndPanicDoNotBreakFallback(t *testing.T) {	host := newRouteModelHostWithRecords(		capabilityRecord{			id:       "fallback",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "panic",			priority: 20,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					panic("router panic")				}),			}},		},		capabilityRecord{			id:       "error",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{}, errors.New("temporary route failure")				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !ok || resp.Target != "fallback" {		t.Fatalf("RouteModel() = %#v, %v; want fallback executor handled", resp, ok)	}	if !host.isPluginFused("panic") {		t.Fatal("panic router was not fused")	}} func TestHostHasModelRoutersReportsAvailableRouters(t *testing.T) {	host := newRouteModelHostWithRecords(		capabilityRecord{			id: "router",			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{}, nil				}),			}},		},		capabilityRecord{id: "other"},	) 	if !host.HasModelRouters() {		t.Fatal("HasModelRouters() = false, want true")	}	if host.HasModelRoutersExcept("router") {		t.Fatal("HasModelRoutersExcept(router) = true, want false")	}} func TestHostRouteModelClonesPluginMetadata(t *testing.T) {	host := newRouteModelHostWithRecords(capabilityRecord{		id: "router",		meta: pluginapi.Metadata{			Name: "Router",			ConfigFields: []pluginapi.ConfigField{{				Name:       "mode",				EnumValues: []string{"safe", "fast"},			}},		},		plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{			Executor: &fakeExecutor{identifier: "fake-provider"},			ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {				req.Plugin.ConfigFields[0].Name = "mutated"				req.Plugin.ConfigFields[0].EnumValues[0] = "mutated"				return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil			}),		}},	}) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original"})	if !ok || resp.Target != "router" {		t.Fatalf("RouteModel() = %#v, %v; want router executor handled", resp, ok)	}	meta := host.Snapshot().records[0].meta	if meta.ConfigFields[0].Name != "mode" || meta.ConfigFields[0].EnumValues[0] != "safe" {		t.Fatalf("snapshot metadata was mutated: %#v", meta.ConfigFields[0])	}} func TestHostRouteModelSkipsOriginatingPlugin(t *testing.T) {	var originCalled bool	host := newRouteModelHostWithRecords(		capabilityRecord{			id:       "origin",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					originCalled = true					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "other",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},	) 	resp, ok := host.RouteModelExcept(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"}, "origin")	if originCalled {		t.Fatal("origin router was called despite skip")	}	if !ok || resp.Target != "other" {		t.Fatalf("RouteModelExcept() = %#v, %v; want other executor handled", resp, ok)	}} // newHostWithAuthProviders builds a host whose AuthManager registers auths for the given// provider keys, so built-in provider routing can be exercised.func newHostWithAuthProviders(t *testing.T, providers []string, records ...capabilityRecord) *Host {	t.Helper()	host := newRouteModelHostWithRecords(records...)	manager := coreauth.NewManager(nil, nil, nil)	for i, provider := range providers {		auth := &coreauth.Auth{ID: fmt.Sprintf("auth-%s-%d", provider, i), Provider: provider}		if _, errRegister := manager.Register(context.Background(), auth); errRegister != nil {			t.Fatalf("Register(%s) error = %v", provider, errRegister)		}	}	host.authManager = manager	return host} func TestHostRouteModelRoutesToBuiltinProvider(t *testing.T) {	host := newHostWithAuthProviders(t, []string{"claude"}, capabilityRecord{		id: "router",		plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{			ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {				return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetProvider, Target: "claude", TargetModel: "claude-sonnet-4"}, nil			}),		}},	}) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !ok || !resp.Handled || resp.Target != "claude" {		t.Fatalf("RouteModel() = %#v, %v; want claude provider handled", resp, ok)	}	if resp.TargetKind != pluginapi.ModelRouteTargetProvider {		t.Fatalf("TargetKind = %q, want provider", resp.TargetKind)	}	if resp.TargetModel != "claude-sonnet-4" {		t.Fatalf("TargetModel = %q, want claude-sonnet-4", resp.TargetModel)	}} func TestHostRouteModelSkipsUnavailableBuiltinProvider(t *testing.T) {	var fallbackCalled bool	host := newHostWithAuthProviders(t, []string{"claude"},		capabilityRecord{			id:       "fallback",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					fallbackCalled = true					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "missing-provider",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetProvider, Target: "unknown-provider"}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !fallbackCalled {		t.Fatal("fallback router was not called after unavailable provider target")	}	if !ok || resp.Target != "fallback" {		t.Fatalf("RouteModel() = %#v, %v; want fallback executor handled", resp, ok)	}} func TestHostRouteModelRejectsProviderAndExecutorBothSet(t *testing.T) {	var fallbackCalled bool	host := newHostWithAuthProviders(t, []string{"claude"},		capabilityRecord{			id:       "fallback",			priority: 1,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					fallbackCalled = true					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil				}),			}},		},		capabilityRecord{			id:       "both",			priority: 10,			plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{				Executor: &fakeExecutor{identifier: "fake-provider"},				ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {					return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetKind("both"), Target: "claude"}, nil				}),			}},		},	) 	resp, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original-model"})	if !fallbackCalled {		t.Fatal("fallback router was not called after mutually exclusive targets")	}	if !ok || resp.Target != "fallback" {		t.Fatalf("RouteModel() = %#v, %v; want fallback executor handled", resp, ok)	}} func TestHostRouteModelPropagatesAvailableProviders(t *testing.T) {	var gotProviders []string	host := newHostWithAuthProviders(t, []string{"claude", "gemini"}, capabilityRecord{		id: "router",		plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{			Executor: &fakeExecutor{identifier: "fake-provider"},			ModelRouter: modelRouterFunc(func(ctx context.Context, req pluginapi.ModelRouteRequest) (pluginapi.ModelRouteResponse, error) {				gotProviders = append([]string(nil), req.AvailableProviders...)				return pluginapi.ModelRouteResponse{Handled: true, TargetKind: pluginapi.ModelRouteTargetSelf}, nil			}),		}},	}) 	if _, ok := host.RouteModel(context.Background(), pluginapi.ModelRouteRequest{RequestedModel: "original"}); !ok {		t.Fatal("RouteModel() not handled")	}	want := []string{"claude", "gemini"}	if fmt.Sprint(gotProviders) != fmt.Sprint(want) {		t.Fatalf("AvailableProviders = %v, want %v", gotProviders, want)	}} func TestHostBuiltinProviderLookup(t *testing.T) {	host := newHostWithAuthProviders(t, []string{"Claude", "codex"})	if !host.HasBuiltinProvider("claude") {		t.Fatal("HasBuiltinProvider(claude) = false, want true")	}	if host.HasBuiltinProvider("missing") {		t.Fatal("HasBuiltinProvider(missing) = true, want false")	}	providers := host.BuiltinProviders()	if fmt.Sprint(providers) != fmt.Sprint([]string{"claude", "codex"}) {		t.Fatalf("BuiltinProviders() = %v, want [claude codex]", providers)	}} func TestHostRouteModelSkipsExecutorWithoutProviderIdentifier(t *testing.T) {

Lines 1–500 of 613.