Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

392 righe
17 KiB

  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "net/http"
  6. "testing"
  7. "time"
  8. "github.com/QuantumNous/new-api/model"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. cmerrs "gitlab.ecloud.com/ecloud/ecloudsdkcore/errs"
  12. cmmodel "gitlab.ecloud.com/ecloud/ecloudsdkmaas/model"
  13. )
  14. func TestChinaMobileAssetAdapterCreateAssetMapsRequestAndResponse(t *testing.T) {
  15. setChinaMobileAssetTestEnv(t, "")
  16. fake := &fakeChinaMobileAssetSDKClient{}
  17. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  18. assert.Equal(t, "ak", credential.AK)
  19. assert.Equal(t, "sk", credential.SK)
  20. assert.Equal(t, defaultChinaMobileAssetPoolID, credential.PoolID)
  21. return fake
  22. }}
  23. spec, ok := ParseAssetAction("CreateAsset")
  24. require.True(t, ok)
  25. resp, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{
  26. Action: spec,
  27. Version: "2024-01-01",
  28. Body: map[string]any{
  29. "GroupId": "group-1",
  30. "Name": "asset",
  31. "URL": "https://example.com/a.png",
  32. "AssetType": "Image",
  33. },
  34. })
  35. require.Nil(t, assetErr)
  36. require.NotNil(t, resp)
  37. require.NotNil(t, fake.createAssetRequest)
  38. assert.Equal(t, "group-1", *fake.createAssetRequest.CreateAssetBody.GroupId)
  39. assert.Equal(t, "asset", *fake.createAssetRequest.CreateAssetBody.AssetName)
  40. assert.Equal(t, "https://example.com/a.png", *fake.createAssetRequest.CreateAssetBody.AssetUrl)
  41. assert.Equal(t, cmmodel.CreateAssetBodyAssetTypeEnumImage, *fake.createAssetRequest.CreateAssetBody.AssetType)
  42. assert.Contains(t, string(resp.Body), `"RequestId":"req-1"`)
  43. assert.Contains(t, string(resp.Body), `"Result":"asset-1"`)
  44. }
  45. func TestChinaMobileAssetCredentialFromEnvDefaultsCenterPool(t *testing.T) {
  46. setChinaMobileAssetTestEnv(t, "")
  47. credential, err := chinaMobileAssetCredentialFromEnv()
  48. require.NoError(t, err)
  49. assert.Equal(t, "ak", credential.AK)
  50. assert.Equal(t, "sk", credential.SK)
  51. assert.Equal(t, defaultChinaMobileAssetPoolID, credential.PoolID)
  52. }
  53. func TestChinaMobileAssetCredentialFromEnvSupportsPoolID(t *testing.T) {
  54. setChinaMobileAssetTestEnv(t, "CIDC-RP-29")
  55. credential, err := chinaMobileAssetCredentialFromEnv()
  56. require.NoError(t, err)
  57. assert.Equal(t, "ak", credential.AK)
  58. assert.Equal(t, "sk", credential.SK)
  59. assert.Equal(t, "CIDC-RP-29", credential.PoolID)
  60. }
  61. func TestChinaMobileAssetCredentialFromEnvRequiresAKAndSK(t *testing.T) {
  62. t.Setenv(chinaMobileAssetAKEnv, "")
  63. t.Setenv(chinaMobileAssetSKEnv, "")
  64. t.Setenv(chinaMobileAssetPoolIDEnv, "")
  65. _, err := chinaMobileAssetCredentialFromEnv()
  66. require.Error(t, err)
  67. assert.Contains(t, err.Error(), chinaMobileAssetAKEnv)
  68. assert.Contains(t, err.Error(), chinaMobileAssetSKEnv)
  69. }
  70. func TestChinaMobileAssetAdapterAllOfficialActions(t *testing.T) {
  71. cases := []struct {
  72. action string
  73. body map[string]any
  74. assert func(t *testing.T, fake *fakeChinaMobileAssetSDKClient)
  75. }{
  76. {"CreateAssetGroup", map[string]any{"Name": "g", "GroupType": "AIGC"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  77. require.NotNil(t, fake.createAssetGroupRequest)
  78. }},
  79. {"CreateAsset", map[string]any{"GroupId": "g", "Name": "n", "URL": "https://example.com/a.png", "AssetType": "Image"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) { require.NotNil(t, fake.createAssetRequest) }},
  80. {"ListAssetGroups", map[string]any{"Filter": map[string]any{"GroupType": "AIGC"}, "PageNumber": float64(1), "PageSize": float64(10)}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  81. require.NotNil(t, fake.listAssetGroupsRequest)
  82. }},
  83. {"ListAssets", map[string]any{"Filter": map[string]any{"GroupType": "AIGC"}, "PageNumber": float64(1), "PageSize": float64(10)}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) { require.NotNil(t, fake.listAssetsRequest) }},
  84. {"GetAsset", map[string]any{"Id": "asset-1"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  85. require.NotNil(t, fake.getAssetRequest)
  86. assert.Equal(t, "asset-1", *fake.getAssetRequest.GetAssetPath.AssetId)
  87. }},
  88. {"GetAssetGroup", map[string]any{"Id": "group-1"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  89. require.NotNil(t, fake.getAssetGroupRequest)
  90. assert.Equal(t, "group-1", *fake.getAssetGroupRequest.GetAssetGroupPath.GroupId)
  91. }},
  92. {"UpdateAsset", map[string]any{"Id": "asset-1", "Name": "n"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) { require.NotNil(t, fake.updateAssetRequest) }},
  93. {"UpdateAssetGroup", map[string]any{"Id": "group-1", "Name": "g"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  94. require.NotNil(t, fake.updateAssetGroupRequest)
  95. }},
  96. {"DeleteAsset", map[string]any{"Id": "asset-1"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) { require.NotNil(t, fake.deleteAssetRequest) }},
  97. {"DeleteAssetGroup", map[string]any{"Id": "group-1"}, func(t *testing.T, fake *fakeChinaMobileAssetSDKClient) {
  98. require.NotNil(t, fake.deleteAssetGroupRequest)
  99. }},
  100. }
  101. for _, tc := range cases {
  102. t.Run(tc.action, func(t *testing.T) {
  103. setChinaMobileAssetTestEnv(t, "")
  104. fake := &fakeChinaMobileAssetSDKClient{}
  105. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  106. return fake
  107. }}
  108. spec, ok := ParseAssetAction(tc.action)
  109. require.True(t, ok)
  110. resp, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{
  111. Action: spec,
  112. Version: "2024-01-01",
  113. Body: tc.body,
  114. })
  115. require.Nil(t, assetErr)
  116. require.NotNil(t, resp)
  117. tc.assert(t, fake)
  118. assert.Contains(t, string(resp.Body), `"ResponseMetadata"`)
  119. })
  120. }
  121. }
  122. func TestChinaMobileAssetAdapterRejectsUnsupportedCompatibilityOptions(t *testing.T) {
  123. setChinaMobileAssetTestEnv(t, "")
  124. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  125. return &fakeChinaMobileAssetSDKClient{}
  126. }}
  127. spec, ok := ParseAssetAction("ListAssets")
  128. require.True(t, ok)
  129. cases := []map[string]any{
  130. {"ProjectName": "prod"},
  131. {"SortBy": "Name"},
  132. {"SortOrder": "Asc"},
  133. }
  134. for _, body := range cases {
  135. _, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "cm-key"}, AssetRequest{
  136. Action: spec,
  137. Body: body,
  138. })
  139. require.NotNil(t, assetErr)
  140. assert.Equal(t, AssetErrorOperationNotSupported, assetErr.Type)
  141. }
  142. }
  143. func TestChinaMobileAssetAdapterMissingIdReturnsInvalidRequest(t *testing.T) {
  144. setChinaMobileAssetTestEnv(t, "")
  145. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  146. return &fakeChinaMobileAssetSDKClient{}
  147. }}
  148. spec, ok := ParseAssetAction("GetAsset")
  149. require.True(t, ok)
  150. _, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{
  151. Action: spec,
  152. Body: map[string]any{},
  153. })
  154. require.NotNil(t, assetErr)
  155. assert.Equal(t, AssetErrorInvalidRequest, assetErr.Type)
  156. assert.Contains(t, assetErr.Message, "Id is required")
  157. }
  158. func TestChinaMobileAssetAdapterStateErrorMapsToUpstreamError(t *testing.T) {
  159. _, assetErr := normalizeChinaMobileAssetResponse(AssetActionSpec{Action: "CreateAsset", Operation: AssetOperationAssetCreate}, "2024-01-01", []byte(`{"requestId":"req","state":"ERROR","errorCode":"Bad","errorMessage":"bad request"}`))
  160. require.NotNil(t, assetErr)
  161. assert.Equal(t, AssetErrorUpstream, assetErr.Type)
  162. assert.Equal(t, "bad request", assetErr.Message)
  163. }
  164. func TestChinaMobileAssetAdapterRequiresGroupTypeForListAssets(t *testing.T) {
  165. setChinaMobileAssetTestEnv(t, "")
  166. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  167. return &fakeChinaMobileAssetSDKClient{}
  168. }}
  169. spec, ok := ParseAssetAction("ListAssets")
  170. require.True(t, ok)
  171. _, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{
  172. Action: spec,
  173. Body: map[string]any{"PageNumber": 1, "PageSize": 10},
  174. })
  175. require.NotNil(t, assetErr)
  176. assert.Equal(t, AssetErrorInvalidRequest, assetErr.Type)
  177. assert.Equal(t, http.StatusBadRequest, assetErr.HTTPStatus)
  178. }
  179. func TestChinaMobileAssetDeleteFalseMapsToUpstreamError(t *testing.T) {
  180. _, assetErr := normalizeChinaMobileAssetResponse(AssetActionSpec{Action: "DeleteAsset", Operation: AssetOperationAssetDelete, Delete: true}, "2024-01-01", []byte(`{"requestId":"req","state":"OK","body":false}`))
  181. require.NotNil(t, assetErr)
  182. assert.Equal(t, AssetErrorUpstream, assetErr.Type)
  183. }
  184. func TestChinaMobileAssetUnknownStateMapsToUpstreamError(t *testing.T) {
  185. _, assetErr := normalizeChinaMobileAssetResponse(AssetActionSpec{Action: "CreateAsset", Operation: AssetOperationAssetCreate}, "2024-01-01", []byte(`{"requestId":"req","state":"","body":"asset-1"}`))
  186. require.NotNil(t, assetErr)
  187. assert.Equal(t, AssetErrorUpstream, assetErr.Type)
  188. }
  189. func TestChinaMobileAssetSDKBadRequestMapsToInvalidRequest(t *testing.T) {
  190. setChinaMobileAssetTestEnv(t, "")
  191. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  192. return &errorChinaMobileAssetSDKClient{
  193. fakeChinaMobileAssetSDKClient: fakeChinaMobileAssetSDKClient{},
  194. err: cmerrs.NewServerResponseError("bad request", nil, http.StatusBadRequest, nil, `{"errorMessage":"invalid asset"}`),
  195. }
  196. }}
  197. spec, ok := ParseAssetAction("CreateAsset")
  198. require.True(t, ok)
  199. _, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{Action: spec, Body: validChinaMobileCreateAssetBody()})
  200. require.NotNil(t, assetErr)
  201. assert.Equal(t, AssetErrorInvalidRequest, assetErr.Type)
  202. assert.Equal(t, http.StatusBadRequest, assetErr.HTTPStatus)
  203. }
  204. func TestChinaMobileAssetSDKServerErrorRemainsUpstreamError(t *testing.T) {
  205. setChinaMobileAssetTestEnv(t, "")
  206. adapter := &ChinaMobileAssetAdapter{newClient: func(credential chinaMobileAssetCredential) chinaMobileAssetSDKClient {
  207. return &errorChinaMobileAssetSDKClient{
  208. fakeChinaMobileAssetSDKClient: fakeChinaMobileAssetSDKClient{},
  209. err: cmerrs.NewServerResponseError("server error", nil, http.StatusInternalServerError, nil, ""),
  210. }
  211. }}
  212. spec, ok := ParseAssetAction("CreateAsset")
  213. require.True(t, ok)
  214. _, assetErr := adapter.DoAssetRequest(context.Background(), &model.Channel{Key: "video-generation-key"}, AssetRequest{Action: spec, Body: validChinaMobileCreateAssetBody()})
  215. require.NotNil(t, assetErr)
  216. assert.Equal(t, AssetErrorUpstream, assetErr.Type)
  217. assert.Equal(t, http.StatusBadGateway, assetErr.HTTPStatus)
  218. }
  219. func TestCallChinaMobileAssetSDKReturnsWhenContextCancelled(t *testing.T) {
  220. client := &blockingChinaMobileAssetSDKClient{fakeChinaMobileAssetSDKClient: fakeChinaMobileAssetSDKClient{}, release: make(chan struct{})}
  221. ctx, cancel := context.WithCancel(context.Background())
  222. cancel()
  223. spec, ok := ParseAssetAction("CreateAsset")
  224. require.True(t, ok)
  225. started := time.Now()
  226. _, err := callChinaMobileAssetSDK(ctx, client, AssetRequest{Action: spec, Body: validChinaMobileCreateAssetBody()})
  227. require.ErrorIs(t, err, context.Canceled)
  228. assert.Less(t, time.Since(started), 500*time.Millisecond)
  229. close(client.release)
  230. }
  231. func setChinaMobileAssetTestEnv(t *testing.T, poolID string) {
  232. t.Helper()
  233. t.Setenv(chinaMobileAssetAKEnv, "ak")
  234. t.Setenv(chinaMobileAssetSKEnv, "sk")
  235. t.Setenv(chinaMobileAssetPoolIDEnv, poolID)
  236. }
  237. type blockingChinaMobileAssetSDKClient struct {
  238. fakeChinaMobileAssetSDKClient
  239. release chan struct{}
  240. }
  241. type errorChinaMobileAssetSDKClient struct {
  242. fakeChinaMobileAssetSDKClient
  243. err error
  244. }
  245. func (f *errorChinaMobileAssetSDKClient) CreateAsset(request *cmmodel.CreateAssetRequest) (*cmmodel.CreateAssetResponse, error) {
  246. if f.err == nil {
  247. return nil, errors.New("missing test error")
  248. }
  249. return nil, f.err
  250. }
  251. func (f *blockingChinaMobileAssetSDKClient) CreateAsset(request *cmmodel.CreateAssetRequest) (*cmmodel.CreateAssetResponse, error) {
  252. <-f.release
  253. return f.fakeChinaMobileAssetSDKClient.CreateAsset(request)
  254. }
  255. func validChinaMobileCreateAssetBody() map[string]any {
  256. return map[string]any{
  257. "GroupId": "group-1",
  258. "Name": "asset",
  259. "URL": "https://example.com/asset.png",
  260. "AssetType": "Image",
  261. }
  262. }
  263. type fakeChinaMobileAssetSDKClient struct {
  264. createAssetRequest *cmmodel.CreateAssetRequest
  265. listAssetsRequest *cmmodel.ListAssetsRequest
  266. getAssetRequest *cmmodel.GetAssetRequest
  267. updateAssetRequest *cmmodel.UpdateAssetRequest
  268. deleteAssetRequest *cmmodel.DeleteAssetRequest
  269. createAssetGroupRequest *cmmodel.CreateAssetGroupRequest
  270. listAssetGroupsRequest *cmmodel.ListAssetGroupsRequest
  271. getAssetGroupRequest *cmmodel.GetAssetGroupRequest
  272. updateAssetGroupRequest *cmmodel.UpdateAssetGroupRequest
  273. deleteAssetGroupRequest *cmmodel.DeleteAssetGroupRequest
  274. }
  275. func (f *fakeChinaMobileAssetSDKClient) CreateAsset(request *cmmodel.CreateAssetRequest) (*cmmodel.CreateAssetResponse, error) {
  276. f.createAssetRequest = request
  277. return (&cmmodel.CreateAssetResponse{}).SetRequestId("req-1").SetState("OK").SetBody("asset-1"), nil
  278. }
  279. func (f *fakeChinaMobileAssetSDKClient) ListAssets(request *cmmodel.ListAssetsRequest) (*cmmodel.ListAssetsResponse, error) {
  280. f.listAssetsRequest = request
  281. item := cmmodel.ListAssetsResponseData{}
  282. item.SetAssetId("asset-1").SetGroupId("group-1").SetAssetName("n").SetStatus(cmmodel.ListAssetsResponseDataStatusEnumActive)
  283. body := (&cmmodel.ListAssetsResponseBody{}).SetData([]cmmodel.ListAssetsResponseData{item}).SetTotal(1)
  284. return (&cmmodel.ListAssetsResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  285. }
  286. func (f *fakeChinaMobileAssetSDKClient) GetAsset(request *cmmodel.GetAssetRequest) (*cmmodel.GetAssetResponse, error) {
  287. f.getAssetRequest = request
  288. body := (&cmmodel.GetAssetResponseBody{}).SetAssetId("asset-1").SetGroupId("group-1").SetAssetName("n").SetStatus(cmmodel.GetAssetResponseBodyStatusEnumActive)
  289. return (&cmmodel.GetAssetResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  290. }
  291. func (f *fakeChinaMobileAssetSDKClient) UpdateAsset(request *cmmodel.UpdateAssetRequest) (*cmmodel.UpdateAssetResponse, error) {
  292. f.updateAssetRequest = request
  293. body := (&cmmodel.UpdateAssetResponseBody{}).SetAssetId("asset-1").SetGroupId("group-1").SetAssetName("n").SetStatus(cmmodel.UpdateAssetResponseBodyStatusEnumActive)
  294. return (&cmmodel.UpdateAssetResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  295. }
  296. func (f *fakeChinaMobileAssetSDKClient) DeleteAsset(request *cmmodel.DeleteAssetRequest) (*cmmodel.DeleteAssetResponse, error) {
  297. f.deleteAssetRequest = request
  298. return (&cmmodel.DeleteAssetResponse{}).SetRequestId("req").SetState("OK").SetBody(true), nil
  299. }
  300. func (f *fakeChinaMobileAssetSDKClient) CreateAssetGroup(request *cmmodel.CreateAssetGroupRequest) (*cmmodel.CreateAssetGroupResponse, error) {
  301. f.createAssetGroupRequest = request
  302. body := (&cmmodel.CreateAssetGroupResponseBody{}).SetGroupId("group-1").SetGroupName("g").SetGroupType(cmmodel.CreateAssetGroupResponseBodyGroupTypeEnumAigc)
  303. return (&cmmodel.CreateAssetGroupResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  304. }
  305. func (f *fakeChinaMobileAssetSDKClient) ListAssetGroups(request *cmmodel.ListAssetGroupsRequest) (*cmmodel.ListAssetGroupsResponse, error) {
  306. f.listAssetGroupsRequest = request
  307. item := cmmodel.ListAssetGroupsResponseData{}
  308. item.SetGroupId("group-1").SetGroupName("g").SetGroupType(cmmodel.ListAssetGroupsResponseDataGroupTypeEnumAigc)
  309. body := (&cmmodel.ListAssetGroupsResponseBody{}).SetData([]cmmodel.ListAssetGroupsResponseData{item}).SetTotal(1)
  310. return (&cmmodel.ListAssetGroupsResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  311. }
  312. func (f *fakeChinaMobileAssetSDKClient) GetAssetGroup(request *cmmodel.GetAssetGroupRequest) (*cmmodel.GetAssetGroupResponse, error) {
  313. f.getAssetGroupRequest = request
  314. body := (&cmmodel.GetAssetGroupResponseBody{}).SetGroupId("group-1").SetGroupName("g").SetGroupType(cmmodel.GetAssetGroupResponseBodyGroupTypeEnumAigc)
  315. return (&cmmodel.GetAssetGroupResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  316. }
  317. func (f *fakeChinaMobileAssetSDKClient) UpdateAssetGroup(request *cmmodel.UpdateAssetGroupRequest) (*cmmodel.UpdateAssetGroupResponse, error) {
  318. f.updateAssetGroupRequest = request
  319. body := (&cmmodel.UpdateAssetGroupResponseBody{}).SetGroupId("group-1").SetGroupName("g").SetGroupType(cmmodel.UpdateAssetGroupResponseBodyGroupTypeEnumAigc)
  320. return (&cmmodel.UpdateAssetGroupResponse{}).SetRequestId("req").SetState("OK").SetBody(body), nil
  321. }
  322. func (f *fakeChinaMobileAssetSDKClient) DeleteAssetGroup(request *cmmodel.DeleteAssetGroupRequest) (*cmmodel.DeleteAssetGroupResponse, error) {
  323. f.deleteAssetGroupRequest = request
  324. return (&cmmodel.DeleteAssetGroupResponse{}).SetRequestId("req").SetState("OK").SetBody(true), nil
  325. }