> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# fixture

> Managing test data

export const FileItem = ({name, description, children}) => {
  const isLeaf = !children;
  const ext = getFileExtension(name);
  const extStyle = ext ? getExtensionStyle(ext) : null;
  return <div style={{
    marginLeft: "30px"
  }}>
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
        <span style={{
    position: "relative",
    display: "inline-block"
  }}>
          <span className="file-icon">{isLeaf && !name.endsWith("/") ? "📄" : "📁"}</span>
          {ext && <span style={{
    position: "absolute",
    bottom: "2px",
    right: "0px",
    fontSize: "5px",
    fontWeight: "bold",
    padding: "1px",
    borderRadius: "2px",
    lineHeight: "1",
    minWidth: "8px",
    textAlign: "center",
    ...extStyle
  }}>
              {ext}
            </span>}
        </span>
        <code>{name}</code>
        {description && <span className="description"> - {description}</span>}
      </div>
      {children}
    </div>;
};

export const FileTree = ({children}) => {
  return <div className="file-tree" style={{
    margin: "20px",
    marginLeft: "-30px"
  }}>
      {children}
    </div>;
};

The `pnpm fixture` command manages **consistent data sets** for testing. You can copy actual data from the development environment to the test environment for stable and repeatable tests.

## Basic Concept

Fixture is a **fixed data set** for testing:

* **Consistency**: All tests start with the same initial data
* **Isolation**: Separate test DB from development DB
* **Reproducibility**: Repeatable tests under same conditions
* **Relationship preservation**: Foreign key relationships maintained

## Commands

### init - Initialize Test DB

Copy development DB schema to test DB.

```bash theme={null}
pnpm fixture init
```

**Execution process**:

```
DUMP...
  ✓ Schema dumped from development_master

SYNC to (REMOTE) Fixture DB...
  ✓ Database myapp_fixture created
  ✓ Schema applied

SYNC to (LOCAL) Testing DB...
  ✓ Database myapp_test created
  ✓ Schema applied

Fixture initialization completed!
```

**Databases created**:

* **Fixture DB** (remote): Shared fixture storage
* **Test DB** (local): Local testing DB

<Info>`init` copies **schema only**. Data is not included.</Info>

### import - Import Data

Save specific records from development DB as fixtures.

```bash theme={null}
pnpm fixture import
```

Interactive prompt appears:

```
? Please select entity: (Use arrow keys)
❯ User
  Post
  Comment

? Enter record IDs (comma-separated): 1,2,3

Importing fixtures...
  ✓ Imported User #1
  ✓ Imported User #2
  ✓ Imported User #3
  ✓ Imported related records (5 dependencies)

Fixture import completed!
```

**Related data automatically included**:

* Records referenced by foreign keys
* Relationship table records
* Reverse reference records (optional)

### sync - Synchronize

Apply saved fixtures to test DB.

```bash theme={null}
pnpm fixture sync
```

**Execution process**:

```
Syncing fixtures...

Clearing test database...
  ✓ Cleared all tables

Applying fixtures...
  ✓ Applied 3 users
  ✓ Applied 7 posts
  ✓ Applied 12 comments
  ✓ Applied 5 categories

Fixture sync completed!
```

<Tip>Calling `sync` before test execution ensures you always start with a clean state.</Tip>

### gen - Auto Fixture Generation

**Automatically generate fixtures** using Entity's cone metadata. Useful when you don't have real data or need virtual test data.

```bash theme={null}
pnpm fixture gen
```

**Interactive mode**:

```
? Select entities to generate fixtures for: (Use arrow keys)
❯ ◯ User
  ◯ Post
  ◯ Comment

? Number to generate per Entity: 5

? User entity is included. Select generation method:
❯ 1. Generate loginable user fixtures
  2. Generate dummy data (not loginable)

? Save method: (Use arrow keys)
❯ Save to Fixture DB
  Save to file (auto filename)
  Save to file (specify filename)
  Don't save (output only)

? Generate more realistic data with LLM? (fixtureHint-based, requires ANTHROPIC_API_KEY) No
```

<Info>
  The generation method prompt is only shown when User entity is included in the selection.
  If User entity is not selected, the flow proceeds directly to the save method selection.
</Info>

**Loginable User Fixture Generation**:

When you select the User entity and choose "Generate loginable user fixtures", the system creates actually loginable users through better-auth's `sign-up/email` endpoint.

How this mode works:

1. `FixtureGenerator` generates User data (name, email, username, etc.)
2. Calls better-auth sign-up API via `Sonamu.auth.handler()` to register users
3. Automatically updates `email_verified` to `true` for all created users
4. Outputs the list of created credentials (email, password) to the console

```
Generating 5 loginable users...
  ✅ alice@example.com created
  ✅ bob@example.com created
  ⚠️  carol@example.com already exists - skipping.
  ✅ dave@example.com created
  ✅ eve@example.com created

email_verified = true update completed

✅ 4 loginable users created

Created accounts:
┌─────────┬──────────────────────┬────────────┐
│ (index) │ email                │ password   │
├─────────┼──────────────────────┼────────────┤
│ 0       │ alice@example.com    │ Test1234!  │
│ 1       │ bob@example.com      │ Test1234!  │
│ 2       │ dave@example.com     │ Test1234!  │
│ 3       │ eve@example.com      │ Test1234!  │
└─────────┴──────────────────────┴────────────┘
```

<Warning>
  All generated users share the same password `Test1234!`. This feature is intended for
  development/test environments only and should never be used against a production database.
</Warning>

Selecting "Generate dummy data (not loginable)" uses the existing `generateBatch()` flow to insert data directly into the DB. In this case, no better-auth authentication records are created, so login is not possible.

**CLI options**:

| Option       | Description                                                                        | Example                                      |
| ------------ | ---------------------------------------------------------------------------------- | -------------------------------------------- |
| `--all`      | Generate for all Entities                                                          | `pnpm fixture gen --all`                     |
| `--include`  | Include specific Entities only                                                     | `pnpm fixture gen --include User,Post`       |
| `--exclude`  | Exclude specific Entities (use with --all)                                         | `pnpm fixture gen --all --exclude Comment`   |
| `--count`    | Number of records to generate                                                      | `pnpm fixture gen --include User --count 10` |
| `--save-to`  | Specify save method                                                                | `pnpm fixture gen --save-to db`              |
| `--use-llm`  | Generate realistic data with LLM (fixtureHint-based, requires `ANTHROPIC_API_KEY`) | `pnpm fixture gen --use-llm`                 |
| `--no-cache` | Disable LLM cache (use with `--use-llm`)                                           | `pnpm fixture gen --use-llm --no-cache`      |

**Save methods**:

* `db`: Save directly to Fixture DB (default)
* `file`: Save to `test/fixtures/{entity_table}.json` file
* `file:{filename}`: Save with specified filename
* `none`: Don't save, just output to console

```bash theme={null}
# Example: Generate 10 each of User, Post and save to DB
pnpm fixture gen --include User,Post --count 10 --save-to db
```

**Value generation priority**:

When generating a value for each field, the following strategies are applied in order. The first matching strategy is used, and subsequent steps are skipped.

1. **Relation** — Fields with foreign key relationships automatically create/reference related records.
2. **cone.note + LLM** — When `--use-llm` is enabled and the field's cone has a `note` defined, LLM generates realistic values based on the `note` content. Falls back to the next step if LLM call fails.
3. **cone.fixtureGenerator** — If the cone has a `fixtureGenerator` (Faker expression, etc.), it is executed.
4. **cone.fixtureDefault** — If the cone has a `fixtureDefault` fixed value, it is used.
5. **Type-based default generation** — If none of the above strategies apply, values are auto-generated based on the field type (string, number, date, etc.).

<Info>
  When using `--use-llm`, LLM takes priority over `fixtureGenerator` for fields with cone.note. This
  allows generating more realistic, domain-appropriate data.
</Info>

<Info>
  If an LLM-generated string exceeds the field's `length` constraint, it is automatically truncated.
  For example, if LLM generates 120 characters for a `varchar(100)` field, only the first 100
  characters are used.
</Info>

**Automatic Companion Fixture Generation**:

When an Entity's cone has `fixtureCompanions` declared, companion Entity fixtures are automatically created alongside the parent fixture. For example, in a better-auth based project, generating a User fixture will also automatically create a credentials Account.

`fixtureCompanions` is declared in the Entity's `id` prop cone:

```json theme={null}
{
  "name": "id",
  "type": "string",
  "cone": {
    "fixtureStrategy": "sequence",
    "fixtureCompanions": [
      {
        "entity": "Account",
        "overrides": {
          "provider_id": "credential",
          "account_id": "{{email}}",
          "password": "{{email}}"
        }
      }
    ]
  }
}
```

| Property    | Description                                                                                                                   |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `entity`    | Name of the companion Entity to create alongside                                                                              |
| `overrides` | Fixed override values for the companion fixture. Use `{{fieldName}}` syntax to reference field values from the parent fixture |
| `count`     | Number of companions to create per parent (default: 1)                                                                        |

To retroactively add `fixtureCompanions` to existing better-auth projects:

```bash theme={null}
pnpm sonamu auth add-companions
```

This command automatically adds `fixtureCompanions` configuration to better-auth Entities (such as User) in their `entity.json`, only when the configuration does not already exist. Entities that already have the configuration are skipped.

**parentId Entity Fixture Generation**:

Entities with `parentId` (subtypes in an inheritance relationship) are handled specially during `gen`. Instead of creating new parent records, the generator finds existing parent records in the DB that don't yet have a corresponding subtype row, and uses those IDs.

For example, if `Paper` is a subtype of `Achievement` (`parentId: "Achievement"`):

1. Queries the `achievements` table for IDs that don't have a corresponding row in the `papers` table
2. Creates `Paper` fixtures with those IDs, forming the parent-child relationship

To add conditions when querying parents, specify `fixtureParentOverrides` in the `id` prop's cone:

```json theme={null}
{
  "name": "id",
  "type": "integer",
  "cone": {
    "fixtureParentOverrides": {
      "achievement_type": "PAPER"
    }
  }
}
```

| Property                 | Description                                                                                                                                         |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fixtureParentOverrides` | WHERE conditions applied when querying available parent records. For example, if there is a discriminator column for subtypes, filter by that value |

<Warning>
  If there are not enough parent records without a subtype row, fixture generation for that entity
  is skipped. Make sure to generate sufficient parent entity fixtures beforehand.
</Warning>

### fetch - Fetch Data from Production DB

**Fetch data from actual production (or development) DB** and save to Fixture DB. Use when you need realistic test data.

```bash theme={null}
pnpm fixture fetch
```

**Interactive mode**:

```
? Select entities to import: (Use arrow keys)
❯ ◯ User
  ◯ Post
  ◯ Comment

Importing User, Post...
  ✓ User: 10 records imported
  ✓ Post: 10 records imported
```

**CLI options**:

| Option       | Description                    | Example                                  |
| ------------ | ------------------------------ | ---------------------------------------- |
| `--all`      | Fetch from all Entities        | `pnpm fixture fetch --all`               |
| `--include`  | Include specific Entities only | `pnpm fixture fetch --include User,Post` |
| `--exclude`  | Exclude specific Entities      | `pnpm fixture fetch --all --exclude Log` |
| `--strategy` | Data selection strategy        | `pnpm fixture fetch --strategy recent`   |
| `--limit`    | Number of records to fetch     | `pnpm fixture fetch --limit 20`          |

**Data selection strategies**:

* `recent` (default): Fetch most recent data first
* `sample`: Random sampling

```bash theme={null}
# Example: Fetch 20 most recent User records
pnpm fixture fetch --include User --strategy recent --limit 20
```

**Features**:

* Related data is automatically fetched together (up to depth level 2)
* Foreign key referential integrity maintained

### explore - Query DB Data

**Query only** DB data. Useful for quickly checking data without saving.

```bash theme={null}
pnpm fixture explore
```

**Interactive mode**:

```
? Entity to explore: User

User 10 records retrieved:
┌─────────┬────┬────────────────────┬───────────┐
│ (index) │ id │ email              │ name      │
├─────────┼────┼────────────────────┼───────────┤
│    0    │  1 │ 'user1@example.com'│ 'John'    │
│    1    │  2 │ 'user2@example.com'│ 'Jane'    │
│   ...   │... │ ...                │ ...       │
└─────────┴────┴────────────────────┴───────────┘
```

**CLI options**:

| Option       | Description                | Example                                  |
| ------------ | -------------------------- | ---------------------------------------- |
| `--include`  | Entity to explore          | `pnpm fixture explore --include User`    |
| `--strategy` | Data selection strategy    | `pnpm fixture explore --strategy sample` |
| `--limit`    | Number of records to query | `pnpm fixture explore --limit 5`         |

**Data selection strategies**:

* `sample` (default): Random sample
* `recent`: Most recent data
* `random`: Completely random
* `ids`: Specify specific IDs
* `query`: Custom query

```bash theme={null}
# Example: Query 5 most recent records from User table
pnpm fixture explore --include User --strategy recent --limit 5
```

## Usage Workflow

### 1. Initial Setup

Run once when starting the project.

```bash theme={null}
# Create test DB and copy schema
pnpm fixture init
```

### 2. Select Needed Data

Import actual data needed for testing.

```bash theme={null}
# Import User #1, #2, #3
pnpm fixture import

# Entity: User
# IDs: 1,2,3
```

### 3. Write Tests

```typescript title="user.model.test.ts" theme={null}
import { FixtureManager } from "sonamu/test";

beforeAll(async () => {
  // Sync fixtures
  await FixtureManager.sync();
});

test("Find User by email", async () => {
  // Use User #1 from fixture
  const user = await UserModel.findByEmail("user1@example.com");

  expect(user).toBeDefined();
  expect(user.id).toBe(1);
});
```

### 4. Run Tests

```bash theme={null}
pnpm test
```

Fixtures are automatically synced each time tests run.

## Fixture Files

### Storage Location

<FileTree>
  <FileItem name="src/">
    <FileItem name="fixtures/">
      <FileItem name="users.json" description="User fixture" />

      <FileItem name="posts.json" description="Post fixture" />

      <FileItem name="comments.json" description="Comment fixture" />
    </FileItem>
  </FileItem>
</FileTree>

### File Format

```json title="src/fixtures/users.json" theme={null}
[
  {
    "id": 1,
    "email": "user1@example.com",
    "name": "John Doe",
    "created_at": "2024-01-15T00:00:00.000Z"
  },
  {
    "id": 2,
    "email": "user2@example.com",
    "name": "Jane Smith",
    "created_at": "2024-01-16T00:00:00.000Z"
  }
]
```

**Features**:

* JSON format
* Separate files per Entity
* Related data automatically included
* Version controlled with Git

## Relationship Data Handling

### Automatic Foreign Key Inclusion

When importing a Post, connected Users are automatically included.

```bash theme={null}
pnpm fixture import

# Entity: Post
# IDs: 1

# Automatically included:
# ✓ Post #1
# ✓ User #5 (author)
# ✓ Category #2 (category)
```

### N:M Relationship Handling

Many-to-many relationship tables are also automatically included.

```bash theme={null}
pnpm fixture import

# Entity: Post
# IDs: 1

# Automatically included:
# ✓ Post #1
# ✓ post_tags (junction table)
# ✓ Tag #1, #2, #3 (connected tags)
```

## Test Isolation

### Reset Before Each Test

```typescript theme={null}
describe("User CRUD", () => {
  beforeEach(async () => {
    // Reapply fixture before each test
    await FixtureManager.sync();
  });

  test("Create user", async () => {
    const user = await UserModel.create({
      email: "new@example.com",
      name: "New User",
    });

    expect(user.id).toBeDefined();
  });

  test("Delete user", async () => {
    await UserModel.deleteById(1);

    const user = await UserModel.findById(1);
    expect(user).toBeNull();
  });
});
```

**Isolation effect**:

* Each test is independent
* Test order doesn't matter
* Parallel execution possible

### Transaction Isolation

```typescript theme={null}
import { Sonamu } from "sonamu";

test("Transaction test", async () => {
  await Sonamu.runScript(async () => {
    // Execute within transaction
    const user = await UserModel.create({...});
    const post = await PostModel.create({...});

    // Auto-rollback after test completes
  });
});
```

## Practical Examples

### Complex Relationship Testing

```typescript theme={null}
describe("Post with Comments", () => {
  beforeAll(async () => {
    // Post #1 (+ User #1, Comments)
    await FixtureManager.sync();
  });

  test("Get Post with comments", async () => {
    const post = await PostModel.findById(1, {
      include: ["comments"],
    });

    expect(post.comments).toHaveLength(3);
    expect(post.comments[0].author_id).toBe(2);
  });
});
```

### Specific Scenario Data

```typescript theme={null}
describe("Premium User Features", () => {
  beforeAll(async () => {
    // Import only premium users
    await FixtureManager.importFixture("User", [10, 11, 12]);
    await FixtureManager.sync();
  });

  test("Premium feature access", async () => {
    const user = await UserModel.findById(10);

    expect(user.isPremium).toBe(true);
    expect(await user.canAccessFeature("advanced")).toBe(true);
  });
});
```

## Troubleshooting

### Fixture DB Connection Failed

**Problem**: Cannot access remote fixture DB

```
Error: connect ECONNREFUSED
```

**Solution**:

```typescript title="sonamu.config.ts" theme={null}
export default {
  database: {
    fixture: {
      client: "pg",
      connection: {
        host: "fixture-db.example.com", // Correct host
        database: "myapp_fixture",
        user: "postgres",
        password: process.env.FIXTURE_DB_PASSWORD,
      },
    },
  },
};
```

### Missing Relationship Data

**Problem**: Foreign key error occurs

```
Error: Foreign key constraint violation
```

**Solution**:

```bash theme={null}
# Import related data first
pnpm fixture import
# Entity: User
# IDs: 1,2,3

pnpm fixture import
# Entity: Post
# IDs: 1,2,3  # References User 1,2,3
```

### Fixture Conflict

**Problem**: ID duplication

```
Error: Duplicate entry '1' for key 'PRIMARY'
```

**Solution**:

```bash theme={null}
# Reinitialize Test DB
pnpm fixture init

# Resync fixtures
pnpm fixture sync
```

## Best Practices

### 1. Minimal Data

```bash theme={null}
# ❌ Import all data
pnpm fixture import
# IDs: 1-1000

# ✅ Only necessary data
pnpm fixture import
# IDs: 1,2,3  # Representative cases only
```

### 2. Meaningful Data

```json theme={null}
// ✅ Data with clear test purpose
{
  "id": 1,
  "email": "admin@example.com",
  "role": "admin",
  "name": "Admin User"
}
{
  "id": 2,
  "email": "user@example.com",
  "role": "user",
  "name": "Regular User"
}
```

### 3. Version Control

```bash theme={null}
# Include fixture files in Git
git add src/fixtures/
git commit -m "Update test fixtures"
```

### 4. CI/CD Integration

```yaml title=".github/workflows/test.yml" theme={null}
jobs:
  test:
    steps:
      - name: Setup Database
        run: |
          pnpm fixture init
          pnpm fixture sync

      - name: Run Tests
        run: pnpm test
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Testing" icon="flask" href="/en/testing/naite">
    Learn about Naite test framework
  </Card>

  <Card title="migrate" icon="database" href="/en/tools-and-cli/sonamu-cli/migrate">
    Manage migrations
  </Card>
</CardGroup>
