Files
Enterprise-Knowledge-Base/internal/application/service/file/s3_test.go
T
ochan.kwon 5c0cda3630 fix(storage): add S3 provider scheme support to frontend and backend allowlists
The S3 storage adapter (s3.go) was added in e309e0b but the provider
scheme integration was incomplete — ParseProviderScheme() and frontend
security.ts were not updated, causing s3:// URLs to fail in the /files
proxy, VLM image processing, and frontend rendering.

Additionally, S3-compatible services (MinIO, Ceph, etc.) require
path-style addressing (endpoint/bucket/key) but the SDK client only
used virtual-hosted style. The deprecated WithEndpointResolver API
is replaced with BaseEndpoint, and path-style is auto-enabled for
non-AWS endpoints (detected by amazonaws.com in the URL).

Changes:
- types/knowledgebase.go: add "s3" to ParseProviderScheme()
- file/s3.go: auto path-style for S3-compatible endpoints + replace
  deprecated WithEndpointResolver with BaseEndpoint
- frontend/security.ts: add s3 to 5 provider scheme allowlists
- knowledgebase_test.go: add ParseProviderScheme + InferStorage tests
- s3_test.go: add path-style detection + parseS3FilePath tests
2026-03-30 16:57:27 +08:00

110 lines
2.4 KiB
Go

package file
import (
"strings"
"testing"
)
func TestNewS3Client_PathStyleForCompatibleEndpoints(t *testing.T) {
tests := []struct {
name string
endpoint string
wantPathStyle bool
}{
{
name: "S3-compatible service uses path-style",
endpoint: "https://storage.internal:9000",
wantPathStyle: true,
},
{
name: "MinIO endpoint uses path-style",
endpoint: "http://minio.local:9000",
wantPathStyle: true,
},
{
name: "AWS S3 regional endpoint uses virtual-hosted",
endpoint: "https://s3.us-east-1.amazonaws.com",
wantPathStyle: false,
},
{
name: "AWS China endpoint uses virtual-hosted",
endpoint: "https://s3.cn-north-1.amazonaws.com.cn",
wantPathStyle: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Verify the path-style detection logic matches our implementation
usePathStyle := !strings.Contains(tt.endpoint, "amazonaws.com")
if usePathStyle != tt.wantPathStyle {
t.Errorf("endpoint %q: usePathStyle = %v, want %v", tt.endpoint, usePathStyle, tt.wantPathStyle)
}
})
}
}
func TestNewS3Client_EmptyEndpoint(t *testing.T) {
// Empty endpoint should not trigger path-style (standard AWS S3)
endpoint := ""
if endpoint != "" {
t.Fatal("expected empty endpoint to skip custom configuration")
}
}
func TestParseS3FilePath(t *testing.T) {
svc := &s3FileService{bucketName: "test-bucket"}
tests := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "valid s3 path",
input: "s3://test-bucket/123/exports/abc.png",
want: "123/exports/abc.png",
},
{
name: "wrong bucket",
input: "s3://other-bucket/key",
wantErr: true,
},
{
name: "not s3 scheme",
input: "minio://test-bucket/key",
wantErr: true,
},
{
name: "missing object key",
input: "s3://test-bucket/",
wantErr: true,
},
{
name: "empty path",
input: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := svc.parseS3FilePath(tt.input)
if tt.wantErr {
if err == nil {
t.Errorf("parseS3FilePath(%q) expected error, got %q", tt.input, got)
}
return
}
if err != nil {
t.Errorf("parseS3FilePath(%q) unexpected error: %v", tt.input, err)
return
}
if got != tt.want {
t.Errorf("parseS3FilePath(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}