package dispatcher import ( "context" "testing" "time" "git.ipao.vip/rogee/go-sip/internal/store" ) func TestPreDialWindowChangeReleasesUnusedReservation(t *testing.T) { controller, originator, _, s := newExecuteFixture(t) first := time.Date(2026, 9, 21, 19, 59, 59, 900_000_000, time.FixedZone("Asia/Shanghai", 8*3600)) second := time.Date(2026, 9, 21, 20, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)) calls := 0 controller.Now = func() time.Time { calls++ if calls == 1 { return first } return second } if err := controller.ProcessExecute(context.Background(), executeBody(t, "boundary-1", "15003164745")); err != nil { t.Fatal(err) } if len(originator.calls) != 0 { t.Fatal("dialed after window closed") } pending, err := s.ListPendingExecute(controller.DispatcherID) if err != nil || len(pending) != 1 || pending[0].EventID != "boundary-1" { t.Fatalf("unused reservation not returned to pending: %+v %v", pending, err) } occupied, err := s.TrunkOccupancy(controller.DispatcherID) if err != nil || occupied["trunk-mock"] != 0 { t.Fatalf("unused reservation held quota: %+v %v", occupied, err) } controller.Now = func() time.Time { return time.Date(2026, 9, 28, 9, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)) } if err := controller.ProcessPending(context.Background()); err != nil || len(originator.calls) != 1 { t.Fatalf("eligible pending call did not resume once: calls=%d err=%v", len(originator.calls), err) } } func TestFutureIssuedAtNeverDialsEarly(t *testing.T) { controller, originator, _, s := newExecuteFixture(t) controller.Now = func() time.Time { return time.Date(2026, 9, 21, 8, 59, 59, 0, time.FixedZone("Asia/Shanghai", 8*3600)) } body := executeBody(t, "future-1", "15003164745") if err := controller.ProcessExecute(context.Background(), body); err != nil { t.Fatal(err) } if len(originator.calls) != 0 { t.Fatal("future issued_at dialed early") } pending, err := s.ListPendingExecute(controller.DispatcherID) if err != nil || len(pending) != 1 { t.Fatalf("future command not durable: %+v %v", pending, err) } controller.Now = func() time.Time { return time.Date(2026, 9, 21, 9, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)) } if err := controller.ProcessPending(context.Background()); err != nil || len(originator.calls) != 1 { t.Fatalf("future command did not dispatch after issued_at: %d %v", len(originator.calls), err) } if _, _, err := s.RecordExecute(store.ExecuteCommand{DispatcherID: controller.DispatcherID, EventID: "future-1", TenantID: 1001, TaskID: "task-asr", Callee: "15830461047", IssuedAt: "2026-09-21T01:00:00Z"}); err == nil { t.Fatal("conflicting identity silently accepted") } }