JamieMason

write-tests

Write tests for Syncpack using the TestBuilder pattern. Use when adding tests for commands, validation logic, or any new functionality. Covers TestBuilder API, assertion patterns, and common test scenarios.

JamieMason 2,057 72 Updated 4mo ago

Resources

1
GitHub

Install

npx skillscat add jamiemason/syncpack/write-tests

Install via the SkillsCat registry.

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"],
            "pinned": "18.0.0"
        }))
        .build_and_visit_packages();

    expect(&ctx).to_have_instances(vec![
        ExpectedInstance {
            state: InstanceState::fixable(DiffersToPinnedVersion),
            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/local_test.rs — Local package 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")