package runner import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/acceptanceemulator" "github.com/easyai/easyai-ai-gateway/apps/api/internal/clients" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestLocalAcceptanceObjectStorageFailoverMatrix(t *testing.T) { emulator := httptest.NewServer(acceptanceemulator.New(acceptanceemulator.Config{}).Handler()) defer emulator.Close() service := &Service{} payload := FileUploadPayload{ Bytes: []byte("acceptance-object"), ContentType: "image/png", Scene: store.FileStorageSceneRequestAsset, } tests := []struct { name string channels []store.FileStorageChannel winner string wantErrCode string }{ { name: "oss_auth_to_s3", channels: []store.FileStorageChannel{ acceptanceStorageChannel("aliyun_oss", emulator.URL+"/storage/oss-auth/bucket", "oss-auth", false), acceptanceStorageChannel("s3", emulator.URL+"/storage/s3", "s3-ok", false), }, winner: "s3-ok", }, { name: "s3_auth_to_oss", channels: []store.FileStorageChannel{ acceptanceStorageChannel("s3", emulator.URL+"/storage/s3-auth", "s3-auth", false), acceptanceStorageChannel("aliyun_oss", emulator.URL+"/storage/oss/bucket", "oss-ok", false), }, winner: "oss-ok", }, { name: "same_channel_transient_retry", channels: []store.FileStorageChannel{ acceptanceStorageChannel("s3", emulator.URL+"/storage/s3-transient", "s3-transient", true), acceptanceStorageChannel("aliyun_oss", emulator.URL+"/storage/oss/bucket", "oss-unused", false), }, winner: "s3-transient", }, { name: "all_channels_failed", channels: []store.FileStorageChannel{ acceptanceStorageChannel("aliyun_oss", emulator.URL+"/storage/oss-fail/bucket", "oss-fail", false), acceptanceStorageChannel("s3", emulator.URL+"/storage/s3-fail", "s3-fail", false), }, wantErrCode: "storage_write_failed", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := service.uploadFileWithFailover(t.Context(), payload, test.channels) if test.wantErrCode != "" { if clients.ErrorCode(err) != test.wantErrCode { t.Fatalf("error=%v code=%s", err, clients.ErrorCode(err)) } return } if err != nil { t.Fatal(err) } channel, _ := result["storageChannel"].(map[string]any) if got := stringFromAny(channel["channelKey"]); got != test.winner { t.Fatalf("winner=%q, want %q", got, test.winner) } }) } response, err := http.Get(emulator.URL + "/report") if err != nil { t.Fatal(err) } defer response.Body.Close() var report acceptanceemulator.Report if err := json.NewDecoder(response.Body).Decode(&report); err != nil { t.Fatal(err) } if report.StoragePuts != 3 || report.StorageFailures != 5 { t.Fatalf("unexpected storage acceptance report: %+v", report) } } func acceptanceStorageChannel(provider string, endpoint string, key string, retry bool) store.FileStorageChannel { channel := testObjectStorageChannel(provider, endpoint, key) channel.Config["publicBaseUrl"] = "" channel.RetryPolicy = map[string]any{"enabled": retry, "maxRetries": 0} if retry { channel.RetryPolicy = map[string]any{"enabled": true, "maxRetries": 1, "backoffSeconds": []any{0.001}} } return channel }