network/pkg/serverless/engine_test.go
anonpenguin23 4f893e08d1 feat: enhance serverless function management and logging
- 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.
2026-01-02 08:40:28 +02:00

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))
}