Files
go-sip/internal/control/http_test.go
T

82 lines
2.9 KiB
Go

package control
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.ipao.vip/rogee/go-sip/contracts"
"git.ipao.vip/rogee/go-sip/internal/store"
)
func setupHandler(t *testing.T) (*Handler, *store.Store) {
t.Helper()
s, err := store.Open(":memory:")
if err != nil {
t.Fatal(err)
}
raw, err := contracts.Read("examples/call.execute.json")
if err != nil {
t.Fatal(err)
}
if _, err := s.IngestCommand(raw, "agent-call.tenant.tenant-demo-key.call.execute"); err != nil {
t.Fatal(err)
}
h := &Handler{Store: s, BearerToken: "test-token"}
t.Cleanup(func() { _ = s.Close() })
return h, s
}
func request(h http.Handler, method, path, body string) *httptest.ResponseRecorder {
r := httptest.NewRequest(method, path, strings.NewReader(body))
r.Header.Set("Authorization", "Bearer test-token")
r.Header.Set("X-Tenant-ID", "tenant-demo")
r.Header.Set("X-Request-ID", "req-1")
r.Header.Set("Idempotency-Key", "idem-1")
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
return w
}
func TestControlEndpointUsesCASAndStrictJSON(t *testing.T) {
h, _ := setupHandler(t)
w := request(h, http.MethodPost, "/internal/v1/outbound/tasks/task-demo/controls", `{"command_id":"ctrl-1","action":"pause","expected_task_revision":1,"reason":"operator"}`)
if w.Code != http.StatusAccepted {
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
}
w = request(h, http.MethodPost, "/internal/v1/outbound/tasks/task-demo/controls", `{"command_id":"ctrl-2","action":"resume","expected_task_revision":99,"reason":"stale"}`)
if w.Code != http.StatusConflict {
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
}
w = request(h, http.MethodPost, "/internal/v1/outbound/tasks/task-demo/controls", `{"command_id":"ctrl-3","action":"pause","expected_task_revision":1,"reason":"operator","extra":true}`)
if w.Code != http.StatusBadRequest {
t.Fatalf("strict JSON status=%d body=%s", w.Code, w.Body.String())
}
}
func TestCommandQueryAndReplayAreTenantScopedAndDurable(t *testing.T) {
h, s := setupHandler(t)
w := request(h, http.MethodGet, "/internal/v1/outbound/commands/cmd_demo_001", "")
if w.Code != http.StatusOK {
t.Fatalf("query status=%d body=%s", w.Code, w.Body.String())
}
var body map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil || body["command_id"] != "cmd_demo_001" || body["task_id"] != "task-demo" || body["execution_id"] != "exec_demo_001" || body["aggregate_version"] != float64(1) {
t.Fatalf("query body=%s err=%v", w.Body.String(), err)
}
w = request(h, http.MethodPost, "/internal/v1/outbound/commands/cmd_demo_001/replays", `{"command_id":"replay-1","reason":"repair"}`)
if w.Code != http.StatusAccepted {
t.Fatalf("replay status=%d body=%s", w.Code, w.Body.String())
}
rows, err := s.DB().Query(`SELECT status FROM outbox WHERE event_id = 'replay-idem-1'`)
if err != nil {
t.Fatal(err)
}
defer rows.Close()
if !rows.Next() {
t.Fatal("replay was not persisted to outbox")
}
}