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

110 lines
4.5 KiB
Go

package store
import (
"strings"
"testing"
"time"
)
func TestStopSuppressesUnstartedCallsButPreservesUnknown(t *testing.T) {
s := preparedCurrentCallStore(t)
pending := currentCall("queued-before-stop")
if _, _, err := s.RecordExecute(pending); err != nil {
t.Fatal(err)
}
unknown := currentCall("unknown-before-stop")
if _, _, err := s.RecordExecute(unknown); err != nil {
t.Fatal(err)
}
if err := s.ReserveExecute(unknown.DispatcherID, unknown.EventID, currentReservation(), currentMondayUTC()); err != nil {
t.Fatal(err)
}
if err := s.MarkExecuteUnknown(unknown.DispatcherID, unknown.EventID); err != nil {
t.Fatal(err)
}
if err := s.ApplyControl(currentDispatcherID, 1001, "task-asr", "stop"); err != nil {
t.Fatal(err)
}
calls, err := s.ListPendingExecute(currentDispatcherID)
if err != nil || len(calls) != 0 {
t.Fatalf("stopped task still has dispatchable commands: %+v %v", calls, err)
}
var pendingStatus, unknownStatus string
if err := s.db.QueryRow(`SELECT status FROM dispatcher_inbox WHERE event_id=?`, pending.EventID).Scan(&pendingStatus); err != nil {
t.Fatal(err)
}
if err := s.db.QueryRow(`SELECT status FROM dispatcher_inbox WHERE event_id=?`, unknown.EventID).Scan(&unknownStatus); err != nil {
t.Fatal(err)
}
if pendingStatus != "suppressed" || unknownStatus != "unknown" {
t.Fatalf("stop did not preserve unknown and silently suppress unstarted: pending=%s unknown=%s", pendingStatus, unknownStatus)
}
if err := s.ApplyControl(currentDispatcherID, 1001, "task-asr", "resume"); err == nil {
t.Fatal("stopped task resumed")
}
if outbox, err := s.ListPendingOutbox(currentDispatcherID); err != nil || len(outbox) != 0 {
t.Fatalf("stop emitted per-call results: %+v %v", outbox, err)
}
}
func TestEditCommitsSnapshotRevisionAndAckTogether(t *testing.T) {
s := preparedCurrentCallStore(t)
updated, err := s.ReadSnapshot(currentDispatcherID, 1001, "task-asr")
if err != nil {
t.Fatal(err)
}
updated.Task.Raw = []byte(strings.Replace(string(updated.Task.Raw), `"task_revision":1`, `"task_revision":2`, 1))
updated.Task.TaskRevision = 2
if status, err := s.CompleteEdit(updated, "edit-success"); err != nil || status != "applied" {
t.Fatalf("edit status=%q: %v", status, err)
}
persisted, err := s.ReadSnapshot(currentDispatcherID, 1001, "task-asr")
if err != nil || persisted.Task.TaskRevision != 2 {
t.Fatalf("edit not durably bound: revision=%d err=%v", persisted.Task.TaskRevision, err)
}
var revision int64
if err := s.db.QueryRow(`SELECT task_revision FROM dispatcher_tasks WHERE task_id='task-asr'`).Scan(&revision); err != nil || revision != 2 {
t.Fatalf("task and snapshot revisions differ: revision=%d err=%v", revision, err)
}
stale := currentStoreSnapshot(t)
if _, err := s.CompleteEdit(stale, "edit-stale"); err == nil {
t.Fatal("stale edit downgraded the task")
}
conflicting := updated
conflicting.Task.Raw = []byte(strings.Replace(string(updated.Task.Raw), `"status":"running"`, `"status":"paused"`, 1))
if _, err := s.CompleteEdit(conflicting, "edit-conflicting"); err == nil {
t.Fatal("same revision changed task contents")
}
persisted, err = s.ReadSnapshot(currentDispatcherID, 1001, "task-asr")
if err != nil || persisted.Task.TaskRevision != 2 || string(persisted.Task.Raw) != string(updated.Task.Raw) {
t.Fatalf("failed edit changed bound snapshot: revision=%d err=%v", persisted.Task.TaskRevision, err)
}
if err := s.ApplyControl(currentDispatcherID, 1001, "task-asr", "stop"); err != nil {
t.Fatal(err)
}
updated.Task.Raw = []byte(strings.Replace(string(updated.Task.Raw), `"task_revision":2`, `"task_revision":3`, 1))
updated.Task.TaskRevision = 3
if status, err := s.CompleteEdit(updated, "edit-stopped"); err != nil || status != "rejected" {
t.Fatalf("stopped edit status=%q: %v", status, err)
}
outbox, err := s.ListPendingOutbox(currentDispatcherID)
if err != nil {
t.Fatal(err)
}
foundRejected := false
for _, item := range outbox {
if strings.Contains(string(item.Body), `"event_id":"edit-stopped"`) && strings.Contains(string(item.Body), `"status":"rejected"`) {
foundRejected = true
}
}
if !foundRejected {
t.Fatal("edit on stopped task lacked durable rejection receipt")
}
persisted, err = s.ReadSnapshot(currentDispatcherID, 1001, "task-asr")
if err != nil || persisted.Task.TaskRevision != 2 {
t.Fatalf("stopped edit modified task: revision=%d err=%v", persisted.Task.TaskRevision, err)
}
}
func currentMondayUTC() time.Time { return time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC) }