Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/visible/cruel/llms.txt

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

Cruel provides custom test matchers for Vitest, Jest, and Bun to make testing chaos behaviors easier.

Setup

import { setupMatchers } from 'cruel/matchers'
import { beforeAll } from 'bun:test'

beforeAll(() => {
  setupMatchers()
})

Matchers

toThrowCruelError

Check if function throws any Cruel error:
import { cruel, matchers } from 'cruel/matchers'
import { expect, test } from 'bun:test'

test('throws cruel error', () => {
  const fn = () => {
    throw new CruelError('test')
  }
  
  expect(fn).toThrowCruelError()
})

toThrowTimeout

Check if function throws a timeout error:
test('throws timeout', async () => {
  const fn = cruel.timeout(async () => 'data', 1)
  
  await expect(fn()).toEventuallyThrow(CruelTimeoutError)
})
received
() => unknown
required
Function to test
Returns: MatcherResult

toThrowNetworkError

Check if function throws a network error, optionally with specific type:
test('throws network error', () => {
  const fn = () => {
    throw new CruelNetworkError('disconnect')
  }
  
  expect(fn).toThrowNetworkError()
  expect(fn).toThrowNetworkError('disconnect')
})
received
() => unknown
required
Function to test
type
string
Expected network error type (disconnect, packet_loss, dns_failure, offline)
Returns: MatcherResult

toThrowHttpError

Check if function throws an HTTP error, optionally with specific status:
test('throws http error', () => {
  const fn = () => {
    throw new CruelHttpError(500)
  }
  
  expect(fn).toThrowHttpError()
  expect(fn).toThrowHttpError(500)
})
received
() => unknown
required
Function to test
status
number
Expected HTTP status code
Returns: MatcherResult

toThrowRateLimit

Check if function throws a rate limit error:
test('throws rate limit', () => {
  const fn = () => {
    throw new CruelRateLimitError(60)
  }
  
  expect(fn).toThrowRateLimit()
})
received
() => unknown
required
Function to test
Returns: MatcherResult

toThrowAIError

Check if function throws an AI error, optionally with specific type:
test('throws AI error', () => {
  const fn = () => {
    throw new CruelAIError('rate_limit')
  }
  
  expect(fn).toThrowAIError()
  expect(fn).toThrowAIError('rate_limit')
})
received
() => unknown
required
Function to test
type
string
Expected AI error type (rate_limit, overloaded, context_length, content_filter, model_unavailable, stream_cut)
Returns: MatcherResult

toEventuallyThrow

Check if async function throws an error:
test('eventually throws', async () => {
  const fn = async () => {
    throw new Error('failed')
  }
  
  await expect(fn()).toEventuallyThrow()
  await expect(fn()).toEventuallyThrow(Error)
})
received
() => Promise<unknown>
required
Async function to test
errorType
new () => Error
Expected error class
Returns: Promise<MatcherResult>

toCompleteWithin

Check if async function completes within time limit:
test('completes quickly', async () => {
  const fn = async () => {
    await new Promise(r => setTimeout(r, 100))
    return 'done'
  }
  
  await expect(fn()).toCompleteWithin(200)
})
received
() => Promise<unknown>
required
Async function to test
ms
number
required
Maximum allowed duration in milliseconds
Returns: Promise<MatcherResult>

toTakeLongerThan

Check if async function takes at least specified time:
test('takes time', async () => {
  const fn = cruel.slow(async () => 'data', 500)
  
  await expect(fn()).toTakeLongerThan(400)
})
received
() => Promise<unknown>
required
Async function to test
ms
number
required
Minimum expected duration in milliseconds
Returns: Promise<MatcherResult>

Usage with testing frameworks

Bun

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from 'bun:test'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).toEventuallyThrow()
})

Vitest

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from 'vitest'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).toEventuallyThrow()
})

Jest

import { setupMatchers } from 'cruel/matchers'
import { beforeAll, test, expect } from '@jest/globals'

beforeAll(() => {
  setupMatchers()
})

test('uses matchers', async () => {
  const fn = cruel.fail(async () => 'data', 1)
  await expect(fn()).resolves.toEventuallyThrow()
})

Type definitions

interface MatcherResult {
  pass: boolean
  message: () => string
}

declare global {
  namespace jest {
    interface Matchers<R> {
      toThrowCruelError(): R
      toThrowTimeout(): R
      toThrowNetworkError(type?: string): R
      toThrowHttpError(status?: number): R
      toThrowRateLimit(): R
      toThrowAIError(type?: string): R
      toEventuallyThrow(errorType?: new () => Error): Promise<R>
      toCompleteWithin(ms: number): Promise<R>
      toTakeLongerThan(ms: number): Promise<R>
    }
  }
}
Matchers are automatically extended to expect when you call setupMatchers().
Call setupMatchers() once in your test setup (beforeAll or setupFiles) to enable all matchers globally.