gateway_routing_integration_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. //go:build integration
  2. package repository
  3. import (
  4. "context"
  5. "testing"
  6. dbent "github.com/Wei-Shaw/sub2api/ent"
  7. "github.com/Wei-Shaw/sub2api/internal/service"
  8. "github.com/stretchr/testify/suite"
  9. )
  10. // GatewayRoutingSuite 测试网关路由相关的数据库查询
  11. // 验证账户选择和分流逻辑在真实数据库环境下的行为
  12. type GatewayRoutingSuite struct {
  13. suite.Suite
  14. ctx context.Context
  15. client *dbent.Client
  16. accountRepo *accountRepository
  17. }
  18. func (s *GatewayRoutingSuite) SetupTest() {
  19. s.ctx = context.Background()
  20. tx := testEntTx(s.T())
  21. s.client = tx.Client()
  22. s.accountRepo = newAccountRepositoryWithSQL(s.client, tx, nil)
  23. }
  24. func TestGatewayRoutingSuite(t *testing.T) {
  25. suite.Run(t, new(GatewayRoutingSuite))
  26. }
  27. // TestListSchedulableByPlatforms_GeminiAndAntigravity 验证多平台账户查询
  28. func (s *GatewayRoutingSuite) TestListSchedulableByPlatforms_GeminiAndAntigravity() {
  29. // 创建各平台账户
  30. geminiAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  31. Name: "gemini-oauth",
  32. Platform: service.PlatformGemini,
  33. Type: service.AccountTypeOAuth,
  34. Status: service.StatusActive,
  35. Schedulable: true,
  36. Priority: 1,
  37. })
  38. antigravityAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  39. Name: "antigravity-oauth",
  40. Platform: service.PlatformAntigravity,
  41. Type: service.AccountTypeOAuth,
  42. Status: service.StatusActive,
  43. Schedulable: true,
  44. Priority: 2,
  45. Credentials: map[string]any{
  46. "access_token": "test-token",
  47. "refresh_token": "test-refresh",
  48. "project_id": "test-project",
  49. },
  50. })
  51. // 创建不应被选中的 anthropic 账户
  52. mustCreateAccount(s.T(), s.client, &service.Account{
  53. Name: "anthropic-oauth",
  54. Platform: service.PlatformAnthropic,
  55. Type: service.AccountTypeOAuth,
  56. Status: service.StatusActive,
  57. Schedulable: true,
  58. Priority: 0,
  59. })
  60. // 查询 gemini + antigravity 平台
  61. accounts, err := s.accountRepo.ListSchedulableByPlatforms(s.ctx, []string{
  62. service.PlatformGemini,
  63. service.PlatformAntigravity,
  64. })
  65. s.Require().NoError(err)
  66. s.Require().Len(accounts, 2, "应返回 gemini 和 antigravity 两个账户")
  67. // 验证返回的账户平台
  68. platforms := make(map[string]bool)
  69. for _, acc := range accounts {
  70. platforms[acc.Platform] = true
  71. }
  72. s.Require().True(platforms[service.PlatformGemini], "应包含 gemini 账户")
  73. s.Require().True(platforms[service.PlatformAntigravity], "应包含 antigravity 账户")
  74. s.Require().False(platforms[service.PlatformAnthropic], "不应包含 anthropic 账户")
  75. // 验证账户 ID 匹配
  76. ids := make(map[int64]bool)
  77. for _, acc := range accounts {
  78. ids[acc.ID] = true
  79. }
  80. s.Require().True(ids[geminiAcc.ID])
  81. s.Require().True(ids[antigravityAcc.ID])
  82. }
  83. // TestListSchedulableByGroupIDAndPlatforms_WithGroupBinding 验证按分组过滤
  84. func (s *GatewayRoutingSuite) TestListSchedulableByGroupIDAndPlatforms_WithGroupBinding() {
  85. // 创建 gemini 分组
  86. group := mustCreateGroup(s.T(), s.client, &service.Group{
  87. Name: "gemini-group",
  88. Platform: service.PlatformGemini,
  89. Status: service.StatusActive,
  90. })
  91. // 创建账户
  92. boundAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  93. Name: "bound-antigravity",
  94. Platform: service.PlatformAntigravity,
  95. Status: service.StatusActive,
  96. Schedulable: true,
  97. })
  98. unboundAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  99. Name: "unbound-antigravity",
  100. Platform: service.PlatformAntigravity,
  101. Status: service.StatusActive,
  102. Schedulable: true,
  103. })
  104. // 只绑定一个账户到分组
  105. mustBindAccountToGroup(s.T(), s.client, boundAcc.ID, group.ID, 1)
  106. // 查询分组内的账户
  107. accounts, err := s.accountRepo.ListSchedulableByGroupIDAndPlatforms(s.ctx, group.ID, []string{
  108. service.PlatformGemini,
  109. service.PlatformAntigravity,
  110. })
  111. s.Require().NoError(err)
  112. s.Require().Len(accounts, 1, "应只返回绑定到分组的账户")
  113. s.Require().Equal(boundAcc.ID, accounts[0].ID)
  114. // 确认未绑定的账户不在结果中
  115. for _, acc := range accounts {
  116. s.Require().NotEqual(unboundAcc.ID, acc.ID, "不应包含未绑定的账户")
  117. }
  118. }
  119. // TestListSchedulableByPlatform_Antigravity 验证单平台查询
  120. func (s *GatewayRoutingSuite) TestListSchedulableByPlatform_Antigravity() {
  121. // 创建多种平台账户
  122. mustCreateAccount(s.T(), s.client, &service.Account{
  123. Name: "gemini-1",
  124. Platform: service.PlatformGemini,
  125. Status: service.StatusActive,
  126. Schedulable: true,
  127. })
  128. antigravity := mustCreateAccount(s.T(), s.client, &service.Account{
  129. Name: "antigravity-1",
  130. Platform: service.PlatformAntigravity,
  131. Status: service.StatusActive,
  132. Schedulable: true,
  133. })
  134. // 只查询 antigravity 平台
  135. accounts, err := s.accountRepo.ListSchedulableByPlatform(s.ctx, service.PlatformAntigravity)
  136. s.Require().NoError(err)
  137. s.Require().Len(accounts, 1)
  138. s.Require().Equal(antigravity.ID, accounts[0].ID)
  139. s.Require().Equal(service.PlatformAntigravity, accounts[0].Platform)
  140. }
  141. // TestSchedulableFilter_ExcludesInactive 验证不可调度账户被过滤
  142. func (s *GatewayRoutingSuite) TestSchedulableFilter_ExcludesInactive() {
  143. // 创建可调度账户
  144. activeAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  145. Name: "active-antigravity",
  146. Platform: service.PlatformAntigravity,
  147. Status: service.StatusActive,
  148. Schedulable: true,
  149. })
  150. // 创建不可调度账户(需要先创建再更新,因为 fixture 默认设置 Schedulable=true)
  151. inactiveAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  152. Name: "inactive-antigravity",
  153. Platform: service.PlatformAntigravity,
  154. Status: service.StatusActive,
  155. })
  156. s.Require().NoError(s.client.Account.UpdateOneID(inactiveAcc.ID).SetSchedulable(false).Exec(s.ctx))
  157. // 创建错误状态账户
  158. mustCreateAccount(s.T(), s.client, &service.Account{
  159. Name: "error-antigravity",
  160. Platform: service.PlatformAntigravity,
  161. Status: service.StatusError,
  162. Schedulable: true,
  163. })
  164. accounts, err := s.accountRepo.ListSchedulableByPlatform(s.ctx, service.PlatformAntigravity)
  165. s.Require().NoError(err)
  166. s.Require().Len(accounts, 1, "应只返回可调度的 active 账户")
  167. s.Require().Equal(activeAcc.ID, accounts[0].ID)
  168. }
  169. // TestPlatformRoutingDecision 验证平台路由决策
  170. // 这个测试模拟 Handler 层在选择账户后的路由决策逻辑
  171. func (s *GatewayRoutingSuite) TestPlatformRoutingDecision() {
  172. // 创建两种平台的账户
  173. geminiAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  174. Name: "gemini-route-test",
  175. Platform: service.PlatformGemini,
  176. Status: service.StatusActive,
  177. Schedulable: true,
  178. })
  179. antigravityAcc := mustCreateAccount(s.T(), s.client, &service.Account{
  180. Name: "antigravity-route-test",
  181. Platform: service.PlatformAntigravity,
  182. Status: service.StatusActive,
  183. Schedulable: true,
  184. })
  185. tests := []struct {
  186. name string
  187. accountID int64
  188. expectedService string
  189. }{
  190. {
  191. name: "Gemini账户路由到ForwardNative",
  192. accountID: geminiAcc.ID,
  193. expectedService: "GeminiMessagesCompatService.ForwardNative",
  194. },
  195. {
  196. name: "Antigravity账户路由到ForwardGemini",
  197. accountID: antigravityAcc.ID,
  198. expectedService: "AntigravityGatewayService.ForwardGemini",
  199. },
  200. }
  201. for _, tt := range tests {
  202. s.Run(tt.name, func() {
  203. // 从数据库获取账户
  204. account, err := s.accountRepo.GetByID(s.ctx, tt.accountID)
  205. s.Require().NoError(err)
  206. // 模拟 Handler 层的路由决策
  207. var routedService string
  208. if account.Platform == service.PlatformAntigravity {
  209. routedService = "AntigravityGatewayService.ForwardGemini"
  210. } else {
  211. routedService = "GeminiMessagesCompatService.ForwardNative"
  212. }
  213. s.Require().Equal(tt.expectedService, routedService)
  214. })
  215. }
  216. }