anthonyoteri / anthonyoteri/cargo-rigtest

Destructive test markers with profile-gated execution

Aberta
#46 0 comentários 0 reações 0 responsáveis Ver no GitHub
enhancement
Linguagem predominante
Rust
Estrelas
0
Forks
0
Merge médio
3d 18h
PRs com merge (30d)
8

Descrição

## Problem

Acceptance suites mix two kinds of tests: read-only verifications (does `/health` return 200, does login succeed) and tests that mutate state (creating records, deleting users, exercising write paths). Read-only tests are safe to run against any environment including production; destructive tests are not. Today rigtest has no way to express this distinction, which means either the suite is restricted to the lowest-common-denominator set of tests that are safe everywhere, or operators manually maintain separate test files per safety class.

## Proposed solution

Tests opt into a `destructive` marker, and profiles can opt *out* of permitting destructive writes. The runner refuses to execute destructive tests against profiles that have opted out.

```rust
#[testcase(destructive)]
async fn deletes_old_records(ctx: Arc) -> Result<(), BoxError> {
// ...
}
```

```toml
[profile.staging]
# destructive omitted — defaults to true (writes allowed)

[profile.prod]
destructive = false # explicit opt-out — read-only
```

**Default behavior:** the `destructive` field on a profile is **optional and defaults to `true`** (writes allowed). Profiles must explicitly opt out by setting `destructive = false`. This keeps the feature additive — existing profiles and tests continue to work without any config changes, and the marker only restricts behavior for users who have deliberately classified at least one profile as read-only.

When the active profile has `destructive = false`, marked tests appear in the run summary as `SKIP destructive (profile 'prod' is read-only)`. They aren't silently dropped — the operator sees exactly which tests were skipped and why.

The three policy layers stay orthogonal and compose cleanly:

| Layer | Question it answers | Default |
|---|---|---|
| `#[testcase(profiles = [...])]` | Which environments is this test valid against? | All profiles (no restriction) |
| `#[testcase(destructive)]` | Does this test mutate state? | Not destructive (no restriction) |
| `[profile.X] destructive = ...` | Does this environment allow mutation? | `true` (writes allowed) |

All three defaults are permissive. The feature only restricts behavior when the user explicitly opts into a restriction.

## Alternatives considered

Defaulting `destructive` to `false` (read-only) would be safer in the abstract but would break every existing suite the moment the field is introduced. The opt-out model trades a slightly less safe default for a non-breaking introduction, which matches rigtest's additive-feature-additions approach. Users who want safer defaults can set `destructive = false` on every profile and only flip it to `true` where needed — the same outcome, just expressed by the user rather than imposed by the framework.

A CLI flag (`--allow-destructive`) without per-profile policy would gate writes globally per run. Simpler but weaker: the operator has to remember the flag, and one suite invocation can't safely target a write-permitted profile and a read-only profile at the same time. The per-profile field puts the policy in the same place as the rest of the environment's truth, where it belongs.

A test-naming convention (`test_destructive_*` runs only with a flag) works but is brittle, invisible to tooling, and doesn't survive refactors.

## Additional context

Depends on the profiles feature (filed separately) — destructive policy is a field on the profile block, and the gate fires at runner time using the active profile's policy. The marker itself is a small addition to `TestCase`, made non-breaking by #40 (`#[non_exhaustive]`).

**Related issues:** #40 (`#[non_exhaustive]` — keeps adding the `destructive` field non-breaking), #41 (tags — orthogonal but often combined: `tags = ["smoke"], destructive`). Profiles feature issue to follow.

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.