mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-24 09:00:13 +08:00
270 lines
10 KiB
Go
270 lines
10 KiB
Go
//
|
|
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
package sandbox
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestUCloudAgentSandboxProvider_ProviderTypeAndLanguages(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{})
|
|
if p.ProviderType() != ProviderUCloudAgentSandbox {
|
|
t.Errorf("ProviderType = %q, want %q", p.ProviderType(), ProviderUCloudAgentSandbox)
|
|
}
|
|
|
|
want := map[string]bool{"python": true, "javascript": true}
|
|
got := map[string]bool{}
|
|
for _, language := range p.SupportedLanguages() {
|
|
if !want[language] {
|
|
t.Errorf("unexpected language: %q", language)
|
|
}
|
|
got[language] = true
|
|
}
|
|
for language := range want {
|
|
if !got[language] {
|
|
t.Errorf("missing required language: %q", language)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_Defaults(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{})
|
|
|
|
if p.region != ucloudAgentSandboxDefaultRegion {
|
|
t.Errorf("region = %q, want %q", p.region, ucloudAgentSandboxDefaultRegion)
|
|
}
|
|
if p.template != ucloudAgentSandboxDefaultTemplate {
|
|
t.Errorf("template = %q, want %q", p.template, ucloudAgentSandboxDefaultTemplate)
|
|
}
|
|
if p.allowInternetAccess {
|
|
t.Error("allowInternetAccess = true, want false by default")
|
|
}
|
|
if p.insecureHTTP {
|
|
t.Error("insecureHTTP = true, want false by default")
|
|
}
|
|
if p.timeoutSec != ucloudAgentSandboxDefaultTimeout {
|
|
t.Errorf("timeoutSec = %d, want %d", p.timeoutSec, ucloudAgentSandboxDefaultTimeout)
|
|
}
|
|
if p.sandboxTimeoutSec != ucloudAgentSandboxDefaultLifetime {
|
|
t.Errorf("sandboxTimeoutSec = %d, want %d", p.sandboxTimeoutSec, ucloudAgentSandboxDefaultLifetime)
|
|
}
|
|
if p.maxOutputBytes != ucloudAgentSandboxDefaultMaxOutputBytes {
|
|
t.Errorf("maxOutputBytes = %d, want %d", p.maxOutputBytes, ucloudAgentSandboxDefaultMaxOutputBytes)
|
|
}
|
|
if p.maxArtifacts != ucloudAgentSandboxDefaultMaxArtifacts {
|
|
t.Errorf("maxArtifacts = %d, want %d", p.maxArtifacts, ucloudAgentSandboxDefaultMaxArtifacts)
|
|
}
|
|
if p.maxArtifactBytes != ucloudAgentSandboxDefaultMaxArtifactSize {
|
|
t.Errorf("maxArtifactBytes = %d, want %d", p.maxArtifactBytes, ucloudAgentSandboxDefaultMaxArtifactSize)
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_ConfigOverrides(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{
|
|
"api_key": "ucloud-key",
|
|
"region": "us-ca",
|
|
"domain": "sandbox.example.com",
|
|
"api_url": "https://api.example.com",
|
|
"template": "custom-template",
|
|
"allow_internet_access": true,
|
|
"insecure_http": true,
|
|
"timeout": float64(45),
|
|
"sandbox_timeout": float64(600),
|
|
"max_output_bytes": float64(2_000_000),
|
|
"max_artifacts": float64(50),
|
|
"max_artifact_bytes": float64(20_000_000),
|
|
})
|
|
|
|
if p.apiKey != "ucloud-key" {
|
|
t.Errorf("apiKey = %q", p.apiKey)
|
|
}
|
|
if p.region != "us-ca" || p.domain != "sandbox.example.com" || p.apiURL != "https://api.example.com" {
|
|
t.Errorf("endpoint config = region:%q domain:%q apiURL:%q", p.region, p.domain, p.apiURL)
|
|
}
|
|
if p.template != "custom-template" {
|
|
t.Errorf("template = %q", p.template)
|
|
}
|
|
if !p.allowInternetAccess || !p.insecureHTTP {
|
|
t.Errorf("boolean overrides not applied: internet=%v insecureHTTP=%v", p.allowInternetAccess, p.insecureHTTP)
|
|
}
|
|
if p.timeoutSec != 45 || p.sandboxTimeoutSec != 600 {
|
|
t.Errorf("timeouts = execution:%d sandbox:%d", p.timeoutSec, p.sandboxTimeoutSec)
|
|
}
|
|
if p.maxOutputBytes != 2_000_000 || p.maxArtifacts != 50 || p.maxArtifactBytes != 20_000_000 {
|
|
t.Errorf("limits = output:%d artifacts:%d artifactBytes:%d", p.maxOutputBytes, p.maxArtifacts, p.maxArtifactBytes)
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_EnvOverrides(t *testing.T) {
|
|
t.Setenv("UCLOUD_SANDBOX_API_KEY", "env-key")
|
|
t.Setenv("UCLOUD_SANDBOX_REGION", "us-ca")
|
|
t.Setenv("UCLOUD_SANDBOX_TEMPLATE", "env-template")
|
|
t.Setenv("UCLOUD_SANDBOX_ALLOW_INTERNET_ACCESS", "true")
|
|
t.Setenv("UCLOUD_SANDBOX_INSECURE_HTTP", "true")
|
|
t.Setenv("UCLOUD_SANDBOX_EXECUTION_TIMEOUT", "60")
|
|
t.Setenv("UCLOUD_SANDBOX_TIMEOUT", "900")
|
|
t.Setenv("UCLOUD_SANDBOX_MAX_OUTPUT_BYTES", "2000")
|
|
t.Setenv("UCLOUD_SANDBOX_MAX_ARTIFACTS", "4")
|
|
t.Setenv("UCLOUD_SANDBOX_MAX_ARTIFACT_BYTES", "3000")
|
|
|
|
p := newUCloudAgentSandboxProviderFromEnv()
|
|
if p.apiKey != "env-key" || p.region != "us-ca" || p.template != "env-template" {
|
|
t.Errorf("env string overrides not applied: key:%q region:%q template:%q", p.apiKey, p.region, p.template)
|
|
}
|
|
if !p.allowInternetAccess || !p.insecureHTTP {
|
|
t.Errorf("env boolean overrides not applied: internet=%v insecureHTTP=%v", p.allowInternetAccess, p.insecureHTTP)
|
|
}
|
|
if p.timeoutSec != 60 || p.sandboxTimeoutSec != 900 {
|
|
t.Errorf("env timeouts = execution:%d sandbox:%d", p.timeoutSec, p.sandboxTimeoutSec)
|
|
}
|
|
if p.maxOutputBytes != 2000 || p.maxArtifacts != 4 || p.maxArtifactBytes != 3000 {
|
|
t.Errorf("env limits = output:%d artifacts:%d artifactBytes:%d", p.maxOutputBytes, p.maxArtifacts, p.maxArtifactBytes)
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_Initialize(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("missing API key", func(t *testing.T) {
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{})
|
|
err := p.Initialize(context.Background())
|
|
if err == nil {
|
|
t.Fatal("Initialize with no API key: got nil error, want one")
|
|
}
|
|
if !strings.Contains(err.Error(), "API key") {
|
|
t.Errorf("err = %v, want to mention API key", err)
|
|
}
|
|
})
|
|
|
|
t.Run("valid local configuration", func(t *testing.T) {
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{"api_key": "fake-key"})
|
|
if err := p.Initialize(context.Background()); err != nil {
|
|
t.Fatalf("Initialize: %v", err)
|
|
}
|
|
if !p.isInitialized() || p.client == nil {
|
|
t.Error("provider did not retain an initialized SDK client")
|
|
}
|
|
if err := p.HealthCheck(context.Background()); err != nil {
|
|
t.Errorf("HealthCheck after Initialize: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid limits", func(t *testing.T) {
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{
|
|
"api_key": "fake-key",
|
|
"max_artifacts": -1,
|
|
})
|
|
if err := p.Initialize(context.Background()); err == nil {
|
|
t.Fatal("Initialize with invalid limits: got nil error, want one")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_AllOpsBeforeInit(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{})
|
|
inst := &SandboxInstance{InstanceID: "x", Provider: ProviderUCloudAgentSandbox}
|
|
|
|
if _, err := p.CreateInstance(context.Background(), "python"); err == nil {
|
|
t.Error("CreateInstance before init: got nil error, want one")
|
|
}
|
|
if _, err := p.ExecuteCode(context.Background(), inst, "x", "python", 5, nil); err == nil {
|
|
t.Error("ExecuteCode before init: got nil error, want one")
|
|
}
|
|
if err := p.DestroyInstance(context.Background(), inst); err == nil {
|
|
t.Error("DestroyInstance before init: got nil error, want one")
|
|
}
|
|
if err := p.HealthCheck(context.Background()); err == nil {
|
|
t.Error("HealthCheck before init: got nil error, want one")
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_ExecuteCodeRejectsBadInputs(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{"api_key": "fake-key"})
|
|
if err := p.Initialize(context.Background()); err != nil {
|
|
t.Fatalf("Initialize: %v", err)
|
|
}
|
|
p.instances["x"] = &ucloudAgentSandboxInstance{}
|
|
|
|
cases := []struct {
|
|
name string
|
|
inst *SandboxInstance
|
|
lang string
|
|
time int
|
|
want string
|
|
}{
|
|
{name: "nil instance", inst: nil, lang: "python", time: 5, want: "instance id"},
|
|
{name: "empty instance id", inst: &SandboxInstance{}, lang: "python", time: 5, want: "instance id"},
|
|
{name: "unknown instance", inst: &SandboxInstance{InstanceID: "missing"}, lang: "python", time: 5, want: "unknown instance"},
|
|
{name: "unsupported language", inst: &SandboxInstance{InstanceID: "x"}, lang: "ruby", time: 5, want: "unsupported language"},
|
|
{name: "timeout too small", inst: &SandboxInstance{InstanceID: "x"}, lang: "python", time: 0, want: "timeout"},
|
|
{name: "timeout too large", inst: &SandboxInstance{InstanceID: "x"}, lang: "python", time: 1000, want: "timeout"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := p.ExecuteCode(context.Background(), tc.inst, "x", tc.lang, tc.time, nil)
|
|
if err == nil {
|
|
t.Fatalf("got nil error, want one containing %q", tc.want)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.want) {
|
|
t.Errorf("err = %v, want to contain %q", err, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_DestroyInstanceRejectsEmptyID(t *testing.T) {
|
|
t.Parallel()
|
|
p := newUCloudAgentSandboxProviderFromConfig(map[string]any{"api_key": "fake-key"})
|
|
if err := p.Initialize(context.Background()); err != nil {
|
|
t.Fatalf("Initialize: %v", err)
|
|
}
|
|
if err := p.DestroyInstance(context.Background(), nil); err == nil {
|
|
t.Error("DestroyInstance(nil): got nil error, want one")
|
|
}
|
|
if err := p.DestroyInstance(context.Background(), &SandboxInstance{}); err == nil {
|
|
t.Error("DestroyInstance(empty id): got nil error, want one")
|
|
}
|
|
if err := p.DestroyInstance(context.Background(), &SandboxInstance{InstanceID: "already-gone"}); err != nil {
|
|
t.Errorf("DestroyInstance(unknown id): %v, want idempotent success", err)
|
|
}
|
|
}
|
|
|
|
func TestUCloudAgentSandboxProvider_ProviderTypeStaysDistinct(t *testing.T) {
|
|
t.Parallel()
|
|
seen := map[ProviderType]bool{}
|
|
for _, provider := range []SandboxProvider{
|
|
newSelfManagedProviderFromEnv(),
|
|
newAliyunProviderFromEnv(),
|
|
newE2BProviderFromEnv(),
|
|
newTenkiProviderFromEnv(),
|
|
newUCloudAgentSandboxProviderFromConfig(map[string]any{}),
|
|
} {
|
|
if seen[provider.ProviderType()] {
|
|
t.Errorf("provider type %q seen twice", provider.ProviderType())
|
|
}
|
|
seen[provider.ProviderType()] = true
|
|
}
|
|
}
|