gateway_service_selection_failure_stats_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package service
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. )
  8. func TestCollectSelectionFailureStats(t *testing.T) {
  9. svc := &GatewayService{}
  10. model := "gpt-5.4"
  11. resetAt := time.Now().Add(2 * time.Minute).Format(time.RFC3339)
  12. accounts := []Account{
  13. // excluded
  14. {
  15. ID: 1,
  16. Platform: PlatformOpenAI,
  17. Status: StatusActive,
  18. Schedulable: true,
  19. },
  20. // unschedulable
  21. {
  22. ID: 2,
  23. Platform: PlatformOpenAI,
  24. Status: StatusActive,
  25. Schedulable: false,
  26. },
  27. // platform filtered
  28. {
  29. ID: 3,
  30. Platform: PlatformAntigravity,
  31. Status: StatusActive,
  32. Schedulable: true,
  33. },
  34. // model unsupported
  35. {
  36. ID: 4,
  37. Platform: PlatformOpenAI,
  38. Status: StatusActive,
  39. Schedulable: true,
  40. Credentials: map[string]any{
  41. "model_mapping": map[string]any{
  42. "gpt-image": "gpt-image",
  43. },
  44. },
  45. },
  46. // model rate limited
  47. {
  48. ID: 5,
  49. Platform: PlatformOpenAI,
  50. Status: StatusActive,
  51. Schedulable: true,
  52. Extra: map[string]any{
  53. "model_rate_limits": map[string]any{
  54. model: map[string]any{
  55. "rate_limit_reset_at": resetAt,
  56. },
  57. },
  58. },
  59. },
  60. // eligible
  61. {
  62. ID: 6,
  63. Platform: PlatformOpenAI,
  64. Status: StatusActive,
  65. Schedulable: true,
  66. },
  67. }
  68. excluded := map[int64]struct{}{1: {}}
  69. stats := svc.collectSelectionFailureStats(context.Background(), accounts, model, PlatformOpenAI, excluded, false)
  70. if stats.Total != 6 {
  71. t.Fatalf("total=%d want=6", stats.Total)
  72. }
  73. if stats.Excluded != 1 {
  74. t.Fatalf("excluded=%d want=1", stats.Excluded)
  75. }
  76. if stats.Unschedulable != 1 {
  77. t.Fatalf("unschedulable=%d want=1", stats.Unschedulable)
  78. }
  79. if stats.PlatformFiltered != 1 {
  80. t.Fatalf("platform_filtered=%d want=1", stats.PlatformFiltered)
  81. }
  82. if stats.ModelUnsupported != 1 {
  83. t.Fatalf("model_unsupported=%d want=1", stats.ModelUnsupported)
  84. }
  85. if stats.ModelRateLimited != 1 {
  86. t.Fatalf("model_rate_limited=%d want=1", stats.ModelRateLimited)
  87. }
  88. if stats.Eligible != 1 {
  89. t.Fatalf("eligible=%d want=1", stats.Eligible)
  90. }
  91. }
  92. func TestDiagnoseSelectionFailure_UnschedulableDetail(t *testing.T) {
  93. svc := &GatewayService{}
  94. acc := &Account{
  95. ID: 7,
  96. Platform: PlatformOpenAI,
  97. Status: StatusActive,
  98. Schedulable: false,
  99. }
  100. diagnosis := svc.diagnoseSelectionFailure(context.Background(), acc, "gpt-5.4", PlatformOpenAI, map[int64]struct{}{}, false)
  101. if diagnosis.Category != "unschedulable" {
  102. t.Fatalf("category=%s want=unschedulable", diagnosis.Category)
  103. }
  104. if diagnosis.Detail != "generic_unschedulable" {
  105. t.Fatalf("detail=%s want=generic_unschedulable", diagnosis.Detail)
  106. }
  107. }
  108. func TestDiagnoseSelectionFailure_ModelRateLimitedDetail(t *testing.T) {
  109. svc := &GatewayService{}
  110. model := "gpt-5.4"
  111. resetAt := time.Now().Add(2 * time.Minute).UTC().Format(time.RFC3339)
  112. acc := &Account{
  113. ID: 8,
  114. Platform: PlatformOpenAI,
  115. Status: StatusActive,
  116. Schedulable: true,
  117. Extra: map[string]any{
  118. "model_rate_limits": map[string]any{
  119. model: map[string]any{
  120. "rate_limit_reset_at": resetAt,
  121. },
  122. },
  123. },
  124. }
  125. diagnosis := svc.diagnoseSelectionFailure(context.Background(), acc, model, PlatformOpenAI, map[int64]struct{}{}, false)
  126. if diagnosis.Category != "model_rate_limited" {
  127. t.Fatalf("category=%s want=model_rate_limited", diagnosis.Category)
  128. }
  129. if !strings.Contains(diagnosis.Detail, "remaining=") {
  130. t.Fatalf("detail=%s want contains remaining=", diagnosis.Detail)
  131. }
  132. }