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

95 lines
3.5 KiB
Go

package store
import (
"path/filepath"
"testing"
)
func TestCurrentSIPNotificationRequiresFullDrainAndExactLoadedRevision(t *testing.T) {
s := preparedCurrentCallStore(t)
if err := s.MarkReadyForSIP(currentDispatcherID, 8); err != nil {
t.Fatal(err)
}
cmd := currentCall("ongoing-before-sip-change")
if _, _, err := s.RecordExecute(cmd); err != nil {
t.Fatal(err)
}
if err := s.ReserveExecute(cmd.DispatcherID, cmd.EventID, currentReservation(), currentMondayUTC()); err != nil {
t.Fatal(err)
}
if err := s.MarkExecuteUnknown(cmd.DispatcherID, cmd.EventID); err != nil {
t.Fatal(err)
}
if count, err := s.OccupiedCalls(currentDispatcherID); err != nil || count != 1 {
t.Fatalf("unknown call was not counted: %d %v", count, err)
}
if err := s.NoteSIPChange(currentDispatcherID, 9); err != nil {
t.Fatal(err)
}
if admitted, err := s.CanAdmit(currentDispatcherID, 1001, "task-asr"); err != nil || admitted {
t.Fatalf("SIP change failed to close admission: %v %v", admitted, err)
}
if applied, pending, err := s.SIPState(currentDispatcherID); err != nil || applied != 8 || pending != 9 {
t.Fatalf("SIP notification not durable: applied=%d pending=%d %v", applied, pending, err)
}
if err := s.MarkReadyForSIP(currentDispatcherID, 9); err == nil {
t.Fatal("changed SIP opened without matching task snapshot or drain")
}
snapshot, err := s.ReadSnapshot(currentDispatcherID, 1001, "task-asr")
if err != nil {
t.Fatal(err)
}
snapshot.SIP.Revision = 9
if err := s.SaveSnapshot(snapshot); err != nil {
t.Fatal(err)
}
if err := s.MarkReadyForSIP(currentDispatcherID, 9); err == nil {
t.Fatal("unknown ongoing call was released by SIP change")
}
// An external definitive call end must be recorded before old SIP loads can
// drain. Unknown outcomes never expire or auto-release on their own.
if _, err := s.db.Exec(`UPDATE dispatcher_inbox SET status='finished' WHERE dispatcher_id=? AND event_id=? AND status='unknown'`, cmd.DispatcherID, cmd.EventID); err != nil {
t.Fatal(err)
}
if err := s.MarkReadyForSIP(currentDispatcherID, 9); err != nil {
t.Fatal(err)
}
if admitted, err := s.CanAdmit(currentDispatcherID, 1001, "task-asr"); err != nil || !admitted {
t.Fatalf("drained verified SIP revision did not reopen: %v %v", admitted, err)
}
if applied, pending, err := s.SIPState(currentDispatcherID); err != nil || applied != 9 || pending != 0 {
t.Fatalf("SIP state not committed after reload: %d %d %v", applied, pending, err)
}
if err := s.NoteSIPChange(currentDispatcherID, 8); err != nil {
t.Fatal(err)
}
if applied, pending, err := s.SIPState(currentDispatcherID); err != nil || applied != 9 || pending != 0 {
t.Fatalf("stale notification downgraded applied SIP: %d %d %v", applied, pending, err)
}
}
func TestCurrentPendingSIPChangeSurvivesRestartWithoutOldDataDeletion(t *testing.T) {
path := filepath.Join(t.TempDir(), "state.db")
s, err := OpenCurrent(path)
if err != nil {
t.Fatal(err)
}
if err := s.NoteSIPChange(currentDispatcherID, 12); err != nil {
t.Fatal(err)
}
if err := s.Close(); err != nil {
t.Fatal(err)
}
reopened, err := OpenCurrent(path)
if err != nil {
t.Fatal(err)
}
defer reopened.Close()
if applied, pending, err := reopened.SIPState(currentDispatcherID); err != nil || applied != 0 || pending != 12 {
t.Fatalf("lost pending SIP notification on restart: %d %d %v", applied, pending, err)
}
if err := reopened.MarkReadyForSIP(currentDispatcherID, 11); err == nil {
t.Fatal("older loaded SIP overrode durable pending notification")
}
}