Skip to content
repo-sdk
Esc
navigateopen⌘Jpreview
On this page

Testing

Exercise your integration with the in-memory provider from repo-sdk/testing.

repo-sdk/testing ships createInMemoryProvider, an in-memory RepoProvider you can drive with createClient to test your integration without hitting a real provider. It uses a small page size, so cursor pagination is exercised too, and it accepts an optional seed of namespaces, repos, commits, branches, tags, and webhooks.

Basic usage

Create the provider, hand it to createClient, and drive it exactly like a real client:

import { createClient } from 'repo-sdk';
import { createInMemoryProvider } from 'repo-sdk/testing';

const provider = createInMemoryProvider({
  namespaces: [{ id: '1', slug: 'acme', name: 'Acme', kind: 'organization' }],
  repositories: {
    'acme/app': { name: 'app', namespace: 'acme', defaultBranch: 'main' },
  },
  commits: {
    'acme/app': [{ sha: 'abc123', message: 'Initial commit' }],
  },
});

const client = createClient({ provider });
const { data } = await client.repos.list({ namespace: 'acme' });

The seed is optional — createInMemoryProvider() starts empty.

Simulating a provider and its capabilities

By default the in-memory provider reports itself as github with every capability enabled. A second options argument lets you simulate a specific provider and its capability gaps, so code that branches on client.capabilities before offering a feature is testable:

const provider = createInMemoryProvider({}, {
  name: 'azure-devops',
  capabilities: { repoSearch: false },
});
const client = createClient({ provider });

client.providerName; // 'azure-devops'
await client.repos.list({ query: 'app' }); // throws RepoError with code 'unsupported'

capabilities is a Partial<RepoCapabilities> merged over the full-featured defaults — override only what your test needs. Check the capability matrix for what each real provider supports.

The seed shape

namespaces is an array; repositories, commits, branches, tags, and webhooks are records keyed by repository path (e.g. acme/app).

PropType
user?InMemoryUserSeed

The authenticated user returned by users.me. Defaults to id user-1 / username in-memory-user.

TypeInMemoryUserSeed
namespaces?InMemoryNamespaceSeed[]

Namespaces, as an array of { id, slug, name, kind, parent? }.

TypeInMemoryNamespaceSeed[]
repositories?Record<string, InMemoryRepositorySeed>

Repositories keyed by path. Fields default sensibly (e.g. defaultBranch → main).

TypeRecord<string, InMemoryRepositorySeed>
commits?Record<string, InMemoryCommitSeed[]>

Commits keyed by repo path, newest first.

TypeRecord<string, InMemoryCommitSeed[]>
branches?Record<string, InMemoryBranchSeed[]>

Branches keyed by repo path, as { name, sha }. Drives branches.list and refs.search.

TypeRecord<string, InMemoryBranchSeed[]>
tags?Record<string, InMemoryTagSeed[]>

Tags keyed by repo path.

TypeRecord<string, InMemoryTagSeed[]>
webhooks?Record<string, InMemoryWebhookSeed[]>

Webhooks keyed by repo path.

TypeRecord<string, InMemoryWebhookSeed[]>

A fuller seed:

const provider = createInMemoryProvider({
  namespaces: [{ id: '1', slug: 'acme', name: 'Acme', kind: 'organization' }],
  repositories: {
    'acme/app': { name: 'app', namespace: 'acme', defaultBranch: 'main', owned: true },
  },
  commits: {
    'acme/app': [
      { sha: 'c2', message: 'Second', refs: ['main'] },
      { sha: 'c1', message: 'First', refs: ['main', 'v1.0.0'] },
    ],
  },
  branches: {
    'acme/app': [{ name: 'main', sha: 'c2' }],
  },
  tags: {
    'acme/app': [{ name: 'v1.0.0', sha: 'c1', isAnnotated: true, date: new Date('2024-01-01') }],
  },
  webhooks: {
    'acme/app': [{ url: 'https://example.com/hook', events: ['push'] }],
  },
});

Asserting against state

The provider exposes its data on provider.state as Maps (plus a namespaces array), so you can assert on the effects of your code — for example, that a webhook was created:

import { expect, test } from 'vitest';

test('registers a webhook', async () => {
  const provider = createInMemoryProvider({
    repositories: { 'acme/app': { name: 'app', namespace: 'acme' } },
  });
  const client = createClient({ provider });

  await client.webhooks.create({
    repo: 'acme/app',
    url: 'https://example.com/hook',
    events: ['push'],
  });

  expect(provider.state.webhooks.get('acme/app')).toHaveLength(1);
});

provider.state is { namespaces: Namespace[], repositories: Map, commits: Map, branches: Map, tags: Map, webhooks: Map }.

Pagination is exercised

The in-memory provider uses a deliberately small page size of 2, so a seed with three or more items returns a cursor and your cursor-following code (list with cursor, or listAll) is exercised rather than always fitting in one page.

const provider = createInMemoryProvider({
  namespaces: [
    { id: '1', slug: 'a', name: 'A', kind: 'organization' },
    { id: '2', slug: 'b', name: 'B', kind: 'organization' },
    { id: '3', slug: 'c', name: 'C', kind: 'organization' },
  ],
});
const client = createClient({ provider });

const first = await client.namespaces.list();
first.data.length; // 2
first.cursor; // defined — there's a third item

let count = 0;
for await (const _ of client.namespaces.listAll()) count++;
count; // 3

Reference: Provider support

Compare the real providers feature by feature.

Was this page helpful?