Writing New Tests (Guidelines for Contributors)
Step-by-step for a new Rust entity/repository
- Add a builder in
src/core/tests/common/builders/if you're testing a new entity (e.g.<entity>_builder.rs), following the shape of existing builders likeReadingTestBuilder/UserTestSessionBuilder— aCreate<Entity>/Update<Entity>builder with sensible defaults and fluent.with_*()setters. - Register the builder in
common/builders/mod.rs. - Structure each test with Arrange / Act / Assert comments, matching the convention used throughout the suite:
#[tokio::test]
async fn it_finds_the_role_when_owner_looks_it_up() {
// Arrange
let pool = test_pool().await;
given_profile(&pool, OWNER_ID).await;
let input = super_admin_role_for(OWNER_ID);
let id = user_roles::insert(&pool, &input).await.expect("insert");
// Act
let found = user_roles::find_by_id(&pool, &id, OWNER_ID).await;
// Assert
assert_matches!(found, Ok(Some(role)) if role.role == "super_admin");
}
- Use
assert_matches!(from theassert_matchescrate) forResult/Optionassertions rather than manualmatches!/unwrapchains — it gives clearer failure output. - Always test the three standard authorization boundaries for any new repository function that takes a
user_id/created_byscope:- Owner success (the row is found/created/updated/deleted as expected).
- Other-user exclusion (a different user's id returns empty/
None, not an error). - Unknown id (
None/empty, not an error).
- Seed foreign key dependencies first — e.g. insert a
profilesrow before anyuser_rolesrow referencing it (see Running Tests Locally). - Name test functions descriptively in the
it_<behavior>style and group related cases in amod <operation> { ... }block.
Frontend components
Colocate new component tests in a __tests__/ folder next to the component (e.g. src/ui/components/dashboard/__tests__/NewComponent.test.tsx), following the existing Vitest + Testing Library patterns already used across components/*/__tests__/.