WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package im
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"testing"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// stubIMFileService implements interfaces.FileService for IM resolver tests.
|
||||
type stubIMFileService struct {
|
||||
getFileURL func(ctx context.Context, filePath string) (string, error)
|
||||
}
|
||||
|
||||
func (s *stubIMFileService) CheckConnectivity(context.Context) error { return nil }
|
||||
|
||||
func (s *stubIMFileService) SaveFile(context.Context, *multipart.FileHeader, uint64, string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (s *stubIMFileService) SaveBytes(context.Context, []byte, uint64, string, bool) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (s *stubIMFileService) GetFile(context.Context, string) (io.ReadCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *stubIMFileService) GetFileURL(ctx context.Context, filePath string) (string, error) {
|
||||
if s.getFileURL != nil {
|
||||
return s.getFileURL(ctx, filePath)
|
||||
}
|
||||
return "https://global-storage.example/" + filePath, nil
|
||||
}
|
||||
|
||||
func (s *stubIMFileService) DeleteFile(context.Context, string) error { return nil }
|
||||
|
||||
func (s *stubIMFileService) CopyFile(context.Context, string, uint64, string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func TestBuildIMFileServiceForProvider_FallbackToGlobal(t *testing.T) {
|
||||
stub := &stubIMFileService{}
|
||||
tenant := &types.Tenant{
|
||||
StorageEngineConfig: &types.StorageEngineConfig{
|
||||
DefaultProvider: "cos",
|
||||
COS: &types.COSEngineConfig{
|
||||
SecretID: "id",
|
||||
SecretKey: "key",
|
||||
BucketName: "bucket",
|
||||
Region: "ap-shanghai",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
svc := buildIMFileServiceForProvider(tenant, "minio", stub)
|
||||
require.NotNil(t, svc)
|
||||
got, err := svc.GetFileURL(context.Background(), "minio://wizard-test/10000/exports/a.png")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "https://global-storage.example/minio://wizard-test/10000/exports/a.png", got)
|
||||
}
|
||||
|
||||
func TestIMFileServiceResolver_CachesPerProvider(t *testing.T) {
|
||||
stub := &stubIMFileService{}
|
||||
tenant := &types.Tenant{
|
||||
StorageEngineConfig: &types.StorageEngineConfig{
|
||||
DefaultProvider: "cos",
|
||||
COS: &types.COSEngineConfig{
|
||||
SecretID: "id",
|
||||
SecretKey: "key",
|
||||
BucketName: "bucket",
|
||||
Region: "ap-shanghai",
|
||||
},
|
||||
},
|
||||
}
|
||||
r := newIMFileServiceResolver(tenant, stub)
|
||||
|
||||
svc1 := r.ResolveFileService("minio://wizard-test/10000/a.png")
|
||||
svc2 := r.ResolveFileService("minio://wizard-test/10000/b.png")
|
||||
assert.Same(t, svc1, svc2, "same provider should reuse cached FileService")
|
||||
|
||||
svc3 := r.ResolveFileService("local://10000/c.png")
|
||||
assert.NotSame(t, svc1, svc3, "different provider should use a different service")
|
||||
}
|
||||
|
||||
func TestRewriteStorageURLs_MinIOFallbackViaGlobal(t *testing.T) {
|
||||
stub := &stubIMFileService{
|
||||
getFileURL: func(_ context.Context, filePath string) (string, error) {
|
||||
return "https://minio.example/presigned?path=" + filePath, nil
|
||||
},
|
||||
}
|
||||
tenant := &types.Tenant{
|
||||
StorageEngineConfig: &types.StorageEngineConfig{
|
||||
DefaultProvider: "cos",
|
||||
COS: &types.COSEngineConfig{
|
||||
SecretID: "id",
|
||||
SecretKey: "key",
|
||||
BucketName: "bucket",
|
||||
Region: "ap-shanghai",
|
||||
},
|
||||
},
|
||||
}
|
||||
in := ``
|
||||
resolver := newIMFileServiceResolver(tenant, stub)
|
||||
out := rewriteStorageURLs(context.Background(), in, resolver)
|
||||
assert.Contains(t, out, "https://minio.example/presigned")
|
||||
assert.NotContains(t, out, "](minio://")
|
||||
}
|
||||
|
||||
func TestRewriteStorageURLs_ScopedPath(t *testing.T) {
|
||||
stub := &stubIMFileService{
|
||||
getFileURL: func(_ context.Context, filePath string) (string, error) {
|
||||
assert.Equal(t, "storage://backend-a/cos://bucket/ap-test/10000/exports/a.png", filePath)
|
||||
return "https://storage.example/a.png", nil
|
||||
},
|
||||
}
|
||||
input := ""
|
||||
output := rewriteStorageURLs(context.Background(), input, newIMFileServiceResolver(&types.Tenant{}, stub))
|
||||
assert.Contains(t, output, "https://storage.example/a.png")
|
||||
}
|
||||
|
||||
// When GetFileURL resolves a resource:// alias to a still-internal storage://
|
||||
// path (no public HTTP URL), the rewrite must be a no-op rather than emit the
|
||||
// unrenderable URL to the IM client.
|
||||
func TestRewriteStorageURLs_NonHTTPResultIsNoOp(t *testing.T) {
|
||||
stub := &stubIMFileService{
|
||||
getFileURL: func(_ context.Context, _ string) (string, error) {
|
||||
return "storage://7cb970a6/oss://bcjy/10000/exports/a.png", nil
|
||||
},
|
||||
}
|
||||
in := ""
|
||||
out := rewriteStorageURLs(context.Background(), in, newIMFileServiceResolver(&types.Tenant{}, stub))
|
||||
assert.Equal(t, in, out)
|
||||
assert.NotContains(t, out, "storage://")
|
||||
}
|
||||
|
||||
// URL schemes are case-insensitive (RFC 3986); an uppercase-scheme result (e.g.
|
||||
// from an OBS_PROXY_DOMAIN configured as HTTPS://...) is renderable and must be
|
||||
// substituted, not dropped as "non-HTTP".
|
||||
func TestRewriteStorageURLs_UppercaseSchemeIsSubstituted(t *testing.T) {
|
||||
stub := &stubIMFileService{
|
||||
getFileURL: func(_ context.Context, _ string) (string, error) {
|
||||
return "HTTPS://cdn.example.com/x.png", nil
|
||||
},
|
||||
}
|
||||
in := ""
|
||||
out := rewriteStorageURLs(context.Background(), in, newIMFileServiceResolver(&types.Tenant{}, stub))
|
||||
assert.Contains(t, out, "HTTPS://cdn.example.com/x.png")
|
||||
assert.NotContains(t, out, "resource://")
|
||||
}
|
||||
|
||||
func TestCleanIMContent_MinIOFallbackIntegration(t *testing.T) {
|
||||
stub := &stubIMFileService{
|
||||
getFileURL: func(_ context.Context, _ string) (string, error) {
|
||||
return "https://minio.example/img.png", nil
|
||||
},
|
||||
}
|
||||
tenant := &types.Tenant{
|
||||
StorageEngineConfig: &types.StorageEngineConfig{DefaultProvider: "cos"},
|
||||
}
|
||||
in := "see  ok"
|
||||
out := cleanIMContent(context.Background(), in, tenant, stub)
|
||||
assert.Contains(t, out, "https://minio.example/img.png")
|
||||
}
|
||||
Reference in New Issue
Block a user