subscription_maintenance_queue_test.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. //go:build unit
  2. package service
  3. import (
  4. "sync"
  5. "sync/atomic"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestSubscriptionMaintenanceQueue_TryEnqueue_QueueFull(t *testing.T) {
  11. q := NewSubscriptionMaintenanceQueue(1, 1)
  12. t.Cleanup(q.Stop)
  13. block := make(chan struct{})
  14. var started atomic.Int32
  15. require.NoError(t, q.TryEnqueue(func() {
  16. started.Store(1)
  17. <-block
  18. }))
  19. // Wait until worker started consuming the first task.
  20. require.Eventually(t, func() bool { return started.Load() == 1 }, time.Second, 10*time.Millisecond)
  21. // Queue size is 1; with the worker blocked, enqueueing one more should fill it.
  22. require.NoError(t, q.TryEnqueue(func() {}))
  23. // Now the queue is full; next enqueue must fail.
  24. err := q.TryEnqueue(func() {})
  25. require.Error(t, err)
  26. require.Contains(t, err.Error(), "full")
  27. close(block)
  28. }
  29. func TestSubscriptionMaintenanceQueue_TryEnqueue_PanicDoesNotKillWorker(t *testing.T) {
  30. q := NewSubscriptionMaintenanceQueue(1, 8)
  31. t.Cleanup(q.Stop)
  32. require.NoError(t, q.TryEnqueue(func() { panic("boom") }))
  33. done := make(chan struct{})
  34. require.NoError(t, q.TryEnqueue(func() { close(done) }))
  35. select {
  36. case <-done:
  37. // ok
  38. case <-time.After(time.Second):
  39. t.Fatalf("worker did not continue after panic")
  40. }
  41. }
  42. func TestSubscriptionMaintenanceQueue_TryEnqueue_AfterStop(t *testing.T) {
  43. q := NewSubscriptionMaintenanceQueue(1, 8)
  44. q.Stop()
  45. err := q.TryEnqueue(func() {})
  46. require.Error(t, err)
  47. require.Contains(t, err.Error(), "stopped")
  48. }
  49. func TestSubscriptionMaintenanceQueue_TryEnqueue_NilReceiver(t *testing.T) {
  50. var q *SubscriptionMaintenanceQueue
  51. err := q.TryEnqueue(func() {})
  52. require.Error(t, err)
  53. require.Contains(t, err.Error(), "nil")
  54. }
  55. func TestSubscriptionMaintenanceQueue_TryEnqueue_NilTask(t *testing.T) {
  56. q := NewSubscriptionMaintenanceQueue(1, 8)
  57. t.Cleanup(q.Stop)
  58. err := q.TryEnqueue(nil)
  59. require.Error(t, err)
  60. require.Contains(t, err.Error(), "nil")
  61. }
  62. func TestSubscriptionMaintenanceQueue_Stop_NilReceiver(t *testing.T) {
  63. var q *SubscriptionMaintenanceQueue
  64. // 不应 panic
  65. q.Stop()
  66. }
  67. func TestSubscriptionMaintenanceQueue_Stop_Idempotent(t *testing.T) {
  68. q := NewSubscriptionMaintenanceQueue(1, 4)
  69. q.Stop()
  70. q.Stop() // 第二次调用不应 panic
  71. }
  72. func TestNewSubscriptionMaintenanceQueue_ZeroParams(t *testing.T) {
  73. q := NewSubscriptionMaintenanceQueue(0, 0)
  74. t.Cleanup(q.Stop)
  75. // workerCount/queueSize 应被修正为 1
  76. err := q.TryEnqueue(func() {})
  77. require.NoError(t, err)
  78. }
  79. func TestNewSubscriptionMaintenanceQueue_NegativeParams(t *testing.T) {
  80. q := NewSubscriptionMaintenanceQueue(-1, -1)
  81. t.Cleanup(q.Stop)
  82. err := q.TryEnqueue(func() {})
  83. require.NoError(t, err)
  84. }
  85. func TestSubscriptionMaintenanceQueue_ConcurrentEnqueueAndStop(t *testing.T) {
  86. // 并发调用 TryEnqueue 和 Stop 不应 panic
  87. for i := 0; i < 100; i++ {
  88. q := NewSubscriptionMaintenanceQueue(2, 4)
  89. var wg sync.WaitGroup
  90. wg.Add(2)
  91. go func() {
  92. defer wg.Done()
  93. for j := 0; j < 50; j++ {
  94. _ = q.TryEnqueue(func() {})
  95. }
  96. }()
  97. go func() {
  98. defer wg.Done()
  99. time.Sleep(time.Microsecond * time.Duration(i%10))
  100. q.Stop()
  101. }()
  102. wg.Wait()
  103. }
  104. }