139 lines
6.7 KiB
Go
139 lines
6.7 KiB
Go
package store
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/mq"
|
|
)
|
|
|
|
func TestRecordLocalTaskStatusObservationIsDurableWithoutReceipt(t *testing.T) {
|
|
st := openLocalDiscoveryTestStore(t)
|
|
seedLocalDiscovery(t, st)
|
|
if _, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "stopped"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.RecordLocalTaskStatusObservation("d-1", "task-a", "tenant-a", "tenant-key-a", "stopped", 2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assignment, err := st.LocalTaskAssignment("d-1", "task-a")
|
|
if err != nil || assignment.Status != "stopped" || assignment.TaskRevision != 2 || assignment.AdmissionState != "stopped" {
|
|
t.Fatalf("observed task state=%+v err=%v", assignment, err)
|
|
}
|
|
var outboxCount int
|
|
if err := st.db.QueryRow(`SELECT COUNT(*) FROM outbox`).Scan(&outboxCount); err != nil || outboxCount != 0 {
|
|
t.Fatalf("status observation wrote a receipt: count=%d err=%v", outboxCount, err)
|
|
}
|
|
if err := st.RecordLocalTaskStatusObservation("d-1", "task-a", "tenant-a", "tenant-key-a", "stopped", 2); err != nil {
|
|
t.Fatalf("idempotent status observation: %v", err)
|
|
}
|
|
if err := st.RecordLocalTaskStatusObservation("d-1", "task-a", "tenant-a", "tenant-key-a", "running", 2); !errors.Is(err, ErrConfigRevisionConflict) {
|
|
t.Fatalf("same-revision status change=%v, want conflict", err)
|
|
}
|
|
if err := st.RecordLocalTaskStatusObservation("d-1", "task-a", "tenant-a", "tenant-key-a", "running", 1); !errors.Is(err, ErrConfigRevisionRollback) {
|
|
t.Fatalf("revision rollback=%v, want rollback error", err)
|
|
}
|
|
}
|
|
|
|
func TestLocalTaskControlBarrierAndResultCommitAtomically(t *testing.T) {
|
|
st := openLocalDiscoveryTestStore(t)
|
|
seedLocalDiscovery(t, st)
|
|
|
|
assignment, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "paused")
|
|
if err != nil || assignment.AdmissionState != "paused" {
|
|
t.Fatalf("barrier assignment=%+v err=%v", assignment, err)
|
|
}
|
|
bundle := localConfigBundle("d-1", "tenant-a", "tenant-key-a", "task-a", 1, 1, 1, 1, 1, st.now())
|
|
if _, err := st.PersistLocalCommand(localAdmissionCommand("command-a", "execution-a", "task-a", bundle, "d-1", "tenant-a", "tenant-key-a")); !errors.Is(err, ErrLocalTaskPaused) {
|
|
t.Fatalf("call admission after pause = %v, want ErrLocalTaskPaused", err)
|
|
}
|
|
|
|
result := LocalTaskControlResultRecord{
|
|
DispatcherID: "d-1", TaskID: "task-a", TenantID: "tenant-a", TenantKey: "tenant-key-a",
|
|
Action: "pause", ResultStatus: "applied", ObservedStatus: "paused", TaskRevision: 2,
|
|
AdmissionState: "paused", Event: localControlResultEvent("event-pause-1", "tenant-key-a"),
|
|
}
|
|
assignment, duplicate, err := st.PersistLocalTaskControlResult(result)
|
|
if err != nil || duplicate || assignment.Status != "paused" || assignment.TaskRevision != 2 || assignment.AdmissionState != "paused" {
|
|
t.Fatalf("control assignment=%+v duplicate=%v err=%v", assignment, duplicate, err)
|
|
}
|
|
assignment, duplicate, err = st.PersistLocalTaskControlResult(result)
|
|
if err != nil || !duplicate || assignment.TaskRevision != 2 {
|
|
t.Fatalf("duplicate control result assignment=%+v duplicate=%v err=%v", assignment, duplicate, err)
|
|
}
|
|
|
|
conflict := result
|
|
conflict.Event.Body = []byte(`{"event_id":"event-pause-1","different":true}`)
|
|
if _, _, err := st.PersistLocalTaskControlResult(conflict); !errors.Is(err, ErrCommandConflict) {
|
|
t.Fatalf("conflicting event ID error=%v", err)
|
|
}
|
|
assignment, err = st.LocalTaskAssignment("d-1", "task-a")
|
|
if err != nil || assignment.Status != "paused" || assignment.TaskRevision != 2 || assignment.AdmissionState != "paused" {
|
|
t.Fatalf("failed result transaction changed task state: %+v err=%v", assignment, err)
|
|
}
|
|
var outboxCount int
|
|
if err := st.db.QueryRow(`SELECT COUNT(*) FROM outbox WHERE event_id='event-pause-1'`).Scan(&outboxCount); err != nil || outboxCount != 1 {
|
|
t.Fatalf("outbox rows=%d err=%v", outboxCount, err)
|
|
}
|
|
}
|
|
|
|
func TestLocalTaskStopBarrierCannotBeReopenedByResume(t *testing.T) {
|
|
st := openLocalDiscoveryTestStore(t)
|
|
seedLocalDiscovery(t, st)
|
|
if _, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "paused"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "stopped"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stop := LocalTaskControlResultRecord{
|
|
DispatcherID: "d-1", TaskID: "task-a", TenantID: "tenant-a", TenantKey: "tenant-key-a",
|
|
Action: "stop", ResultStatus: "applied", ObservedStatus: "stopped", TaskRevision: 2,
|
|
AdmissionState: "stopped", Event: localControlResultEvent("event-stop-1", "tenant-key-a"),
|
|
}
|
|
assignment, _, err := st.PersistLocalTaskControlResult(stop)
|
|
if err != nil || assignment.AdmissionState != "stopped" {
|
|
t.Fatalf("stop assignment=%+v err=%v", assignment, err)
|
|
}
|
|
assignment, err = st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "paused")
|
|
if err != nil || assignment.AdmissionState != "stopped" {
|
|
t.Fatalf("pause reopened stopped task: %+v err=%v", assignment, err)
|
|
}
|
|
|
|
resumeRejected := LocalTaskControlResultRecord{
|
|
DispatcherID: "d-1", TaskID: "task-a", TenantID: "tenant-a", TenantKey: "tenant-key-a",
|
|
Action: "resume", ResultStatus: "rejected", ObservedStatus: "running", TaskRevision: 3,
|
|
AdmissionState: "stopped", Event: localControlResultEvent("event-resume-rejected-1", "tenant-key-a"),
|
|
}
|
|
assignment, _, err = st.PersistLocalTaskControlResult(resumeRejected)
|
|
if err != nil || assignment.Status != "running" || assignment.TaskRevision != 3 || assignment.AdmissionState != "stopped" {
|
|
t.Fatalf("rejected resume assignment=%+v err=%v", assignment, err)
|
|
}
|
|
}
|
|
|
|
func TestLocalTaskResumeBarrierReopensPausedButNotStopped(t *testing.T) {
|
|
st := openLocalDiscoveryTestStore(t)
|
|
seedLocalDiscovery(t, st)
|
|
if _, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "paused"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assignment, err := st.ResumeLocalTaskAdmission("d-1", "task-a", "tenant-a", "tenant-key-a", "running", 2)
|
|
if err != nil || assignment.AdmissionState != "running" || assignment.Status != "running" || assignment.TaskRevision != 2 {
|
|
t.Fatalf("resume assignment=%+v err=%v", assignment, err)
|
|
}
|
|
if _, err := st.SetLocalTaskAdmissionBarrier("d-1", "task-a", "tenant-a", "tenant-key-a", "stopped"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assignment, err = st.ResumeLocalTaskAdmission("d-1", "task-a", "tenant-a", "tenant-key-a", "running", 3)
|
|
if !errors.Is(err, ErrLocalTaskStopped) || assignment.AdmissionState != "stopped" {
|
|
t.Fatalf("resume reopened terminal task: %+v err=%v", assignment, err)
|
|
}
|
|
}
|
|
|
|
func localControlResultEvent(eventID, tenantKey string) LocalEventRecord {
|
|
return LocalEventRecord{
|
|
EventID: eventID, TenantKey: tenantKey, Exchange: mq.ResultsExchangeV3,
|
|
RoutingKey: "d.d-1.out", Body: []byte(`{"event_id":"` + eventID + `"}`),
|
|
}
|
|
}
|