mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 20:53:04 +00:00
- Updated the serverless functions table schema to remove the version constraint for uniqueness, allowing for more flexible function definitions. - Enhanced the serverless engine to support HTTP fetch functionality, enabling external API calls from serverless functions. - Implemented logging capabilities for function invocations, capturing detailed logs for better debugging and monitoring. - Improved the authentication middleware to handle public endpoints more effectively, ensuring seamless access to serverless functions. - Added new configuration options for serverless functions, including memory limits, timeout settings, and retry parameters, to optimize performance and reliability.
203 lines
5.7 KiB
Go
203 lines
5.7 KiB
Go
package serverless
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestEngine_Execute(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
registry := NewMockRegistry()
|
|
hostServices := NewMockHostServices()
|
|
|
|
cfg := DefaultConfig()
|
|
cfg.ModuleCacheSize = 2
|
|
|
|
engine, err := NewEngine(cfg, registry, hostServices, logger)
|
|
if err != nil {
|
|
t.Fatalf("failed to create engine: %v", err)
|
|
}
|
|
defer engine.Close(context.Background())
|
|
|
|
// Use a minimal valid WASM module that exports _start (WASI)
|
|
// This is just 'nop' in WASM
|
|
wasmBytes := []byte{
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
|
|
0x03, 0x02, 0x01, 0x00,
|
|
0x07, 0x0a, 0x01, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
|
|
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
|
|
}
|
|
|
|
fnDef := &FunctionDefinition{
|
|
Name: "test-func",
|
|
Namespace: "test-ns",
|
|
MemoryLimitMB: 64,
|
|
TimeoutSeconds: 5,
|
|
}
|
|
|
|
_, err = registry.Register(context.Background(), fnDef, wasmBytes)
|
|
if err != nil {
|
|
t.Fatalf("failed to register function: %v", err)
|
|
}
|
|
|
|
fn, err := registry.Get(context.Background(), "test-ns", "test-func", 0)
|
|
if err != nil {
|
|
t.Fatalf("failed to get function: %v", err)
|
|
}
|
|
|
|
// Execute function
|
|
ctx := context.Background()
|
|
output, err := engine.Execute(ctx, fn, []byte("input"), nil)
|
|
if err != nil {
|
|
t.Errorf("failed to execute function: %v", err)
|
|
}
|
|
|
|
// Our minimal WASM doesn't write to stdout, so output should be empty
|
|
if len(output) != 0 {
|
|
t.Errorf("expected empty output, got %d bytes", len(output))
|
|
}
|
|
|
|
// Test cache stats
|
|
size, capacity := engine.GetCacheStats()
|
|
if size != 1 {
|
|
t.Errorf("expected cache size 1, got %d", size)
|
|
}
|
|
if capacity != 2 {
|
|
t.Errorf("expected cache capacity 2, got %d", capacity)
|
|
}
|
|
|
|
// Test Invalidate
|
|
engine.Invalidate(fn.WASMCID)
|
|
size, _ = engine.GetCacheStats()
|
|
if size != 0 {
|
|
t.Errorf("expected cache size 0 after invalidation, got %d", size)
|
|
}
|
|
}
|
|
|
|
func TestEngine_Precompile(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
registry := NewMockRegistry()
|
|
hostServices := NewMockHostServices()
|
|
engine, _ := NewEngine(nil, registry, hostServices, logger)
|
|
defer engine.Close(context.Background())
|
|
|
|
wasmBytes := []byte{
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
|
|
0x03, 0x02, 0x01, 0x00,
|
|
0x07, 0x0a, 0x01, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
|
|
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
|
|
}
|
|
|
|
err := engine.Precompile(context.Background(), "test-cid", wasmBytes)
|
|
if err != nil {
|
|
t.Fatalf("failed to precompile: %v", err)
|
|
}
|
|
|
|
size, _ := engine.GetCacheStats()
|
|
if size != 1 {
|
|
t.Errorf("expected cache size 1, got %d", size)
|
|
}
|
|
}
|
|
|
|
func TestEngine_Timeout(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
registry := NewMockRegistry()
|
|
hostServices := NewMockHostServices()
|
|
engine, _ := NewEngine(nil, registry, hostServices, logger)
|
|
defer engine.Close(context.Background())
|
|
|
|
wasmBytes := []byte{
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
|
|
0x03, 0x02, 0x01, 0x00,
|
|
0x07, 0x0a, 0x01, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
|
|
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
|
|
}
|
|
|
|
fn, _ := registry.Get(context.Background(), "test", "timeout", 0)
|
|
if fn == nil {
|
|
_, _ = registry.Register(context.Background(), &FunctionDefinition{Name: "timeout", Namespace: "test"}, wasmBytes)
|
|
fn, _ = registry.Get(context.Background(), "test", "timeout", 0)
|
|
}
|
|
fn.TimeoutSeconds = 1
|
|
|
|
// Test with already canceled context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
_, err := engine.Execute(ctx, fn, nil, nil)
|
|
if err == nil {
|
|
t.Error("expected error for canceled context, got nil")
|
|
}
|
|
}
|
|
|
|
func TestEngine_MemoryLimit(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
registry := NewMockRegistry()
|
|
hostServices := NewMockHostServices()
|
|
engine, _ := NewEngine(nil, registry, hostServices, logger)
|
|
defer engine.Close(context.Background())
|
|
|
|
wasmBytes := []byte{
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
|
|
0x03, 0x02, 0x01, 0x00,
|
|
0x07, 0x0a, 0x01, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
|
|
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
|
|
}
|
|
|
|
_, _ = registry.Register(context.Background(), &FunctionDefinition{Name: "memory", Namespace: "test", MemoryLimitMB: 1, TimeoutSeconds: 5}, wasmBytes)
|
|
fn, _ := registry.Get(context.Background(), "test", "memory", 0)
|
|
|
|
// This should pass because the minimal WASM doesn't use much memory
|
|
_, err := engine.Execute(context.Background(), fn, nil, nil)
|
|
if err != nil {
|
|
t.Errorf("expected success for minimal WASM within memory limit, got error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEngine_RealWASM(t *testing.T) {
|
|
wasmPath := "../../examples/functions/bin/hello.wasm"
|
|
if _, err := os.Stat(wasmPath); os.IsNotExist(err) {
|
|
t.Skip("hello.wasm not found")
|
|
}
|
|
|
|
wasmBytes, err := os.ReadFile(wasmPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read hello.wasm: %v", err)
|
|
}
|
|
|
|
logger := zap.NewNop()
|
|
registry := NewMockRegistry()
|
|
hostServices := NewMockHostServices()
|
|
engine, _ := NewEngine(nil, registry, hostServices, logger)
|
|
defer engine.Close(context.Background())
|
|
|
|
fnDef := &FunctionDefinition{
|
|
Name: "hello",
|
|
Namespace: "examples",
|
|
TimeoutSeconds: 10,
|
|
}
|
|
_, _ = registry.Register(context.Background(), fnDef, wasmBytes)
|
|
fn, _ := registry.Get(context.Background(), "examples", "hello", 0)
|
|
|
|
output, err := engine.Execute(context.Background(), fn, []byte(`{"name": "Tester"}`), nil)
|
|
if err != nil {
|
|
t.Fatalf("execution failed: %v", err)
|
|
}
|
|
|
|
expected := "Hello, Tester!"
|
|
if !contains(string(output), expected) {
|
|
t.Errorf("output %q does not contain %q", string(output), expected)
|
|
}
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s[:len(substr)] == substr || contains(s[1:], substr))
|
|
}
|