Files
go-sip/internal/store/store_test.go
T

186 lines
5.5 KiB
Go

package store
import (
"database/sql"
"errors"
"testing"
"time"
"git.ipao.vip/rogee/go-sip/contracts"
"git.ipao.vip/rogee/go-sip/internal/tenant"
_ "modernc.org/sqlite"
)
func testStore(t *testing.T) *Store {
t.Helper()
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatal(err)
}
now := time.Date(2026, 9, 18, 0, 0, 0, 0, time.UTC)
s, err := New(db, func() time.Time { return now })
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = s.Close() })
return s
}
func TestIngestIsDurableAndIdempotent(t *testing.T) {
s := testStore(t)
raw, err := contracts.Read("examples/call.execute.json")
if err != nil {
t.Fatal(err)
}
result, err := s.IngestCommand(raw, "agent-call.tenant.tenant-demo-key.call.execute")
if err != nil {
t.Fatal(err)
}
if result.Duplicate {
t.Fatal("first command marked duplicate")
}
second, err := s.IngestCommand(raw, tenant.CommandRoutingPrefix+"tenant-demo-key"+tenant.CommandRoutingSuffix)
if err != nil {
t.Fatal(err)
}
if !second.Duplicate {
t.Fatal("second command was not idempotent")
}
outbox, err := s.ClaimOutbox(10)
if err != nil {
t.Fatal(err)
}
if len(outbox) != 1 {
t.Fatalf("outbox rows = %d, want 1", len(outbox))
}
}
func TestIngestRejectsRoutingMismatch(t *testing.T) {
s := testStore(t)
raw, err := contracts.Read("examples/call.execute.json")
if err != nil {
t.Fatal(err)
}
if _, err := s.IngestCommand(raw, "agent-call.tenant.other.call.execute"); err == nil {
t.Fatal("expected routing mismatch")
}
}
func TestQuotaIsAtomicAndUnknownIsNotReleased(t *testing.T) {
s := testStore(t)
for _, scope := range []string{"tenant:tenant-demo-key", "global", "cell:cell-1"} {
if err := s.SetQuota(scope, 1); err != nil {
t.Fatal(err)
}
}
if err := s.Reserve("r1", "e1", "tenant-demo-key", []string{"tenant:tenant-demo-key", "global", "cell:cell-1"}); err != nil {
t.Fatal(err)
}
if err := s.Reserve("r2", "e2", "tenant-demo-key", []string{"tenant:tenant-demo-key", "global", "cell:cell-1"}); !errors.Is(err, ErrNoCapacity) {
t.Fatalf("reserve error = %v, want ErrNoCapacity", err)
}
if err := s.ReleaseReservationWithScopes("r1", []string{"tenant:tenant-demo-key", "global", "cell:cell-1"}, true); err != nil {
t.Fatal(err)
}
if err := s.Reserve("r3", "e3", "tenant-demo-key", []string{"tenant:tenant-demo-key", "global", "cell:cell-1"}); !errors.Is(err, ErrNoCapacity) {
t.Fatalf("unknown reservation released capacity: %v", err)
}
}
func TestFinalizeReservationRequeuesBeforeRemoteSubmission(t *testing.T) {
s := testStore(t)
if err := s.SetQuota("global", 1); 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)
}
if err := s.Reserve("r-requeue", "exec_demo_001", "tenant-demo-key", []string{"global"}); err != nil {
t.Fatal(err)
}
if err := s.MarkTaskReserved("exec_demo_001"); err != nil {
t.Fatal(err)
}
if err := s.FinalizeReservation("r-requeue", "exec_demo_001", false); err != nil {
t.Fatal(err)
}
var taskStatus string
if err := s.DB().QueryRow(`SELECT status FROM tasks WHERE execution_id = 'exec_demo_001'`).Scan(&taskStatus); err != nil {
t.Fatal(err)
}
if taskStatus != "accepted" {
t.Fatalf("task status=%q, want accepted", taskStatus)
}
var reserved, unknown int64
if err := s.DB().QueryRow(`SELECT reserved_value, unknown_value FROM quotas WHERE scope = 'global'`).Scan(&reserved, &unknown); err != nil {
t.Fatal(err)
}
if reserved != 0 || unknown != 0 {
t.Fatalf("quota reserved=%d unknown=%d, want 0/0", reserved, unknown)
}
}
func TestFinalizeReservationKeepsUnknownCounted(t *testing.T) {
s := testStore(t)
if err := s.SetQuota("global", 1); 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)
}
if err := s.Reserve("r-unknown", "exec_demo_001", "tenant-demo-key", []string{"global"}); err != nil {
t.Fatal(err)
}
if err := s.MarkTaskReserved("exec_demo_001"); err != nil {
t.Fatal(err)
}
if err := s.FinalizeReservation("r-unknown", "exec_demo_001", true); err != nil {
t.Fatal(err)
}
var taskStatus string
if err := s.DB().QueryRow(`SELECT status FROM tasks WHERE execution_id = 'exec_demo_001'`).Scan(&taskStatus); err != nil {
t.Fatal(err)
}
if taskStatus != "unknown" {
t.Fatalf("task status=%q, want unknown", taskStatus)
}
var reserved, unknown int64
if err := s.DB().QueryRow(`SELECT reserved_value, unknown_value FROM quotas WHERE scope = 'global'`).Scan(&reserved, &unknown); err != nil {
t.Fatal(err)
}
if reserved != 0 || unknown != 1 {
t.Fatalf("quota reserved=%d unknown=%d, want 0/1", reserved, unknown)
}
}
func TestControlCASAndStopBarrier(t *testing.T) {
s := testStore(t)
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)
}
if err := s.ApplyControl("exec_demo_001", 1, "pause"); err != nil {
t.Fatal(err)
}
if err := s.ApplyControl("exec_demo_001", 99, "resume"); !errors.Is(err, ErrCASConflict) {
t.Fatalf("revision mismatch error = %v", err)
}
if err := s.ApplyControl("exec_demo_001", 1, "stop"); err != nil {
t.Fatal(err)
}
if err := s.ApplyControl("exec_demo_001", 1, "resume"); !errors.Is(err, ErrCASConflict) {
t.Fatalf("stopped resume error = %v", err)
}
}