---
title: "Testing"
description: "An in-memory fake provider and recorded fixtures."
canonical_url: "https://forges.link/reference/testing"
---
# Testing

> An in-memory fake provider and recorded fixtures.

See [Test your code](https://forges.link/guides/testing) for a walkthrough.

## Fake provider

Import from `forges/fake`.

### `FakeRepoSeed`

A repository to seed, as `owner/name` plus whatever the test cares about.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        defaultBranch?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        main
      </code>
    </td>
    
    <td>
      The default branch.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        visibility?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#repo">
        <code>
          Repo['visibility']
        </code>
      </a>
    </td>
    
    <td>
      <code>
        public
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        labels?
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Names of the labels that the repository defines.
    </td>
  </tr>
</tbody>
</table>

### `FakeThreadSeed`

An issue, pull request or discussion to seed.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        kind
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#threadkind">
        <code>
          Exclude<ThreadKind, 'other' | 'commit'>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        number?
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Defaults to the next free number in the repository.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        body?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        state?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#threadstate">
        <code>
          ThreadState
        </code>
      </a>
    </td>
    
    <td>
      <code>
        open
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isDraft?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        author?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The login of the author. Defaults to the viewer.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        authorAssociation?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#authorassociation">
        <code>
          AuthorAssociation
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        labels?
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        assignees?
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        milestone?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The title of the milestone the thread belongs to.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#authorassociation">
        <code>
          Array<{ body: string, author?: string, authorAssociation?: AuthorAssociation }>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#checkstate">
        <code>
          Array<{ name: string, state: CheckState }>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        files?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#filestatus">
        <code>
          Array<{ path: string, status?: FileStatus, additions?: number, deletions?: number, patch?: string }>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Files the pull request changes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits?
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Shas of seeded commits on the pull request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        head?
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
    
    <td>
      The head branch of a pull request. <code>
        sha
      </code>
      
       defaults to the last of <code>
        commits
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mergeable?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakemergeability">
        <code>
          FakeMergeability
        </code>
      </a>
    </td>
    
    <td>
      <code>
        clean
      </code>
    </td>
    
    <td>
      Whether <code>
        threads.merge()
      </code>
      
       can merge the pull request.
    </td>
  </tr>
</tbody>
</table>

### `FakeMergeability`

Whether a pull request can merge: `blocked` rejects a merge with
`MergeBlockedError`, and `conflict` with `MergeConflictError`.

**one of:**

- `'clean'`
- `'blocked'`
- `'conflict'`

### `FakeLatency`

A delay in milliseconds for every request, or a function of the verb, which names a listing by its page, such as `threads.commentsPage`.

**one of:**

- `number`
- [`((verb: ForgeVerb) => number)`](https://forges.link/reference/provider#forgeverb)

### `FakeRateLimit`

A request budget that every request spends, starting a new window once `window` elapses on the clock.
`provider.rateLimits()` reports it as the latest request left it, until its window ends.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        limit
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Requests per window.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        remaining?
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Requests left in the first window. Defaults to <code>
        limit
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        window?
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3600000
      </code>
    </td>
    
    <td>
      Length of a window in milliseconds.
    </td>
  </tr>
</tbody>
</table>

### `FakeFailureOptions`

Options for `failNext()`.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        applied?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Applies the write to the store before rejecting, as when a request times out after the forge received it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        times?
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How many calls in a row reject.
    </td>
  </tr>
</tbody>
</table>

### `FakeRunSeed`

A CI run to seed, with its jobs and their logs.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        state?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#checkstate">
        <code>
          CheckState
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        branch?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sha?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        jobs?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#checkstate">
        <code>
          Array<{ name: string, state?: CheckState, log?: string }>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

### `FakeFileSeed`

A file to seed into a repository, at `ref` or on every ref.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        content
      </code>
    </td>
    
    <td>
      <code>
        string | Uint8Array
      </code>
    </td>
    
    <td>
      Text, or bytes for a binary file.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ref?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Branch, tag or sha the file exists at. Defaults to every ref.
    </td>
  </tr>
</tbody>
</table>

### `FakeCommitSeed`

A commit to seed, with the files it changed.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sha
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        message
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        author?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        parents?
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        committedAt?
      </code>
    </td>
    
    <td>
      <code>
        Date
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        files?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#filestatus">
        <code>
          Array<{ path: string, status?: FileStatus, additions?: number, deletions?: number, patch?: string }>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

### `FakeNotificationSeed`

A notification to seed, for an issue or pull request that is seeded as well.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The number of the thread the notification is about.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reason?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#notificationreason">
        <code>
          NotificationReason
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Why the viewer got the notification.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unread?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

### `FakeReleaseSeed`

A release to seed.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tag
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        name?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        body?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isDraft?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isPrerelease?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        immutable?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      The release is immutable, so its tag and target cannot change.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        publishedAt?
      </code>
    </td>
    
    <td>
      <code>
        Date
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        assets?
      </code>
    </td>
    
    <td>
      <code>
        Array<{ name: string, content: string | Uint8Array, contentType?: string }>
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

### `FakeSecurityAlertSeed`

A security alert to seed. Alerts of each kind are numbered separately, from 1.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The repository as <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        kind
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#securityalertkind">
        <code>
          Exclude<SecurityAlertKind, 'other'>
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        severity?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#securityseverity">
        <code>
          SecuritySeverity
        </code>
      </a>
    </td>
    
    <td>
      <code>
        medium
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        state?
      </code>
    </td>
    
    <td>
      <code>
        'open' | 'fixed' | 'dismissed'
      </code>
    </td>
    
    <td>
      <code>
        open
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        package?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#securityalert">
        <code>
          SecurityAlert['package']
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
</tbody>
</table>

### `FakeSeed`

The data a fake forge starts with.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repos?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakereposeed">
        <code>
          FakeRepoSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        files?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakefileseed">
        <code>
          FakeFileSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakecommitseed">
        <code>
          FakeCommitSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        runs?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakerunseed">
        <code>
          FakeRunSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakethreadseed">
        <code>
          FakeThreadSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakenotificationseed">
        <code>
          FakeNotificationSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakereleaseseed">
        <code>
          FakeReleaseSeed[]
        </code>
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        securityAlerts?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakesecurityalertseed">
        <code>
          FakeSecurityAlertSeed[]
        </code>
      </a>
    </td>
  </tr>
</tbody>
</table>

### `FakeSupport`

Support for one verb: a level, or a level per thread kind for per-kind verbs.

**one of:**

- [`SupportInput`](https://forges.link/contributing/provider-kit#supportinput)
- `Partial<Record<VerbKind, SupportInput>>`

### `FakeOptions`

Options for `fake()`.

**extends:** [`Omit<ForgeOptionsBase, 'fetch'>`](https://forges.link/reference/providers#forgeoptionsbase)

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        forge?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#forgekind">
        <code>
          ForgeKind
        </code>
      </a>
    </td>
    
    <td>
      <code>
        fake
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        viewer?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        fake-user
      </code>
    </td>
    
    <td>
      Login of the authenticated account; authors every write.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        seed?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakeseed">
        <code>
          FakeSeed
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        support?
      </code>
    </td>
    
    <td>
      <code>
        Partial<Record<ForgeVerb, FakeSupport>>
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Overrides the default support of individual verbs.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        capabilities?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/provider#forgecapabilities">
        <code>
          ForgeCapabilities
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Support to copy from a real provider, such as <code>
        github().create().capabilities
      </code>
      
      . <code>
        support
      </code>
      
       wins where both set a verb.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        now()?
      </code>
    </td>
    
    <td>
      <code>
        () => Date
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The clock behind every timestamp the forge writes and every rate limit window. Defaults to the system time.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        latency?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakelatency">
        <code>
          FakeLatency
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      How long each request takes. A request slower than <code>
        timeout
      </code>
      
       rejects with <code>
        ForgeTimeoutError
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rateLimit?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakeratelimit">
        <code>
          FakeRateLimit
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      A request budget. Without one, requests are unlimited.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        baseUrl?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      API base URL. Defaults to the forge's public instance. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        instance?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Host used in refs and keys. Defaults to the host of <code>
        baseUrl
      </code>
      
      , with API subdomains mapped to the public host. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        timeout?
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30000
      </code>
    </td>
    
    <td>
      Request timeout in milliseconds. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhookSecret?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Shared secret used to verify inbound webhook deliveries. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        userAgent?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      The <code>
        user-agent
      </code>
      
       header. Defaults to <code>
        forges
      </code>
      
      , except in a browser, which sends its own. Setting it in a browser can trigger a CORS preflight that some forges reject. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        instanceVersion?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Version of the forge instance, when the caller already knows it. Gates version-dependent capabilities without the network call <code>
        refreshCapabilities()
      </code>
      
       makes. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        readOnly?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Every write, including a mutating <code>
        request()
      </code>
      
      , rejects with <code>
        ReadOnlyError
      </code>
      
       and reports <code>
        false
      </code>
      
      . Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        onRetry?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/contributing/provider-kit#fetcheroptions">
        <code>
          FetcherOptions['onRetry']
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Called before a request is retried after a rate limit that names a wait of at most a minute, with the wait in milliseconds. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        onRateLimit?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/contributing/provider-kit#fetcheroptions">
        <code>
          FetcherOptions['onRateLimit']
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
    
    <td>
      Called with the rate limit of every API response that reports one, so that you can follow the budget without reading <code>
        page.rateLimit
      </code>
      
      . <code>
        rateLimit.resource
      </code>
      
       names the budget where the forge has several. Inherited from <a href="https://forges.link/reference/providers#forgeoptionsbase">
        <code>
          ForgeOptionsBase
        </code>
      </a>
      
      .
    </td>
  </tr>
</tbody>
</table>

### `FakeStore`

The in-memory state behind a fake forge. Mutate it to set up a test, read it to assert.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        repos
      </code>
    </td>
    
    <td>
      <code>
        Map<string, { repo: Repo labels: string[] threads: Map<string, FakeThreadState> releases: Release[] statuses: Map<string, Check[]> runs: FakeRunState[] files: Map<string, Uint8Array> commits: Commit[] branches: Map<string, string> assets: Map<string, Uint8Array> securityAlerts: SecurityAlert[] }>
      </code>
    </td>
    
    <td>
      Every repository with its threads, releases, runs, files and commits, keyed by <code>
        owner/name
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#notification">
        <code>
          Notification[]
        </code>
      </a>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        events
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#forgeevent">
        <code>
          ForgeEvent[]
        </code>
      </a>
    </td>
    
    <td>
      Every event a write produced, in order.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#webhook">
        <code>
          Webhook[]
        </code>
      </a>
    </td>
    
    <td>
      Registered webhooks. Secrets are held separately and never readable.
    </td>
  </tr>
</tbody>
</table>

### `FakeForgeFactory`

What `fake()` returns: a provider factory that also exposes the store and
controls the network between the providers and the store. Every provider
that `create()` returns shares both.

**extends:** `ForgeProviderFactory<ForgeProvider>`

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        store
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakestore">
        <code>
          FakeStore
        </code>
      </a>
    </td>
    
    <td>
      The in-memory state of the forge. Results are copies, so changing one leaves the store as it was.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        failNext()
      </code>
    </td>
    
    <td>
      <code>
        (verb: ForgeVerb, error: Error | (() => Error), options?: FakeFailureOptions) => void
      </code>
    </td>
    
    <td>
      Rejects the next call of <code>
        verb
      </code>
      
       with <code>
        error
      </code>
      
      . A listing and its page share a verb, so <code>
        threads.comments
      </code>
      
       also fails <code>
        threads.commentsPage
      </code>
      
      . Verbs that core builds from other requests, such as <code>
        threads.upsertComment
      </code>
      
      , fail through those requests.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        offline()
      </code>
    </td>
    
    <td>
      <code>
        () => void
      </code>
    </td>
    
    <td>
      Rejects every request with <code>
        ForgeNetworkError
      </code>
      
       until <code>
        online()
      </code>
      
      , and closes open subscriptions with <code>
        SubscriptionClosedError
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        online()
      </code>
    </td>
    
    <td>
      <code>
        () => void
      </code>
    </td>
    
    <td>
      Ends <code>
        offline()
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        setLatency()
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakelatency">
        <code>
          (latency: FakeLatency | undefined) => void
        </code>
      </a>
    </td>
    
    <td>
      Replaces the <code>
        latency
      </code>
      
       option. <code>
        undefined
      </code>
      
       removes the delay.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        setRateLimit()
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakeratelimit">
        <code>
          (budget: FakeRateLimit | undefined) => void
        </code>
      </a>
    </td>
    
    <td>
      Replaces the <code>
        rateLimit
      </code>
      
       option and starts a new window, which <code>
        rateLimits()
      </code>
      
       reports from the next request. <code>
        undefined
      </code>
      
       removes the budget.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        as()
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/provider">
        <code>
          (login: string) => ForgeProvider
        </code>
      </a>
    </td>
    
    <td>
      A provider on the same store whose writes are by <code>
        login
      </code>
      
      , with no notifications or subscriptions of its own. A comment, review, state change or assignment by <code>
        login
      </code>
      
       notifies the viewer when it mentions the viewer, assigns the viewer or requests the viewer's review, or when the viewer is subscribed to the thread. The notification for the thread then becomes unread, or is added.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        forge
      </code>
    </td>
    
    <td>
      <code>
        Forge
      </code>
    </td>
    
    <td>
      Inherited from <a href="https://forges.link/reference/providers#forgeproviderfactory">
        <code>
          ForgeProviderFactory
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        experimental?
      </code>
    </td>
    
    <td>
      <code>
        Experimental extends true ? true : undefined
      </code>
    </td>
    
    <td>
      The provider has not reached parity; see <code>
        ForgeCapabilities.experimental
      </code>
      
      . Inherited from <a href="https://forges.link/reference/providers#forgeproviderfactory">
        <code>
          ForgeProviderFactory
        </code>
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        create()
      </code>
    </td>
    
    <td>
      <code>
        () => T
      </code>
    </td>
    
    <td>
      Creates the provider. Nothing touches the network before this. Inherited from <a href="https://forges.link/reference/providers#forgeproviderfactory">
        <code>
          ForgeProviderFactory
        </code>
      </a>
      
      .
    </td>
  </tr>
</tbody>
</table>

### `fake()`

An in-memory forge implementing the whole provider surface, for testing
code that writes. Every write changes `store` and appends a `ForgeEvent`
that `threads.events()` and `sources.subscribe()` return. Webhook
deliveries carry normalised events as JSON, signed as `signDelivery('fake', ...)`
from `forges/testing` signs them.

**Example**

```ts
import { createForges } from 'forges'
import { fake } from 'forges/fake'

const factory = fake({
  seed: {
    repos: [{ repo: 'acme/widgets' }],
    threads: [{ repo: 'acme/widgets', kind: 'issue', title: 'Broken build' }],
  },
})
const forges = createForges([factory])
```

```ts
function fake(options: FakeOptions = {}): FakeForgeFactory
```

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        options?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fakeoptions">
        <code>
          FakeOptions
        </code>
      </a>
    </td>
    
    <td>
      The data the forge starts with, and the support of individual operations. Defaults to <code>
        {}
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

**Returns:** [`FakeForgeFactory`](https://forges.link/reference/testing#fakeforgefactory)

## Fixtures

Import from `forges/testing`.

### `Fixture`

One recorded or hand-authored request and its response.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        handAuthored?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
      
       when the fixture was written from documentation, not recorded.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      GraphQL requests share one URL, so they are matched on <code>
        operationName
      </code>
      
       and, when recorded, <code>
        variables
      </code>
      
       too.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request.method
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request.url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request.operationName?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The GraphQL operation name, for a GraphQL request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request.variables?
      </code>
    </td>
    
    <td>
      <code>
        Record<string, unknown>
      </code>
    </td>
    
    <td>
      The GraphQL variables, for a GraphQL request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request.body?
      </code>
    </td>
    
    <td>
      <code>
        unknown
      </code>
    </td>
    
    <td>
      The JSON or text the request sent, when recorded with <code>
        requestBodies
      </code>
      
      . Replay does not match on it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response
      </code>
    </td>
    
    <td>
      
    </td>
    
    <td>
      A JSON body, the text of a response that is not JSON, or the base64 of one that is not text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response.status
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response.headers?
      </code>
    </td>
    
    <td>
      <code>
        Record<string, string>
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response.body?
      </code>
    </td>
    
    <td>
      <code>
        unknown
      </code>
    </td>
    
    <td>
      The response body: parsed JSON, the text of a response that is not JSON, or base64 when <code>
        encoding
      </code>
      
       is set.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        response.encoding?
      </code>
    </td>
    
    <td>
      <code>
        'base64'
      </code>
    </td>
    
    <td>
      Set when <code>
        body
      </code>
      
       holds the base64 of a binary response.
    </td>
  </tr>
</tbody>
</table>

### `FixtureCall`

A request that `fixtureFetch()` received.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        method
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        body?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        operationName?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The GraphQL operation name, for a GraphQL request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variables?
      </code>
    </td>
    
    <td>
      <code>
        Record<string, unknown>
      </code>
    </td>
    
    <td>
      The GraphQL variables, for a GraphQL request.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        authorization?
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The <code>
        Authorization
      </code>
      
       header that the request carried.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        headers
      </code>
    </td>
    
    <td>
      <code>
        Headers
      </code>
    </td>
    
    <td>
      All the request headers.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fixture?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixture">
        <code>
          Fixture
        </code>
      </a>
    </td>
    
    <td>
      The fixture that answered, or <code>
        undefined
      </code>
      
       where an override did.
    </td>
  </tr>
</tbody>
</table>

### `FixtureFetchOptions`

Options for [`fixtureFetch`](https://forges.link/reference/testing#fixturefetch-1).

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        sequential?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Serve the fixtures that share a request in the order they were recorded, repeating the last, instead of always serving the last. Use it to replay a recording that reads the same URL before and after a write.
    </td>
  </tr>
</tbody>
</table>

### `FixtureFetch`

A `fetch` that answers from fixtures, and records the calls it received.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        fetch
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/requests#fetchlike">
        <code>
          FetchLike
        </code>
      </a>
    </td>
    
    <td>
      The <code>
        fetch
      </code>
      
       to pass to a provider.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        calls
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixturecall">
        <code>
          FixtureCall[]
        </code>
      </a>
    </td>
    
    <td>
      Every request made, in order, including ones no fixture matched.
    </td>
  </tr>
</tbody>
</table>

### `fixtureFetch()`

A `fetch` that serves `fixtures` and records every call. Requests match on
method, URL (query order ignored), GraphQL `operationName` and, where the
fixture has them, `variables`. `overrides`
replace or add responses, keyed `METHOD url [operationName]`. An unmatched
request throws.

**Example**

```ts
import { github } from 'forges'
import { fixtureFetch, loadFixtures } from 'forges/testing'

const { fetch, calls } = fixtureFetch(await loadFixtures(new URL('./fixtures', import.meta.url)))
const provider = github({ fetch }).create()
```

```ts
function fixtureFetch(fixtures: Iterable<Fixture>, overrides: Record<string, Fixture['response']> = {}, options: FixtureFetchOptions = {}): FixtureFetch
```

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        fixtures
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixture">
        <code>
          Iterable<Fixture>
        </code>
      </a>
    </td>
    
    <td>
      The recorded or hand-written requests and responses to serve.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overrides?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixture">
        <code>
          Record<string, Fixture['response']>
        </code>
      </a>
    </td>
    
    <td>
      Responses that replace or add to the fixtures. Defaults to <code>
        {}
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        options?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixturefetchoptions">
        <code>
          FixtureFetchOptions
        </code>
      </a>
    </td>
    
    <td>
      How to serve fixtures that share a request. Defaults to <code>
        {}
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

**Returns:** [`FixtureFetch`](https://forges.link/reference/testing#fixturefetch)

### `RecordingFetch`

A `fetch` that forwards to the real one and records every exchange as a fixture.

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        fetch
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/requests#fetchlike">
        <code>
          FetchLike
        </code>
      </a>
    </td>
    
    <td>
      The <code>
        fetch
      </code>
      
       to pass to a provider.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fixtures
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixture">
        <code>
          Fixture[]
        </code>
      </a>
    </td>
    
    <td>
      One fixture per response, in order; pass them to <a href="https://forges.link/reference/testing#fixturefetch-1">
        <code>
          fixtureFetch
        </code>
      </a>
      
       to replay.
    </td>
  </tr>
</tbody>
</table>

### `RecordingFetchOptions`

Options for [`recordingFetch`](https://forges.link/reference/testing#recordingfetch-1).

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        requestBodies?
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Record the body of each request that sends a string, parsed as JSON where it is JSON. A request body can hold a secret (a password, a webhook secret), so redact it too.
    </td>
  </tr>
</tbody>
</table>

### `recordingFetch()`

Wraps `fetch` (the global one by default) and records every response as a
[`Fixture`](https://forges.link/reference/testing#fixture). Request headers are never recorded, but URLs and response
bodies are, and either can hold a secret (an installation token, a query
token). Use `redact` to rewrite each fixture before it is kept. A body that
is not UTF-8 text is kept as base64.

```ts
function recordingFetch(fetch: FetchLike = globalThis.fetch, redact: (fixture: Fixture) => Fixture = fixture => fixture, options: RecordingFetchOptions = {}): RecordingFetch
```

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        fetch?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/requests#fetchlike">
        <code>
          FetchLike
        </code>
      </a>
    </td>
    
    <td>
      The <code>
        fetch
      </code>
      
       to record. Defaults to the global one. Defaults to <code>
        globalThis.fetch
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        redact?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#fixture">
        <code>
          (fixture: Fixture) => Fixture
        </code>
      </a>
    </td>
    
    <td>
      Rewrites each fixture before it is kept, to remove secrets. Defaults to <code>
        fixture => fixture
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        options?
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/testing#recordingfetchoptions">
        <code>
          RecordingFetchOptions
        </code>
      </a>
    </td>
    
    <td>
      What else to record. Defaults to <code>
        {}
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

**Returns:** [`RecordingFetch`](https://forges.link/reference/testing#recordingfetch)

### `loadFixtures()`

Reads every `*.json` fixture in `directory`, skipping `manifest.json` and
files without a `request`. Node, Bun and Deno only.

```ts
function loadFixtures(directory: string | URL): Promise<Fixture[]>
```

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        directory
      </code>
    </td>
    
    <td>
      <code>
        string | URL
      </code>
    </td>
    
    <td>
      The directory that holds the fixture JSON files.
    </td>
  </tr>
</tbody>
</table>

**Returns:** [`Promise<Fixture[]>`](https://forges.link/reference/testing#fixture)

### `signDelivery()`

Builds the headers a forge would sign `body` with, merged over `headers`
(the event name and delivery id, which only the caller knows). `secret` is
the webhook secret, or for Cursor Origin the base64 PKCS#8 Ed25519 private
key matching the provider's verification key.

```ts
function signDelivery(forge: ForgeKind, body: string, secret: string, headers: Record<string, string> = {}): Promise<Record<string, string>>
```

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        forge
      </code>
    </td>
    
    <td>
      <a href="https://forges.link/reference/data-model#forgekind">
        <code>
          ForgeKind
        </code>
      </a>
    </td>
    
    <td>
      The forge whose signature scheme to use.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        body
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The raw body of the delivery.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        secret
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The secret that signs it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        headers?
      </code>
    </td>
    
    <td>
      <code>
        Record<string, string>
      </code>
    </td>
    
    <td>
      Headers to add to the signed headers. Defaults to <code>
        {}
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

**Returns:** `Promise<Record<string, string>>`


## Sitemap

See the full [sitemap](https://forges.link/sitemap.md) for all pages.
