터미널 세션 core와 CLI mode executor 경계를 분리해 후속 remote terminal bridge가 재사용할 내부 실행 기반을 만든다. adapter 기본값과 vLLM experimental surface도 명시해 production 실행 경로가 mock fallback이나 미구현 실행으로 숨지 않게 한다.
112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
package router
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
"iop/apps/node/internal/adapters"
|
|
"iop/apps/node/internal/runtime"
|
|
)
|
|
|
|
type stubAdapter struct{ name string }
|
|
|
|
func (s *stubAdapter) Name() string { return s.name }
|
|
func (s *stubAdapter) Capabilities(_ context.Context) (runtime.Capabilities, error) {
|
|
return runtime.Capabilities{AdapterName: s.name}, nil
|
|
}
|
|
func (s *stubAdapter) Execute(_ context.Context, _ runtime.ExecutionSpec, _ runtime.EventSink) error {
|
|
return nil
|
|
}
|
|
|
|
func TestResolve_PreservesSessionFields(t *testing.T) {
|
|
reg := adapters.NewRegistry()
|
|
reg.Register(&stubAdapter{name: "cli"})
|
|
r := New(reg, zap.NewNop())
|
|
|
|
req := runtime.RunRequest{
|
|
RunID: "run-1",
|
|
Adapter: "cli",
|
|
Target: "codex",
|
|
SessionID: "session-a",
|
|
SessionMode: runtime.SessionModeRequireExisting,
|
|
Background: true,
|
|
}
|
|
|
|
spec, err := r.Resolve(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("resolve: %v", err)
|
|
}
|
|
if spec.SessionID != req.SessionID {
|
|
t.Errorf("SessionID: got %q want %q", spec.SessionID, req.SessionID)
|
|
}
|
|
if spec.SessionMode != req.SessionMode {
|
|
t.Errorf("SessionMode: got %q want %q", spec.SessionMode, req.SessionMode)
|
|
}
|
|
if spec.Background != req.Background {
|
|
t.Errorf("Background: got %v want %v", spec.Background, req.Background)
|
|
}
|
|
}
|
|
|
|
func TestResolveAdapter_Found(t *testing.T) {
|
|
reg := adapters.NewRegistry()
|
|
expected := &stubAdapter{name: "cli"}
|
|
reg.Register(expected)
|
|
r := New(reg, zap.NewNop())
|
|
|
|
req := runtime.RunRequest{
|
|
RunID: "run-1",
|
|
Adapter: "cli",
|
|
Target: "codex",
|
|
}
|
|
|
|
spec, adapter, err := r.ResolveAdapter(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("ResolveAdapter: %v", err)
|
|
}
|
|
if spec.Adapter != "cli" {
|
|
t.Errorf("expected adapter name 'cli', got %q", spec.Adapter)
|
|
}
|
|
if adapter != expected {
|
|
t.Fatal("expected same adapter instance")
|
|
}
|
|
}
|
|
|
|
func TestResolveAdapter_NotFound(t *testing.T) {
|
|
reg := adapters.NewRegistry()
|
|
r := New(reg, zap.NewNop())
|
|
|
|
req := runtime.RunRequest{
|
|
RunID: "run-1",
|
|
Adapter: "nonexistent",
|
|
Target: "codex",
|
|
}
|
|
|
|
_, _, err := r.ResolveAdapter(context.Background(), req)
|
|
if err == nil {
|
|
t.Fatal("expected error for unregistered adapter")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found") {
|
|
t.Fatalf("expected 'not found' in error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestResolveAdapter_RequiresExplicitAdapter(t *testing.T) {
|
|
reg := adapters.NewRegistry()
|
|
reg.Register(&stubAdapter{name: "mock"})
|
|
r := New(reg, zap.NewNop())
|
|
|
|
req := runtime.RunRequest{
|
|
RunID: "run-1",
|
|
Target: "mock-echo",
|
|
}
|
|
|
|
_, _, err := r.ResolveAdapter(context.Background(), req)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty adapter")
|
|
}
|
|
if !strings.Contains(err.Error(), "adapter is required") {
|
|
t.Fatalf("expected adapter required error, got %v", err)
|
|
}
|
|
}
|