Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.edgespark.dev/llms.txt

Use this file to discover all available pages before exploring further.

EdgeSpark has no local development server, which shapes how testing works. Unit tests run locally against pure logic. Integration tests run against a deployed environment.

What to unit test

Unit tests are best for pure functions — logic that does not depend on the platform client. Extract this logic from your route handlers and test it in isolation.
server/src/lib/posts.ts
export function formatPost(post: { title: string; createdAt: number }) {
  return {
    ...post,
    formattedDate: new Date(post.createdAt * 1000).toISOString(),
  };
}

export function validatePostInput(body: unknown): { title: string; content?: string } {
  if (typeof body !== "object" || body === null) {
    throw new Error("Body must be an object");
  }
  const { title } = body as Record<string, unknown>;
  if (typeof title !== "string" || title.trim().length === 0) {
    throw new Error("title is required");
  }
  return { title: (body as any).title, content: (body as any).content };
}
server/src/lib/posts.test.ts
import { describe, it, expect } from "vitest";
import { formatPost, validatePostInput } from "./posts";

describe("formatPost", () => {
  it("formats the date as ISO string", () => {
    const result = formatPost({ title: "Hello", createdAt: 1700000000 });
    expect(result.formattedDate).toBe("2023-11-14T22:13:20.000Z");
  });
});

describe("validatePostInput", () => {
  it("returns validated input for valid body", () => {
    const result = validatePostInput({ title: "Hello" });
    expect(result.title).toBe("Hello");
  });

  it("throws if title is missing", () => {
    expect(() => validatePostInput({ content: "no title" })).toThrow("title is required");
  });

  it("throws if body is not an object", () => {
    expect(() => validatePostInput("string")).toThrow("Body must be an object");
  });
});

Set up Vitest

npm install -D vitest
server/package.json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}
server/vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "node",
  },
});
Run tests:
cd server && npm test

Integration testing against a deployed environment

For routes that depend on the database, auth, or storage, deploy and test with HTTP requests against your project URL.

Test with curl

BASE=https://my-app.edgespark.app

# Public route
curl "$BASE/api/public/feed"

# Create a post (requires session)
curl -X POST "$BASE/api/posts" \
  -H "Cookie: better-auth.session_token=$SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Integration test post"}'

# Get the post back
curl "$BASE/api/posts/1" \
  -H "Cookie: better-auth.session_token=$SESSION_TOKEN"

Test with a fetch script

scripts/test-deployed.ts
const BASE = "https://my-app.edgespark.app";
const SESSION = process.env.SESSION_TOKEN!;

const headers = {
  Cookie: `better-auth.session_token=${SESSION}`,
  "Content-Type": "application/json",
};

// Create a post
const createRes = await fetch(`${BASE}/api/posts`, {
  method: "POST",
  headers,
  body: JSON.stringify({ title: "Test post" }),
});
const post = await createRes.json();
console.assert(createRes.status === 201, "Expected 201");
console.assert(post.title === "Test post", "Expected title");

// Read it back
const getRes = await fetch(`${BASE}/api/posts/${post.id}`, { headers });
const fetched = await getRes.json();
console.assert(getRes.status === 200, "Expected 200");
console.assert(fetched.id === post.id, "Expected same ID");

console.log("All assertions passed");
SESSION_TOKEN=your_token npx tsx scripts/test-deployed.ts

Testing checklist before deploys

Before relying on a deploy, verify:
  • All new routes return the expected status codes
  • Protected routes return 401 without a session cookie
  • Public routes return data without a session cookie
  • Webhook routes accept requests without auth
  • Error cases return consistent error shapes
  • Database reads and writes work as expected
  • File uploads and presigned URL flows work end to end
Public staging environments are coming soon. Today, integration testing happens against the current deployed project environment, so prefer edgespark deploy --dry-run before a real deploy and keep deployed test changes small.

See also

Error handling

Patterns for consistent error responses in your routes.

Deploy and test loop

The iterative workflow for dry runs, deploys, and deployed integration testing.
Last modified on April 9, 2026