Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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