# @absmartly/api-mocks

> OpenAPI-driven mock server, typed factories, and schema validation for the ABsmartly API. Built on [MSW (Mock Service Worker)](https://mswjs.io/) with handlers auto-generated from the ABsmartly OpenAPI specification.

Latest version **1.0.9** (published 2026-09-26) · UNLICENSED license · 0 weekly downloads

## Install

```sh
npm install @absmartly/api-mocks
pnpm add @absmartly/api-mocks
yarn add @absmartly/api-mocks
bun add @absmartly/api-mocks
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 1.0.9 |
| Published | 2026-09-26 |
| First published | 2026-04-10 |
| Weekly downloads | 0 |
| License | UNLICENSED |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 5 |
| Unpacked size | 3.8 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | ABsmartly |
| Maintainers | marcioapm, joalves, caltheabtester, marcioabs |
| Keywords | absmartly, api, mocks, msw, openapi, testing |

## Links

- npm: https://www.npmjs.com/package/@absmartly/api-mocks
- npm.io page: https://npm.io/package/@absmartly/api-mocks

## Dependencies (5)

- [ajv](https://npm.io/package/ajv.md) ^8.17.1
- [js-yaml](https://npm.io/package/js-yaml.md) ^4.1.1
- [ajv-formats](https://npm.io/package/ajv-formats.md) ^3.0.1
- [@faker-js/faker](https://npm.io/package/@faker-js/faker.md) ^10.2.0
- [@apidevtools/json-schema-ref-parser](https://npm.io/package/@apidevtools/json-schema-ref-parser.md) ^15.2.2

## Alternatives

- [pagerjs](https://npm.io/package/pagerjs.md) — 60 weekly downloads
- [whistle.savefor-mock](https://npm.io/package/whistle.savefor-mock.md) — 4 weekly downloads
- [foss-design](https://npm.io/package/foss-design.md) — 0 weekly downloads
- [@chrismessina/raycast-faker](https://npm.io/package/@chrismessina/raycast-faker.md) — 0 weekly downloads
- [visual-companion](https://npm.io/package/visual-companion.md) — 0 weekly downloads

## Recent versions

- 1.0.9 (latest) — 2026-09-26
- 1.0.0-beta.1 (beta) — 2026-04-10
- 1.0.8 — 2026-04-24
- 1.0.7 — 2026-04-23
- 1.0.6 — 2026-04-10
- 1.0.5 — 2026-04-10
- 1.0.4 — 2026-04-10
- 1.0.2 — 2026-04-10
- 1.0.1 — 2026-04-10
- 1.0.0 — 2026-04-10

## README

# @absmartly/api-mocks

OpenAPI-driven mock server, typed factories, and schema validation for the ABsmartly API. Built on [MSW (Mock Service Worker)](https://mswjs.io/) with handlers auto-generated from the ABsmartly OpenAPI specification.

## Installation

```bash
npm install --save-dev @absmartly/api-mocks
```

**Peer dependency:** This package requires [`msw`](https://mswjs.io/) v2+:

```bash
npm install --save-dev msw
```

## Quick Start

```typescript
import { createServer } from '@absmartly/api-mocks/server';
import { beforeAll, afterEach, afterAll, test, expect } from 'vitest';

const server = createServer('https://your-instance.absmartly.com/v1');

beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('fetches experiments', async () => {
  const response = await fetch('https://your-instance.absmartly.com/v1/experiments');
  const data = await response.json();
  expect(response.ok).toBe(true);
});
```

## Package Exports

| Import path | Description |
|---|---|
| `@absmartly/api-mocks` | Everything: factories, handlers, validation, and `createServer` |
| `@absmartly/api-mocks/server` | `createServer(baseUrl)` -- creates a pre-configured MSW server |
| `@absmartly/api-mocks/handlers` | MSW request handlers (generated + error simulation) |
| `@absmartly/api-mocks/factories` | Faker-based factory functions for all API resources |
| `@absmartly/api-mocks/validation` | Schema validation utilities powered by AJV |

## MSW Mock Server

`createServer(baseUrl)` returns an MSW [`setupServer`](https://mswjs.io/docs/api/setup-server) instance with all generated handlers pre-registered.

```typescript
import { createServer } from '@absmartly/api-mocks/server';

const server = createServer('https://your-instance.absmartly.com/v1');
```

### Vitest Setup

Create a setup file to start/stop the server around your tests:

```typescript
// test/setup.ts
import { beforeAll, afterEach, afterAll } from 'vitest';
import { createServer } from '@absmartly/api-mocks/server';

const server = createServer('https://your-instance.absmartly.com/v1');

beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```

Then reference it in your Vitest config:

```typescript
// vitest.config.ts
export default defineConfig({
  test: {
    setupFiles: ['./test/setup.ts'],
  },
});
```

### Pre-configured Server

The package also exports a pre-configured server instance that reads the base URL from environment variables:

```typescript
import { server } from '@absmartly/api-mocks/src/mocks/server';
```

The base URL is resolved in this order:
1. `API_BASE_URL` environment variable
2. `ABSMARTLY_API_ENDPOINT` environment variable
3. `https://sandbox.absmartly.com/v1` (placeholder default — only used by MSW mocks, which intercept all requests before they reach the network)

## Handlers

### Generated Handlers

Handlers are auto-generated from the OpenAPI specification. Every endpoint in the spec gets a corresponding MSW handler that returns realistic example data.

```typescript
import { createHandlers } from '@absmartly/api-mocks/handlers';

const handlers = createHandlers('https://your-instance.absmartly.com/v1');
```

The generated handlers cover 40+ API resources including experiments, goals, metrics, segments, users, teams, applications, environments, and more.

**How generated handlers work:**

- **GET (list)** -- Returns the static example from the OpenAPI spec
- **GET (by ID)** -- Clones the example and sets the `id` field from the URL path parameter
- **POST / PUT / PATCH** -- Clones the example, deep-merges the request body into the response, and validates the request using `withValidation()`
- **DELETE** -- Returns 204 No Content

The deep-merge strategy preserves response arrays (arrays from the example response take precedence over arrays in the request body), which ensures list fields always return realistic data.

### Error Simulation Handlers

In addition to the generated handlers, the package provides error simulation handlers for testing error scenarios:

```typescript
import { createErrorHandlers } from '@absmartly/api-mocks/handlers';

const errorHandlers = createErrorHandlers('https://your-instance.absmartly.com/v1');
```

These handlers simulate common error cases:

| Endpoint | Trigger | Response |
|---|---|---|
| `POST /experiments` | Missing `name` | 400 -- name is required |
| `POST /experiments` | `name: "duplicate_name"` | 409 -- already exists |
| `POST /experiments` | Missing `unit_type_id` | 400 -- unit_type_id is required |
| `PUT /experiments/999/start` | ID = 999 | 400 -- already running |
| `PUT /experiments/888/stop` | ID = 888 | 400 -- not running |
| `POST /goals` | Missing `name` | 400 -- name is required |
| `POST /goals` | `name: "duplicate"` | 409 -- already exists |
| `POST /segments` | Missing `name` or `value_source_attribute` | 400 -- field is required |
| `DELETE /segments/666` | ID = 666 | 409 -- in use by active experiments |
| `POST /api_keys` | Missing `name` | 400 -- name is required |

## Factories

All factories follow the same pattern: `createMock*(overrides?)` returns a single instance, `createMock*s(count)` returns an array. Every factory is typed using the generated TypeScript types from the OpenAPI schema.

```typescript
import { createMockExperiment, createMockExperiments } from '@absmartly/api-mocks/factories';

// Single instance with defaults
const experiment = createMockExperiment();

// With overrides
const experiment = createMockExperiment({
  name: 'my_test',
  state: 'running',
});

// Multiple instances
const experiments = createMockExperiments(5);
```

### Available Factories

| Factory | Type |
|---|---|
| `createMockExperiment` / `createMockExperiments` | `ExperimentShort` |
| `createMockGoal` / `createMockGoals` | `Goal` |
| `createMockSegment` / `createMockSegments` | `Segment` |
| `createMockMetric` / `createMockMetrics` | `Metric` |
| `createMockUser` / `createMockUsers` | `User` |
| `createMockTeam` / `createMockTeams` | `Team` |
| `createMockApplication` / `createMockApplications` | `Application` |
| `createMockEnvironment` / `createMockEnvironments` | `Environment` |
| `createMockUnitType` / `createMockUnitTypes` | `UnitType` |
| `createMockApiKey` / `createMockApiKeys` | `ApiKey` |
| `createMockWebhook` / `createMockWebhooks` | `Webhook` |
| `createMockRole` / `createMockRoles` | `Role` |
| `createMockPermission` / `createMockPermissions` | `Permission` |
| `createMockPermissionCategory` / `createMockPermissionCategories` | `PermissionCategory` |
| `createMockMetricCategory` / `createMockMetricCategories` | `MetricCategory` |
| `createMockAlert` / `createMockAlerts` | `Alert` |
| `createMockNote` / `createMockNotes` | `Note` |
| `createMockExperimentTag` / `createMockExperimentTags` | `ExperimentTag` |
| `createMockGoalTag` / `createMockGoalTags` | `GoalTag` |
| `createMockMetricTag` / `createMockMetricTags` | `MetricTag` |

## Validation

The validation layer uses [AJV](https://ajv.js.org/) to validate request and response payloads against the OpenAPI schema. All validation functions load and dereference the bundled OpenAPI spec automatically.

### Request Validation (Lenient)

`validateRequest` is intentionally lenient -- it strips `required`, `minItems`, `minProperties`, and `pattern` constraints so you can send partial payloads in tests. It validates structure and types only.

```typescript
import { validateRequest } from '@absmartly/api-mocks/validation';

const result = await validateRequest('/goals', 'POST', {
  name: 'My Goal',
});
// result: { valid: true, errors: [] }
```

### Response Validation (Strict)

`validateResponse` applies the full schema including all constraints.

```typescript
import { validateResponse } from '@absmartly/api-mocks/validation';

const result = await validateResponse('/goals', 'GET', 200, responseData);
if (!result.valid) {
  console.log(result.errors);
  // [{ path: '/items/0', message: '...', keyword: 'type' }]
}
```

### Strict Request/Response Validation

For when you want full constraint checking on requests too:

```typescript
import { validateRequestStrict, validateResponseStrict } from '@absmartly/api-mocks/validation';

const result = await validateRequestStrict('/goals', 'POST', payload);
```

`findUnknownProperties` can detect extra properties not defined in the schema:

```typescript
import { findUnknownProperties, loadOpenAPISpec } from '@absmartly/api-mocks/validation';

const spec = await loadOpenAPISpec();
const unknowns = findUnknownProperties(data, schema, spec);
```

### Handler Validation Wrapper

`withValidation` wraps an MSW handler resolver to automatically validate incoming requests (and optionally responses):

```typescript
import { withValidation } from '@absmartly/api-mocks/validation';
import { http, HttpResponse } from 'msw';

http.post(
  `${baseUrl}/goals`,
  withValidation('/goals', 'POST', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ ok: true, goal: body }, { status: 201 });
  }, {
    validateRequests: true,   // default: true
    validateResponses: false, // default: false
  })
);
```

When request validation fails, it returns a 422 response:

```json
{
  "ok": false,
  "error": "validation_error",
  "message": "Request validation failed",
  "status": 422,
  "details": [{ "path": "/name", "message": "must be string", "keyword": "type" }]
}
```

### Payload Generation

`generatePayloadForEndpoint` creates a minimal valid payload that satisfies the request schema for a given endpoint:

```typescript
import { generatePayloadForEndpoint } from '@absmartly/api-mocks/validation';

const payload = await generatePayloadForEndpoint('/goals', 'POST', {
  name: 'My Custom Goal',
});
```

It generates sensible defaults for each type (`string` -> `"test-string"`, `integer` -> `1`, `date-time` -> `"2025-01-01T00:00:00.000Z"`, `email` -> `"test@example.com"`, etc.) and deep-merges any overrides you provide.

### Schema Inspection

Lower-level functions for working with the OpenAPI spec directly:

```typescript
import {
  loadOpenAPISpec,
  getSchemaForEndpoint,
  getRequestSchemaForEndpoint,
  createValidator,
  validateAgainstSchema,
} from '@absmartly/api-mocks/validation';

// Load the full dereferenced spec
const spec = await loadOpenAPISpec();

// Get the response schema for a specific endpoint
const responseSchema = await getSchemaForEndpoint('/experiments', 'GET', 200);

// Get the request body schema
const requestSchema = await getRequestSchemaForEndpoint('/goals', 'POST');

// Compile a reusable validator
const validate = createValidator(responseSchema);
const isValid = validate(data);

// One-shot validation
const result = await validateAgainstSchema(data, schema, { coerceTypes: true });
```

## OpenAPI Specification

The package bundles the ABsmartly API OpenAPI 3.0 specification:

```
openapi/
  openapi.yaml              # Main spec (references paths/ and components/)
  openapi.bundle.yaml       # Fully dereferenced bundle (used at runtime)
  openapi_with_examples.yaml
  paths/                    # Per-resource path definitions (44 files)
  examples/                 # Response examples per resource
  components/               # Shared schema components
  live-payloads/            # Sanitized payloads captured from the live API
```

The bundled spec at `openapi/openapi.bundle.yaml` is what the validation layer loads at runtime. It has all `$ref` references resolved inline.

## Code Generation

Two things are auto-generated from the OpenAPI spec and **must be regenerated** whenever the spec changes:

### TypeScript Types

```bash
npm run generate:types
```

Runs `openapi-typescript` to produce `src/generated/schema.d.ts` from `openapi/openapi.yaml`. This file contains TypeScript interfaces for all API paths, operations, and component schemas. All factories are typed using these generated types.

### MSW Handlers

```bash
npm run generate:handlers
```

Runs `scripts/generate-handlers.mjs` to produce one handler file per API resource in `src/handlers/generated/`. The generator:

1. Reads each path from the OpenAPI spec
2. Extracts response examples (preferring `_200`/`_201` suffixed examples)
3. Creates MSW handlers for each HTTP method (GET, POST, PUT, DELETE, etc.)
4. Wraps POST/PUT/PATCH handlers with `withValidation()` for request validation
5. Generates an `index.ts` that aggregates all resource handlers

### Regenerate Everything

```bash
npm run generate
```

This runs both `generate:types` and `generate:handlers`. It also runs automatically before `npm test` (via `pretest`) and before `npm publish` (via `prepublishOnly`).

## Testing

### Unit Tests

```bash
npm test              # Watch mode
npm run test:run      # Single run
npm run test:coverage # With coverage report
```

Unit tests validate the mock server handlers, factories, and validation logic using MSW's in-memory server. No network access required.

### Contract Tests

```bash
npm run test:contract
```

Validates that the mock server responses conform to the OpenAPI schema. Compiles schemas with AJV and checks that generated responses pass validation.

### Live API Validation

```bash
npm run validate:live
```

Runs 40+ test files against the real ABsmartly API, validating that actual API responses match the OpenAPI schema. Requires `API_KEY` and `API_BASE_URL` environment variables (or a `.env.local` file).

```bash
npm run validate:live:report   # Generates a markdown report
npm run validate:comprehensive # Full validation suite with reports
```

### Mutation Tests

```bash
npm run validate:mutations       # Interactive (asks for confirmation)
npm run validate:mutations:force # Non-interactive
```

Full CRUD lifecycle tests that create, read, update, and delete resources on the live API. Includes automatic cleanup of created resources via a `ResourceTracker`.

### Schemathesis

```bash
npm run validate:schemathesis
```

Property-based API testing using [Schemathesis](https://github.com/schemathesis/schemathesis). Generates random valid requests from the OpenAPI spec and checks all responses for conformance. Requires Python 3.11+ and the `schemathesis` pip package.

## Environment Variables

| Variable | Description | Default |
|---|---|---|
| `API_BASE_URL` | Base URL for the ABsmartly API (required for live tests and scripts) | -- |
| `ABSMARTLY_API_ENDPOINT` | Alternative base URL variable | -- |
| `API_KEY` | API key for live API tests | -- |
| `ABSMARTLY_API_KEY` | Alternative API key variable | -- |
| `ENABLE_LIVE_API_TESTS` | Set to `true` to enable live API test suites | `false` |

Create a `.env.local` file for local development:

```
API_KEY=your_api_key_here
API_BASE_URL=https://your-instance.absmartly.com/v1
```

## CI/CD

Pushes to `main` run tests, then publish to npm via GitHub Actions with [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC -- no tokens required) **only if `package.json`'s `version` isn't already published**. The version is never bumped automatically -- bump it yourself (e.g. `npm version patch`) as part of the PR that should trigger a release; a push that doesn't change the version just runs tests.

## Project Structure

```
src/
  index.ts                  # Main export aggregator
  server.ts                 # createServer(baseUrl) factory
  mocks/
    server.ts               # Pre-configured MSW server instance
    handlers.ts             # Handler initialization from env vars
  handlers/
    index.ts                # Handler exports and composition
    errors.ts               # Error simulation handlers
    generated/              # Auto-generated (one file per resource)
  factories/                # Faker-based data factories (18 resources)
  validation/
    schema-validator.ts     # OpenAPI spec loading and AJV validation
    request-validator.ts    # Lenient request validation
    response-validator.ts   # Strict response validation
    validated-handler.ts    # MSW handler validation wrapper
    strict-validator.ts     # Strict validation + unknown property detection
    payload-generator.ts    # Generate valid payloads from schemas
  generated/
    schema.d.ts             # Auto-generated TypeScript types
  __tests__/                # Unit tests
  contract-tests/           # Schema contract tests
  live-validation/          # Live API validation tests
  live-validation-mutations/# CRUD lifecycle mutation tests
openapi/
  openapi.yaml              # Main OpenAPI spec
  openapi.bundle.yaml       # Dereferenced bundle
  paths/                    # Per-resource path definitions
  examples/                 # Response examples
  components/               # Shared schemas
  live-payloads/            # Captured live API payloads
scripts/
  generate-handlers.mjs     # Handler generation from OpenAPI
  generate-live-validation-tests-v2.mjs
  validate-live-api.sh
  run-mutation-tests.sh
  sync-examples-from-live.mjs
```

## License

Proprietary - ABsmartly

---
_Source: https://npm.io/package/@absmartly/api-mocks · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
