write-tests

SKILL.md

Write Tests

Guide for writing tests in Syncpack using the TestBuilder pattern.

TDD Workflow (Mandatory)

  1. Study existing tests — Read 2-3 tests in same file, identify the pattern, match it exactly
  2. Write failing test — Never invent APIs; read source to see what exists
  3. Verify it fails — Run just test to confirm
  4. Ask user to confirm — Get approval before implementing
  5. Implement minimal code — Only what's needed to pass
  6. Clean up — Run just format, fix warnings, refactor if needed

Golden Rule

Always use TestBuilder — Never manually construct Context in tests.

Quick Start

use {
  crate::{
    instance_state::{FixableInstance::*, InstanceState, ValidInstance::*},
    test::{
      builder::TestBuilder,
      expect::{expect, ExpectedInstance},
    },
  },
  serde_json::json,
};

#[test]
fn test_descriptive_name() {
    let ctx = TestBuilder::new()
        .with_packages(vec![
            json!({"name": "pkg-a", "dependencies": {"react": "17.0.0"}}),
        ])
        .with_version_group(json!({
            "dependencies": ["react"],
            "pinVersion": "18.0.0"
        }))
        .build_and_visit_packages();

    expect(&ctx).to_have_instances(vec![
        ExpectedInstance {
            state: InstanceState::fixable(DiffersToPin),
            dependency_name: "react",
            id: "react in /dependencies of pkg-a",
            actual: "17.0.0",
            expected: Some("18.0.0"),
            overridden: None,
        },
    ]);
}

TestBuilder Methods

Packages

.with_package(json!({...}))           // Single package
.with_packages(vec![json!({...})])    // Multiple packages

Version Groups

.with_version_group(json!({...}))     // Single group
.with_version_groups(vec![...])       // Multiple groups

Configuration

.with_config(json!({...}))            // Custom config
.with_semver_group(json!({...}))      // Semver rules
.with_strict(true)                    // Strict mode

Registry (for update command)

.with_registry_updates(json!({"react": ["17.0.0", "18.0.0"]}))
.with_update_target(UpdateTarget::Latest)

Build

.build()                              // Without visiting (rare)
.build_and_visit_packages()           // With visiting (most common)

Location String Format

{dependency} in {location} of {package}

Examples:

  • "react in /dependencies of pkg-a"
  • "lodash in /devDependencies of pkg-b"
  • "pnpm in /packageManager of pkg-c"

Running Tests

just test                              # All tests
cargo test test_name -- --nocapture   # Specific test with output
cargo test banned_test                 # Tests matching pattern

Test Organisation

  • Integration tests: src/visit_packages/*_test.rs (preferred)
  • Unit tests: Co-located as *_test.rs (e.g., src/foo.rssrc/foo_test.rs)
  • Test utilities: src/test/builder.rs, src/test/expect.rs

Common Patterns

→ Full patterns and examples: patterns.md

Good Test Examples

Study these files:

  • src/visit_packages/banned_test.rs — Comprehensive examples
  • src/visit_packages/pinned_test.rs — Version group testing
  • src/visit_packages/preferred_semver_test.rs — Local package and highest/lowest semver handling

Common Mistakes

Wrong Right
Context { ... } TestBuilder::new()...
.build() then check states .build_and_visit_packages()
ctx.instances[0] .find(|i| i.dependency.name == "react")
Weekly Installs
10
GitHub Stars
1.9K
First Seen
Feb 20, 2026
Installed on
opencode10
github-copilot10
codex10
amp10
kimi-cli10
gemini-cli10