# How to Test Supermemory Code: Complete Vitest Testing Guide

> Learn to test Supermemory code effectively using Vitest. This guide covers mocking HTTP calls and environment variables for robust validation without a live server.

- Repository: [supermemory/supermemory](https://github.com/supermemoryai/supermemory)
- Tags: tutorial
- Published: 2026-03-25

---

**Supermemory ships with a modular, type-safe Vitest test suite that mocks HTTP calls and environment variables to validate memory injection, caching, and conversation persistence without requiring a live server.**

The `supermemoryai/supermemory` repository uses Vitest to ensure every layer of the memory system—from the `withSupermemory` wrapper to the Mastra processors—functions correctly. Understanding how to test Supermemory code allows you to verify that memory transformations, context injections, and conversation saves work as intended before submitting changes.

## Test Architecture Overview

Supermemory organizes tests into distinct layers that mirror the package structure. Each layer targets specific functionality while maintaining isolation through mocked dependencies.

### Wrapper Layer Tests (withSupermemory)

Tests in [`packages/tools/test/with-supermemory/unit.test.ts`](https://github.com/supermemoryai/supermemory/blob/main/packages/tools/test/with-supermemory/unit.test.ts) validate that the wrapper creates a model which injects memories into prompts, persists responses when configured, and throws descriptive errors when the API key is missing. These tests mock `globalThis.fetch` using `vi.fn()` and verify that `process.env.SUPERMEMORY_API_KEY` is correctly consumed.

### Processor Tests (Mastra)

The Mastra-specific suite in [`packages/tools/test/mastra/unit.test.ts`](https://github.com/supermemoryai/supermemory/blob/main/packages/tools/test/mastra/unit.test.ts) exercises `SupermemoryInputProcessor` and `SupermemoryOutputProcessor`. These tests ensure that processors call the Supermemory HTTP API, cache results in `MemoryCache`, and handle error responses gracefully without crashing the inference pipeline.

### Middleware Tests (transformParamsWithMemory)

The core memory-search flow—including query building, API invocation, and prompt injection—is tested via the `transformParamsWithMemory` function. Tests verify that the returned `prompt` array contains the injected memory string and that `MemoryCache` entries are populated with the expected keys.

### Conversation Client Tests

Integration tests for conversation persistence validate the `addConversation` flow. When `addMemory: "always"` is set and a `conversationId` is supplied, tests confirm that the request body sent to the Supermemory endpoint contains the correct conversation metadata.

## Setting Up the Test Environment

All Supermemory tests are self-contained and do not rely on external files or a running server. To prepare the environment:

1. Install dependencies using Bun.
2. Set a dummy API key in `process.env.SUPERMEMORY_API_KEY`.
3. Replace `globalThis.fetch` with a Vitest mock (`vi.fn()`) that returns canned JSON responses.

This setup guarantees deterministic CI runs defined in [`.github/workflows/ci.yml`](https://github.com/supermemoryai/supermemory/blob/main/.github/workflows/ci.yml).

## Unit Testing the withSupermemory Wrapper

The following example from [`packages/tools/test/with-supermemory/unit.test.ts`](https://github.com/supermemoryai/supermemory/blob/main/packages/tools/test/with-supermemory/unit.test.ts) demonstrates how to test the Vercel AI SDK wrapper.

```typescript
// packages/tools/test/with-supermemory/unit.test.ts
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"
import { withSupermemory } from "../../src/vercel"
import { openai } from "@ai-sdk/openai"

describe("withSupermemory wrapper", () => {
  const TEST_CONFIG = {
    apiKey: "test-api-key",
    containerTag: "test-container",
  }

  let fetchMock: ReturnType<typeof vi.fn>
  let originalEnv: string | undefined
  let originalFetch: typeof globalThis.fetch

  beforeEach(() => {
    originalEnv = process.env.SUPERMEMORY_API_KEY
    process.env.SUPERMEMORY_API_KEY = TEST_CONFIG.apiKey
    originalFetch = globalThis.fetch
    fetchMock = vi.fn()
    globalThis.fetch = fetchMock as any
  })

  afterEach(() => {
    if (originalEnv) process.env.SUPERMEMORY_API_KEY = originalEnv
    else delete process.env.SUPERMEMORY_API_KEY
    globalThis.fetch = originalFetch
  })

  it("injects memories and returns a wrapped model", async () => {
    // Mock the Supermemory profile endpoint
    fetchMock.mockResolvedValue({
      ok: true,
      json: () =>
        Promise.resolve({
          profile: {
            static: [{ memory: "User likes TypeScript" }],
            dynamic: [{ memory: "Recent interest in AI" }],
          },
        }),
    })

    const model = withSupermemory(openai("gpt-4"), TEST_CONFIG.containerTag, {
      mode: "profile",
    })

    const result = await model.doGenerate({
      prompt: [{ role: "user", content: "What languages do I prefer?" }],
    })

    // The mocked response is injected as a system prompt
    const systemMsg = result.prompt?.[0]
    expect(systemMsg?.role).toBe("system")
    expect(systemMsg?.content).toContain("User likes TypeScript")
    expect(fetchMock).toHaveBeenCalledTimes(1)
  })
})

```

**Key implementation details:**

- Lines 16-31 preserve the original environment and fetch implementation before mocking.
- The mock returns a profile containing static and dynamic memories.
- Assertions verify that the memory content appears in the system prompt role.

## Testing Memory Caching and Processors

To validate that the `MemoryCache` prevents redundant API calls, test the processors directly.

```typescript
// packages/tools/test/mastra/unit.test.ts (excerpt)
it("caches memories on second call with same message", async () => {
  fetchMock.mockResolvedValue({
    ok: true,
    json: () => Promise.resolve(createMockProfileResponse(["Cached memory"])),
  })

  const processor = new SupermemoryInputProcessor("test-tag", {
    apiKey: "test-key",
    mode: "profile",
  })

  const messages = [createMessage("user", "Hello")]
  const args1 = {
    messages,
    systemMessages: [],
    messageList: createMockMessageList(),
    abort: vi.fn() as never,
    retryCount: 0,
  }

  await processor.processInput(args1) // first call → fetch
  expect(fetchMock).toHaveBeenCalledTimes(1)

  const args2 = { ...args1, messageList: createMockMessageList() }
  await processor.processInput(args2) // second call → cached
  expect(fetchMock).toHaveBeenCalledTimes(1) // no extra fetch
})

```

**Caching validation:**

- The first call to `processInput` triggers a network request.
- The second call with identical parameters reuses the cached value.
- `fetchMock` reports exactly one invocation, confirming cache efficiency.

## Integration Testing with Conversations

When testing conversation persistence, verify that the `SupermemoryOutputProcessor` correctly formats payloads for the `/v4/conversations` endpoint.

```typescript
// packages/tools/test/mastra/integration.test.ts (excerpt)
it("saves a conversation when addMemory is always", async () => {
  const conversationId = `test-conv-${Date.now()}`
  fetchMock.mockResolvedValue({
    ok: true,
    json: () => Promise.resolve({ id: "mem-123", status: "created" }),
  })

  const processor = new SupermemoryOutputProcessor("test-tag", {
    apiKey: "test-key",
    addMemory: "always",
    threadId: conversationId,
  })

  const args = {
    messages: [
      createMessage("user", "Hello"),
      createMessage("assistant", "Hi!"),
    ],
    messageList: createMockMessageList(),
    abort: vi.fn() as never,
    retryCount: 0,
  }

  await processor.processOutputResult(args)
  expect(fetchMock).toHaveBeenCalledTimes(1)

  const body = JSON.parse(fetchMock.mock.calls[0][1].body)
  expect(body.conversationId).toBe(conversationId)
})

```

**Payload verification:**

- The test constructs a realistic message history with user and assistant roles.
- After processing, the request body is parsed to confirm `conversationId` matches the input.

## Running Tests Locally and in CI

Execute the full test suite using Bun and Turbo.

```bash

# Install dependencies

bun install

# Run the full suite scoped to the tools package

bunx turbo run test --filter='@supermemory/tools'

```

The `turbo` command targets the `@supermemory/tools` package, which contains all unit and integration tests. The [`package.json`](https://github.com/supermemoryai/supermemory/blob/main/package.json) defines the test script as `vitest --testTimeout 100000`, allowing sufficient time for async memory operations.

In CI, as defined in [`.github/workflows/ci.yml`](https://github.com/supermemoryai/supermemory/blob/main/.github/workflows/ci.yml), type checking runs separately for `@supermemory/ai-sdk` and `@supermemory/memory-graph`, while functional tests execute via Vitest. This separation keeps the pipeline fast while ensuring comprehensive coverage.

## Summary

- **Supermemory tests are self-contained**: They mock `fetch` and environment variables to avoid external dependencies.
- **Vitest powers the suite**: All tests run with `bunx turbo run test --filter='@supermemory/tools'`.
- **Three layers are validated**: Wrappers (`withSupermemory`), processors (`SupermemoryInputProcessor`, `SupermemoryOutputProcessor`), and middleware (`transformParamsWithMemory`).
- **Caching is verified**: Tests confirm that `MemoryCache` prevents duplicate API calls across identical prompts.
- **Conversation persistence is tested**: Integration tests verify that `conversationId` and message lists reach the Supermemory endpoint when `addMemory: "always"` is configured.

## Frequently Asked Questions

### How do I mock the Supermemory API in my own tests?

Create a `vi.fn()` mock and assign it to `globalThis.fetch`. Return a resolved Response object with the JSON shape expected from the Supermemory API, including `profile`, `searchResults`, or conversation creation payloads. This technique is used in [`packages/tools/test/with-supermemory/unit.test.ts`](https://github.com/supermemoryai/supermemory/blob/main/packages/tools/test/with-supermemory/unit.test.ts) to test memory injection without network calls.

### Why does the test suite use Bun instead of Node.js?

Supermemory uses Bun for its faster package resolution and built-in TypeScript support. The test commands (`bun install` and `bunx turbo run test`) leverage Bun's performance characteristics while remaining compatible with Vitest, which handles the actual test execution and assertions.

### What is the difference between unit and integration tests in Supermemory?

Unit tests mock all external HTTP calls and validate logic such as prompt transformation and caching in `SupermemoryInputProcessor`. Integration tests, located in [`packages/tools/test/mastra/integration.test.ts`](https://github.com/supermemoryai/supermemory/blob/main/packages/tools/test/mastra/integration.test.ts), simulate full conversation flows and verify that the request payloads sent to the `/v4/conversations` endpoint contain valid `conversationId` values and message structures.

### How does the CI pipeline handle test timeouts?

The [`package.json`](https://github.com/supermemoryai/supermemory/blob/main/package.json) in the tools package specifies `--testTimeout 100000` (100 seconds) to accommodate asynchronous memory operations. The GitHub Actions workflow runs type checking separately from the Vitest suite to ensure fast feedback on type errors while allowing the functional tests sufficient time to complete.