import { describe, it, expect } from 'vitest'; import { createMockedFunction } from './test-utils'; describe('Edge Functions', () => { const functionName = `hello-world-${Date.now()}`; // Simple WASI module that prints "Hello from WASM!" to stdout const wat = ` (module (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) (memory 1) (export "memory" (memory 0)) (data (i32.const 8) "Hello from WASM!") (func $main (export "_start") (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base (i32.store (i32.const 4) (i32.const 16)) ;; iov.iov_len (call $fd_write (i32.const 1) ;; stdout (i32.const 0) ;; iovs ptr (i32.const 1) ;; iovs len (i32.const 20) ;; nwritten ptr ) drop ) ) `; it('should deploy a function', async () => { const res = await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name: functionName, code_base64: Buffer.from(wat).toString('base64') }) }); if (res.status !== 200) { console.error('Deploy failed:', await res.text()); } expect(res.status).toBe(200); }); it('should invoke a function', async () => { const res = await fetch(`${process.env.MADBASE_URL}/functions/v1/${functionName}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: { name: 'World' } }) }); if (res.status !== 200) { console.error('Invoke failed:', await res.text()); } expect(res.status).toBe(200); const data = await res.json(); console.log('Invoke response:', data); expect(data.result).toContain('Hello from WASM!'); }); it('should deploy and invoke a Deno function', async () => { const name = `deno-hello-${Date.now()}`; // Simple Deno function that uses Deno.serve shim const code = ` Deno.serve(async (req) => { const body = await req.json(); return new Response("Hello " + body.name + " from Deno!"); }); `; // Deploy const deployRes = await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); if (deployRes.status !== 200) { console.error('Deno Deploy failed:', await deployRes.text()); } expect(deployRes.status).toBe(200); // Invoke const invokeRes = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: { name: 'World' } }) }); if (invokeRes.status !== 200) { console.error('Deno Invoke failed:', await invokeRes.text()); } expect(invokeRes.status).toBe(200); const data = await invokeRes.json(); console.log('Deno Invoke response:', data); expect(data.result).toBe('Hello World from Deno!'); }); describe('Unit Tests (Component Logic)', () => { it('should handle missing environment variables', async () => { const name = `env-check-${Date.now()}`; const code = createMockedFunction(` Deno.serve(async (req) => { const key = Deno.env.get("MY_SECRET_KEY"); if (!key) { return new Response("Missing Key", { status: 500 }); } return new Response("Found Key: " + key); }); `, { env: {} }); // Empty env // Deploy await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); // Invoke const res = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: {} }) }); const data = await res.json(); expect(data.result).toBe("Missing Key"); expect(data.status).toBe(500); }); it('should validate request body', async () => { const name = `body-check-${Date.now()}`; const code = createMockedFunction(` Deno.serve(async (req) => { const body = await req.json(); if (!body.requiredField) { return new Response("Missing Field", { status: 400 }); } return new Response("OK"); }); `); // Deploy await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); // Invoke (Missing Field) const res = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: {} }) }); const data = await res.json(); expect(data.result).toBe("Missing Field"); expect(data.status).toBe(400); }); }); describe('Integration Tests (System Interactions)', () => { it('should handle CORS preflight requests', async () => { const name = `cors-check-${Date.now()}`; const code = createMockedFunction(` const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") { return new Response("ok", { headers: corsHeaders }); } return new Response("ok", { headers: corsHeaders }); }); `); await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); // Invoke with OPTIONS (Note: The Gateway might handle this or pass it through. // Our Deno runtime shim creates a request with POST method by default for invocations, // so testing OPTIONS strictly via invocation endpoint might need support in the handler/shim. // For now, we test that the function *can* set headers in response.) const res = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: {} }) }); const data = await res.json(); // Check if headers are returned (requires handler update to return headers, which we did) expect(data.headers['access-control-allow-origin']).toBe('*'); }); }); describe('E2E Workflows (User Flows)', () => { it('should execute invite staff workflow', async () => { const name = `invite-staff-${Date.now()}`; const code = createMockedFunction(` Deno.serve(async (req) => { const { email } = await req.json(); // 1. Insert into DB (mocked) const supabase = createClient(); const { error } = await supabase.from('invitations').insert({ email }); if (error) return new Response("DB Error", { status: 500 }); // 2. Send Email (mocked fetch) const res = await fetch("https://api.resend.com/emails", { method: "POST", body: JSON.stringify({ to: email }) }); if (!res.ok) return new Response("Email Error", { status: 502 }); return new Response("Invite Sent"); }); `, { fetch: [{ urlPattern: "api.resend.com", status: 200, response: { id: "email_123" } }], supabase: { insertResult: { id: "invite_123" } } }); await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); const res = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: { email: "newuser@example.com" } }) }); const data = await res.json(); expect(data.result).toBe("Invite Sent"); expect(data.status).toBe(200); }); }); it('should deploy and invoke a complex Polar Checkout-like function', async () => { const name = `polar-checkout-${Date.now()}`; const code = createMockedFunction(` const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const POLAR_API_KEY = Deno.env.get("POLAR_API_KEY"); if (!POLAR_API_KEY) throw new Error("POLAR_API_KEY is not configured"); // Authenticate user const authHeader = req.headers.get("Authorization"); if (!authHeader || !authHeader.startsWith("Bearer ")) { return new Response(JSON.stringify({ error: "Unauthorized: Missing or invalid token" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const supabase = createClient( Deno.env.get("SUPABASE_URL"), Deno.env.get("SUPABASE_ANON_KEY"), { global: { headers: { Authorization: authHeader } } } ); const token = authHeader.replace("Bearer ", ""); const { data: claimsData, error: claimsError } = await supabase.auth.getClaims(token); if (claimsError || !claimsData?.claims) { return new Response(JSON.stringify({ error: "Unauthorized: Invalid claims" }), { status: 401 }); } const { productId, successUrl } = await req.json(); // Create Polar checkout session const polarRes = await fetch("https://sandbox-api.polar.sh/v1/checkouts/", { method: "POST", headers: { Authorization: "Bearer " + POLAR_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ products: [productId], success_url: successUrl, metadata: { user_id: claimsData.claims.sub } }), }); const polarData = await polarRes.json(); if (!polarRes.ok) { throw new Error("Polar API error"); } return new Response( JSON.stringify({ url: polarData.url, id: polarData.id }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error) { return new Response(JSON.stringify({ error: String(error) }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } }); `, { env: { "POLAR_API_KEY": "mock_polar_key", "SUPABASE_URL": "http://mock-supabase", "SUPABASE_ANON_KEY": "mock_anon_key", "SUPABASE_SERVICE_ROLE_KEY": "mock_service_key" }, supabase: { claims: { sub: "user_123", email: "test@example.com" } }, fetch: [{ urlPattern: "sandbox-api.polar.sh/v1/checkouts/", status: 200, response: { url: "https://sandbox.polar.sh/checkout/123", id: "checkout_123" } }] }); // Deploy const deployRes = await fetch(`${process.env.MADBASE_URL}/functions/v1`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_SERVICE_ROLE_KEY}` }, body: JSON.stringify({ name, code_base64: Buffer.from(code).toString('base64'), runtime: 'deno' }) }); expect(deployRes.status).toBe(200); // Invoke const invokeRes = await fetch(`${process.env.MADBASE_URL}/functions/v1/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.MADBASE_ANON_KEY}` }, body: JSON.stringify({ payload: { productId: "prod_123", successUrl: "http://example.com" } }) }); expect(invokeRes.status).toBe(200); const data = await invokeRes.json(); console.log('Polar Invoke response:', data); const result = JSON.parse(data.result); expect(result.url).toBe("https://sandbox.polar.sh/checkout/123"); }); });