From c7a93703ba037c88e43a9a58e152aa35125af6a2 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sat, 11 Apr 2026 14:09:24 +0700 Subject: [PATCH] test(ws): cover resolveAgentUUIDCached cache hit fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing tests exercise only the DB fallback path via errorAgentStore. Add a test that primes the router cache with a stub implementing both agent.Agent and agentUUIDProvider, then asserts the cache-aware helper returns the cached UUID without touching the store — the raison d'etre of the helper. Uses the same sentinel-error store so a broken fast path surfaces as a sentinel error rather than a silent pass. --- .../methods/agent_resolve_cached_test.go | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/gateway/methods/agent_resolve_cached_test.go b/internal/gateway/methods/agent_resolve_cached_test.go index e7055ca5..70a92c3c 100644 --- a/internal/gateway/methods/agent_resolve_cached_test.go +++ b/internal/gateway/methods/agent_resolve_cached_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/nextlevelbuilder/goclaw/internal/agent" + "github.com/nextlevelbuilder/goclaw/internal/providers" "github.com/nextlevelbuilder/goclaw/internal/store" ) @@ -74,3 +75,57 @@ func TestResolveAgentUUIDCached_UUIDInputTakesDBPath(t *testing.T) { t.Errorf("expected sentinel miss error, got %v", err) } } + +// cacheHitStubAgent implements agent.Agent + agentUUIDProvider so it can be +// registered in the router and the cache-aware helper can extract its UUID +// without hitting the store. +type cacheHitStubAgent struct { + id string + uid uuid.UUID +} + +func (s *cacheHitStubAgent) ID() string { return s.id } +func (s *cacheHitStubAgent) UUID() uuid.UUID { return s.uid } +func (s *cacheHitStubAgent) Run(context.Context, agent.RunRequest) (*agent.RunResult, error) { + return nil, nil +} +func (s *cacheHitStubAgent) IsRunning() bool { return false } +func (s *cacheHitStubAgent) Model() string { return "test-model" } +func (s *cacheHitStubAgent) ProviderName() string { return "test" } +func (s *cacheHitStubAgent) Provider() providers.Provider { return nil } + +// TestResolveAgentUUIDCached_CacheHitSkipsDBPath pins the fast path: when the +// caller passes an agent_key AND the Loop is cached in the router AND the +// cached Loop satisfies agentUUIDProvider, the helper must return the cached +// UUID without invoking the store. The store is an errorAgentStore that +// returns a sentinel error on any call — if the fast path misbehaves and +// falls through to the DB lookup, that sentinel surfaces as a test failure. +func TestResolveAgentUUIDCached_CacheHitSkipsDBPath(t *testing.T) { + r := agent.NewRouter() + expectedUUID := uuid.New() + agentKey := "cached-agent" + + // Prime the router cache via a resolver that returns the stub. Router.Get + // canonicalizes under `tenantID:agentKey` so a subsequent cache-aware + // lookup with the same (ctx, agentKey) resolves via the fast path. + r.SetResolver(func(_ context.Context, _ string) (agent.Agent, error) { + return &cacheHitStubAgent{id: agentKey, uid: expectedUUID}, nil + }) + tenantID := uuid.New() + ctx := store.WithTenantID(context.Background(), tenantID) + if _, err := r.Get(ctx, agentKey); err != nil { + t.Fatalf("prime Router.Get: %v", err) + } + + // Any store call is a test failure. If the fast path works, the store is + // never touched and no error surfaces. + stub := &errorAgentStore{err: errSentinelMiss} + + got, err := resolveAgentUUIDCached(ctx, r, stub, agentKey) + if err != nil { + t.Fatalf("fast path returned error %v — expected cache hit without DB lookup", err) + } + if got != expectedUUID { + t.Errorf("fast path returned UUID %v, want %v", got, expectedUUID) + } +}