Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# Kill any test once it crosses 60s.
slow-timeout = { period = "60s", terminate-after = 1, grace-period = "5s" }

# Put a hard ceiling on the whole run.
global-timeout = "5m"
# Accommodate cargo-spawning tests that deliberately run one at a time.
global-timeout = "20m"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[test-groups.cargo-spawning]
# Tests that shell out to `cargo` contend for the package-cache lock and for
Expand All @@ -27,4 +27,4 @@ test-group = "cargo-spawning"

[profile.long]
slow-timeout = { period = "180s", terminate-after = 1, grace-period = "5s" }
global-timeout = "15m"
global-timeout = "30m"
8 changes: 5 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,16 @@ project:
diagnostics.
- In production code and shared fixtures, avoid `.expect()` entirely: return
`Result` and use `?` to propagate errors instead of panicking.
- Keep `expect_used` **strict**; do not suppress the lint.
- Keep `expect_used` strict outside recognized test cases; do not suppress the
lint.
- Recognize that `allow-expect-in-tests = true` **doesn’t cover** helpers
outside `#[cfg(test)]` or `#[test]`; avoid `expect` in such fixtures.
- Use `anyhow`/`eyre` with `.context(...)` to **preserve backtraces** and
provide clear, typed failure paths.
- Update helpers (e.g., `set_dir`) to **return errors** rather than panicking.
- Consume fallible fixtures in `rstest` by **making the test return `Result`**
and applying `?` to the fixture.
- Tests may consume fallible `rstest` fixtures with `.expect(...)` when setup
failure should fail the test; their signatures need not return `Result` only
to propagate fixture errors.

### Observability

Expand Down
5 changes: 5 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# Align with CodeScene’s ceiling
cognitive-complexity-threshold = 12 # default is 25

# Tests may use `.expect(...)` or `panic!(...)` for fallible setup at their
# own boundary. This applies to built-in `#[test]` and `rstest` cases.
allow-expect-in-tests = true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
allow-panic-in-tests = true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
14 changes: 8 additions & 6 deletions crates/cargo-bdd/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ mod tests {
use super::*;

#[test]
fn write_skip_reports_json_emits_fields() -> eyre::Result<()> {
fn write_skip_reports_json_emits_fields() {
let report = SkipReport {
feature: "feature",
scenario: "scenario",
Expand All @@ -250,12 +250,14 @@ mod tests {
}),
};
let mut buffer = Vec::new();
serde_json::to_writer(&mut buffer, &[report])?;
let parsed: serde_json::Value = serde_json::from_slice(&buffer)?;
serde_json::to_writer(&mut buffer, &[report]).expect("test setup should succeed");
let parsed: serde_json::Value =
serde_json::from_slice(&buffer).expect("test setup should succeed");
let entry = parsed
.as_array()
.and_then(|array| array.first())
.ok_or_else(|| eyre::eyre!("missing entry"))?;
.ok_or_else(|| eyre::eyre!("missing entry"))
.expect("test setup should succeed");
assert_eq!(
entry.get("feature"),
Some(&serde_json::Value::String("feature".into()))
Expand All @@ -272,7 +274,8 @@ mod tests {
let step = entry
.get("step")
.and_then(serde_json::Value::as_object)
.ok_or_else(|| eyre::eyre!("missing step object"))?;
.ok_or_else(|| eyre::eyre!("missing step object"))
.expect("test setup should succeed");
assert_eq!(
step.get("keyword"),
Some(&serde_json::Value::String("Given".into()))
Expand All @@ -281,6 +284,5 @@ mod tests {
step.get("pattern"),
Some(&serde_json::Value::String("x".into()))
);
Ok(())
}
}
4 changes: 0 additions & 4 deletions crates/cargo-bdd/src/registry/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ fn ignores_unrelated_failures_containing_dump_steps() {
}

#[test]
#[expect(
clippy::expect_used,
reason = "Test should fail fast when the registry dump JSON is invalid."
)]
fn parses_registry_dump_with_bypassed_steps() {
let json = r#"
{
Expand Down
53 changes: 25 additions & 28 deletions crates/cargo-bdd/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,18 @@ fn run_cargo_bdd_steps() -> Result<String> {

#[test]
#[serial]
fn list_steps_runs() -> Result<()> {
let stdout = run_cargo_bdd_steps()?;
fn list_steps_runs() {
let stdout = run_cargo_bdd_steps().expect("test setup should succeed");
assert!(
!stdout.is_empty(),
"Expected non-empty output from steps command",
);
Ok(())
}

#[test]
#[serial]
fn steps_output_includes_skipped_statuses() -> Result<()> {
let stdout = run_cargo_bdd_steps()?;
fn steps_output_includes_skipped_statuses() {
let stdout = run_cargo_bdd_steps().expect("test setup should succeed");
assert!(
stdout.contains("skipped tests/features/diagnostics.fixture :: fixture skipped scenario"),
"expected skipped scenario heading in cargo bdd output: {stdout}"
Expand All @@ -130,13 +129,12 @@ fn steps_output_includes_skipped_statuses() -> Result<()> {
stdout.contains("fixture skip message"),
"expected skip message to appear in cargo bdd output: {stdout}"
);
Ok(())
}

#[test]
#[serial]
fn steps_output_marks_forced_failure_skips() -> Result<()> {
let stdout = run_cargo_bdd_steps()?;
fn steps_output_marks_forced_failure_skips() {
let stdout = run_cargo_bdd_steps().expect("test setup should succeed");
assert!(
stdout.contains("[forced failure]"),
"expected forced failure annotation in cargo bdd output: {stdout}",
Expand All @@ -149,13 +147,12 @@ fn steps_output_marks_forced_failure_skips() -> Result<()> {
stdout.contains("fixture forced skip"),
"expected forced skip message in cargo bdd output: {stdout}",
);
Ok(())
}

#[test]
#[serial]
fn skipped_subcommand_includes_reasons_and_lines() -> Result<()> {
let stdout = run_cargo_bdd(&["skipped", "--reasons"])?;
fn skipped_subcommand_includes_reasons_and_lines() {
let stdout = run_cargo_bdd(&["skipped", "--reasons"]).expect("test setup should succeed");
assert!(
stdout.contains("tests/features/diagnostics.fixture:7"),
"skipped output should include feature location",
Expand All @@ -165,18 +162,19 @@ fn skipped_subcommand_includes_reasons_and_lines() -> Result<()> {
"skip reason should appear in skipped output",
);
assert!(stdout.contains("[forced failure]"));
Ok(())
}

#[test]
#[serial]
fn skipped_subcommand_emits_json() -> Result<()> {
let stdout = run_cargo_bdd(&["skipped", "--json"])?;
let entries: Vec<SkipReport> = serde_json::from_str(&stdout)?;
fn skipped_subcommand_emits_json() {
let stdout = run_cargo_bdd(&["skipped", "--json"]).expect("test setup should succeed");
let entries: Vec<SkipReport> =
serde_json::from_str(&stdout).expect("test setup should succeed");
let fixture_entry = entries
.iter()
.find(|entry| entry.scenario == "fixture skipped scenario")
.ok_or_else(|| eyre::eyre!("expected fixture skipped scenario entry"))?;
.ok_or_else(|| eyre::eyre!("expected fixture skipped scenario entry"))
.expect("test setup should succeed");
assert_eq!(fixture_entry.feature, "tests/features/diagnostics.fixture",);
assert_eq!(
fixture_entry.reason.as_deref(),
Expand All @@ -188,27 +186,28 @@ fn skipped_subcommand_emits_json() -> Result<()> {
vec!["@allow_skipped".to_string()],
"expected fixture skipped scenario tags to be preserved"
);
Ok(())
}

#[test]
#[serial]
fn steps_skipped_outputs_bypassed_definitions() -> Result<()> {
let stdout = run_cargo_bdd(&["steps", "--skipped"])?;
fn steps_skipped_outputs_bypassed_definitions() {
let stdout = run_cargo_bdd(&["steps", "--skipped"]).expect("test setup should succeed");
assert!(stdout.contains("fixture bypassed step"));
assert!(stdout.contains("fixture skip message"));
Ok(())
}

#[test]
#[serial]
fn steps_skipped_emits_json() -> Result<()> {
let stdout = run_cargo_bdd(&["steps", "--skipped", "--json"])?;
let entries: Vec<SkipReport> = serde_json::from_str(&stdout)?;
fn steps_skipped_emits_json() {
let stdout =
run_cargo_bdd(&["steps", "--skipped", "--json"]).expect("test setup should succeed");
let entries: Vec<SkipReport> =
serde_json::from_str(&stdout).expect("test setup should succeed");
let forced_entry = entries
.iter()
.find(|entry| entry.scenario == "fixture forced failure skip")
.ok_or_else(|| eyre::eyre!("expected forced failure skip entry"))?;
.ok_or_else(|| eyre::eyre!("expected forced failure skip entry"))
.expect("test setup should succeed");
assert_eq!(forced_entry.reason.as_deref(), Some("fixture forced skip"),);
assert!(
forced_entry.step.is_some(),
Expand All @@ -228,16 +227,14 @@ fn steps_skipped_emits_json() -> Result<()> {
step.line > 0,
"expected bypassed step to include a line number"
);
Ok(())
}

#[test]
#[serial]
fn steps_json_requires_skipped_flag() -> Result<()> {
let stderr = run_cargo_bdd_failure(&["steps", "--json"])?;
fn steps_json_requires_skipped_flag() {
let stderr = run_cargo_bdd_failure(&["steps", "--json"]).expect("test setup should succeed");
assert!(
stderr.contains("--json") && stderr.contains("--skipped"),
"error should mention --json requires --skipped: {stderr}"
);
Ok(())
}
28 changes: 0 additions & 28 deletions crates/rstest-bdd-harness/src/trybuild_staging/prop_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,6 @@ proptest! {
for index in 0..existing_depth {
existing = existing.join(format!("existing_{index}"));
}
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup and canonicalization after explicit setup"
)]
{
fs::create_dir_all(&existing).expect("create existing ancestor");
let mut expected = fs::canonicalize(&existing).expect("canonicalize ancestor");
Expand All @@ -142,10 +138,6 @@ proptest! {
let _ = fs::remove_dir_all(&root);
let src = root.join("src");
let destination = path_resolving_back_to_source(&root, "src", missing_depth);
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup and canonicalization after explicit setup"
)]
{
fs::create_dir_all(&src).expect("create src");
let canonical_src = fs::canonicalize(&src).expect("canonicalize src");
Expand All @@ -167,10 +159,6 @@ proptest! {
let _ = fs::remove_dir_all(&root);
let src = root.join("src");
let dst = path_resolving_back_to_source(&root, "src", missing_depth);
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup and err-kind extraction after explicit guards"
)]
{
fs::create_dir_all(&src).expect("create src");
fs::write(src.join("f.txt"), b"x").expect("write f.txt");
Expand Down Expand Up @@ -198,10 +186,6 @@ proptest! {
) {
let root = unique_root("top_level");
let _ = fs::remove_dir_all(&root);
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup failures abort the test"
)]
let (src, dst) = build_flat_source_with_symlink(
&root,
&file_names,
Expand All @@ -211,10 +195,6 @@ proptest! {
let _ = fs::remove_dir_all(&root);
prop_assert_eq!(
{
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup and err-kind extraction after explicit guards"
)]
result
.expect_err("expected error from copy_dir_tree property test")
.kind()
Expand All @@ -232,10 +212,6 @@ proptest! {
) {
let root = unique_root("nested");
let _ = fs::remove_dir_all(&root);
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup failures abort the test"
)]
let (src, dst) = build_nested_source_with_symlink(
&root,
depth,
Expand All @@ -245,10 +221,6 @@ proptest! {
let _ = fs::remove_dir_all(&root);
prop_assert_eq!(
{
#[expect(
clippy::expect_used,
reason = "property-test temp-dir setup and err-kind extraction after explicit guards"
)]
result
.expect_err("expected error from copy_dir_tree property test")
.kind()
Expand Down
Loading
Loading