openai_guardian_affinity_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "net/http"
  6. "net/http/httptest"
  7. "testing"
  8. "time"
  9. "github.com/Wei-Shaw/sub2api/internal/config"
  10. "github.com/gin-gonic/gin"
  11. "github.com/stretchr/testify/require"
  12. )
  13. type guardianAffinityGroupRepo struct {
  14. GroupRepository
  15. group *Group
  16. err error
  17. }
  18. type guardianAffinityAccountRepo struct {
  19. schedulerGroupAwareOpenAIAccountRepo
  20. setErrorCalls int
  21. }
  22. func (r *guardianAffinityAccountRepo) SetError(context.Context, int64, string) error {
  23. r.setErrorCalls++
  24. return nil
  25. }
  26. func (r guardianAffinityGroupRepo) GetByID(context.Context, int64) (*Group, error) {
  27. if r.err != nil {
  28. return nil, r.err
  29. }
  30. return r.group, nil
  31. }
  32. func (r guardianAffinityGroupRepo) GetByIDLite(context.Context, int64) (*Group, error) {
  33. if r.err != nil {
  34. return nil, r.err
  35. }
  36. return r.group, nil
  37. }
  38. func guardianAffinityTestContext(t *testing.T, model, subagent, parentHeader, metadata string) context.Context {
  39. t.Helper()
  40. gin.SetMode(gin.TestMode)
  41. rec := httptest.NewRecorder()
  42. c, _ := gin.CreateTestContext(rec)
  43. c.Request = httptest.NewRequest(http.MethodPost, "/openai/v1/responses", nil)
  44. c.Request.Header.Set(openAISubagentHeader, subagent)
  45. if parentHeader != "" {
  46. c.Request.Header.Set(codexParentThreadIDHeader, parentHeader)
  47. }
  48. if metadata != "" {
  49. c.Request.Header.Set(codexTurnMetadataHeader, metadata)
  50. }
  51. return WithOpenAIGuardianParentAffinity(context.Background(), c, nil, model)
  52. }
  53. func TestWithOpenAIGuardianParentAffinity_RequiresUnambiguousReviewLineage(t *testing.T) {
  54. parentID := "11111111-1111-4111-8111-111111111111"
  55. wantHash := DeriveSessionHashFromSeed(parentID)
  56. for _, subagent := range []string{"guardian", "review", "GUARDIAN"} {
  57. t.Run(subagent, func(t *testing.T) {
  58. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, subagent, parentID, `{"parent_thread_id":"`+parentID+`"}`)
  59. affinity, ok := openAIGuardianParentAffinityFromContext(ctx)
  60. require.True(t, ok)
  61. require.Equal(t, wantHash, affinity.currentSessionHash)
  62. })
  63. }
  64. t.Run("metadata only", func(t *testing.T) {
  65. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", "", `{"parent_thread_id":"`+parentID+`"}`)
  66. _, ok := openAIGuardianParentAffinityFromContext(ctx)
  67. require.True(t, ok)
  68. })
  69. t.Run("websocket envelope metadata", func(t *testing.T) {
  70. gin.SetMode(gin.TestMode)
  71. rec := httptest.NewRecorder()
  72. c, _ := gin.CreateTestContext(rec)
  73. c.Request = httptest.NewRequest(http.MethodGet, "/openai/v1/responses", nil)
  74. body := []byte(`{"type":"response.create","response":{"model":"codex-auto-review","client_metadata":{"x-codex-turn-metadata":"{\"parent_thread_id\":\"` + parentID + `\",\"subagent_kind\":\"guardian\"}"}}}`)
  75. ctx := WithOpenAIGuardianParentAffinity(context.Background(), c, body, codexAutoReviewModel)
  76. affinity, ok := openAIGuardianParentAffinityFromContext(ctx)
  77. require.True(t, ok)
  78. require.Equal(t, wantHash, affinity.currentSessionHash)
  79. })
  80. for name, ctx := range map[string]context.Context{
  81. "ordinary model": guardianAffinityTestContext(t, "gpt-5.6-sol", "guardian", parentID, ""),
  82. "ordinary subagent": guardianAffinityTestContext(t, codexAutoReviewModel, "collab_spawn", parentID, ""),
  83. "missing parent": guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", "", ""),
  84. "conflicting lineage": guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, `{"parent_thread_id":"different-parent"}`),
  85. "conflicting subagent": guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, `{"parent_thread_id":"`+parentID+`","subagent_kind":"collab_spawn"}`),
  86. } {
  87. t.Run(name, func(t *testing.T) {
  88. _, ok := openAIGuardianParentAffinityFromContext(ctx)
  89. require.False(t, ok)
  90. })
  91. }
  92. }
  93. func TestOpenAIGatewayService_GuardianParentAffinitySelectsParentAccountAcrossSchedulers(t *testing.T) {
  94. parentID := "22222222-2222-4222-8222-222222222222"
  95. parentHash := DeriveSessionHashFromSeed(parentID)
  96. groupID := int64(102001)
  97. for _, mode := range []struct {
  98. name string
  99. advanced string
  100. stickyWeighted string
  101. }{
  102. {name: "legacy", advanced: "false"},
  103. {name: "advanced", advanced: "true"},
  104. {name: "advanced sticky weighted", advanced: "true", stickyWeighted: "true"},
  105. } {
  106. t.Run(mode.name, func(t *testing.T) {
  107. accounts := []Account{
  108. {
  109. ID: 39001, Platform: PlatformOpenAI, Type: AccountTypeOAuth,
  110. Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 10,
  111. GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"},
  112. },
  113. {
  114. ID: 39002, Platform: PlatformOpenAI, Type: AccountTypeOAuth,
  115. Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 0,
  116. GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"},
  117. },
  118. }
  119. cfg := &config.Config{}
  120. cfg.Gateway.OpenAIWS.LBTopK = 2
  121. cache := &schedulerTestGatewayCache{sessionBindings: map[string]int64{"openai:" + parentHash: 39001}}
  122. svc := &OpenAIGatewayService{
  123. accountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}},
  124. cache: cache,
  125. cfg: cfg,
  126. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService(mode.advanced, mode.stickyWeighted),
  127. concurrencyService: NewConcurrencyService(schedulerTestConcurrencyCache{acquireResults: map[int64]bool{39001: true, 39002: true}}),
  128. }
  129. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, "")
  130. selection, decision, err := svc.SelectAccountWithScheduler(
  131. ctx, &groupID, "", "guardian-child-session", codexAutoReviewModel,
  132. nil, OpenAIUpstreamTransportAny, false,
  133. )
  134. require.NoError(t, err)
  135. require.NotNil(t, selection)
  136. require.Equal(t, int64(39001), selection.Account.ID)
  137. require.Equal(t, openAIAccountScheduleLayerGuardianParent, decision.Layer)
  138. require.Zero(t, cache.deletedSessions["openai:"+parentHash])
  139. if selection.ReleaseFunc != nil {
  140. selection.ReleaseFunc()
  141. }
  142. })
  143. }
  144. }
  145. func TestOpenAIGatewayService_GuardianParentAffinityFallsBackWithoutCrossGroupOrFailoverBypass(t *testing.T) {
  146. parentID := "33333333-3333-4333-8333-333333333333"
  147. parentHash := DeriveSessionHashFromSeed(parentID)
  148. groupID := int64(102011)
  149. otherGroupID := int64(102012)
  150. for name, excluded := range map[string]map[int64]struct{}{
  151. "parent moved out of group": nil,
  152. "parent excluded after upstream failure": {39011: {}},
  153. } {
  154. t.Run(name, func(t *testing.T) {
  155. parentGroups := []int64{groupID}
  156. if excluded == nil {
  157. parentGroups = []int64{otherGroupID}
  158. }
  159. accounts := []Account{
  160. {ID: 39011, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 0, GroupIDs: parentGroups, Credentials: map[string]any{"plan_type": "team"}},
  161. {ID: 39012, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 5, GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"}},
  162. }
  163. cache := &schedulerTestGatewayCache{sessionBindings: map[string]int64{"openai:" + parentHash: 39011}}
  164. svc := &OpenAIGatewayService{
  165. accountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}},
  166. cache: cache,
  167. cfg: &config.Config{},
  168. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService("true"),
  169. concurrencyService: NewConcurrencyService(schedulerTestConcurrencyCache{acquireResults: map[int64]bool{39011: true, 39012: true}}),
  170. }
  171. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, "")
  172. selection, _, err := svc.SelectAccountWithScheduler(
  173. ctx, &groupID, "", "guardian-fallback-child", codexAutoReviewModel,
  174. excluded, OpenAIUpstreamTransportAny, false,
  175. )
  176. require.NoError(t, err)
  177. require.NotNil(t, selection)
  178. require.Equal(t, int64(39012), selection.Account.ID)
  179. require.Zero(t, cache.deletedSessions["openai:"+parentHash], "a child request must never delete its parent's binding")
  180. if selection.ReleaseFunc != nil {
  181. selection.ReleaseFunc()
  182. }
  183. })
  184. }
  185. }
  186. func TestOpenAIGatewayService_GuardianParentHashCollisionPreservesParentBinding(t *testing.T) {
  187. parentID := "44444444-4444-4444-8444-444444444444"
  188. parentHash := DeriveSessionHashFromSeed(parentID)
  189. groupID := int64(102021)
  190. otherGroupID := int64(102022)
  191. for _, advanced := range []string{"false", "true"} {
  192. t.Run(map[string]string{"false": "legacy", "true": "advanced"}[advanced], func(t *testing.T) {
  193. accounts := []Account{
  194. {ID: 39021, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 0, GroupIDs: []int64{otherGroupID}, Credentials: map[string]any{"plan_type": "team"}},
  195. {ID: 39022, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 5, GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"}},
  196. }
  197. cache := &schedulerTestGatewayCache{sessionBindings: map[string]int64{"openai:" + parentHash: 39021}}
  198. svc := &OpenAIGatewayService{
  199. accountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}},
  200. cache: cache,
  201. cfg: &config.Config{},
  202. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService(advanced),
  203. concurrencyService: NewConcurrencyService(schedulerTestConcurrencyCache{acquireResults: map[int64]bool{39021: true, 39022: true}}),
  204. }
  205. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, "")
  206. selection, _, err := svc.SelectAccountWithScheduler(
  207. ctx, &groupID, "", parentHash, codexAutoReviewModel,
  208. nil, OpenAIUpstreamTransportAny, false,
  209. )
  210. require.NoError(t, err)
  211. require.NotNil(t, selection)
  212. require.Equal(t, int64(39022), selection.Account.ID)
  213. require.NoError(t, svc.BindStickySessionAfterProfitAdmission(ctx, &groupID, parentHash, selection.Account.ID))
  214. require.Equal(t, int64(39021), cache.sessionBindings["openai:"+parentHash])
  215. require.Zero(t, cache.deletedSessions["openai:"+parentHash])
  216. if selection.ReleaseFunc != nil {
  217. selection.ReleaseFunc()
  218. }
  219. })
  220. }
  221. }
  222. func TestOpenAIGatewayService_GuardianParentAffinityHonorsRequiredPrivacy(t *testing.T) {
  223. parentID := "55555555-5555-4555-8555-555555555555"
  224. parentHash := DeriveSessionHashFromSeed(parentID)
  225. groupID := int64(102031)
  226. for _, advanced := range []string{"false", "true"} {
  227. t.Run(map[string]string{"false": "legacy", "true": "advanced"}[advanced], func(t *testing.T) {
  228. accounts := []Account{
  229. {ID: 39031, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 0, GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"}},
  230. {ID: 39032, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 5, GroupIDs: []int64{groupID}, Credentials: map[string]any{"plan_type": "team"}, Extra: map[string]any{"privacy_mode": PrivacyModeTrainingOff}},
  231. }
  232. repo := &guardianAffinityAccountRepo{schedulerGroupAwareOpenAIAccountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}}}
  233. cache := &schedulerTestGatewayCache{sessionBindings: map[string]int64{"openai:" + parentHash: 39031}}
  234. svc := &OpenAIGatewayService{
  235. accountRepo: repo,
  236. cache: cache,
  237. cfg: &config.Config{},
  238. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService(advanced),
  239. concurrencyService: NewConcurrencyService(schedulerTestConcurrencyCache{acquireResults: map[int64]bool{39031: true, 39032: true}}),
  240. schedulerSnapshot: &SchedulerSnapshotService{
  241. accountRepo: repo,
  242. groupRepo: guardianAffinityGroupRepo{group: &Group{ID: groupID, Name: "privacy", RequirePrivacySet: true}},
  243. },
  244. }
  245. ctx := guardianAffinityTestContext(t, codexAutoReviewModel, "guardian", parentID, "")
  246. selection, _, err := svc.SelectAccountWithScheduler(
  247. ctx, &groupID, "", "guardian-privacy-child", codexAutoReviewModel,
  248. nil, OpenAIUpstreamTransportAny, false,
  249. )
  250. require.NoError(t, err)
  251. require.NotNil(t, selection)
  252. require.Equal(t, int64(39032), selection.Account.ID)
  253. require.Zero(t, repo.setErrorCalls, "a group-scoped privacy gate must not globally error a shared account")
  254. require.False(t, svc.isOpenAIAccountRequestRuntimeBlocked(&accounts[0], codexAutoReviewModel))
  255. if selection.ReleaseFunc != nil {
  256. selection.ReleaseFunc()
  257. }
  258. })
  259. }
  260. }
  261. func TestOpenAIGatewayService_PreviousResponseHonorsGroupAndRequiredPrivacy(t *testing.T) {
  262. groupID := int64(3904)
  263. tests := []struct {
  264. name string
  265. boundAccount Account
  266. groupErr error
  267. }{
  268. {
  269. name: "privacy unset",
  270. boundAccount: Account{
  271. ID: 39041, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  272. Status: StatusActive, Schedulable: true, Concurrency: 1,
  273. GroupIDs: []int64{groupID},
  274. Extra: map[string]any{"openai_apikey_responses_websockets_v2_enabled": true},
  275. },
  276. },
  277. {
  278. name: "privacy policy lookup error fails closed",
  279. boundAccount: Account{
  280. ID: 39041, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  281. Status: StatusActive, Schedulable: true, Concurrency: 1,
  282. GroupIDs: []int64{groupID},
  283. Extra: map[string]any{"openai_apikey_responses_websockets_v2_enabled": true},
  284. },
  285. groupErr: errors.New("group repository unavailable"),
  286. },
  287. {
  288. name: "different group",
  289. boundAccount: Account{
  290. ID: 39041, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  291. Status: StatusActive, Schedulable: true, Concurrency: 1,
  292. GroupIDs: []int64{groupID + 1},
  293. Extra: map[string]any{
  294. "openai_apikey_responses_websockets_v2_enabled": true,
  295. "privacy_mode": PrivacyModeTrainingOff,
  296. },
  297. },
  298. },
  299. }
  300. for _, tc := range tests {
  301. t.Run(tc.name, func(t *testing.T) {
  302. fallback := Account{
  303. ID: 39042, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  304. Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 5,
  305. GroupIDs: []int64{groupID},
  306. Extra: map[string]any{
  307. "openai_apikey_responses_websockets_v2_enabled": true,
  308. "privacy_mode": PrivacyModeTrainingOff,
  309. },
  310. }
  311. accounts := []Account{tc.boundAccount, fallback}
  312. repo := &guardianAffinityAccountRepo{schedulerGroupAwareOpenAIAccountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}}}
  313. cache := &schedulerTestGatewayCache{}
  314. store := NewOpenAIWSStateStore(cache)
  315. groupRepo := guardianAffinityGroupRepo{
  316. group: &Group{
  317. ID: groupID, Name: "privacy-required", Platform: PlatformOpenAI,
  318. Status: StatusActive, RequirePrivacySet: true,
  319. },
  320. err: tc.groupErr,
  321. }
  322. svc := &OpenAIGatewayService{
  323. accountRepo: repo,
  324. cache: cache,
  325. cfg: &config.Config{},
  326. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService("true"),
  327. concurrencyService: NewConcurrencyService(&schedulerTestConcurrencyCache{}),
  328. openaiWSStateStore: store,
  329. schedulerSnapshot: &SchedulerSnapshotService{
  330. accountRepo: repo,
  331. groupRepo: groupRepo,
  332. },
  333. }
  334. responseID := "resp_privacy_guard"
  335. require.NoError(t, store.BindResponseAccount(context.Background(), groupID, responseID, tc.boundAccount.ID, time.Hour))
  336. directSelection, directErr := svc.SelectAccountByPreviousResponseID(
  337. context.Background(), &groupID, responseID, codexAutoReviewModel, nil, false,
  338. )
  339. require.NoError(t, directErr)
  340. require.Nil(t, directSelection, "the previous-response helper must enforce fresh group/privacy state")
  341. selection, decision, err := svc.SelectAccountWithSchedulerForCapability(
  342. context.Background(), &groupID, responseID, "", codexAutoReviewModel,
  343. nil, OpenAIUpstreamTransportAny, OpenAIEndpointCapabilityResponses,
  344. false, false, true,
  345. )
  346. require.NoError(t, err)
  347. require.NotNil(t, selection)
  348. require.Equal(t, fallback.ID, selection.Account.ID)
  349. require.NotEqual(t, openAIAccountScheduleLayerPreviousResponse, decision.Layer)
  350. require.Zero(t, repo.setErrorCalls)
  351. require.False(t, svc.isOpenAIAccountRequestRuntimeBlocked(&accounts[0], codexAutoReviewModel))
  352. boundAccountID, getErr := store.GetResponseAccount(context.Background(), groupID, responseID)
  353. require.NoError(t, getErr)
  354. require.Equal(t, tc.boundAccount.ID, boundAccountID, "transient policy misses must preserve the response binding")
  355. if selection.ReleaseFunc != nil {
  356. selection.ReleaseFunc()
  357. }
  358. })
  359. }
  360. }
  361. func TestOpenAIGatewayService_PreviousResponseSimpleModeIgnoresGroupMembership(t *testing.T) {
  362. groupID := int64(3905)
  363. bound := Account{
  364. ID: 39051, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  365. Status: StatusActive, Schedulable: true, Concurrency: 1,
  366. GroupIDs: []int64{groupID + 1},
  367. Extra: map[string]any{"openai_apikey_responses_websockets_v2_enabled": true},
  368. }
  369. fallback := Account{
  370. ID: 39052, Platform: PlatformOpenAI, Type: AccountTypeAPIKey,
  371. Status: StatusActive, Schedulable: true, Concurrency: 1, Priority: 10,
  372. GroupIDs: []int64{groupID},
  373. Extra: map[string]any{"openai_apikey_responses_websockets_v2_enabled": true},
  374. }
  375. accounts := []Account{bound, fallback}
  376. repo := &guardianAffinityAccountRepo{schedulerGroupAwareOpenAIAccountRepo: schedulerGroupAwareOpenAIAccountRepo{schedulerTestOpenAIAccountRepo{accounts: accounts}}}
  377. cache := &schedulerTestGatewayCache{}
  378. store := NewOpenAIWSStateStore(cache)
  379. cfg := &config.Config{RunMode: config.RunModeSimple}
  380. svc := &OpenAIGatewayService{
  381. accountRepo: repo,
  382. cache: cache,
  383. cfg: cfg,
  384. rateLimitService: newOpenAIAdvancedSchedulerRateLimitService("true"),
  385. concurrencyService: NewConcurrencyService(&schedulerTestConcurrencyCache{}),
  386. openaiWSStateStore: store,
  387. schedulerSnapshot: &SchedulerSnapshotService{
  388. accountRepo: repo,
  389. groupRepo: guardianAffinityGroupRepo{group: &Group{
  390. ID: groupID, Name: "simple-mode", Platform: PlatformOpenAI, Status: StatusActive,
  391. }},
  392. },
  393. }
  394. responseID := "resp_simple_mode_cross_group"
  395. require.NoError(t, store.BindResponseAccount(context.Background(), groupID, responseID, bound.ID, time.Hour))
  396. directSelection, err := svc.SelectAccountByPreviousResponseID(
  397. context.Background(), &groupID, responseID, codexAutoReviewModel, nil, false,
  398. )
  399. require.NoError(t, err)
  400. require.NotNil(t, directSelection)
  401. require.Equal(t, bound.ID, directSelection.Account.ID)
  402. if directSelection.ReleaseFunc != nil {
  403. directSelection.ReleaseFunc()
  404. }
  405. selection, decision, err := svc.SelectAccountWithSchedulerForCapability(
  406. context.Background(), &groupID, responseID, "", codexAutoReviewModel,
  407. nil, OpenAIUpstreamTransportAny, OpenAIEndpointCapabilityResponses,
  408. false, false, true,
  409. )
  410. require.NoError(t, err)
  411. require.NotNil(t, selection)
  412. require.Equal(t, bound.ID, selection.Account.ID)
  413. require.Equal(t, openAIAccountScheduleLayerPreviousResponse, decision.Layer)
  414. if selection.ReleaseFunc != nil {
  415. selection.ReleaseFunc()
  416. }
  417. }