Files
pad/internal/cli/client_session_activity_test.go
xarmian f8756a0b5b feat(server,cli,plugin): agent presence — agent name, worker id and busy/idle activity (TASK-4040) (#2293)
* test: pin agent presence and worker-scoped session activity

* feat: carry agent presence and track activity by user and worker

* docs: describe worker presence and session activity states

* fix: include hook activity examples in CLI help

* fix(cli): keep session activity hooks silent on failures

* fix(server): tolerate activity read failures in session lists

* docs: place activity keys after presence registry details

* fix(plugin): wire silent Claude Code activity hooks

* docs: explain session busy-state hook limits

* fix: bound the entire session activity command

* fix: batch session activity reads across workers

* docs: describe detached activity reports and timestamp ordering

* fix: order worker activity atomically by client timestamp

* fix: detach hook activity reports with captured timestamps

* test: reproduce detached activity losing registered identity

* fix: capture activity identity before detaching the reporter

* test: isolate activity registry home on Windows

* docs: clarify activity ordering and hook failure behavior

* fix: bound activity reordering to ten seconds

* fix: replace activity records with corrupt client timestamps

* test: prove activity parent exits while child response is held

* fix(session): derive activity reorder window from report bound

* fix: replace non-string Redis session activity records

* test: cover cleared and expired session activity ordering

* test: cover older timestamp after legacy activity

* test: cover corrupt activity nanos and malformed JSON

* test: verify activity child deadline and process isolation

* docs: clarify read-only tokens cannot report hook activity

* test: strengthen session activity liveness and timing guarantees

* test: pin malformed null session activity timestamps

* test: harden session activity detach regression checks

* test: skip activity process-group check when ps is unavailable

* test: tie activity parent-exit bounds to the HTTP timeout

* test: bound activity liveness checks by the child HTTP deadline

* test: require ps for process-group checks in Go CI
2026-10-23 19:46:02 -04:00

64 lines
2.1 KiB
Go

package cli
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestSetSessionActivity(t *testing.T) {
const worker = "wk_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
for _, status := range []int{http.StatusNoContent, http.StatusServiceUnavailable} {
t.Run(http.StatusText(status), func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" || r.URL.Path != "/api/v1/sessions/activity" {
t.Errorf("request = %s %s", r.Method, r.URL.Path)
}
if r.Header.Get("X-Pad-Worker") != worker || r.Header.Get("Authorization") != "Bearer activity-test" {
t.Error("missing worker or credential")
}
var body struct {
State string `json:"state"`
At string `json:"at"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.State != "busy" || body.At != "2026-10-23T00:00:00.123456789Z" {
t.Errorf("body = %+v, %v", body, err)
}
w.WriteHeader(status)
}))
defer server.Close()
c := NewClientFromURL(server.URL)
c.SetAuthToken("activity-test")
if err := c.SetWorkerID(worker); err != nil {
t.Fatal(err)
}
if err := c.SetSessionActivity(context.Background(), "busy", "2026-10-23T00:00:00.123456789Z"); (err != nil) != (status >= 400) {
t.Fatalf("status %d: %v", status, err)
}
})
}
}
func TestSetSessionActivityTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]string
_ = json.NewDecoder(r.Body).Decode(&body)
<-r.Context().Done()
}))
defer server.Close()
c := NewClientFromURL(server.URL)
if err := c.SetWorkerID("wk_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); err != nil {
t.Fatal(err)
}
start := time.Now()
if err := c.SetSessionActivity(context.Background(), "idle", time.Now().UTC().Format(time.RFC3339Nano)); err == nil {
t.Fatal("expected timeout")
}
if elapsed := time.Since(start); elapsed < time.Second || elapsed > 3*time.Second {
t.Fatalf("timeout took %v; want about 2s", elapsed)
}
}