> ## 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.

# Test Scaffolding

> Auto-generating model.test.ts

Sonamu automatically generates model test files with the `pnpm generate` command.

## Scaffolding Overview

<CardGroup cols={2}>
  <Card title="Auto Generation" icon="wand-magic-sparkles">
    model.test.ts creation Basic structure included
  </Card>

  <Card title="Bootstrap Included" icon="bolt">
    Auto test environment setup Immediately executable
  </Card>

  <Card title="CRUD Tests" icon="list-check">
    Basic test skeleton Customizable
  </Card>

  <Card title="Type Safe" icon="shield-check">
    Model integration Auto-completion support
  </Card>
</CardGroup>

## Test File Generation

### Command

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

When an Entity is defined, `{model}.test.ts` files are automatically generated.

**Generation location**:

```
api/src/models/
├── user.model.ts
├── user.model.test.ts       # Auto-generated
├── post.model.ts
└── post.model.test.ts        # Auto-generated
```

### Generated File Structure

```typescript theme={null}
// api/src/models/user.model.test.ts
import { bootstrap, test } from "sonamu/test";
import { expect, vi } from "vitest";
import { UserModel } from "./user.model";

// Initialize test environment
bootstrap(vi);

test("Create user", async () => {
  const userModel = new UserModel();

  // Write test code
  const { user } = await userModel.create({
    username: "john",
    email: "john@example.com",
    password: "password123",
  });

  expect(user.id).toBeGreaterThan(0);
  expect(user.username).toBe("john");
});

test("Get user", async () => {
  const userModel = new UserModel();

  // Create test data
  const { user } = await userModel.create({
    username: "jane",
    email: "jane@example.com",
    password: "password123",
  });

  // Test retrieval
  const { user: found } = await userModel.getUser("C", user.id);

  expect(found.id).toBe(user.id);
  expect(found.username).toBe("jane");
});
```

## Running Tests

### Run Single File

```bash theme={null}
# Run only specific model test
pnpm vitest user.model.test.ts
```

### Run All Tests

```bash theme={null}
# Run all tests
pnpm test

# Or
pnpm vitest
```

### Watch Mode

```bash theme={null}
# Auto re-run on file changes
pnpm vitest --watch
```

## Test Writing Guide

### CRUD Test Pattern

```typescript theme={null}
import { bootstrap, test } from "sonamu/test";
import { expect, vi } from "vitest";
import { PostModel } from "./post.model";

bootstrap(vi);

// Create
test("Create post", async () => {
  const postModel = new PostModel();

  const { post } = await postModel.create({
    title: "Test Post",
    content: "Test Content",
    author_id: 1,
  });

  expect(post.id).toBeGreaterThan(0);
  expect(post.title).toBe("Test Post");
});

// Read
test("Get post", async () => {
  const postModel = new PostModel();

  // Create test data
  const { post } = await postModel.create({
    title: "Test Post",
    content: "Test Content",
    author_id: 1,
  });

  // Retrieve
  const { post: found } = await postModel.getPost("C", post.id);

  expect(found.id).toBe(post.id);
  expect(found.title).toBe("Test Post");
});

// Update
test("Update post", async () => {
  const postModel = new PostModel();

  // Create test data
  const { post } = await postModel.create({
    title: "Original Title",
    content: "Original Content",
    author_id: 1,
  });

  // Update
  await postModel.update(post.id, {
    title: "Updated Title",
  });

  // Verify
  const { post: updated } = await postModel.getPost("C", post.id);
  expect(updated.title).toBe("Updated Title");
  expect(updated.content).toBe("Original Content"); // Not changed
});

// Delete
test("Delete post", async () => {
  const postModel = new PostModel();

  // Create test data
  const { post } = await postModel.create({
    title: "Test Post",
    content: "Test Content",
    author_id: 1,
  });

  // Delete
  await postModel.delete(post.id);

  // Verify
  const deleted = await postModel.findById("C", post.id);
  expect(deleted).toBeNull();
});
```

### Relationship Tests

```typescript theme={null}
test("User and post relationship", async () => {
  const userModel = new UserModel();
  const postModel = new PostModel();

  // Create user
  const { user } = await userModel.create({
    username: "author",
    email: "author@example.com",
    password: "password",
  });

  // Create post
  const { post } = await postModel.create({
    title: "User's Post",
    content: "Content",
    author_id: user.id,
  });

  // Verify relationship
  expect(post.author_id).toBe(user.id);

  // Querying with Subset "C" includes author info
  const { post: fullPost } = await postModel.getPost("C", post.id);
  expect(fullPost.author?.username).toBe("author");
});
```

### Error Tests

```typescript theme={null}
test("Duplicate email validation", async () => {
  const userModel = new UserModel();

  // Create first user
  await userModel.create({
    username: "user1",
    email: "duplicate@example.com",
    password: "password",
  });

  // Try to create with same email
  await expect(
    userModel.create({
      username: "user2",
      email: "duplicate@example.com",
      password: "password",
    }),
  ).rejects.toThrow("Email already exists");
});

test("Get non-existent post", async () => {
  const postModel = new PostModel();

  await expect(postModel.getPost("C", 999999)).rejects.toThrow("Post not found");
});
```

## Structuring Tests

### Grouping Multiple Tests

```typescript theme={null}
import { bootstrap, test } from "sonamu/test";
import { expect, vi } from "vitest";
import { describe } from "vitest";
import { UserModel } from "./user.model";

bootstrap(vi);

describe("User creation", () => {
  test("Normal creation", async () => {
    const userModel = new UserModel();
    const { user } = await userModel.create({
      username: "john",
      email: "john@example.com",
      password: "password",
    });
    expect(user.id).toBeGreaterThan(0);
  });

  test("Duplicate email validation", async () => {
    const userModel = new UserModel();
    await userModel.create({
      username: "user1",
      email: "duplicate@example.com",
      password: "password",
    });

    await expect(
      userModel.create({
        username: "user2",
        email: "duplicate@example.com",
        password: "password",
      }),
    ).rejects.toThrow();
  });
});

describe("User retrieval", () => {
  test("Get by ID", async () => {
    const userModel = new UserModel();
    const { user } = await userModel.create({
      username: "test",
      email: "test@example.com",
      password: "password",
    });

    const { user: found } = await userModel.getUser("C", user.id);
    expect(found.id).toBe(user.id);
  });

  test("Get non-existent user", async () => {
    const userModel = new UserModel();
    await expect(userModel.getUser("C", 999999)).rejects.toThrow();
  });
});
```

## Best Practices

### 1. Test Isolation

Each test should run independently.

```typescript theme={null}
// ✅ Correct: Each test is independent
test("Test 1", async () => {
  const userModel = new UserModel();
  const { user } = await userModel.create({
    /* ... */
  });
  // Auto rollback after test ends
});

test("Test 2", async () => {
  const userModel = new UserModel();
  // Starts with clean DB state
  const { user } = await userModel.create({
    /* ... */
  });
});

// ❌ Wrong: Dependencies between tests
let sharedUserId: number;

test("Test 1", async () => {
  const userModel = new UserModel();
  const { user } = await userModel.create({
    /* ... */
  });
  sharedUserId = user.id; // ❌ Shared with other tests
});

test("Test 2", async () => {
  const userModel = new UserModel();
  const { user } = await userModel.getUser("C", sharedUserId); // ❌ Fails!
});
```

### 2. Clear Test Names

```typescript theme={null}
// ✅ Correct
test("Error thrown when creating user with duplicate email", async () => {
  // ...
});

// ❌ Wrong
test("Test 1", async () => {
  // ...
});
```

### 3. Arrange-Act-Assert Pattern

```typescript theme={null}
test("Update post", async () => {
  // Arrange
  const postModel = new PostModel();
  const { post } = await postModel.create({
    title: "Original",
    content: "Content",
    author_id: 1,
  });

  // Act
  await postModel.update(post.id, {
    title: "Updated",
  });

  // Assert
  const { post: updated } = await postModel.getPost("C", post.id);
  expect(updated.title).toBe("Updated");
});
```

## Cautions

<Warning>
  **Cautions when writing tests**: 1. **Don't modify auto-generated files**: Will be overwritten on
  regeneration 2. **Independent tests**: No dependencies between tests 3. **Transaction based**:
  Auto rollback after each test 4. **Async required**: All tests must be async functions 5. **Clear
  names**: Clearly state what the test verifies
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Test Structure" icon="sitemap" href="/en/testing/writing-tests/test-structure">
    bootstrap, test, testAs
  </Card>

  <Card title="Test Helpers" icon="wrench" href="/en/testing/writing-tests/test-helpers">
    Fixtures and utilities
  </Card>

  <Card title="Vitest Docs" icon="external-link" href="https://vitest.dev">
    Vitest guide
  </Card>

  <Card title="Model System" icon="cube" href="/en/core-concepts/model-system/overview">
    Understanding Models
  </Card>
</CardGroup>
