hero_rpc#123 follow-up: codegen-emit per-(field × shape) _find E2E tests #128

Merged
timur merged 5 commits from issue-123-find-tests-codegen into development 2026-05-22 12:15:22 +00:00
Owner

Follow-up to merged hero_rpc#127 (commit e62d448). The typed <root>_find method and <Root>FindParams struct are emitted by codegen but only StrFilter::Eq (str+enum equality) and NumFilter::Lt are exercised over the real wire today (via the osis_benches preflight asserts). Closes the gap.

What this PR emits

For every rootobject with at least one @index field the workspace-root tests emitter now writes a sibling tests/generated/<root>_find_e2e.rs alongside the existing CRUD lifecycle file. One #[tokio::test] per (indexed field × filter shape) pair:

  • str @indexeq, prefix, contains (StrFilter)
  • bool @indexeq (BoolFilter)
  • numeric @indexeq, gt, gte, lt, lte, range (NumFilter)
  • enum @indexeq, one_of (EnumFilter)

Plus, per rootobject:

  • one <root>_find_and_across_fields (when ≥2 indexed fields) — pins AND-across-fields semantics against the 4-row combinatorial seed
  • one <root>_find_no_match_returns_empty — pins the empty-result contract on a probe value no seeded row carries

Each test owns its seed rows (5–10 per test, deterministic), asserts on returned sids as a HashSet against the expected subset, and deletes its seeds so cargo test reruns are idempotent. The generated file embeds an inline indexer_reachable() helper that mirrors crates/osis/tests/indexer_smoke.rs::indexer_reachable — CI environments without hero_indexer skip each test cleanly with eprintln! rather than panicking.

Per-shape filter test counts on hero_service's bench domain

Validated by rebuilding hero_service (/private/tmp/hero_service_findtests worktree) against this branch:

Rootobject Indexed fields Tests emitted
IndexedSingle (1 str @index) title 4 (3 str + 1 negative)
IndexedMulti (1 str + 1 enum) title, category 7 (3 str + 2 enum + 1 AND + 1 negative)
IndexedNonStr (1 numeric) priority: u32 @index 7 (6 num + 1 negative)
ServiceDefinition (1 str) name 4 (3 str + 1 negative)

Total across the four indexed rootobjects: 22 new #[tokio::test] bodies. Pre-PR baseline (the existing <root>_full_lifecycle CRUD entry per rootobject) keeps emitting unchanged.

No #[ignore] gates were needed — EnumFilter::OneOf and BoolFilter::Eq have full server-side lowerings in crates/generator/src/rust/rust_struct.rs::render_find_params_to_indexer_query already, so every filter shape this PR emits is end-to-end testable on a live hero_indexer.

What changed in the generator

  • crates/generator/src/build/fixture.rsFieldInfo now carries is_indexed: bool + rust_type: String so the find-test emitter can pick concrete NumFilter<T> / EnumFilter<T> type parameters and enum variant identifiers.
  • crates/generator/src/build/find_tests_emit.rs (new, ~860 LOC) — renders the <root>_find_e2e.rs content. One render fn per (filter shape, variant) pair. The AND-across-fields and no-match templates pick representative match / miss values per shape via a small filter_match_pair() helper.
  • crates/generator/src/build/tests_emit.rs — drives find-file emission, extends mod.rs index, and now also rewrites the trailing [[test]] section of tests/Cargo.toml in place so existing consumers (whose tests/Cargo.toml is write_preserved by the scaffolder) pick up the new [[test]] registrations on cargo build.
  • crates/generator/src/build/scaffold.rs — emits the new [[test]] Cargo.toml entries on initial scaffold runs too.

Validation

  • cargo build -p hero_rpc_generator clean.
  • cargo test -p hero_rpc_generator --lib — 137 passed; 0 failed; 1 ignored (baseline).
  • Rebuilt /private/tmp/hero_service_findtests (off origin/development @ 7763ab0) with hero_rpc deps repointed at this branch:
    • cargo build --workspace clean.
    • cargo test --workspace --no-run clean — every find_e2e binary links.
    • Test enumeration matches the table above (cargo test --test <name> -- --list).
    • Wire-level runtime validation requires a fix to the orthogonal OsisIndexer Runtime::new() + block_on() issue (#127 introduced this; pre-existing CRUD tests hit the same Cannot start a runtime from within a runtime panic on hero_service 7763ab0 — not caused by this PR; the codegen surface is independently green).

Companion hero_service PR

Will open against lhumina_code/hero_service development after this lands, restamping the tests/generated/ payload + tests/Cargo.toml against the merged hero_rpc tip.

Follow-up to merged hero_rpc#127 (commit e62d448). The typed `<root>_find` method and `<Root>FindParams` struct are emitted by codegen but only `StrFilter::Eq` (str+enum equality) and `NumFilter::Lt` are exercised over the real wire today (via the osis_benches `preflight` asserts). Closes the gap. ## What this PR emits For every rootobject with at least one `@index` field the workspace-root tests emitter now writes a sibling `tests/generated/<root>_find_e2e.rs` alongside the existing CRUD lifecycle file. One `#[tokio::test]` per (indexed field × filter shape) pair: - `str @index` → `eq`, `prefix`, `contains` (StrFilter) - `bool @index` → `eq` (BoolFilter) - numeric `@index` → `eq`, `gt`, `gte`, `lt`, `lte`, `range` (NumFilter<T>) - enum `@index` → `eq`, `one_of` (EnumFilter<T>) Plus, per rootobject: - one `<root>_find_and_across_fields` (when ≥2 indexed fields) — pins AND-across-fields semantics against the 4-row combinatorial seed - one `<root>_find_no_match_returns_empty` — pins the empty-result contract on a probe value no seeded row carries Each test owns its seed rows (5–10 per test, deterministic), asserts on returned sids as a `HashSet` against the expected subset, and deletes its seeds so `cargo test` reruns are idempotent. The generated file embeds an inline `indexer_reachable()` helper that mirrors `crates/osis/tests/indexer_smoke.rs::indexer_reachable` — CI environments without hero_indexer skip each test cleanly with `eprintln!` rather than panicking. ## Per-shape filter test counts on hero_service's bench domain Validated by rebuilding hero_service (`/private/tmp/hero_service_findtests` worktree) against this branch: | Rootobject | Indexed fields | Tests emitted | |---|---|---| | `IndexedSingle` (1 str @index) | `title` | 4 (3 str + 1 negative) | | `IndexedMulti` (1 str + 1 enum) | `title`, `category` | 7 (3 str + 2 enum + 1 AND + 1 negative) | | `IndexedNonStr` (1 numeric) | `priority: u32 @index` | 7 (6 num + 1 negative) | | `ServiceDefinition` (1 str) | `name` | 4 (3 str + 1 negative) | Total across the four indexed rootobjects: 22 new `#[tokio::test]` bodies. Pre-PR baseline (the existing `<root>_full_lifecycle` CRUD entry per rootobject) keeps emitting unchanged. No `#[ignore]` gates were needed — `EnumFilter::OneOf` and `BoolFilter::Eq` have full server-side lowerings in `crates/generator/src/rust/rust_struct.rs::render_find_params_to_indexer_query` already, so every filter shape this PR emits is end-to-end testable on a live `hero_indexer`. ## What changed in the generator - `crates/generator/src/build/fixture.rs` — `FieldInfo` now carries `is_indexed: bool` + `rust_type: String` so the find-test emitter can pick concrete `NumFilter<T>` / `EnumFilter<T>` type parameters and enum variant identifiers. - `crates/generator/src/build/find_tests_emit.rs` (new, ~860 LOC) — renders the `<root>_find_e2e.rs` content. One render fn per (filter shape, variant) pair. The AND-across-fields and no-match templates pick representative match / miss values per shape via a small `filter_match_pair()` helper. - `crates/generator/src/build/tests_emit.rs` — drives find-file emission, extends `mod.rs` index, and now also rewrites the trailing `[[test]]` section of `tests/Cargo.toml` in place so existing consumers (whose `tests/Cargo.toml` is `write_preserved` by the scaffolder) pick up the new `[[test]]` registrations on `cargo build`. - `crates/generator/src/build/scaffold.rs` — emits the new `[[test]]` Cargo.toml entries on initial scaffold runs too. ## Validation - `cargo build -p hero_rpc_generator` clean. - `cargo test -p hero_rpc_generator --lib` — 137 passed; 0 failed; 1 ignored (baseline). - Rebuilt `/private/tmp/hero_service_findtests` (off `origin/development` @ `7763ab0`) with hero_rpc deps repointed at this branch: - `cargo build --workspace` clean. - `cargo test --workspace --no-run` clean — every find_e2e binary links. - Test enumeration matches the table above (`cargo test --test <name> -- --list`). - Wire-level runtime validation requires a fix to the orthogonal `OsisIndexer` `Runtime::new() + block_on()` issue (#127 introduced this; pre-existing CRUD tests hit the same `Cannot start a runtime from within a runtime` panic on hero_service `7763ab0` — not caused by this PR; the codegen surface is independently green). ## Companion hero_service PR Will open against `lhumina_code/hero_service` development after this lands, restamping the `tests/generated/` payload + `tests/Cargo.toml` against the merged hero_rpc tip.
hero_rpc#123 follow-up: emit per-(field × shape) _find E2E tests
Some checks failed
Test / test (push) Failing after 2m12s
f57c464bb6
For every rootobject with at least one @index field, the workspace-root
tests emitter now writes a sibling tests/generated/<root>_find_e2e.rs
alongside the existing CRUD lifecycle file. Each indexed field gets one
#[tokio::test] per filter shape that the typed FindParams surface
supports:

- str @index  → eq / prefix / contains   (StrFilter)
- bool @index → eq                       (BoolFilter)
- numeric     → eq / gt / gte / lt / lte / range  (NumFilter<T>)
- enum        → eq / one_of              (EnumFilter<T>)

Plus a cross-field AND test for rootobjects with >=2 indexed fields,
and a no-match negative test that pins the empty-result contract.

Each test owns its seed rows (5–10 per test, deterministic by
construction), asserts on the returned sids as a HashSet against the
combinatorial expected subset, and deletes its seeds so cargo test
reruns are idempotent. The new files also include an inline
indexer_reachable() helper — mirrors crates/osis/tests/indexer_smoke.rs
— so CI environments without hero_indexer skip cleanly with an
eprintln! rather than panicking.

The scaffold emitter also registers a [[test]] entry per find file in
the consumer's tests/Cargo.toml so Cargo discovers them as standalone
test binaries (same model as the existing _e2e entries).
tests_emit: rewrite tests/Cargo.toml [[test]] section in place
Some checks failed
Test / test (push) Has been cancelled
c331227584
The scaffolder writes tests/Cargo.toml with write_preserved (first-run
only), so existing consumers regenerating their tree via build.rs
wouldn't otherwise pick up the new <root>_find_e2e entries. Sync the
trailing [[test]] block from the build path: strip the contiguous tail
of [[test]] tables at EOF and re-emit the canonical set (CRUD entry
per root + find entry per root with >=1 @index field).

Leaves the [package] / [dependencies] sections untouched. No-ops on
malformed layouts (mid-file [[test]] tables) — the contributor's
existing config keeps working.
find_tests_emit: fix format-string brace escaping in range assertion message
Some checks failed
Test / test (push) Failing after 2m15s
Test / test (pull_request) Failing after 2m14s
2beb5172c9
chore: apply cargo fmt across new + pre-existing files
Some checks failed
Test / test (pull_request) Failing after 3m25s
Test / test (push) Failing after 3m31s
fe635915f3
chore(clippy): fix doc list indent + collapsible if + Copy-on-clone + format!
All checks were successful
Test / test (pull_request) Successful in 2m20s
Test / test (push) Successful in 2m22s
d3c1415c8e
The 1.95 clippy --workspace -- -D warnings step now passes:

- crates/generator/src/build/tests_emit.rs: collapse nested if-let.
- crates/generator/src/build/scaffold.rs: reflow doc comment so the
  continuation lines aren't picked up as a list item.
- crates/generator/src/rust/rust_struct.rs: same — reflow the
  EnumFilter bullet's continuation lines. Also add file-level
  #[allow(clippy::needless_borrows_for_generic_args)] on the generated
  osis_impl.rs so the serde_json::to_value(&self.<enum_field>) calls
  stop tripping needless_borrows on Copy enum types. Mark the emitted
  impl From<&T> for TInput block with #[allow(clippy::clone_on_copy)]
  for the same reason — every user field gets .clone() uniformly so
  the codegen doesn't have to introspect Copy.
- crates/generator/src/rust/rust_osis.rs: drop obj.created_at = now.clone()
  in the emitted server CREATE handler — OTime is Copy now. Indent the
  doc list continuation under the bullet.
- crates/generator/src/js/js_struct.rs: collapse the inner if-let into
  the outer let-chain; drop two useless format!() calls.
- crates/hero_rpc2/src/find.rs: drop a needless borrow in the bool
  filter test.
timur merged commit f834ff9997 into development 2026-05-22 12:15:22 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_rpc!128
No description provided.