mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-27 14:57:15 +00:00
f8756a0b5b
* 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
64 lines
2.1 KiB
Go
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)
|
|
}
|
|
}
|