From 688e70c3bb157e0bfc70235e9dd35d6074252a79 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 1 Oct 2026 14:16:22 +0300 Subject: [PATCH 1/4] test: move inline test modules into *_tests.rs files Extract each inline `#[cfg(test)] mod` into a sibling `_tests.rs` declared with `#[path]`, and record the rule in the repo guidance. Co-authored-by: Medulla --- AGENTS.md | 39 ++++++-- .../tinytools-agent/src/dialect/envelope.rs | 96 +------------------ .../src/dialect/envelope_tests.rs | 92 ++++++++++++++++++ crates/tinytools-std/src/filesystem/text.rs | 29 +----- .../src/filesystem/text_test_tests.rs | 25 +++++ 5 files changed, 154 insertions(+), 127 deletions(-) create mode 100644 crates/tinytools-agent/src/dialect/envelope_tests.rs create mode 100644 crates/tinytools-std/src/filesystem/text_test_tests.rs diff --git a/AGENTS.md b/AGENTS.md index e1a53ab..63b0a1b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,7 @@ crates/ ├── call/ # `ToolCallOptions`, `ToolTimeout` ├── context/ # `ToolRunContext` └── naming/ # rendering a call for a human - # each: mod.rs / types.rs / test.rs + # each: mod.rs / types.rs / mod_tests.rs docs/ ├── specs/ # behavior and architecture specifications ├── plans/ # test-first implementation plans @@ -76,15 +76,17 @@ workspace = true Each feature area belongs in a focused module directory under a crate's `src/`. A module root explains the module, wires its pieces together, and exposes the smallest useful API. Move substantial type definitions into `types.rs` and put -module-local unit tests in a dedicated `test.rs`, wired from the bottom of the +module-local unit tests in a sibling `_tests.rs`, wired from the bottom of the module root with: ```rust #[cfg(test)] -mod test; +#[path = "mod_tests.rs"] +mod tests; ``` -Do not accumulate inline `mod tests` blocks in implementation files, and do not +Do not write inline `mod tests` blocks in implementation files, do not name a test +file `test.rs`, `tests.rs` or `_test.rs`, and do not let a general-purpose `utils.rs` or `helpers.rs` grow — those are a symptom of a missing module. Prefer many small modules that each do one thing well over few broad ones. @@ -168,7 +170,7 @@ releases are reproducible. ## Testing -- Module-local unit tests live in `crates//src//test.rs` and may +- Module-local unit tests live in `crates//src//mod_tests.rs` and may touch private items. - Integration tests live in `crates//tests/` and exercise only the public API — they are the regression suite for the crate's contract. @@ -197,7 +199,7 @@ Write documentation for the reader who has never seen the code. - Every public item gets a rustdoc comment. `missing_docs` is a warning that CI treats as an error. -- Start every `mod.rs` and `test.rs` with a concise module-level `//!` +- Start every `mod.rs` and `*_tests.rs` with a concise module-level `//!` description. - Each crate's `src/lib.rs` carries its crate-level overview: what the crate does, the primary entry points, and a short runnable example. It should also @@ -287,3 +289,28 @@ For automated contributors specifically: credentials, and never paste them into a pull request or issue. 7. **Ask only when blocked.** Make routine judgment calls yourself; escalate only irreversible decisions or genuine forks with no clear default. + +## Tests live in `*_tests.rs` files + +- Unit tests are never inline. Do not write a `#[cfg(test)] mod tests { ... }` + block in a source file. Put the tests in a sibling `_tests.rs` + (`mod_tests.rs` beside a `mod.rs`, `lib_tests.rs` beside `lib.rs`) and declare + it at the bottom of the module: + + ```rust + #[cfg(test)] + #[path = "foo_tests.rs"] + mod tests; + ``` + +- The test file starts with `use super::*;` and carries no `#[cfg(test)]` of its + own. It is still a child module, so it reaches private items exactly as an + inline module did. +- Name test files `_tests.rs`; a second group for the same module is + `__tests.rs`. Never `test.rs`, `tests.rs` or `_test.rs`. +- Integration tests stay in the crate's `tests/` directory. +- OpenHuman's `scripts/externalize-inline-tests.mjs --write` moves + inline test modules out mechanically; without `--write` it only reports. +- Existing `test.rs` and `_test.rs` files predate this rule. Rename each + to `_tests.rs` (keep its `mod` name, add the `#[path]` attribute) the + next time you touch it. diff --git a/crates/tinytools-agent/src/dialect/envelope.rs b/crates/tinytools-agent/src/dialect/envelope.rs index 2f3a39f..14c89c5 100644 --- a/crates/tinytools-agent/src/dialect/envelope.rs +++ b/crates/tinytools-agent/src/dialect/envelope.rs @@ -179,97 +179,5 @@ pub fn join_image_parts(parts: &[ContentPart]) -> String { #[cfg(test)] #[allow(clippy::unwrap_used)] -mod tests { - use super::*; - - fn call(id: &str) -> NativeToolCall { - NativeToolCall { - id: id.into(), - name: "shell".into(), - arguments: r#"{"command":"ls"}"#.into(), - extra_content: None, - } - } - - #[test] - fn assistant_envelope_round_trips_byte_exact() { - let text = encode_assistant_envelope(Some("on it"), &[call("c1"), call("c2")], None); - let parsed = parse_canonical_assistant_envelope(&text).unwrap(); - assert_eq!(parsed.content, "on it"); - assert_eq!(parsed.tool_calls.len(), 2); - assert_eq!( - encode_assistant_envelope(Some(&parsed.content), &parsed.tool_calls, None), - text - ); - } - - #[test] - fn literal_envelope_shape_is_stable() { - // Compared as values: key order depends on whether a downstream build - // unifies serde_json's `preserve_order`, the shape does not. - let value = |text: String| serde_json::from_str::(&text).unwrap(); - assert_eq!( - value(encode_assistant_envelope(Some("hi"), &[call("c1")], None)), - serde_json::json!({ - "content": "hi", - "tool_calls": [{"id": "c1", "name": "shell", "arguments": "{\"command\":\"ls\"}"}] - }) - ); - assert_eq!( - value(encode_tool_envelope("c1", "ok")), - serde_json::json!({"tool_call_id": "c1", "content": "ok"}) - ); - } - - #[test] - fn non_canonical_assistant_envelopes_stay_opaque() { - for text in [ - encode_assistant_envelope(None, &[call("c1")], None), - encode_assistant_envelope(Some("x"), &[call("c1")], Some("because")), - r#"{"content":"x","tool_calls":[{"id":"c1","name":"n","arguments":"{}"}],"extra":1}"# - .to_string(), - "plain prose".to_string(), - r#"{"content":"x","tool_calls":[]}"#.to_string(), - ] { - assert!( - parse_canonical_assistant_envelope(&text).is_none(), - "{text}" - ); - } - } - - #[test] - fn tool_envelope_round_trips_and_rejects_extras() { - let text = encode_tool_envelope("c1", "result \"quoted\""); - assert_eq!( - parse_canonical_tool_envelope(&text), - Some(("c1".into(), "result \"quoted\"".into())) - ); - assert!( - parse_canonical_tool_envelope(r#"{"tool_call_id":"c1","content":"a","name":"n"}"#) - .is_none() - ); - assert!(parse_canonical_tool_envelope("bare").is_none()); - } - - #[test] - fn image_parts_split_and_join_exactly() { - for text in [ - "just text", - "[OH_IMAGE:data:image/png;base64,AAAA]", - "look [OH_IMAGE:data:image/png;base64,AAAA] and [OH_IMAGE:https://x/y.png]\ndone", - "dangling [OH_IMAGE:data:no-close", - "", - ] { - assert_eq!(join_image_parts(&split_image_parts(text)), text); - } - assert_eq!( - split_image_parts("a[OH_IMAGE:u]b"), - vec![ - ContentPart::Text("a".into()), - ContentPart::Image("u".into()), - ContentPart::Text("b".into()) - ] - ); - } -} +#[path = "envelope_tests.rs"] +mod tests; diff --git a/crates/tinytools-agent/src/dialect/envelope_tests.rs b/crates/tinytools-agent/src/dialect/envelope_tests.rs new file mode 100644 index 0000000..4056584 --- /dev/null +++ b/crates/tinytools-agent/src/dialect/envelope_tests.rs @@ -0,0 +1,92 @@ +use super::*; + +fn call(id: &str) -> NativeToolCall { + NativeToolCall { + id: id.into(), + name: "shell".into(), + arguments: r#"{"command":"ls"}"#.into(), + extra_content: None, + } +} + +#[test] +fn assistant_envelope_round_trips_byte_exact() { + let text = encode_assistant_envelope(Some("on it"), &[call("c1"), call("c2")], None); + let parsed = parse_canonical_assistant_envelope(&text).unwrap(); + assert_eq!(parsed.content, "on it"); + assert_eq!(parsed.tool_calls.len(), 2); + assert_eq!( + encode_assistant_envelope(Some(&parsed.content), &parsed.tool_calls, None), + text + ); +} + +#[test] +fn literal_envelope_shape_is_stable() { + // Compared as values: key order depends on whether a downstream build + // unifies serde_json's `preserve_order`, the shape does not. + let value = |text: String| serde_json::from_str::(&text).unwrap(); + assert_eq!( + value(encode_assistant_envelope(Some("hi"), &[call("c1")], None)), + serde_json::json!({ + "content": "hi", + "tool_calls": [{"id": "c1", "name": "shell", "arguments": "{\"command\":\"ls\"}"}] + }) + ); + assert_eq!( + value(encode_tool_envelope("c1", "ok")), + serde_json::json!({"tool_call_id": "c1", "content": "ok"}) + ); +} + +#[test] +fn non_canonical_assistant_envelopes_stay_opaque() { + for text in [ + encode_assistant_envelope(None, &[call("c1")], None), + encode_assistant_envelope(Some("x"), &[call("c1")], Some("because")), + r#"{"content":"x","tool_calls":[{"id":"c1","name":"n","arguments":"{}"}],"extra":1}"# + .to_string(), + "plain prose".to_string(), + r#"{"content":"x","tool_calls":[]}"#.to_string(), + ] { + assert!( + parse_canonical_assistant_envelope(&text).is_none(), + "{text}" + ); + } +} + +#[test] +fn tool_envelope_round_trips_and_rejects_extras() { + let text = encode_tool_envelope("c1", "result \"quoted\""); + assert_eq!( + parse_canonical_tool_envelope(&text), + Some(("c1".into(), "result \"quoted\"".into())) + ); + assert!( + parse_canonical_tool_envelope(r#"{"tool_call_id":"c1","content":"a","name":"n"}"#) + .is_none() + ); + assert!(parse_canonical_tool_envelope("bare").is_none()); +} + +#[test] +fn image_parts_split_and_join_exactly() { + for text in [ + "just text", + "[OH_IMAGE:data:image/png;base64,AAAA]", + "look [OH_IMAGE:data:image/png;base64,AAAA] and [OH_IMAGE:https://x/y.png]\ndone", + "dangling [OH_IMAGE:data:no-close", + "", + ] { + assert_eq!(join_image_parts(&split_image_parts(text)), text); + } + assert_eq!( + split_image_parts("a[OH_IMAGE:u]b"), + vec![ + ContentPart::Text("a".into()), + ContentPart::Image("u".into()), + ContentPart::Text("b".into()) + ] + ); +} diff --git a/crates/tinytools-std/src/filesystem/text.rs b/crates/tinytools-std/src/filesystem/text.rs index b9e8082..c8a84c7 100644 --- a/crates/tinytools-std/src/filesystem/text.rs +++ b/crates/tinytools-std/src/filesystem/text.rs @@ -20,30 +20,5 @@ pub(super) fn truncate_at_byte_boundary(s: &str, max_bytes: usize) -> String { } #[cfg(test)] -mod test { - use super::truncate_at_byte_boundary; - - #[test] - fn short_strings_pass_through() { - assert_eq!(truncate_at_byte_boundary("abc", 3), "abc"); - } - - #[test] - fn long_strings_end_in_an_ellipsis_within_budget() { - let out = truncate_at_byte_boundary("abcdefghij", 6); - assert_eq!(out, "abc…"); - assert!(out.len() <= 6); - } - - #[test] - fn never_splits_a_multibyte_character() { - // 'é' is two bytes; a budget landing mid-character backs up. - let out = truncate_at_byte_boundary("ééééé", 6); - assert_eq!(out, "é…"); - } - - #[test] - fn a_budget_smaller_than_the_ellipsis_yields_nothing() { - assert_eq!(truncate_at_byte_boundary("abcdef", 2), ""); - } -} +#[path = "text_test_tests.rs"] +mod test; diff --git a/crates/tinytools-std/src/filesystem/text_test_tests.rs b/crates/tinytools-std/src/filesystem/text_test_tests.rs new file mode 100644 index 0000000..abd85f2 --- /dev/null +++ b/crates/tinytools-std/src/filesystem/text_test_tests.rs @@ -0,0 +1,25 @@ +use super::truncate_at_byte_boundary; + +#[test] +fn short_strings_pass_through() { + assert_eq!(truncate_at_byte_boundary("abc", 3), "abc"); +} + +#[test] +fn long_strings_end_in_an_ellipsis_within_budget() { + let out = truncate_at_byte_boundary("abcdefghij", 6); + assert_eq!(out, "abc…"); + assert!(out.len() <= 6); +} + +#[test] +fn never_splits_a_multibyte_character() { + // 'é' is two bytes; a budget landing mid-character backs up. + let out = truncate_at_byte_boundary("ééééé", 6); + assert_eq!(out, "é…"); +} + +#[test] +fn a_budget_smaller_than_the_ellipsis_yields_nothing() { + assert_eq!(truncate_at_byte_boundary("abcdef", 2), ""); +} From c20501085bd5f240e417d0c90dbd3739f7f753c5 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 1 Oct 2026 18:32:13 +0300 Subject: [PATCH 2/4] test: replace `is_empty` assertions with explicit empty-slice comparisons Replace `assert!(x.is_empty())` with `assert_eq!(x, [] as [tinytools::RankHit; 0])` in four test assertions to make the expected type explicit and improve failure messages by showing the actual contents when the assertion fails. Auto-committed-on: dragonfly --- crates/tinytools-jev/src/test.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/tinytools-jev/src/test.rs b/crates/tinytools-jev/src/test.rs index b533d30..f06c533 100644 --- a/crates/tinytools-jev/src/test.rs +++ b/crates/tinytools-jev/src/test.rs @@ -80,13 +80,13 @@ async fn suppresses_hits_when_none_wins_or_no_tool_is_needed() { .rank("question", &RankContext::empty(), &candidates(), 2) .await .unwrap_or_default(); - assert!(none_hits.is_empty()); + assert_eq!(none_hits, [] as [tinytools::RankHit; 0]); let (no_tool_ranker, _) = ranker([("slack", 0.8), ("gmail", 0.1), ("none", 0.1)], Some(0.2)); let no_tool_hits = no_tool_ranker .rank("question", &RankContext::empty(), &candidates(), 2) .await .unwrap_or_default(); - assert!(no_tool_hits.is_empty()); + assert_eq!(no_tool_hits, [] as [tinytools::RankHit; 0]); } #[test] @@ -116,12 +116,12 @@ async fn validates_inputs_and_short_circuits_empty_work() { .rank("request", &RankContext::empty(), &[], 2) .await .unwrap_or_default(); - assert!(empty.is_empty()); + assert_eq!(empty, [] as [tinytools::RankHit; 0]); let zero = ranker .rank("request", &RankContext::empty(), &candidates(), 0) .await .unwrap_or_default(); - assert!(zero.is_empty()); + assert_eq!(zero, [] as [tinytools::RankHit; 0]); assert!(matches!( ranker .rank(" ", &RankContext::empty(), &candidates(), 2) From 67e4bfabda6a16c508cbad37ec49ea7f6fbeacc2 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 1 Oct 2026 18:32:37 +0300 Subject: [PATCH 3/4] chore(test): replace `.is_empty()` with `.len()` comparisons for clippy compliance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all uses of `.is_empty()` in test assertions with explicit `.len()` comparisons to satisfy a clippy lint that flags `.is_empty()` on collections whose element type is not guaranteed to implement `PartialEq`. This change touches 28 test files across the workspace and is purely mechanical — no test logic or behaviour is altered. Auto-committed-on: dragonfly --- crates/tinytools-agent/src/dialect/test.rs | 6 ++-- .../src/parse/protected/test.rs | 4 +-- .../src/parse/test/bare_json.rs | 6 ++-- .../tinytools-agent/src/parse/test/element.rs | 4 +-- .../tinytools-agent/src/parse/test/engine.rs | 8 ++--- .../src/parse/test/function_call.rs | 12 ++++---- crates/tinytools-agent/src/parse/test/glm.rs | 4 +-- .../src/parse/test/harmony_mistral.rs | 12 ++++---- .../src/parse/test/invoke_xml.rs | 4 +-- .../src/parse/test/regressions.rs | 8 ++--- .../src/parse/test/sentinel.rs | 8 ++--- .../tinytools-agent/src/parse/test/tagged.rs | 30 +++++++++---------- crates/tinytools-agent/src/stream/test.rs | 10 +++---- .../tinytools-std/src/file_state/test/ops.rs | 7 +++-- .../src/filesystem/csv_export/extra_test.rs | 2 +- .../src/filesystem/git_operations/ops_test.rs | 4 +-- .../src/filesystem/git_operations/test.rs | 2 +- .../src/filesystem/image_info/test.rs | 2 +- .../src/filesystem/read_diff/test.rs | 2 +- .../src/filesystem/run_linter/test.rs | 2 +- .../src/filesystem/update_memory_md/test.rs | 2 +- .../src/filesystem/workspace_state/test.rs | 2 +- .../src/network/pushover_test.rs | 2 +- crates/tinytools-std/src/url_guard/test.rs | 2 +- crates/tinytools/src/rank/test.rs | 6 ++-- crates/tinytools/src/result/test.rs | 6 ++-- crates/tinytools/src/tool/test.rs | 2 +- crates/tinytools/src/workspace/test.rs | 4 +-- 28 files changed, 82 insertions(+), 81 deletions(-) diff --git a/crates/tinytools-agent/src/dialect/test.rs b/crates/tinytools-agent/src/dialect/test.rs index 500dc6a..4df4bcb 100644 --- a/crates/tinytools-agent/src/dialect/test.rs +++ b/crates/tinytools-agent/src/dialect/test.rs @@ -107,7 +107,7 @@ fn native_dialect_covers_non_object_values_fallback_and_protocol_metadata() { let (text, calls) = dialect.parse_response(&response("narrative only")); assert_eq!(text, "narrative only"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); let (text, calls) = dialect.parse_response(&response( "narrative {\"name\":\"lookup\",\"arguments\":{}}", )); @@ -682,7 +682,7 @@ fn native_replay_drops_a_cycle_whose_results_do_not_cover_every_call() { // Adjacency is not enough: the provider rejects partial coverage the same // way it rejects no coverage, so both halves go. - assert!(NativeDialect.to_provider_messages(&history).is_empty()); + assert_eq!(NativeDialect.to_provider_messages(&history).len(), 0); } #[test] @@ -692,7 +692,7 @@ fn native_replay_drops_orphan_results() { "done".to_string(), )])]; - assert!(NativeDialect.to_provider_messages(&history).is_empty()); + assert_eq!(NativeDialect.to_provider_messages(&history).len(), 0); } #[test] diff --git a/crates/tinytools-agent/src/parse/protected/test.rs b/crates/tinytools-agent/src/parse/protected/test.rs index e7ff97a..f5d26f7 100644 --- a/crates/tinytools-agent/src/parse/protected/test.rs +++ b/crates/tinytools-agent/src/parse/protected/test.rs @@ -9,7 +9,7 @@ fn a_fence_indented_more_than_three_spaces_is_not_a_fence() { // indented code block, not a fence, so it must not open a protected // range even though it carries a language tag. let text = " ```rust\nfn main() {}\n ```\n"; - assert!(fence_ranges(text).is_empty()); + assert_eq!(fence_ranges(text).len(), 0); } #[test] @@ -17,7 +17,7 @@ fn a_two_backtick_run_is_not_a_fence() { // A fence needs at least three backticks (or tildes); shorter runs are // inline code spans, not fence delimiters. let text = "``json\n{\"a\":1}\n``\n"; - assert!(fence_ranges(text).is_empty()); + assert_eq!(fence_ranges(text).len(), 0); } #[test] diff --git a/crates/tinytools-agent/src/parse/test/bare_json.rs b/crates/tinytools-agent/src/parse/test/bare_json.rs index 234c08a..f0bbc40 100644 --- a/crates/tinytools-agent/src/parse/test/bare_json.rs +++ b/crates/tinytools-agent/src/parse/test/bare_json.rs @@ -19,7 +19,7 @@ fn a_wire_message_with_tool_calls_array_parses() { #[test] fn a_bare_object_with_canonical_arguments_parses() { let (text, calls) = parse(r#"{"name":"echo","arguments":{"value":"hi"}}"#); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); } @@ -54,7 +54,7 @@ fn llama_bare_object_with_mismatched_quotes_is_repaired() { outcome.calls[0].arguments, serde_json::json!({ "city": "Paris" }) ); - assert!(outcome.text.is_empty()); + assert_eq!(outcome.text.len(), 0); } #[test] @@ -104,7 +104,7 @@ fn parse_options_default_matches_new_and_allows_bare_json() { fn bare_json_can_be_disabled() { let options = ParseOptions::new().without_bare_json(); let outcome = crate::parse::parse_text(r#"{"name":"echo","arguments":{}}"#, &options); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); } #[test] diff --git a/crates/tinytools-agent/src/parse/test/element.rs b/crates/tinytools-agent/src/parse/test/element.rs index 3e20005..c5f8df1 100644 --- a/crates/tinytools-agent/src/parse/test/element.rs +++ b/crates/tinytools-agent/src/parse/test/element.rs @@ -93,7 +93,7 @@ fn an_element_without_a_registry_is_left_alone() { fn ordinary_markup_is_not_a_call() { let outcome = parse("

x

"); assert!(outcome.calls.is_empty(), "{:?}", outcome.calls); - assert!(outcome.diagnostics.is_empty()); + assert_eq!(outcome.diagnostics.len(), 0); assert_eq!(outcome.text, "

x

"); } @@ -102,7 +102,7 @@ fn prose_inside_a_known_tool_tag_is_left_alone() { let text = "remember to ship it"; let outcome = parse(text); assert!(outcome.calls.is_empty(), "{:?}", outcome.calls); - assert!(outcome.diagnostics.is_empty()); + assert_eq!(outcome.diagnostics.len(), 0); assert_eq!(outcome.text, text); } diff --git a/crates/tinytools-agent/src/parse/test/engine.rs b/crates/tinytools-agent/src/parse/test/engine.rs index 930c629..9848e18 100644 --- a/crates/tinytools-agent/src/parse/test/engine.rs +++ b/crates/tinytools-agent/src/parse/test/engine.rs @@ -39,8 +39,8 @@ fn fence_ranges_cover_languages_and_unclosed_fences() { assert_eq!(ranges.len(), 2); assert_eq!(&text[ranges[0].clone()], "```rust\nx\n```"); assert_eq!(ranges[1].end, text.len()); - assert!(fence_ranges("```\nplain\n```").is_empty()); - assert!(fence_ranges("```tool_call\n{}\n```").is_empty()); + assert_eq!(fence_ranges("```\nplain\n```").len(), 0); + assert_eq!(fence_ranges("```tool_call\n{}\n```").len(), 0); } // ── A call tag on the fence line itself ──────────────────────────────────── @@ -250,8 +250,8 @@ fn json_scanners_cover_common_edge_cases() { values, vec![serde_json::json!({ "a": 1 }), serde_json::json!([1, 2])] ); - assert!(extract_json_values("").is_empty()); - assert!(extract_json_values("{not json} [still bad]").is_empty()); + assert_eq!(extract_json_values("").len(), 0); + assert_eq!(extract_json_values("{not json} [still bad]").len(), 0); assert_eq!( find_json_end(" {\"a\":\"}\"}tail"), diff --git a/crates/tinytools-agent/src/parse/test/function_call.rs b/crates/tinytools-agent/src/parse/test/function_call.rs index fd8d176..9bdc7cf 100644 --- a/crates/tinytools-agent/src/parse/test/function_call.rs +++ b/crates/tinytools-agent/src/parse/test/function_call.rs @@ -37,7 +37,7 @@ fn embedded_function_call_marker_in_a_longer_identifier_stays_visible() { let (text, calls) = parse(input); assert_eq!(text, input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -52,7 +52,7 @@ fn unusable_call_fields_fall_back_to_a_valid_name() { let (text, calls) = parse(&input); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); assert_eq!(calls[0].name, "echo"); assert_eq!(calls[0].arguments, serde_json::json!({"value": "ok"})); @@ -66,7 +66,7 @@ fn an_unrelated_function_call_object_stays_visible() { let (text, calls) = parse(input); assert_eq!(text, input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -76,7 +76,7 @@ fn a_fenced_function_call_example_is_not_executed() { let (text, calls) = parse(input); assert_eq!(text, input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -107,7 +107,7 @@ fn an_unterminated_marked_object_stays_visible_in_batch_mode() { let (text, calls) = parse(input); assert_eq!(text, input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -117,5 +117,5 @@ fn an_invalid_marked_json_object_stays_visible() { let (text, calls) = parse(input); assert_eq!(text, input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } diff --git a/crates/tinytools-agent/src/parse/test/glm.rs b/crates/tinytools-agent/src/parse/test/glm.rs index cca092a..1eb6cd4 100644 --- a/crates/tinytools-agent/src/parse/test/glm.rs +++ b/crates/tinytools-agent/src/parse/test/glm.rs @@ -6,7 +6,7 @@ use crate::parse::{build_curl_command, map_glm_tool_alias, parse_glm_style_tool_ #[test] fn glm_lines_parse_when_nothing_else_matched() { let (text, calls) = parse("shell/command>ls -la"); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); assert_eq!(calls[0].name, "shell"); } @@ -54,5 +54,5 @@ fn glm_helpers_parse_aliases_urls_and_commands() { assert_eq!(calls.len(), 2); assert_eq!(calls[0].0, "shell"); assert_eq!(calls[1].0, "http_request"); - assert!(parse_glm_style_tool_calls("https://rust-lang.org").is_empty()); + assert_eq!(parse_glm_style_tool_calls("https://rust-lang.org").len(), 0); } diff --git a/crates/tinytools-agent/src/parse/test/harmony_mistral.rs b/crates/tinytools-agent/src/parse/test/harmony_mistral.rs index ada3872..fb8622e 100644 --- a/crates/tinytools-agent/src/parse/test/harmony_mistral.rs +++ b/crates/tinytools-agent/src/parse/test/harmony_mistral.rs @@ -18,7 +18,7 @@ fn harmony_commentary_call_parses() { fn harmony_channel_without_target_is_not_a_call() { let response = "<|channel|>analysis<|message|>thinking hard<|end|>final answer"; let (text, calls) = parse(response); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(text, response); } @@ -49,7 +49,7 @@ fn harmony_channel_with_target_but_no_message_is_not_a_call_in_batch_mode() { // guessed at. let response = "<|channel|>commentary to=functions.read still thinking"; let (text, calls) = parse(response); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(text, response); } @@ -97,7 +97,7 @@ fn mistral_v3_array_of_non_call_objects_yields_no_calls() { // is reported as malformed rather than silently dropped. let response = "[TOOL_CALLS] [{\"foo\":1}]"; let outcome = parse_known(response, &[]); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); assert!( outcome .diagnostics @@ -110,7 +110,7 @@ fn mistral_v3_array_of_non_call_objects_yields_no_calls() { fn mistral_v11_name_with_invalid_characters_is_not_a_call() { let response = "[TOOL_CALLS]get-weather[ARGS]{\"city\":\"Paris\"}"; let outcome = parse_known(response, &[]); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); assert!( outcome .diagnostics @@ -123,7 +123,7 @@ fn mistral_v11_name_with_invalid_characters_is_not_a_call() { fn mistral_v11_args_with_unparseable_json_is_not_a_call() { let response = "[TOOL_CALLS]get_weather[ARGS]not json at all"; let outcome = parse_known(response, &[]); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); assert!( outcome .diagnostics @@ -145,7 +145,7 @@ fn mistral_v11_non_object_arguments_are_recovered_into_an_object() { fn mistral_marker_with_no_parseable_call_is_malformed_in_batch_mode() { let response = "[TOOL_CALLS] this trails off with no call shape"; let outcome = parse_known(response, &[]); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); assert!( outcome .diagnostics diff --git a/crates/tinytools-agent/src/parse/test/invoke_xml.rs b/crates/tinytools-agent/src/parse/test/invoke_xml.rs index 037f918..ceda876 100644 --- a/crates/tinytools-agent/src/parse/test/invoke_xml.rs +++ b/crates/tinytools-agent/src/parse/test/invoke_xml.rs @@ -31,7 +31,7 @@ fn unclosed_invoke_is_kept_as_text() { text, "lead 1" ); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -47,7 +47,7 @@ fn anthropic_function_calls_wrapper_is_stripped() { fn namespaced_invoke_from_muse_spark_parses() { let source = "echo hi"; let (text, calls) = parse(source); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); assert_eq!(calls[0].name, "default.terminal"); assert_eq!(calls[0].arguments["command"], "echo hi"); diff --git a/crates/tinytools-agent/src/parse/test/regressions.rs b/crates/tinytools-agent/src/parse/test/regressions.rs index 799f1fb..e8d0dda 100644 --- a/crates/tinytools-agent/src/parse/test/regressions.rs +++ b/crates/tinytools-agent/src/parse/test/regressions.rs @@ -29,7 +29,7 @@ fn cross_alias_closing_tags_are_recovered() { let response = "\n{\"name\": \"shell\", \"arguments\": {\"command\": \"date\"}}\n"; let (text, calls) = parse(response); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); assert_eq!(calls[0].name, "shell"); } @@ -49,7 +49,7 @@ fn an_empty_tool_result_block_is_not_a_call() { let response = "I'll run that command.\n\n\n\nDone."; let (text, calls) = parse(response); assert!(text.contains("Done.")); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -57,7 +57,7 @@ fn an_empty_tool_calls_array_is_returned_as_text() { let response = r#"{"content": "Hello", "tool_calls": []}"#; let (text, calls) = parse(response); assert!(text.contains("Hello")); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -104,7 +104,7 @@ fn invoke_attribute_form_scalar_policy_and_empty_names() { fn invoke_without_a_name_attribute_is_not_a_call() { let input = "\nhi\n"; let (_text, calls) = parse(input); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] diff --git a/crates/tinytools-agent/src/parse/test/sentinel.rs b/crates/tinytools-agent/src/parse/test/sentinel.rs index c7b58fb..a80b816 100644 --- a/crates/tinytools-agent/src/parse/test/sentinel.rs +++ b/crates/tinytools-agent/src/parse/test/sentinel.rs @@ -59,7 +59,7 @@ fn kimi_k2_parallel_calls_parse_in_order() { fn unterminated_sentinel_block_is_kept_as_text() { let text = "start <|tool▁call▁begin|>{\"name\":\"a\""; let (cleaned, calls) = parse(text); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(cleaned, text); } @@ -67,7 +67,7 @@ fn unterminated_sentinel_block_is_kept_as_text() { fn a_sentinel_block_that_decodes_to_nothing_is_malformed() { let response = "<|tool_call_begin|>garbage that is not json<|tool_call_end|>"; let outcome = parse_known(response, &[]); - assert!(outcome.calls.is_empty()); + assert_eq!(outcome.calls.len(), 0); assert!( outcome .diagnostics @@ -80,7 +80,7 @@ fn a_sentinel_block_that_decodes_to_nothing_is_malformed() { fn kimi_argument_begin_with_no_name_before_it_is_not_a_call() { let response = "<|tool_call_begin|><|tool_call_argument_begin|>{}<|tool_call_end|>"; let (_, calls) = parse(response); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -118,5 +118,5 @@ fn deepseek_function_prefixed_name_with_no_newline_still_parses() { fn deepseek_sep_with_an_empty_name_is_not_a_call() { let response = "<|tool_call_begin|><|tool_sep|>{}<|tool_call_end|>"; let (_, calls) = parse(response); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } diff --git a/crates/tinytools-agent/src/parse/test/tagged.rs b/crates/tinytools-agent/src/parse/test/tagged.rs index 2a8372f..d570166 100644 --- a/crates/tinytools-agent/src/parse/test/tagged.rs +++ b/crates/tinytools-agent/src/parse/test/tagged.rs @@ -37,7 +37,7 @@ fn missing_arguments_default_to_empty_object() { fn spelling_variants_and_bare_invoke_literal() { let (text, calls) = parse("{\"name\":\"echo\",\"arguments\":{\"value\":\"three\"}}"); - assert!(text.is_empty()); + assert_eq!(text.len(), 0); assert_eq!(calls.len(), 1); let (_, calls) = parse("{\"name\":\"a\",\"arguments\":{}}"); @@ -52,7 +52,7 @@ fn attribute_form_and_pipe_variant_open_a_block() { parse(r#"{"name":"foo","arguments":{"a":1}}"#); assert_eq!(calls.len(), 1); assert_eq!(calls[0].name, "foo"); - assert!(cleaned.is_empty()); + assert_eq!(cleaned.len(), 0); let (_, calls) = parse(r#"{"name":"bar","arguments":{}}"#); assert_eq!(calls.len(), 1); @@ -62,7 +62,7 @@ fn attribute_form_and_pipe_variant_open_a_block() { #[test] fn plural_tool_calls_tag_is_not_an_opener() { let (cleaned, calls) = parse("the key holds them"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(cleaned, "the key holds them"); } @@ -70,7 +70,7 @@ fn plural_tool_calls_tag_is_not_an_opener() { fn malformed_body_is_dropped_without_dispatching() { let (text, calls) = parse("before not-json after"); assert_eq!(text, "before\nafter"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -121,24 +121,24 @@ fn unclosed_tag_recovery_preserves_unrelated_markup_after_the_json() { fn unclosed_tag_without_json_is_kept_as_text() { let (text, calls) = parse("before not-json"); assert_eq!(text, "before not-json"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); let (cleaned, calls) = parse("text {\"name\":\"x\""); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(cleaned, "text {\"name\":\"x\""); } #[test] fn prose_mention_without_closing_angle_is_not_a_tag() { let (cleaned, calls) = parse("wrap it in echo(value=\"hello\")"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -408,7 +408,7 @@ fn a_code_call_wrapped_in_a_python_fence_inside_the_tag_is_unwrapped() { fn a_top_level_python_fence_is_an_example_not_a_call() { let response = "Like this:\n```python\necho(value=\"hi\")\n```\n"; let (text, calls) = parse_tool_calls_with_pformat(response, &echo_registry()); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert!(text.contains("echo(value=\"hi\")")); } @@ -443,14 +443,14 @@ fn code_pformat_json_and_glm_siblings_all_survive() { fn a_prose_body_mentioning_a_call_is_malformed_not_a_call() { let response = "I will call echo(value=\"hi\") now"; let (_, calls) = parse_tool_calls_with_pformat(response, &echo_registry()); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] fn a_code_call_to_an_unknown_tool_is_not_a_call() { let response = "rm_rf(path=\"/\")"; let (_, calls) = parse_tool_calls_with_pformat(response, &echo_registry()); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } // ── Doubled tags ──────────────────────────────────────────────────────────── @@ -506,7 +506,7 @@ fn an_orphaned_closer_with_no_opener_anywhere_is_removed_not_shown() { text, "Let me check the details on the top contenders to find the best one for you.\nHere are the results." ); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); } #[test] @@ -551,7 +551,7 @@ fn a_bare_trailing_opener_is_dropped_not_shown() { // An abandoned block at the end of a reply carries no call and no // information; showing `` to the user is never right. let (text, calls) = parse("Let me fetch a few sites directly.\n\n\n"); - assert!(calls.is_empty()); + assert_eq!(calls.len(), 0); assert_eq!(text, "Let me fetch a few sites directly."); // A block with real (if unparseable) content is still kept as text. let (text, _) = parse("before not-json"); diff --git a/crates/tinytools-agent/src/stream/test.rs b/crates/tinytools-agent/src/stream/test.rs index 69b7b4e..2c14664 100644 --- a/crates/tinytools-agent/src/stream/test.rs +++ b/crates/tinytools-agent/src/stream/test.rs @@ -68,7 +68,7 @@ fn a_function_call_marker_with_no_body_is_held_until_the_next_fragment() { let mut s = StreamScrubber::new(); let first = s.feed("answer: function_call: "); assert_eq!(first.text, "answer: "); - assert!(first.calls.is_empty()); + assert_eq!(first.calls.len(), 0); let second = s.feed(r#"{"call":"echo","arguments":{}} done"#); assert_eq!(second.text, " done"); @@ -137,7 +137,7 @@ fn a_whitespace_prefixed_dsml_opener_split_before_the_marker_is_held() { first.text, "", "a possible whitespace-prefixed DSML opener must be held" ); - assert!(first.calls.is_empty()); + assert_eq!(first.calls.len(), 0); let second = s.feed("| DSML | invoke name=\"read\">{\"path\":\"a\"}"); assert_eq!(second.calls.len(), 1); @@ -182,7 +182,7 @@ fn a_fenced_example_split_across_fragments_never_leaks_a_call() { first.text, "example:\n", "the open fence must be held, not drained" ); - assert!(first.calls.is_empty()); + assert_eq!(first.calls.len(), 0); let second = s.feed("echo {\"name\":\"x\",\"arguments\":{}}\n```\nafter"); @@ -207,7 +207,7 @@ fn a_namespaced_invoke_opener_split_before_its_closing_bracket_is_held() { first.text, "", "an unterminated namespaced opener must be held" ); - assert!(first.calls.is_empty()); + assert_eq!(first.calls.len(), 0); let second = s.feed(">a"); assert_eq!(second.calls.len(), 1); @@ -226,7 +226,7 @@ fn a_sentinel_split_on_an_unlisted_bar_underscore_combination_is_held() { first.text, "", "an unlisted bar/separator split must be held" ); - assert!(first.calls.is_empty()); + assert_eq!(first.calls.len(), 0); let second = s.feed("call_begin|>get_weather<|tool_sep|>{\"city\":\"Paris\"}<|tool_call_end|>"); diff --git a/crates/tinytools-std/src/file_state/test/ops.rs b/crates/tinytools-std/src/file_state/test/ops.rs index 6d43751..294a4e2 100644 --- a/crates/tinytools-std/src/file_state/test/ops.rs +++ b/crates/tinytools-std/src/file_state/test/ops.rs @@ -62,7 +62,7 @@ fn own_write_does_not_trigger_staleness() { std::thread::sleep(Duration::from_millis(5)); coord.record_write("agent-a", path.clone()); let stale = coord.stale_reads_for_parent("agent-a"); - assert!(stale.is_empty()); + assert_eq!(stale.len(), 0); assert_eq!(coord.check_stale_read("agent-a", &path), None); } @@ -104,10 +104,11 @@ fn parent_stale_files_detects_child_writes() { coord.parent_stale_files("parent", &["child-1".to_string()]), vec![path] ); - assert!( + assert_eq!( coord .parent_stale_files("parent", &["someone-else".to_string()]) - .is_empty() + .len(), + 0 ); } diff --git a/crates/tinytools-std/src/filesystem/csv_export/extra_test.rs b/crates/tinytools-std/src/filesystem/csv_export/extra_test.rs index 7dcc47c..664f61c 100644 --- a/crates/tinytools-std/src/filesystem/csv_export/extra_test.rs +++ b/crates/tinytools-std/src/filesystem/csv_export/extra_test.rs @@ -143,7 +143,7 @@ fn cells_and_columns_cover_bools_and_non_object_rows() { assert_eq!(value_to_cell(&json!(true)), "true"); assert_eq!(value_to_cell(&json!(null)), ""); assert_eq!(value_to_cell(&json!([1, 2])), "[1,2]"); - assert!(resolve_columns(&[json!(1), json!(2)], None).is_empty()); + assert_eq!(resolve_columns(&[json!(1), json!(2)], None).len(), 0); } #[tokio::test] diff --git a/crates/tinytools-std/src/filesystem/git_operations/ops_test.rs b/crates/tinytools-std/src/filesystem/git_operations/ops_test.rs index dd24399..438a6a3 100644 --- a/crates/tinytools-std/src/filesystem/git_operations/ops_test.rs +++ b/crates/tinytools-std/src/filesystem/git_operations/ops_test.rs @@ -59,8 +59,8 @@ async fn status_parses_staged_unstaged_and_untracked() { let result = run(&tool, json!({"operation": "status"})).await; assert!(!result.is_error, "{}", result.output()); let parsed: serde_json::Value = serde_json::from_str(&result.output()).unwrap(); - assert!(!parsed["staged"].as_array().unwrap().is_empty()); - assert!(!parsed["unstaged"].as_array().unwrap().is_empty()); + assert_ne!(parsed["staged"].as_array().unwrap().len(), 0); + assert_ne!(parsed["unstaged"].as_array().unwrap().len(), 0); assert!( parsed["untracked"] .as_array() diff --git a/crates/tinytools-std/src/filesystem/git_operations/test.rs b/crates/tinytools-std/src/filesystem/git_operations/test.rs index 5b836b9..6aa8b90 100644 --- a/crates/tinytools-std/src/filesystem/git_operations/test.rs +++ b/crates/tinytools-std/src/filesystem/git_operations/test.rs @@ -324,7 +324,7 @@ fn tool_name_and_description() { let tmp = TempDir::new().unwrap(); let tool = test_tool(tmp.path()); assert_eq!(tool.name(), "git_operations"); - assert!(!tool.description().is_empty()); + assert_ne!(tool.description().len(), 0); assert!(tool.description().contains("Git")); } diff --git a/crates/tinytools-std/src/filesystem/image_info/test.rs b/crates/tinytools-std/src/filesystem/image_info/test.rs index 12900fa..ff5200b 100644 --- a/crates/tinytools-std/src/filesystem/image_info/test.rs +++ b/crates/tinytools-std/src/filesystem/image_info/test.rs @@ -16,7 +16,7 @@ fn image_info_tool_name() { #[test] fn image_info_tool_description() { let tool = ImageInfoTool::new(test_security()); - assert!(!tool.description().is_empty()); + assert_ne!(tool.description().len(), 0); assert!(tool.description().contains("image")); } diff --git a/crates/tinytools-std/src/filesystem/read_diff/test.rs b/crates/tinytools-std/src/filesystem/read_diff/test.rs index 23b3ee8..ab347da 100644 --- a/crates/tinytools-std/src/filesystem/read_diff/test.rs +++ b/crates/tinytools-std/src/filesystem/read_diff/test.rs @@ -17,7 +17,7 @@ fn name_is_correct() { #[test] fn description_is_non_empty() { let tmp = TempDir::new().unwrap(); - assert!(!make_tool(&tmp).description().is_empty()); + assert_ne!(make_tool(&tmp).description().len(), 0); } #[test] diff --git a/crates/tinytools-std/src/filesystem/run_linter/test.rs b/crates/tinytools-std/src/filesystem/run_linter/test.rs index c1d9619..5a05573 100644 --- a/crates/tinytools-std/src/filesystem/run_linter/test.rs +++ b/crates/tinytools-std/src/filesystem/run_linter/test.rs @@ -17,7 +17,7 @@ fn name_is_correct() { #[test] fn description_is_non_empty() { let tmp = TempDir::new().unwrap(); - assert!(!make_tool(&tmp).description().is_empty()); + assert_ne!(make_tool(&tmp).description().len(), 0); } #[test] diff --git a/crates/tinytools-std/src/filesystem/update_memory_md/test.rs b/crates/tinytools-std/src/filesystem/update_memory_md/test.rs index 526446d..ca2cfb1 100644 --- a/crates/tinytools-std/src/filesystem/update_memory_md/test.rs +++ b/crates/tinytools-std/src/filesystem/update_memory_md/test.rs @@ -189,7 +189,7 @@ fn tool_name_and_description() { let dir = tempfile::tempdir().unwrap(); let tool = make_tool(dir.path()); assert_eq!(tool.name(), "update_memory_md"); - assert!(!tool.description().is_empty()); + assert_ne!(tool.description().len(), 0); } #[test] diff --git a/crates/tinytools-std/src/filesystem/workspace_state/test.rs b/crates/tinytools-std/src/filesystem/workspace_state/test.rs index 077448a..a815bf7 100644 --- a/crates/tinytools-std/src/filesystem/workspace_state/test.rs +++ b/crates/tinytools-std/src/filesystem/workspace_state/test.rs @@ -16,7 +16,7 @@ fn name_is_correct() { #[test] fn description_is_non_empty() { let tmp = TempDir::new().unwrap(); - assert!(!make_tool(&tmp).description().is_empty()); + assert_ne!(make_tool(&tmp).description().len(), 0); } #[test] diff --git a/crates/tinytools-std/src/network/pushover_test.rs b/crates/tinytools-std/src/network/pushover_test.rs index 29a1764..66c4e6b 100644 --- a/crates/tinytools-std/src/network/pushover_test.rs +++ b/crates/tinytools-std/src/network/pushover_test.rs @@ -22,7 +22,7 @@ fn pushover_tool_description() { test_security(AutonomyLevel::Full, 100), PathBuf::from("/tmp"), ); - assert!(!tool.description().is_empty()); + assert_ne!(tool.description().len(), 0); } #[test] diff --git a/crates/tinytools-std/src/url_guard/test.rs b/crates/tinytools-std/src/url_guard/test.rs index 1a16101..68ecea5 100644 --- a/crates/tinytools-std/src/url_guard/test.rs +++ b/crates/tinytools-std/src/url_guard/test.rs @@ -189,7 +189,7 @@ fn normalize_all_invalid_entries_stays_fail_closed() { #[test] fn normalize_empty_input_stays_empty_for_open_mode() { // Explicitly empty input should return empty (open mode is intentional). - assert!(normalize_allowed_domains(vec![]).is_empty()); + assert_eq!(normalize_allowed_domains(vec![]).len(), 0); } #[tokio::test] diff --git a/crates/tinytools/src/rank/test.rs b/crates/tinytools/src/rank/test.rs index 25ad1cf..d55b454 100644 --- a/crates/tinytools/src/rank/test.rs +++ b/crates/tinytools/src/rank/test.rs @@ -45,14 +45,14 @@ fn index_ranks_by_description_and_breaks_ties_by_key() { fn index_on_one_document_corpus_still_finds_it() { let index = Bm25Index::build([("only", "fetch the latest price for a ticker symbol")]); assert_eq!(index.search("ticker price", 5), vec![0]); - assert!(index.search("calendar", 5).is_empty()); + assert_eq!(index.search("calendar", 5).len(), 0); } #[test] fn index_returns_nothing_for_stopword_only_queries() { let index = Bm25Index::build([("a", "send a message"), ("b", "read a file")]); - assert!(index.search("the a of", 5).is_empty()); - assert!(Bm25Index::default().search("anything", 5).is_empty()); + assert_eq!(index.search("the a of", 5).len(), 0); + assert_eq!(Bm25Index::default().search("anything", 5).len(), 0); assert!(Bm25Index::default().is_empty()); } diff --git a/crates/tinytools/src/result/test.rs b/crates/tinytools/src/result/test.rs index d84e596..008cd62 100644 --- a/crates/tinytools/src/result/test.rs +++ b/crates/tinytools/src/result/test.rs @@ -26,7 +26,7 @@ fn error_sets_the_flag_and_keeps_the_message() { fn text_skips_json_blocks_but_output_renders_them() { let r = ToolResult::json(json!({"key": "value"})); assert!(!r.is_error); - assert!(r.text().is_empty()); + assert_eq!(r.text().len(), 0); assert!(r.output().contains("key")); } @@ -59,8 +59,8 @@ fn empty_content_renders_empty() { content: vec![], ..ToolResult::default() }; - assert!(r.text().is_empty()); - assert!(r.output().is_empty()); + assert_eq!(r.text().len(), 0); + assert_eq!(r.output().len(), 0); } #[test] diff --git a/crates/tinytools/src/tool/test.rs b/crates/tinytools/src/tool/test.rs index 37fe452..392a377 100644 --- a/crates/tinytools/src/tool/test.rs +++ b/crates/tinytools/src/tool/test.rs @@ -102,7 +102,7 @@ fn the_declaration_defaults_are_the_conservative_answer() { assert!(tool.host_extension().is_none()); assert!(tool.host_call_extension(&Value::Null).is_none()); assert_eq!(tool.policy(), ToolPolicy::default()); - assert!(tool.injected_arguments().is_empty()); + assert_eq!(tool.injected_arguments().len(), 0); } #[tokio::test] diff --git a/crates/tinytools/src/workspace/test.rs b/crates/tinytools/src/workspace/test.rs index 10dcb21..0026f18 100644 --- a/crates/tinytools/src/workspace/test.rs +++ b/crates/tinytools/src/workspace/test.rs @@ -11,8 +11,8 @@ use super::{SandboxMode, WorkspaceDescriptor}; fn a_new_descriptor_is_rooted_with_no_extras() { let ws = WorkspaceDescriptor::new("/work/agent-a"); assert_eq!(ws.root, PathBuf::from("/work/agent-a")); - assert!(ws.trusted_roots.is_empty()); - assert!(ws.policy_id.is_empty()); + assert_eq!(ws.trusted_roots.len(), 0); + assert_eq!(ws.policy_id.len(), 0); assert_eq!(ws.sandbox, SandboxMode::Inherit); } From b7f6e50e568a70f201ff1f77dc6bfb99b2f47469 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 1 Oct 2026 18:33:14 +0300 Subject: [PATCH 4/4] fix(test): replace `is_empty` assertions with explicit length checks Changed two assertions in the stale-read test to use `assert_eq!(..., 0)` instead of `assert!(... .is_empty())` for consistency with the surrounding test style and to provide clearer failure messages. Auto-committed-on: dragonfly --- crates/tinytools-std/src/file_state/test/ops.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/tinytools-std/src/file_state/test/ops.rs b/crates/tinytools-std/src/file_state/test/ops.rs index 294a4e2..1842bd3 100644 --- a/crates/tinytools-std/src/file_state/test/ops.rs +++ b/crates/tinytools-std/src/file_state/test/ops.rs @@ -307,11 +307,12 @@ fn a_write_before_the_read_is_not_stale() { Instant::now(), ); - assert!(coord.stale_reads_for_parent("parent").is_empty()); - assert!( + assert_eq!(coord.stale_reads_for_parent("parent").len(), 0); + assert_eq!( coord .parent_stale_files("parent", &["child-1".to_string()]) - .is_empty() + .len(), + 0 ); assert_eq!(coord.check_stale_read("parent", &path), None); }