Skip to content

test: coverage for useCatalogSortIndex#71

Open
doolin wants to merge 1 commit into
EasyDynamics:mainfrom
doolin:contrib/catalog-sort-index-coverage
Open

test: coverage for useCatalogSortIndex#71
doolin wants to merge 1 commit into
EasyDynamics:mainfrom
doolin:contrib/catalog-sort-index-coverage

Conversation

@doolin

@doolin doolin commented May 28, 2026

Copy link
Copy Markdown
Contributor

14 unit tests for the hook merged in #57. Pure test additions — no executable source code changes.

What's covered

  • No-catalog fallback: empty map, hasSortIds: false, numeric-aware compare so AC-2 still sorts before AC-10 even without a catalog loaded.
  • Catalog without sort-id props: same fallback path, but reached via the loaded-catalog branch.
  • Sort-id index population:
    • top-level controls
    • groups themselves (group sort-ids)
    • control enhancements (controls.controls)
    • nested groups (groups.groups)
    • top-level catalog.controls (rare but supported)
    • confirms non-sort-id props are skipped
  • Comparator with index loaded:
    • sort-id ordering for indexed operands
    • raw-input fallback when one operand isn't indexed
    • case-insensitive lookup so pages passing lowercase IDs hit the catalog
  • Memoization:
    • stable instance across rerenders when catalog reference is unchanged
    • re-derives when the catalog entry reference changes

One BUG lock-in

The walker stores IDs case-as-found in the catalog, but the comparator only tries .toLowerCase() then raw — never .toUpperCase(). So lowercase lookups miss the index when the catalog stored uppercase IDs. In practice OSCAL convention is lowercase IDs so this rarely bites, but the asymmetry is real. Documented in the test with a clear BUG: prefix and an explanatory comment; the fix would be a one-line normalize in buildSortMap.

Locking in the current behavior in this PR means a future fix is a clean assertion-flip in one place rather than a guess about what the comparator was supposed to do.

Setup

Tests use OscalProvider with a small seeding component (Seed) that calls setCatalog on mount via useEffect. The fixture factories (ctrl(), group(), catalogOf()) keep the test bodies readable.

Verification

npm test -- --run src/hooks/useCatalogSortIndex.test.tsx
# Test Files  1 passed (1)
#      Tests  14 passed (14)

14 unit tests against the hook returned by useCatalogSortIndex():

- No-catalog fallback: empty map, hasSortIds=false, numeric-aware
  compare (AC-2 before AC-10) still works.
- Catalog without sort-id props: same fallback path, but reached
  via the loaded-catalog branch.
- Sort-id index population: top-level controls, groups themselves,
  control enhancements (controls.controls), nested groups
  (groups.groups), and the rare top-level catalog.controls case.
  Confirms non-sort-id props are skipped.
- Comparator with index: ordering by sort-id, raw-input fallback
  when an operand is not indexed, case-insensitive lookup so pages
  passing lowercase IDs hit the catalog.
- Memoization: stable instance across rerenders with the same
  catalog reference; re-derives when the catalog entry changes.
- One BUG lock-in: the walker stores ids case-as-found in the
  catalog, but the comparator only tries toLowerCase() then raw —
  never toUpperCase(). So lowercase lookups miss when the catalog
  stored uppercase IDs. In practice OSCAL convention is lowercase
  so this rarely bites, but the asymmetry is documented here. The
  fix would be a one-line normalize in buildSortMap.

Pure test additions. No executable source code changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@doolin doolin requested a review from pjavan as a code owner May 28, 2026 23:05
`toLowerCase()` then raw — never `toUpperCase()`. In practice OSCAL
convention is lowercase IDs so this rarely bites, but the asymmetry is
visible and would be a one-line fix (normalize keys to lowercase in
buildSortMap). Locked in here per the lock-in-before-fix discipline. */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locking in the behavior which has already shipped costs nothing and gains a baseline to push against when fixing. That is, fixing the bug requires updating the test to ensure correct behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude is pretty adamant about this one! But I haven't done the due diligence on it personally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OSCAL standard expects string comparisons to be case sensitive per my understanding.

OSCAL leans on Metaschema, and this is the definition of string:
https://pages.nist.gov/metaschema/specification/datatypes/#string

Metschema in turn leans on the XML standard:
https://www.w3.org/TR/xmlschema11-2/#string

If the current comparison is case sensitive, then it should remain that way, otherwise it should be fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some reading on this, and as I understand it, the value associated with a sort-id could be upper, lower, or even mixed case. In other words, case sensitive. In this case, it might not be a bug per se, until it a match fails, then it would definitely be a bug.

My SOP for these situations is to "lock in" existing behavior with a passing test, then later follow with an update to the test which enforces correct implementation.

Thanks for your time!

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.

2 participants