Some checks failed
CI / Build & Test (push) Failing after 1m31s
- Add user management endpoints (list/create/get/update/delete) with pagination and search - Add dashboard stats endpoint with tenant/user/online counts and growth metrics - Add tenant resolver middleware for multi-tenant request scoping - Add i18n kernel with zh/en message files and AcceptLanguage middleware - Add WebSocket hub/handler for real-time communication - Add job scheduler kernel with cron support - Add plugin sandbox for isolated execution - Add storage kernel (local filesystem) - Add event bus kernel for pub/sub - Add cache kernel abstraction - Add database migration runner and version upgrade checker - Add rate limiting middleware with Redis backend - Add SQL migrations for rbac, audit_logs, settings, notifications, examples - Extend user repository with list/delete/count operations - Register all module routes with tenant resolver
192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package plugin
|
|
|
|
import (
|
|
"io/fs"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/fx"
|
|
"go.uber.org/zap"
|
|
"mengstack/internal/kernel/tenant"
|
|
)
|
|
|
|
func TestSandbox_CheckDatabase(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("test-plugin", Permissions{
|
|
Database: []string{"users", "posts"},
|
|
})
|
|
|
|
tests := []struct {
|
|
plugin string
|
|
table string
|
|
wantOK bool
|
|
}{
|
|
{"test-plugin", "users", true},
|
|
{"test-plugin", "posts", true},
|
|
{"test-plugin", "orders", false},
|
|
{"unknown-plugin", "users", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
err := sb.CheckDatabase(tt.plugin, tt.table)
|
|
if tt.wantOK && err != nil {
|
|
t.Errorf("CheckDatabase(%q, %q) unexpected error: %v", tt.plugin, tt.table, err)
|
|
}
|
|
if !tt.wantOK && err == nil {
|
|
t.Errorf("CheckDatabase(%q, %q) expected error, got nil", tt.plugin, tt.table)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckDatabaseWildcard(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("all-access", Permissions{
|
|
Database: []string{""},
|
|
})
|
|
|
|
if err := sb.CheckDatabase("all-access", "any_table"); err != nil {
|
|
t.Errorf("wildcard database access should be allowed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckStorage(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("with-storage", Permissions{Storage: true})
|
|
sb.Register("no-storage", Permissions{Storage: false})
|
|
|
|
if err := sb.CheckStorage("with-storage"); err != nil {
|
|
t.Errorf("expected storage access allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckStorage("no-storage"); err == nil {
|
|
t.Error("expected storage access denied")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckNetwork(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("net-plugin", Permissions{
|
|
Network: []string{"api.example.com"},
|
|
})
|
|
|
|
if err := sb.CheckNetwork("net-plugin", "api.example.com"); err != nil {
|
|
t.Errorf("expected network access allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckNetwork("net-plugin", "evil.com"); err == nil {
|
|
t.Error("expected network access denied for unauthorized domain")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckEvent(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("event-plugin", Permissions{
|
|
Events: []string{"user.created", "user.deleted"},
|
|
})
|
|
|
|
if err := sb.CheckEvent("event-plugin", "user.created"); err != nil {
|
|
t.Errorf("expected event allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckEvent("event-plugin", "order.created"); err == nil {
|
|
t.Error("expected event denied for unauthorized event")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckEventWildcard(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("wildcard-events", Permissions{
|
|
Events: []string{"*"},
|
|
})
|
|
|
|
if err := sb.CheckEvent("wildcard-events", "anything"); err != nil {
|
|
t.Errorf("wildcard event access should be allowed, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckRoute(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("route-plugin", Permissions{
|
|
Routes: []string{"/api/v1/items", "/api/v1/items/*"},
|
|
})
|
|
|
|
if err := sb.CheckRoute("route-plugin", "/api/v1/items"); err != nil {
|
|
t.Errorf("expected route allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckRoute("route-plugin", "/api/v1/items/123"); err != nil {
|
|
t.Errorf("expected wildcard route allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckRoute("route-plugin", "/api/v1/users"); err == nil {
|
|
t.Error("expected route denied for unauthorized path")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_CheckAdminPanel(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
sb.Register("admin-plugin", Permissions{AdminPanel: true})
|
|
sb.Register("no-admin", Permissions{AdminPanel: false})
|
|
|
|
if err := sb.CheckAdminPanel("admin-plugin"); err != nil {
|
|
t.Errorf("expected admin access allowed, got: %v", err)
|
|
}
|
|
if err := sb.CheckAdminPanel("no-admin"); err == nil {
|
|
t.Error("expected admin access denied")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_SafeInit_PanicRecovery(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
panicPlugin := &mockPlugin{
|
|
name: "panic-plugin",
|
|
initFn: func() error {
|
|
panic("something went wrong")
|
|
},
|
|
}
|
|
|
|
err := sb.SafeInit(panicPlugin)
|
|
if err == nil {
|
|
t.Error("expected error from panicked init")
|
|
}
|
|
}
|
|
|
|
func TestSandbox_SafeInit_Success(t *testing.T) {
|
|
log := zap.NewNop()
|
|
sb := NewSandbox(log)
|
|
|
|
okPlugin := &mockPlugin{
|
|
name: "ok-plugin",
|
|
initFn: func() error { return nil },
|
|
}
|
|
|
|
if err := sb.SafeInit(okPlugin); err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
type mockPlugin struct {
|
|
name string
|
|
initFn func() error
|
|
}
|
|
|
|
func (m *mockPlugin) Metadata() Metadata { return Metadata{Name: m.name, Version: "0.0.1", Description: "mock"} }
|
|
func (m *mockPlugin) Permissions() Permissions { return Permissions{} }
|
|
func (m *mockPlugin) FxOption() fx.Option { return fx.Options() }
|
|
func (m *mockPlugin) SetupRoutes(_ *gin.Engine, _ gin.HandlerFunc, _ tenant.Resolver) {}
|
|
func (m *mockPlugin) Init() error { return m.initFn() }
|
|
func (m *mockPlugin) MigrationsFS() fs.FS { return nil }
|