npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sna-sdk/testing

v0.17.2

Published

Testing utilities for SNA — mock Anthropic/OpenAI APIs, oneshot runner, and sna-test CLI

Readme

@sna-sdk/testing

Testing utilities for SNA — mock Anthropic and OpenAI-compatible APIs plus the sna-test CLI for running Claude Code in an isolated environment.

Install

npm install --save-dev @sna-sdk/testing

Why

Real LLM calls in CI burn budget. The mocks implement the Anthropic Messages API and common OpenAI-compatible endpoints, so runtime integration tests can run deterministically without live model calls.

Set ANTHROPIC_BASE_URL to the Anthropic mock for Claude Code tests. Use the OpenAI mock for Codex-style Responses API paths, OpenAI-compatible chat completion paths, model catalog tests, or adapter tests that only need stable request/response behavior.

The mock echoes user text reversed:

"hello world"      → "dlrow olleh"
"SNA SDK 테스트"    → "트스테 KDS ANS"

sna-test CLI

Each invocation creates a named "instance": metadata is stored under .sna/instances/<name>.json and the logs + isolated CLAUDE_CONFIG_DIR live under .sna/<name>/. Each instance gets its own mock server and JSONL request/response log.

sna-test claude [args...]      # Launch Claude Code with mock API + isolated config
sna-test claude -p "prompt"    # Print mode (oneshot, non-interactive)
sna-test ls                    # List instances
sna-test logs <name> [-f]      # Show / follow API request/response log
sna-test rm <name|--all>       # Cleanup

Why isolation matters

sna-test claude builds a fresh env (PATH, HOME, SHELL, TERM, LANG only, plus mock-specific overrides). It does NOT inherit the parent's ANTHROPIC_API_KEY, OAuth tokens, or CLAUDE_CONFIG_DIR. This prevents:

  • OAuth conflicts ("Auth conflict" warnings)
  • Real API calls leaking through
  • Polluting your dev Claude account history / analytics

Each instance gets its own claude-config/ with customApiKeyResponses pre-approved for the mock key, so the trust dialog doesn't pop up.

Programmatic API

import { startMockAnthropicServer, runOneshot } from "@sna-sdk/testing";

const mock = await startMockAnthropicServer();
// mock.port      — server port
// mock.requests  — array of received request bodies
// mock.onLog     — JSONL log callback
// mock.close()   — shutdown

process.env.ANTHROPIC_BASE_URL = `http://localhost:${mock.port}`;
process.env.ANTHROPIC_API_KEY = "sk-test";
process.env.CLAUDE_CONFIG_DIR = "/tmp/isolated-config";

// ...spawn Claude Code or run integration logic...

mock.close();

runOneshot() is a convenience wrapper that boots a mock, runs claude -p, captures the response, and tears down.

OpenAI-compatible mock

import { startMockOpenAIServer } from "@sna-sdk/testing";

const mock = await startMockOpenAIServer({
  models: [{ id: "gpt-5.4", owned_by: "openai" }],
  responseText: ({ endpoint, userText }) => `${endpoint}: ${userText}`,
});

const res = await fetch(`${mock.url}/v1/responses`, {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: "Bearer sk-test" },
  body: JSON.stringify({ model: "gpt-5.4", input: "hello" }),
});

console.log(await res.json());
console.log(mock.requests[0]);
await mock.close();

Supported routes:

  • GET /v1/models
  • POST /v1/chat/completions
  • POST /v1/responses

Both chat completions and responses support non-streaming JSON and streaming SSE. By default the mock replies with the last user text reversed.

Mock-attached real runtimes

Use mock-attached mode for high-confidence provider tests: run the real runtime CLI, but route its model API traffic to a local mock server. This verifies the actual CLI contract while keeping CI deterministic and offline from paid LLMs.

import {
  createClaudeMockEnv,
  createCodexMockEnv,
  createGrokMockEnv,
  createOpenCodeMockConfig,
  startMockAnthropicServer,
  startMockOpenAIServer,
} from "@sna-sdk/testing";

const anthropic = await startMockAnthropicServer();
const claudeEnv = createClaudeMockEnv({
  anthropicBaseUrl: `http://127.0.0.1:${anthropic.port}`,
});
// Run real `claude` with claudeEnv.env.

const openai = await startMockOpenAIServer();
const codexEnv = createCodexMockEnv({
  openAIBaseUrl: openai.url,
});
// Run real `codex` with codexEnv.env.

const grokEnv = createGrokMockEnv({
  openAIBaseUrl: openai.url,
});
// Pass grokEnv.env and grokEnv.providerOptions to SNA's Grok provider.

const opencodeConfig = createOpenCodeMockConfig({
  openAIBaseUrl: openai.url,
});
// Pass opencodeConfig.providerOptions to SNA's OpenCode provider.

When a runtime binary is not on PATH, set the same command override that the provider uses in production before running the mock-attached tests:

SNA_CLAUDE_COMMAND=/absolute/path/to/claude \
SNA_CODEX_COMMAND=/absolute/path/to/codex \
SNA_GROK_COMMAND=/absolute/path/to/grok \
SNA_OPENCODE_COMMAND=/absolute/path/to/opencode \
pnpm --filter @sna-sdk/core exec tsx --test test/runtime-mock-attached.test.ts

Runtime CLI fakes

Use the mock CLIs when you need to test SNA providers or consumer app launch configuration without depending on a real Claude Code or Codex installation.

import {
  createClaudeMockEnv,
  createMockClaudeCli,
  createMockCodexExecCli,
  startMockAnthropicServer,
  startMockOpenAIServer,
} from "@sna-sdk/testing";

const anthropic = await startMockAnthropicServer();
const claude = createMockClaudeCli(anthropic);
const claudeEnv = createClaudeMockEnv({
  anthropicBaseUrl: `http://127.0.0.1:${anthropic.port}`,
  extraEnv: { LOOM_API_URL: "http://127.0.0.1:57787" },
});

process.env.SNA_CLAUDE_COMMAND = claude.command;
// pass claudeEnv.env to the provider/process under test

const openai = await startMockOpenAIServer();
const codex = createMockCodexExecCli(openai);
process.env.SNA_CODEX_COMMAND = codex.command;

// ...run provider or app integration logic...

claude.close();
codex.close();
anthropic.close();
await openai.close();

Harness helpers

import { waitForRequest, withMockOpenAIServer, readSseData } from "@sna-sdk/testing";

await withMockOpenAIServer({ responseText: "ok" }, async (mock) => {
  const pending = waitForRequest(mock, (req) => req.endpoint === "responses");
  const res = await fetch(`${mock.url}/v1/responses`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ model: "gpt-5.4", stream: true, input: "hello" }),
  });
  const request = await pending;
  const sseLines = await readSseData(res);
});

Exports

| Name | Role | |------|------| | startMockAnthropicServer() | Boot a mock Anthropic Messages API on a random port | | startMockOpenAIServer() | Boot a mock OpenAI-compatible API on a random port | | createClaudeMockEnv() | Create isolated Claude config and env for mock Anthropic routing | | createCodexMockEnv() | Create isolated Codex config and env for mock OpenAI routing | | createGrokMockEnv() | Create isolated Grok env/providerOptions for mock OpenAI Responses routing | | createOpenCodeMockConfig() | Create OpenCode config/providerOptions for mock OpenAI routing | | createMockClaudeCli() | Create a fake claude executable backed by the Anthropic mock | | createMockCodexExecCli() | Create a fake codex exec executable backed by the OpenAI mock | | withMockAnthropicServer(), withMockOpenAIServer() | Start a mock for a callback and always close it | | waitForRequest() | Wait until a mock receives a matching request | | readSseData() | Parse SSE data: lines from a mock response | | runOneshot(opts) | Boot mock → run claude -p → capture → teardown | | MockServer, MockLogEntry, MockOpenAIServer, MockOpenAIRequest, MockOpenAILogEntry | Types | | generateInstanceName, getInstanceDir, listInstances, readInstanceMeta, writeInstanceMeta, removeInstance | Instance helpers used by the CLI |

License

MIT