plugin_runtime_integration_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package service
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "io"
  7. "net/http"
  8. "net/http/httptest"
  9. "os"
  10. "path/filepath"
  11. "testing"
  12. "time"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. )
  16. type pluginFailingRequestBody struct{}
  17. func (pluginFailingRequestBody) Read([]byte) (int, error) {
  18. return 0, errors.New("测试请求体读取失败")
  19. }
  20. func (pluginFailingRequestBody) Close() error { return nil }
  21. func TestPluginRuntimeIntegration(t *testing.T) {
  22. packagePath := os.Getenv("SUB2API_TEST_PLUGIN_PACKAGE")
  23. if packagePath == "" {
  24. t.Skip("未提供 SUB2API_TEST_PLUGIN_PACKAGE,跳过本地插件进程集成测试")
  25. }
  26. packageFile, err := os.Open(packagePath)
  27. require.NoError(t, err)
  28. defer func() { _ = packageFile.Close() }()
  29. root := t.TempDir()
  30. cfg := testPluginConfig(root, false)
  31. installer := NewPluginPackageInstaller(cfg, PluginHostInfo{Version: "0.1.179", BuildType: "release"})
  32. installation, err := installer.Install(context.Background(), packageFile, nil)
  33. require.NoError(t, err)
  34. assert.Equal(t, PluginSignatureTrusted, installation.SignatureStatus)
  35. for _, relative := range []string{
  36. "ui/index.html",
  37. "ui/assets/styles.css",
  38. "ui/assets/bridge-v1.js",
  39. "ui/assets/app.js",
  40. } {
  41. assert.FileExists(t, filepath.Join(installation.InstallPath, filepath.FromSlash(relative)))
  42. }
  43. runtime, err := startPluginRuntime(context.Background(), installation, 10*time.Second, filepath.Join(root, "runtime"))
  44. require.NoError(t, err)
  45. defer runtime.kill()
  46. require.NoError(t, runtime.validateAndApplyConfig(context.Background(), []byte(`{
  47. "request_timeout_seconds":30,
  48. "response_header_timeout_seconds":10,
  49. "idle_connection_timeout_seconds":30,
  50. "max_idle_connections":10,
  51. "max_idle_connections_per_host":5,
  52. "enable_http2":true,
  53. "tls_min_version":"1.2",
  54. "proxy_mode":"disabled",
  55. "extra_headers":{"X-Plugin-Test":"enabled"}
  56. }`)))
  57. upstream := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
  58. body, readErr := io.ReadAll(request.Body)
  59. require.NoError(t, readErr)
  60. assert.Equal(t, "payload", string(body))
  61. assert.Equal(t, "enabled", request.Header.Get("X-Plugin-Test"))
  62. assert.Equal(t, int64(len("payload")), request.ContentLength)
  63. assert.Empty(t, request.TransferEncoding)
  64. writer.Header().Set("X-Upstream", "plugin-runtime")
  65. writer.WriteHeader(http.StatusCreated)
  66. _, _ = writer.Write([]byte("response-through-plugin"))
  67. }))
  68. defer upstream.Close()
  69. request, err := http.NewRequestWithContext(context.Background(), http.MethodPost, upstream.URL, bytes.NewBufferString("payload"))
  70. require.NoError(t, err)
  71. request.Header.Set("Content-Type", "application/json")
  72. account := &Account{ID: 42, Platform: PlatformOpenAI, Type: AccountTypeOAuth, Concurrency: 1}
  73. require.True(t, runtime.beginRequest())
  74. response, err := runtime.roundTrip(context.Background(), request, "", account)
  75. if err != nil {
  76. runtime.finishRequest()
  77. }
  78. require.NoError(t, err)
  79. body, err := io.ReadAll(response.Body)
  80. require.NoError(t, err)
  81. assert.Equal(t, http.StatusCreated, response.StatusCode)
  82. assert.Equal(t, "plugin-runtime", response.Header.Get("X-Upstream"))
  83. assert.Equal(t, "response-through-plugin", string(body))
  84. assert.Equal(t, int64(len("response-through-plugin")), response.ContentLength)
  85. require.NoError(t, response.Body.Close())
  86. failingRequest, err := http.NewRequestWithContext(context.Background(), http.MethodPost, upstream.URL, pluginFailingRequestBody{})
  87. require.NoError(t, err)
  88. failingRequest.ContentLength = -1
  89. require.True(t, runtime.beginRequest())
  90. started := time.Now()
  91. _, err = runtime.roundTrip(context.Background(), failingRequest, "", account)
  92. runtime.finishRequest()
  93. require.Error(t, err)
  94. assert.Less(t, time.Since(started), 2*time.Second)
  95. }