Skip to content

test(viewer-3d): cover the plate, mesh and grid scene guards - #505

Merged
pawellisowski merged 1 commit into
mainfrom
routine/test-hygiene-2026-09-09
Sep 9, 2026
Merged

test(viewer-3d): cover the plate, mesh and grid scene guards#505
pawellisowski merged 1 commit into
mainfrom
routine/test-hygiene-2026-09-09

Conversation

@pawellisowski

Copy link
Copy Markdown
Contributor

Summary

  • classify_scene refuses a malformed scene record before any HTML is produced, but most of that refusal logic had only ever been reached on its happy path — one plate frame, one well-formed mesh, one valid set of grid bounds. Eight tests close that, in cli/src/render/viewer_3d.rs.
  • Every added test was proven to fail under a mutation of the guard it covers, then the mutation reverted. The table below records each one.
  • No production code changed. Tests only.

Type of change

  • Other (specify): test coverage, no behaviour change

Decalog check

  • This change respects all five decalog truths (app=text, AI=runtime, OSS=inherent, no vendor in the loop, AECO=wedge-not-limit).

What was uncovered, and why it matters

validate_plate, validate_mesh and validate_grid_bounds are the guards standing between a producer's scene and a viewer that would otherwise draw something wrong in silence. Before this PR the only plate frame ever rendered under test was the axis-aligned uDir x / vDir y / normal z one, the only mesh was well formed, and the only grid bounds were valid.

Two of the newly covered cases are worth naming:

  • A flipped plate normal. [0,0,-1] is a perfectly good unit normal to the same outline, so nothing downstream objects — the plate is simply mirrored, and the extrusion grows away from the face the author drew. Only this guard catches it.
  • A zero frame direction. It has to be refused before the arithmetic that follows, not by it: with uDir zero, dot3(u,v)/(ul*vl) is 0/0, and NaN > 1e-6 is false, so the orthogonality test waves it through — and so does the handedness test, for the same reason. Delete the nonzero guard and a degenerate frame renders.

Mutation evidence

Each row: the guard was broken as shown, cargo test --bin aware render::viewer_3d was run, the listed test went red, and the guard was restored. All twenty mutations were killed.

# Mutation applied to viewer_3d.rs Test that went red
1 if ul <= 1e-9 || vl <= 1e-9 || nl <= 1e-9 {if false { a_plate_frame_must_be_nonzero_orthogonal_and_right_handed
2 orthogonality tolerance > 1.0e-6> 1.0e9 same
3 handedness < 1.0 - 1.0e-6< -2.0 same
4 positive_number(…thicknessMm…)finite_number(…) a_plate_needs_a_positive_thickness_and_three_outline_points
5 outline.len() >= 3outline.len() >= 2 same
6 non-array holes arm returns &[] instead of Err a_non_array_holes_field_is_refused_rather_than_treated_as_none
7 hole separation distance <= (d + other)/2 + 1e-9distance < 0.0 plate_holes_must_stay_clear_of_each_other_and_accept_the_uv_alias
8 hole.get("center").or_else(|| hole.get("uv"))hole.get("center") same
9 positions filter drops len() % 3 == 0 a_mesh_needs_complete_triples_and_indices_that_name_a_real_vertex
10 positions filter drops len() >= 9 same
11 the finite-coordinate sweep over positions deleted same
12 indices filter drops len() % 3 == 0 same
13 index filter (*value as usize) < vertex_counttrue same
14 min_x >= max_x || min_y >= max_ymin_x > max_x || min_y > max_y a_structural_grid_needs_bounds_with_increasing_extents_on_both_axes
15 same condition → min_x >= max_x (Y half deleted) same
16 Some("x" | "y") => {}Some(_) => {} grid_axes_and_levels_must_be_planar_numeric_and_labelled
17 the axis/level label check → if false { same, and renders_parametric_…_with_receipts
18 the empty-levels check → if false { renders_parametric_…_with_receipts only
19 the unsupported-system child loop emptied unknown_records_are_exhaustively_unsupported only
20 "plate" => validate_plate(…) arm deleted all four plate tests, plus eight pre-existing ones

Rows 18 and 19 were killed by pre-existing tests alone. That is the point of running them: both cases turned out to be covered already, so the tests I had drafted for them were dropped rather than shipped as duplicates. Row 17 is covered for an axis label by the existing test and for a level label by the new one.

Row 20 is the guard on the guards: it proves the plate fixture reaches validate_plate at all, which is what makes the four refusal tests non-vacuous. the_plate_fixture_the_refusal_tests_edit_is_itself_valid asserts the unedited fixture renders and is receipted, so no refusal below it can be a broken fixture rather than the guard talking. Each of the mesh and grid tests carries the same positive control inline.

Deletions: none, and why

Nothing in this area asserts nothing. The nearest candidate was renders_a_tessellated_mesh_element, two of whose four assertions (html.contains("BufferGeometry"), html.contains("setIndex")) hold for any scene at all, because they pin strings in the static template rather than anything about the mesh under test. They are kept: the renderer's contract genuinely includes "the shipped template carries mesh support", and stripping a template pin from a renderer unattended is a judgement call, not hygiene. Flagging it here instead.

Gates

Run from cli/ on the pinned toolchain (1.95.0), with clang libsecret-1-dev libdbus-1-dev pkg-config installed as CI does:

  • cargo fmt --all -- --check — pass
  • cargo clippy --all-targets -- -D warnings — pass
  • cargo test — 1641 passed, 0 failed

Notes for reviewers

The plate, mesh and grid fixtures are each a single valid scene that every refusal case then breaks in exactly one place, so a failure names the guard rather than the fixture. plate_holes_must_stay_clear_of_each_other_and_accept_the_uv_alias also asserts that a uv hole is still bounds-checked — the alias is a spelling, not a way past containment.

🤖 Generated with Claude Code

https://claude.ai/code/session_0124mWeBTCLtG1xnCf1hGq4m


Generated by Claude Code

`classify_scene` validates every scene record before any HTML is
produced, so a malformed plate, mesh or structural grid fails atomically
instead of disappearing inside the generated JS. Most of that refusal
logic had only ever been reached on its happy path: one plate frame, one
well-formed mesh, one valid set of grid bounds.

Eight tests close that. The plate frame must be a nonzero, orthogonal,
right-handed basis — the flipped-normal case is the interesting one,
because `[0,0,-1]` is a perfectly good normal that merely mirrors the
plate, so nothing downstream objects. The nonzero-direction guard has to
fire first for a separate reason: with `uDir` zero the orthogonality and
handedness tests both divide by zero, and `NaN > 1e-6` is false, so both
wave the frame through.

Also covered: positive thickness and three-point outline arity; holes
that overlap or exactly touch, and the legacy `uv` spelling of a hole
centre, which nothing covered and which would have looked free to
delete; a `holes` value that is present but not an array, refused rather
than read as "no holes"; mesh positions that do not divide into xyz
triples, a coordinate that is not finite, index lists that do not divide
into triangles, and an index past the last vertex; and grid bounds with
equal or inverted extents on either axis, a non-planar axis direction,
and a level with no label or a non-numeric elevation.

Every added test was proven to fail under a mutation of the guard it
covers; the mutations are listed in the pull request. Two neighbouring
cases were left out deliberately because
`renders_parametric_connection_solids_and_structural_references_with_receipts`
already reaches them: an axis with no label, and an empty `levels` list.

Copy link
Copy Markdown
Contributor Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T03:16:06.424263Z 228884a Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 228884addd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@pawellisowski
pawellisowski merged commit fa6eebb into main Sep 9, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant