AI agent skill · by @dust-tt
Dust Test
Step-by-step guide for writing focused, practical tests for Dust codebases following the 80/20 principle.
Archived 2026-10-23 · Source updated 2026-10-11 · License: MIT. Check the original repository for the latest version.
SKILL.md instructions
# Creating automated tests for Dust codebases
Write focused, practical tests for the current file following the 80/20 principle.
## Instructions
When writing tests for a file:
1. **Identify the core functionality**: Focus on the most important paths and edge cases that provide 80% of the value
2. **Keep it simple**: Write straightforward tests that are easy to understand and maintain
3. **Minimal mocking**:
- DO NOT mock the database
- Only mock external services (APIs, third-party services)
- Prefer real implementations when possible
4. **Use shared factories**: Set up data with the existing factories, never by hand (see below)
5. **Focus on behavior**: Test what the code does, not how it does it
## For Front and Front-api (TypeScript)
Front-api tests import the same helpers from `@app/tests/utils/...`.
### Setup
Every test runs inside a database transaction that is rolled back afterwards, so tests can create
real records freely without cleanup.
Before writing any helper, look in `front/tests/utils/` for one that already exists:
| Need | Use |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Workspace + user + membership + `Authenticator` | `createResourceTest({ role })` from `generic_resource_tests` |
| Request/response for an API route handler | `createPrivateApiMockRequest`, `createPublicApiMockRequest`, `createPokeApiMockRequest` |
| A single database record | The matching `<Name>Factory` (e.g. `WorkspaceFactory.basic()`, `UserFactory.basic()`, `SpaceFactory.regular(...)`, `AgentConfigurationFactory.createTestAgent(...)`, `ConversationFactory.create(...)`) |
| A plain object for a component or pure-logic test | `LightWorkspaceFactory.build()`, `LightUserFactory.build()`, `LightPlanFactory`, `LightSubscriptionFactory` |
| A mocked service (Redis, file storage, WorkOS...) | `front/tests/utils/mocks/`. Redis, cache, file storage and Temporal are already mocked globally in `front/vite.setup.ts`; do not mock them again |
Rules (backed by the `testing` contracts in `front/CONTRACTS` and `no-unsafe-type-assertions` in the
root `CONTRACTS`):
- Set up data through factories, not `SomeResource.makeNew(...)` or `SomeModel.create(...)` in the test
(`test-setup-through-factories`, `tests-use-resources`). Direct model access is allowed only when no
Resource API can express the fixture.
- If a factory exists but does not support what you need (a status, a date, a relation), add an option to
the factory instead of working around it in the test. If no factory exists, add a new
`<Name>Factory.ts` in `front/tests/utils/` rather than a local helper in the test file
(`shared-test-fixtures`).
- Do not hand-write typed objects or cast partial ones (`{ sId: "w_1" } as LightWorkspaceType`). Use the
matching `Light*Factory.build({ ...overrides })` (`shared-test-fixtures`, `no-unsafe-type-assertions`).
- Do not `vi.mock` a module that `front/vite.setup.ts` already mocks (`no-duplicate-global-mocks`).
### Structure
```typescript
import { describe, expect, it } from "vitest";
import { createResourceTest } from "@app/tests/utils/generic_resource_tests";
import { AgentConfigurationFactory } from "@app/tests/utils/AgentConfigurationFactory";
describe("functionUnderTest", () => {
it("handles the main happy path", async () => {
// Arrange: set up with shared helpers and factories.
const { authenticator } = await createResourceTest({ role: "admin" });
const agent = await AgentConfigurationFactory.createTestAgent(authenticator);
// Act.
const result = await functionUnderTest(authenticator, agent.sId);
// Assert.
expect(result.isOk()).toBe(true);
});
it("rejects callers without permission", async () => {
const { authenticator } = await createResourceTest({ role: "user" });
// ...
});
});
```
### What to test (80/20 focus)
- Main success paths
- Most common error conditions
- Critical edge cases (null/undefined, empty arrays, etc.)
- Permission checks (if applicable)
### What to skip (diminishing returns)
- Exhaustive parameter combinations
- Unlikely edge cases
- Internal implementation details
- UI component rendering (unless critical)
## For Connectors/Core
Follow similar principles:
- Use factories appropriate to the service
- Focus on integration points
- Mock external APIs only (Slack, Notion, GitHub, etc.)
- Test the database interactions directly
## Execution Steps
1. Read the file to understand its purpose and main exports
2. Check if a test file already exists (e.g., `file.test.ts`)
3. Identify the 2-4 most important functions/behaviors to test
4. Check `front/tests/utils/` for the factories and helpers you need; extend or add a factory there if one
is missing
5. Write concise, focused tests
6. Run tests from `front/` with `npm run test -- path/to/file.test.ts` to verify they pass