mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 10:15:48 +00:00
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.
73 lines
2.4 KiB
Go
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")
|
|
}
|
|
}
|