package pier_test import ( "context" "net/http" "testing" "time" "git.golem15.com/golem15/summercms/modules/pact" ) // TestPierReaperSmoke is the tracer of phase 19.7 plan 06 (A-CR-01): on the // memory store, a stateful session whose record expired is closed by the // reaper (it leaves the local map, so fan-out no longer reaches it), its id // answers 404 next, and a session kept active by a request survives a pass. func TestPierReaperSmoke(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() in := startInstance(t, newApp(t, appSpec{ mcp: "enabled: false\nsessions:\n store: memory\n idle_ttl: 30m\n", guards: []pact.MCPGuard{&acmeGuard{}}, }), acmePlugin{tools: []pact.MCPTool{echoTool()}}) p := in.pier() t.Cleanup(p.StopReaper) // 1. An SDK client opens a session on this instance. abandoned := sdkClient(t, ctx, in.url(), "oas_at_test", clientOpts{}) if n := p.LocalSessions(); n != 1 { t.Fatalf("local sessions after connect = %d, want 1", n) } // 2. The client goes away without DELETE; past the idle TTL its record // is gone and one reaper pass closes the local copy. if !p.AdvanceStoreClock(31 * time.Minute) { t.Fatal("the instance has no memory store") } if n := p.ReapNow(ctx); n != 1 { t.Fatalf("reaped = %d, want 1", n) } if n := p.LocalSessions(); n != 0 { t.Fatalf("local sessions after the reap = %d, want 0", n) } // 3. The old session id answers 404, so the client re-initialises. if gone := rpc(t, ctx, in.url(), "oas_at_test", abandoned.ID(), callBody); gone.status != http.StatusNotFound { t.Fatalf("call with the reaped session = %d %s", gone.status, gone.body) } // 4. A session touched by a request within the TTL survives a pass. active := openSession(t, ctx, in, "oas_at_test") if !p.AdvanceStoreClock(20 * time.Minute) { t.Fatal("the instance has no memory store") } if call := rpc(t, ctx, in.url(), "oas_at_test", active, callBody); call.status != http.StatusOK { t.Fatalf("call on the active session = %d %s", call.status, call.body) } if !p.AdvanceStoreClock(20 * time.Minute) { t.Fatal("the instance has no memory store") } if n := p.ReapNow(ctx); n != 0 { t.Fatalf("reaped the active session (%d closed)", n) } if n := p.LocalSessions(); n != 1 { t.Fatalf("local sessions after the second pass = %d, want 1", n) } }