一键导入
tdd
Test-first workflow for features, bug fixes, refactors, and project-specific test patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Test-first workflow for features, bug fixes, refactors, and project-specific test patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use this skill to generate well-branded interfaces and assets for RIPDPI (an Android-native, Compose-first VPN and DPI-bypass app), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, brand-ship icons, and an interactive UI kit of every public composable from the live ui/components tree.
Use when managing the Rust workspace, adding/removing crates, editing workspace dependencies, running cargo nextest/audit/deny, configuring Cargo profiles for Android cross-compilation, debugging Cargo.lock churn, migrating crate edition, or wiring Gradle to cargo via the ripdpi.android.rust-native plugin.
Use when modifying the diagnostics scan pipeline, ScanRequest/ScanReport types, ProbeTask families, ripdpi-monitor-engine / ripdpi-diagnostics-* crates, strategy-probe candidates, the diagnostics catalog (packs/profiles), wire-schema contracts between Rust and Kotlin, DIAGNOSTICS_ENGINE_SCHEMA_VERSION, golden contract tests, or adding a new probe type / profile. Triggers on diagnostics scans, strategy probes, automatic audit, dpi-detector profiles, or anything in core/diagnostics or native/rust/crates/ripdpi-monitor-*.
Android-specific Rust build, verification, and packaging — per-target 16 KiB page alignment, size-optimized release profile, ELF symbol allowlist, .so size budgets, NDK 29 specifics. Use when modifying .cargo/config.toml for Android targets, the workspace [profile.release] / [profile.android-jni] block, or when verifying a built .so before release.
Telemetry and observability discipline for the RIPDPI Android Rust stack — control-plane vs data-plane logging, bounded flume event queues, atomic counters, snapshot polling, readiness callbacks, and deterministic JSON contracts. Use when authoring or modifying telemetry emission code, the bounded event ring, the Kotlin-side telemetry consumer, or any per-packet logging.
Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage.
| name | tdd |
| description | Test-first workflow for features, bug fixes, refactors, and project-specific test patterns. |
This skill is RIGID. Follow every step exactly. Do not skip RED. Do not write implementation before a failing test exists.
Full test stack documentation: docs/testing.md. Do not duplicate it here -- read it when you need runner details, CI lanes, or fixture locations.
Repeat this cycle for every behavior change:
./gradlew staticAnalysis before considering the cycle complete.Never batch multiple behaviors into one cycle. One test, one behavior, one cycle.
# Single test method
./gradlew :core:engine:testDebugUnitTest --tests "ClassName.method name"
# Single test class
./gradlew :core:engine:testDebugUnitTest --tests "ClassName"
# Single module
./gradlew :core:engine:testDebugUnitTest
Replace :core:engine with the target module (:core:service, :core:data, :core:diagnostics).
# Single test
cargo nextest run --locked -p crate_name test_name
# Single crate
cargo nextest run --locked -p crate_name
# Full workspace
cargo nextest run --locked --workspace
Golden tests are read-only by default. If your change intentionally alters a contract:
# Bless all golden fixtures
bash scripts/tests/bless-telemetry-goldens.sh
# Manual single-suite bless
RIPDPI_BLESS_GOLDENS=1 ./gradlew :core:engine:testDebugUnitTest
RIPDPI_BLESS_GOLDENS=1 cargo test --locked -p crate_name
Always review blessed diffs before committing. Golden changes require explanation in the commit message.
All test doubles are hand-written Fakes in core/engine/src/test/kotlin/com/poyka/ripdpi/core/TestDoubles.kt. The project does not use MockK, Mockito, or any mocking library.
Pattern:
Fake + interface name (e.g., FakeRipDpiProxyRuntime)Fault injection uses FaultQueue<T> from core/engine-api/src/main/kotlin/com/poyka/ripdpi/core/testing/FaultModel.kt.
Key types:
FaultQueue<T> -- ordered queue of faults matched by target enumFaultSpec<T> -- target + outcome + scope + optional message/payloadFaultScope.ONE_SHOT -- fires once then is consumedFaultScope.PERSISTENT -- fires on every matching call until clearedFaultOutcome -- EXCEPTION, TIMEOUT, DROP, RESET, MALFORMED_PAYLOAD, BLANK_PAYLOAD, PANICUsage in tests:
val bindings = FakeRipDpiProxyBindings()
bindings.faults.enqueue(
FaultSpec(
target = ProxyBindingFaultTarget.START,
outcome = FaultOutcome.EXCEPTION,
message = "simulated native crash",
)
)
| What you test | Location | Runner |
|---|---|---|
| Kotlin business logic | core/*/src/test/ | ./gradlew :core:*:testDebugUnitTest |
| Rust native logic | native/rust/crates/*/tests/ | cargo nextest run --locked -p crate |
| JNI integration | app/src/androidTest/.../integration/ | connectedDebugAndroidTest |
| Network E2E | app/src/androidTest/.../e2e/ | connectedDebugAndroidTest |
| Rust network E2E | native/rust/crates/*/tests/ | bash scripts/ci/run-rust-network-e2e.sh |
For non-trivial features, use context isolation:
This prevents the "write test and implementation together" anti-pattern.
./gradlew staticAnalysis covers detekt, ktlint, and Android lint. It applies to test code too.TestDoubles.kt.FaultSpec + FaultQueue, not ad-hoc exception throwing.@Test fun proxy start propagates native exception().fn proxy_start_propagates_native_exception().| Mistake | Fix |
|---|---|
| Using MockK or Mockito | Write a Fake* class in TestDoubles.kt |
| Skipping the RED step | Run the test first. If it passes, your test is wrong. |
| Multiple assertions per cycle | Split into separate test methods, one behavior each |
runBlocking in coroutine tests | Use runTest from kotlinx-coroutines-test |
| Blessing goldens without review | Run git diff on fixture files before committing |
| Testing private internals | Test through the public API of the class under test |
| Putting helpers in test classes | Add shared helpers to TestDoubles.kt |
FaultScope.PERSISTENT when ONE_SHOT suffices | Default to ONE_SHOT; use PERSISTENT only for repeated-call scenarios |
For deeper coverage of anti-patterns, see references/testing-anti-patterns.md.