Files
goclaw/internal/agent/router_isrunning_test.go
viettranx c8ebe9a789 refactor(comments): remove plan/phase refs from agent identity hardening code
Rewrite inline comments added during the agent identity hardening so
they explain the code as it stands today, rather than tying to internal
plan terminology (phase numbers, FR/NFR/H/M/C codes, PR references,
trap zone labels). Commit history already carries the plan archaeology.
Comments now keep the non-obvious invariants (cache boundaries, bypass
gaps, silent-nil traps, dual-tenant semantics) and drop the scaffolding.
Comment-only — no runtime behavior change.
2026-04-11 21:22:23 +07:00

73 lines
2.4 KiB
Go

package agent
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
// TestIsRunning_TenantScopedLookup asserts that Router.IsRunning accepts ctx
// and looks up under the tenant-scoped cache key. A previous bare
// `r.agents[agentID]` lookup always returned false for any tenant-scoped
// deployment, so the WS `agents.list` response incorrectly showed every live
// agent as `isRunning: false`.
func TestIsRunning_TenantScopedLookup(t *testing.T) {
r := NewRouter()
tenantA := uuid.New()
tenantB := uuid.New()
ctxA := store.WithTenantID(context.Background(), tenantA)
ctxB := store.WithTenantID(context.Background(), tenantB)
// Directly populate cache under canonical tenant-scoped key with a
// running stub agent in tenant A.
keyA := agentCacheKey(ctxA, "foo")
r.agents[keyA] = &agentEntry{
agent: &stubAgent{id: "foo", running: true},
cachedAt: time.Now(),
}
if !r.IsRunning(ctxA, "foo") {
t.Error("IsRunning(ctxA, foo) should return true — agent cached under tenantA")
}
if r.IsRunning(ctxB, "foo") {
t.Error("IsRunning(ctxB, foo) should return false — tenantB has no entry")
}
// Empty ctx → no tenant → bare key lookup. Should still return false
// because the actual entry is under a tenant-scoped key.
if r.IsRunning(context.Background(), "foo") {
t.Error("IsRunning(emptyCtx, foo) should return false — bare lookup cannot see tenant-scoped entries")
}
}
// TestIsRunning_NoBareLookupLeak guards against regressing to the pre-fix
// bare-key lookup which could surface a cross-tenant leak for
// non-tenant-scoped routers.
func TestIsRunning_NoBareLookupLeak(t *testing.T) {
r := NewRouter()
// Bare entry (no tenant) with running=true.
r.agents["bare-agent"] = &agentEntry{
agent: &stubAgent{id: "bare-agent", running: true},
cachedAt: time.Now(),
}
// Without a tenant, ctx-scoped lookup yields the bare key directly.
if !r.IsRunning(context.Background(), "bare-agent") {
t.Error("IsRunning should find a bare entry when ctx has no tenant")
}
// WITH a tenant, the cache key becomes `tenant:bare-agent` which does
// NOT exist — must return false (no fallback to bare lookup).
tenantID := uuid.New()
ctxT := store.WithTenantID(context.Background(), tenantID)
if r.IsRunning(ctxT, "bare-agent") {
t.Error("IsRunning must NOT fall back to bare lookup when tenant scope is present — cross-tenant leak")
}
}