a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
203 lines
6.8 KiB
Go
203 lines
6.8 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func boolp(b bool) *bool { return &b }
|
|
|
|
// recordingRemote returns an httptest server plus a flag that flips when
|
|
// it is hit, so a test can assert a gate fired BEFORE any outbound call.
|
|
func recordingRemote(t *testing.T) (*httptest.Server, *bool) {
|
|
t.Helper()
|
|
hit := false
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
hit = true
|
|
_, _ = w.Write([]byte(`{"from":"remote"}`))
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
return srv, &hit
|
|
}
|
|
|
|
// TestRouter_DisabledRemoteRefused asserts an explicit route to a
|
|
// disabled remote returns the structured remote_disabled refusal and
|
|
// never sends a request to the remote.
|
|
func TestRouter_DisabledRemoteRefused(t *testing.T) {
|
|
srv, hit := recordingRemote(t)
|
|
cfg := &ServersConfig{Server: []ServerEntry{{Slug: "r2", URL: srv.URL, Default: true, Enabled: boolp(false)}}}
|
|
router := NewRouter(RouterConfig{
|
|
Servers: cfg,
|
|
Rosters: NewWorkspaceRosterCache(time.Minute),
|
|
CwdResolver: func(string) (string, bool) { return "", false },
|
|
LocalSlug: LocalServerSentinel,
|
|
LocalExecute: func(context.Context, string, []byte) ([]byte, int, error) {
|
|
t.Error("local executor must not run for an explicit disabled-remote route")
|
|
return nil, 0, nil
|
|
},
|
|
})
|
|
out, status, err := router.RouteToolCall(context.Background(), "find_usages", []byte(`{}`), RouteContext{})
|
|
if err != nil {
|
|
t.Fatalf("RouteToolCall: %v", err)
|
|
}
|
|
if status != http.StatusForbidden {
|
|
t.Fatalf("disabled remote should refuse with 403, got %d", status)
|
|
}
|
|
var env map[string]any
|
|
_ = json.Unmarshal(out, &env)
|
|
if env["error_code"] != "remote_disabled" {
|
|
t.Fatalf("expected remote_disabled, got %v", env["error_code"])
|
|
}
|
|
if *hit {
|
|
t.Fatal("the disabled remote must never be contacted")
|
|
}
|
|
}
|
|
|
|
// TestRouter_WriteGateRefusesRemote asserts a mutating tool routed to an
|
|
// enabled remote is refused before any outbound HTTP.
|
|
func TestRouter_WriteGateRefusesRemote(t *testing.T) {
|
|
srv, hit := recordingRemote(t)
|
|
cfg := &ServersConfig{Server: []ServerEntry{{Slug: "r2", URL: srv.URL, Default: true}}}
|
|
router := NewRouter(RouterConfig{
|
|
Servers: cfg,
|
|
Rosters: NewWorkspaceRosterCache(time.Minute),
|
|
CwdResolver: func(string) (string, bool) { return "", false },
|
|
LocalSlug: LocalServerSentinel,
|
|
LocalExecute: func(context.Context, string, []byte) ([]byte, int, error) {
|
|
return []byte(`{}`), 200, nil
|
|
},
|
|
})
|
|
for _, tool := range []string{"edit_file", "batch_edit", "rename_symbol", "track_repository"} {
|
|
out, status, err := router.RouteToolCall(context.Background(), tool, []byte(`{}`), RouteContext{})
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", tool, err)
|
|
}
|
|
if status != http.StatusForbidden {
|
|
t.Fatalf("%s to a remote should refuse with 403, got %d", tool, status)
|
|
}
|
|
var env map[string]any
|
|
_ = json.Unmarshal(out, &env)
|
|
if env["error_code"] != "remote_read_only" {
|
|
t.Fatalf("%s: expected remote_read_only, got %v", tool, env["error_code"])
|
|
}
|
|
}
|
|
if *hit {
|
|
t.Fatal("a write tool must never reach a remote")
|
|
}
|
|
}
|
|
|
|
// TestRouter_LocalWriteAllowed asserts the write-gate is remote-only: a
|
|
// mutating tool that resolves locally runs normally.
|
|
func TestRouter_LocalWriteAllowed(t *testing.T) {
|
|
ran := false
|
|
// No roster => everything resolves local.
|
|
router := NewRouter(RouterConfig{
|
|
Servers: &ServersConfig{},
|
|
LocalExecute: func(context.Context, string, []byte) ([]byte, int, error) {
|
|
ran = true
|
|
return []byte(`{"ok":true}`), 200, nil
|
|
},
|
|
})
|
|
if _, _, err := router.RouteToolCall(context.Background(), "edit_file", []byte(`{}`), RouteContext{}); err != nil {
|
|
t.Fatalf("local write: %v", err)
|
|
}
|
|
if !ran {
|
|
t.Fatal("a local write must run (the gate is remote-only)")
|
|
}
|
|
}
|
|
|
|
// TestRouter_EffectiveEnabledRemotes covers the precedence rules: global
|
|
// Enabled, session override (both directions), fail-closed for absent,
|
|
// and ignore a session override for a slug not in the roster.
|
|
func TestRouter_EffectiveEnabledRemotes(t *testing.T) {
|
|
cfg := &ServersConfig{Server: []ServerEntry{
|
|
{Slug: "r2", URL: "https://r2:4747"}, // global on (nil)
|
|
{Slug: "r3", URL: "https://r3:4747", Enabled: boolp(false)}, // global off
|
|
}}
|
|
router := NewRouter(RouterConfig{Servers: cfg, LocalSlug: LocalServerSentinel})
|
|
|
|
slugs := func(es []ServerEntry) map[string]bool {
|
|
m := map[string]bool{}
|
|
for _, e := range es {
|
|
m[e.Slug] = true
|
|
}
|
|
return m
|
|
}
|
|
|
|
// No session: r2 on, r3 off.
|
|
g := slugs(router.EffectiveEnabledRemotes(nil))
|
|
if !g["r2"] || g["r3"] {
|
|
t.Fatalf("global: want {r2}, got %v", g)
|
|
}
|
|
|
|
// Session disables r2 (beats global on) and enables r3 (beats global off).
|
|
sess := &Session{ID: "a"}
|
|
sess.SetRemoteOverride("r2", false)
|
|
sess.SetRemoteOverride("r3", true)
|
|
sess.SetRemoteOverride("ghost", true) // not in roster — must be ignored
|
|
s := slugs(router.EffectiveEnabledRemotes(sess))
|
|
if s["r2"] {
|
|
t.Error("session off must beat global on")
|
|
}
|
|
if !s["r3"] {
|
|
t.Error("session on must beat global off")
|
|
}
|
|
if s["ghost"] {
|
|
t.Error("a session override for a slug not in the roster must be ignored")
|
|
}
|
|
}
|
|
|
|
// TestRouter_ReloadConfig_ConcurrentNoRace hammers ReloadConfig against
|
|
// concurrent routing + reads; the race detector must stay clean and no
|
|
// call may panic on a torn router.
|
|
func TestRouter_ReloadConfig_ConcurrentNoRace(t *testing.T) {
|
|
router := NewRouter(RouterConfig{
|
|
Servers: &ServersConfig{},
|
|
Rosters: NewWorkspaceRosterCache(time.Minute),
|
|
LocalExecute: func(context.Context, string, []byte) ([]byte, int, error) {
|
|
return []byte(`{}`), 200, nil
|
|
},
|
|
})
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 8; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
for j := 0; j < 200; j++ {
|
|
switch (i + j) % 3 {
|
|
case 0:
|
|
cfg := &ServersConfig{Server: []ServerEntry{{Slug: "r2", URL: "https://r2:4747"}}}
|
|
router.ReloadConfig(cfg, NewWorkspaceRosterCache(time.Minute))
|
|
case 1:
|
|
_, _, _ = router.RouteToolCall(context.Background(), "find_usages", []byte(`{}`), RouteContext{})
|
|
default:
|
|
_ = router.CurrentConfig()
|
|
_ = router.EffectiveEnabledRemotes(nil)
|
|
}
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestRouter_ReloadConfig_ReflectsNewRoster asserts a swap is observed by
|
|
// the next call.
|
|
func TestRouter_ReloadConfig_ReflectsNewRoster(t *testing.T) {
|
|
router := NewRouter(RouterConfig{Servers: &ServersConfig{}, LocalSlug: LocalServerSentinel})
|
|
if got := len(router.CurrentConfig().Server); got != 0 {
|
|
t.Fatalf("initial roster should be empty, got %d", got)
|
|
}
|
|
router.ReloadConfig(&ServersConfig{Server: []ServerEntry{{Slug: "r2", URL: "https://r2:4747"}}}, NewWorkspaceRosterCache(time.Minute))
|
|
if got := len(router.CurrentConfig().Server); got != 1 {
|
|
t.Fatalf("after reload roster should hold 1, got %d", got)
|
|
}
|
|
if e := router.EffectiveEnabledRemotes(nil); len(e) != 1 || e[0].Slug != "r2" {
|
|
t.Fatalf("enabled set should reflect the reloaded roster, got %v", e)
|
|
}
|
|
}
|