Skip to content

test: add comprehensive unit test suites for PHP and JS - #177

Merged
jackgranatowski merged 3 commits into
mainfrom
claude/slashed-plugins-coverage-audit-1pyr9h
Jul 8, 2026
Merged

test: add comprehensive unit test suites for PHP and JS#177
jackgranatowski merged 3 commits into
mainfrom
claude/slashed-plugins-coverage-audit-1pyr9h

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds comprehensive unit test coverage for pure/near-pure logic across the plugin:

PHP tests (tests-php/):

  • ColorResolverTest — golden-value + invariant tests for Slashed_Color_Resolver (light/dark mode, scale derivation, semantic aliases)
  • CssGeneratorDerivedOverridesTest — scale-knob expansion logic (--sf-radius-scale, --sf-border-scale, --sf-motion-scale) via reflection
  • TokenDefaultsShapeTest — structural invariants for Slashed_Token_Defaults (section routing, hex-hint pairing, fluid ramp ordering)
  • CategoryMapTest — first-segment → category-label lookup and display-order list
  • RestControllerSanitizeOverridesTest — override name/value sanitization (property-name gate, type coercion, injection prevention)
  • RestControllerSettingsValidatorsTest — POST /settings allowlists (font sizes, CSS bundles, configurator URL scheme gate)

JS tests (tests/verify-sync-failures.test.js):

  • Failure-path tests for verify-sync's runChecks() — builds minimal fixture checkouts, confirms clean state passes, then mutates version metadata one piece at a time to prove each check actually fires (dist bundle versions, CSS_REF constants, inventory.json copies, readme.txt Stable tag, plugin Version: headers, *_VERSION constants)

Refactoring:

  • Extracted three inline validate_callback closures from Slashed_REST_Controller::register_routes() into public static methods (is_allowed_html_font_size(), is_allowed_css_bundle(), is_valid_configurator_url()) so the allowlists are testable in isolation
  • Updated verify-sync.js to accept an optional root parameter (defaults to ROOT) so tests can point the same logic at fixture trees with deliberately broken metadata

All tests are pure (no WordPress runtime except stubs in tests-php/bootstrap.php) and cover the dependency-free approximations and invariants that guard against silent visual/functional drift.

Type

  • feat
  • fix
  • docs
  • chore / tooling

Checklist

  • Conventional Commit messages (feat:, fix:, docs:, …)
  • npm test passes
  • npm run lint passes (stylelint + php -l)
  • npm run verify passes (version metadata in sync)
  • Generated artifacts not hand-edited
  • CHANGELOG.md updated (no user-facing changes)
  • Built SPA assets committed if editor-app/admin-app source changed (N/A)

Notes

  • PHP tests run via composer phpunit (wired into CI's quality job)
  • JS failure-path tests run via node --test tests/verify-sync-failures.test.js
  • Both suites are included in npm test and CI
  • No changes to production code logic — only test coverage and extraction of testable validators

https://claude.ai/code/session_01YNzFE6py1whVTmrohL34Ra

Summary by CodeRabbit

  • Bug Fixes
    • Improved settings validation for font size, CSS bundle, and configurator URL inputs.
    • Strengthened safeguards around custom CSS overrides and accepted value formats.
    • Added broader consistency checks to help catch mismatched version or asset data earlier.
    • Improved reliability of color, token, and category handling through expanded validation coverage.

…rify-sync failures

Fills the highest-value zero-coverage zones found in the coverage audit.

PHPUnit (70 -> 166 tests):
- ColorResolverTest: golden light/dark hex maps, alias/step invariants,
  key-set parity, and override + malicious-fallback paths for the previously
  untested pure resolver.
- CssGeneratorDerivedOverridesTest: radius/border/motion scale expansion and
  fmt_num() edges (reflection over the pure private helpers).
- RestControllerSanitizeOverridesTest: the `--sf-` name gate and type-drop on
  the flat override map (complements the existing value-allowlist tests).
- RestControllerSettingsValidatorsTest: html_font_size / css_bundle allowlists
  and the configurator_url http(s) gate (javascript:/data: breakout).
- CategoryMapTest and TokenDefaultsShapeTest: pure lookup + data-shape invariants.

JS (144 -> 151 tests):
- verify-sync-failures.test.js: fixture-based proof that runChecks() actually
  fires on every version-metadata drift, not just that the committed tree passes.

Enabling, behavior-preserving refactors:
- Extract the three POST /settings validate_callbacks to public static
  predicates so they can be unit-tested in isolation.
- Make verify-sync runChecks(root) honour a custom checkout root instead of
  throwing, so failure paths can be exercised against a fixture tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNzFE6py1whVTmrohL34Ra
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jackgranatowski, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 833af33e-788a-44d1-88f1-9904de18594e

📥 Commits

Reviewing files that changed from the base of the PR and between 32f58f4 and 0e367ef.

📒 Files selected for processing (7)
  • scripts/gen-class-hints.js
  • scripts/gen-variables-hints.js
  • tests-php/CssGeneratorEmissionTest.php
  • tests-php/TokenStoreTest.php
  • tests-php/bootstrap.php
  • tests/gen-class-hints.test.js
  • tests/gen-variables-hints.test.js
📝 Walkthrough

Walkthrough

Refactors REST controller inline validation closures into static validator methods, parameterizes verify-sync.js helpers with an explicit root path, and adds extensive PHPUnit tests (Category Map, Color Resolver, CSS Generator, sanitize_overrides, settings validators, Token Defaults) plus Node tests for verify-sync failure paths.

Changes

REST Controller Validator Refactor

Layer / File(s) Summary
Static validator extraction and tests
SLASHED-for-WP/includes/class-rest-controller.php, tests-php/RestControllerSettingsValidatorsTest.php, tests-php/RestControllerSanitizeOverridesTest.php
Inline closures for html_font_size, css_bundle, configurator_url validation are replaced with new static methods is_allowed_html_font_size, is_allowed_css_bundle, is_valid_configurator_url; new PHPUnit tests cover these validators and the existing sanitize_overrides method.

Verify-Sync Root Parameterization

Layer / File(s) Summary
Root-parameterized helpers and failure tests
scripts/verify-sync.js, tests/verify-sync-failures.test.js
Version/hash/read helpers accept an explicit root argument instead of a hardcoded module constant; new Node tests build a fixture checkout and mutate version sources to verify runChecks reports expected errors.

PHP Test Suite Additions

Layer / File(s) Summary
Bootstrap and new test classes
tests-php/bootstrap.php, tests-php/CategoryMapTest.php, tests-php/ColorResolverTest.php, tests-php/CssGeneratorDerivedOverridesTest.php, tests-php/TokenDefaultsShapeTest.php
Bootstrap requires additional class files; new PHPUnit suites validate Category Map label/order logic, Color Resolver golden values and overrides, CSS Generator derived overrides/formatting, and Token Defaults structural invariants.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested labels: codex

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding broad unit test coverage for PHP and JavaScript.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/slashed-plugins-coverage-audit-1pyr9h

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add PHP unit tests and verify-sync failure-path JS tests

🧪 Tests ✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Add PHPUnit coverage for resolver/generator invariants and REST override sanitization.
• Refactor REST settings validators into static predicates for isolated allowlist testing.
• Add fixture-based JS tests proving verify-sync detects each version-metadata drift.
Diagram

graph TD
  PHPTests["PHPUnit tests"] --> RestCtrl["REST Controller"] --> TokenStore["Token Store"]
  PHPTests --> CssGen["CSS Generator"]
  PHPTests --> ColorRes["Color Resolver"] --> TokenDefs["Token Defaults"]
  PHPTests --> CatMap["Category Map"]
  JSTests["Node tests"] --> VerifySync["verify-sync runChecks"] --> RepoTree["Fixture / repo tree"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Integration tests with WP REST server + WP_UnitTestCase
  • ➕ Exercises the full WordPress REST stack (args, sanitization, permissions) end-to-end
  • ➕ Catches wiring issues that pure predicate tests cannot
  • ➖ Significantly heavier runtime and setup vs the current pure-PHP suite
  • ➖ More brittle/flaky (DB/filesystem/WP boot), slower CI feedback
2. Make private helpers public instead of reflection-based tests
  • ➕ Avoids reflection and tests exactly the published API surface
  • ➕ Simplifies test code and failure messages
  • ➖ Expands production public API surface just for testing
  • ➖ Makes it harder to change internal derivation without supporting legacy callers
3. Keep verify-sync CLI-only; test by spawning node process
  • ➕ Validates real CLI invocation/exit codes and console output
  • ➕ No need to change runChecks signature
  • ➖ Slower and more brittle than importing runChecks()
  • ➖ Harder to surgically assert individual error cases without parsing stdout/stderr

Recommendation: Current approach is the best tradeoff: keep production behavior unchanged while enabling fast, deterministic unit tests. The static REST validator extraction is a clean testability refactor with minimal surface-area risk, and allowing runChecks(root) is a pragmatic seam for fixture-based failure-path testing without relying on CLI process orchestration.

Files changed (10) +752 / -31

Enhancement (1) +25 / -21
verify-sync.jsAllow verify-sync checks to run against a custom checkout root +25/-21

Allow verify-sync checks to run against a custom checkout root

• Threads a root parameter through file-reading helpers and exports runChecks(root = ROOT) without rejecting custom roots. This enables fixture-based tests to validate that each version-consistency check emits an error when corresponding metadata drifts.

scripts/verify-sync.js

Refactor (1) +35 / -10
class-rest-controller.phpExtract REST /settings validators into testable static methods +35/-10

Extract REST /settings validators into testable static methods

• Replaces inline validate_callback closures for html_font_size, css_bundle, and configurator_url with public static predicate methods. Adds docblocks and preserves the existing allowlists and http(s)-only configurator_url gate to prevent javascript:/data: scheme storage.

SLASHED-for-WP/includes/class-rest-controller.php

Tests (8) +692 / -0
CategoryMapTest.phpAdd unit tests for category label lookup and ordering invariants +72/-0

Add unit tests for category label lookup and ordering invariants

• Introduces PHPUnit coverage for Slashed_Category_Map::label_for() on known/unmapped segments. Adds invariants ensuring order() has no duplicates and contains all labels produced by label_for().

tests-php/CategoryMapTest.php

ColorResolverTest.phpAdd golden + invariant tests for color resolution (light/dark) +141/-0

Add golden + invariant tests for color resolution (light/dark)

• Adds golden-value assertions for selected resolved hex outputs in light and dark modes to catch silent visual drift. Validates invariants (alias targets, key-set parity, hex formatting) and override/fallback behavior for parseable/unparseable inputs.

tests-php/ColorResolverTest.php

CssGeneratorDerivedOverridesTest.phpTest derived override expansion for radius/border/motion scales +119/-0

Test derived override expansion for radius/border/motion scales

• Adds reflection-based tests for private Slashed_CSS_Generator helpers that expand scale knobs into concrete token ramps. Covers numeric formatting edge-cases (trailing zeros, negative zero) and guards ignoring non-numeric/injection-shaped scale inputs.

tests-php/CssGeneratorDerivedOverridesTest.php

RestControllerSanitizeOverridesTest.phpTest override-name gate and coercion in REST override sanitization +93/-0

Test override-name gate and coercion in REST override sanitization

• Adds reflection-based tests for Slashed_REST_Controller::sanitize_overrides() focusing on custom property name validation, dropping non-scalar values, and coercing accepted numeric values to strings. Complements existing tests that focus on override *value* allowlisting.

tests-php/RestControllerSanitizeOverridesTest.php

RestControllerSettingsValidatorsTest.phpAdd tests for POST /settings allowlists and URL scheme gate +70/-0

Add tests for POST /settings allowlists and URL scheme gate

• Covers the extracted static validators for html_font_size allowlist, css_bundle allowlist, and configurator_url http(s)-or-empty requirement. Explicitly tests rejection of javascript:, data:, scheme-relative, and host-only inputs.

tests-php/RestControllerSettingsValidatorsTest.php

TokenDefaultsShapeTest.phpAdd structural invariant tests for token default data shapes +83/-0

Add structural invariant tests for token default data shapes

• Adds tests ensuring Slashed_Token_Defaults exposes expected sections and that get_section() routing behaves correctly. Validates that oklch source groups have matching *_hex_hints groups with identical keys, hex hints are well-formed, and fluid min/max ramps do not invert.

tests-php/TokenDefaultsShapeTest.php

bootstrap.phpExtend PHP unit bootstrap to include additional pure classes +4/-0

Extend PHP unit bootstrap to include additional pure classes

• Updates the pure-PHP PHPUnit bootstrap to require token store, category map, token defaults, and color resolver classes so new suites can run without a WordPress runtime. Retains the sanitize_key() stub for minimal WP-function coverage.

tests-php/bootstrap.php

verify-sync-failures.test.jsAdd fixture-driven tests proving verify-sync detects each drift case +110/-0

Add fixture-driven tests proving verify-sync detects each drift case

• Creates a minimal consistent temp checkout fixture, asserts runChecks(root) returns no errors, then mutates one metadata source at a time (dist headers, CSS_REF, inventory copies, stable tag, Version: header, *_VERSION constants) and asserts the corresponding failure is reported. Prevents regressions where checks silently stop firing while the committed tree still passes.

tests/verify-sync-failures.test.js

@coderabbitai coderabbitai Bot added the codex label Jul 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
tests-php/RestControllerSettingsValidatorsTest.php (1)

41-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add optimal-utilities to the bundles data provider.

Slashed_Token_Store::ALLOWED_CSS_BUNDLES contains four values, but the positive cases only cover three. Adding optimal-utilities would complete the allowlist coverage.

As per coding guidelines, tests-php/ contains the PHPUnit suite for pure/near-pure PHP logic that does not require a WordPress runtime.

♻️ Suggested addition
 		return array(
 			'optimal'            => array( 'optimal', true ),
 			'optimal-components' => array( 'optimal-components', true ),
+			'optimal-utilities'  => array( 'optimal-utilities', true ),
 			'full'               => array( 'full', true ),
 			'unknown'            => array( 'kitchen-sink', false ),
 			'empty'              => array( '', false ),
 		);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests-php/RestControllerSettingsValidatorsTest.php` around lines 41 - 49, The
bundles data provider in bundles() is missing coverage for the full allowlist
exposed by Slashed_Token_Store::ALLOWED_CSS_BUNDLES. Add a positive case for
optimal-utilities alongside the existing optimal, optimal-components, and full
entries so the PHPUnit test fully exercises all allowed CSS bundle values.

Source: Coding guidelines

tests/verify-sync-failures.test.js (2)

36-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused cssName parameter from entry helper.

The second argument is passed 'x' at all three call sites but never referenced in the template string. Removing it eliminates a potential source of confusion for future maintainers.

♻️ Proposed fix
-  const entry = (cssRef, cssName, verName) =>
+  const entry = (cssRef, verName) =>
     `<?php\n/**\n * Version: ${PV}\n */\n` +
     `define( '${cssRef}', 'v${FW}' );\n` +
     `define( '${verName}', '${PV}' );\n`;

And update call sites:

-  write(root, `${PLUGIN}/slashed.php`, entry('SLASHED_CSS_REF', 'x', 'SLASHED_VERSION'));
-  write(root, `${PLUGIN}/integrations/bricks/slashed-bricks.php`, entry('SLASHED_BRICKS_CSS_REF', 'x', 'SLASHED_BRICKS_VERSION'));
-  write(root, `${PLUGIN}/integrations/gutenberg/slashed-gutenberg.php`, entry('SLASHED_GUTENBERG_CSS_REF', 'x', 'SLASHED_GUTENBERG_VERSION'));
+  write(root, `${PLUGIN}/slashed.php`, entry('SLASHED_CSS_REF', 'SLASHED_VERSION'));
+  write(root, `${PLUGIN}/integrations/bricks/slashed-bricks.php`, entry('SLASHED_BRICKS_CSS_REF', 'SLASHED_BRICKS_VERSION'));
+  write(root, `${PLUGIN}/integrations/gutenberg/slashed-gutenberg.php`, entry('SLASHED_GUTENBERG_CSS_REF', 'SLASHED_GUTENBERG_VERSION'));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/verify-sync-failures.test.js` around lines 36 - 39, The entry helper in
verify-sync-failures.test.js has an unused cssName parameter that is passed as
'x' at every call site but never used in the template. Remove cssName from the
entry function signature and update each call site to pass only the two
referenced arguments. Keep the helper name entry and the existing cssRef/verName
usage intact so the intent stays clear.

31-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding null-path ("cannot find") failure tests.

The suite covers drift/mismatch scenarios well, but runChecks also has null-return paths that aren't exercised: missing dist header (line 103), missing CSS_REF define (line 122), missing Version: header (line 148), and missing VERSION constant define (line 153). Adding tests for these would fully prove that every error branch fires, strengthening the PR's stated goal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/verify-sync-failures.test.js` around lines 31 - 110, Add test coverage
for the null-return “cannot find” branches in runChecks so every error path is
exercised. Extend verify-sync-failures.test.js with cases that remove the dist
header, the CSS_REF define, the Version: header, and the VERSION constant
define, then assert the resulting errors mention the corresponding missing
symbol or file. Use the existing runChecks helper and the fixture setup in
buildFixture to keep the tests aligned with the current drift/mismatch coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests-php/RestControllerSettingsValidatorsTest.php`:
- Around line 41-49: The bundles data provider in bundles() is missing coverage
for the full allowlist exposed by Slashed_Token_Store::ALLOWED_CSS_BUNDLES. Add
a positive case for optimal-utilities alongside the existing optimal,
optimal-components, and full entries so the PHPUnit test fully exercises all
allowed CSS bundle values.

In `@tests/verify-sync-failures.test.js`:
- Around line 36-39: The entry helper in verify-sync-failures.test.js has an
unused cssName parameter that is passed as 'x' at every call site but never used
in the template. Remove cssName from the entry function signature and update
each call site to pass only the two referenced arguments. Keep the helper name
entry and the existing cssRef/verName usage intact so the intent stays clear.
- Around line 31-110: Add test coverage for the null-return “cannot find”
branches in runChecks so every error path is exercised. Extend
verify-sync-failures.test.js with cases that remove the dist header, the CSS_REF
define, the Version: header, and the VERSION constant define, then assert the
resulting errors mention the corresponding missing symbol or file. Use the
existing runChecks helper and the fixture setup in buildFixture to keep the
tests aligned with the current drift/mismatch coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef20fd22-3788-4baa-b207-3413681f077f

📥 Commits

Reviewing files that changed from the base of the PR and between 5422d25 and 32f58f4.

📒 Files selected for processing (10)
  • SLASHED-for-WP/includes/class-rest-controller.php
  • scripts/verify-sync.js
  • tests-php/CategoryMapTest.php
  • tests-php/ColorResolverTest.php
  • tests-php/CssGeneratorDerivedOverridesTest.php
  • tests-php/RestControllerSanitizeOverridesTest.php
  • tests-php/RestControllerSettingsValidatorsTest.php
  • tests-php/TokenDefaultsShapeTest.php
  • tests-php/bootstrap.php
  • tests/verify-sync-failures.test.js

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 8 rules

Grey Divider


Remediation recommended

1. runChecks throws on ENOENT 🐞 Bug ☼ Reliability
Description
runChecks(root) now supports custom roots, but it still uses synchronous readFileSync()/hash
reads without try/catch, so a missing file under a fixture root aborts the entire check instead of
returning errors[]. This makes the new test/fixture-based usage less robust and can turn simple
drift into a hard crash.
Code

scripts/verify-sync.js[R59-65]

+function read(root, rel) {
+  return fs.readFileSync(path.join(root, rel), 'utf8');
}

-function sha256(rel) {
-  return crypto.createHash('sha256').update(fs.readFileSync(path.join(ROOT, rel))).digest('hex');
+function sha256(root, rel) {
+  return crypto.createHash('sha256').update(fs.readFileSync(path.join(root, rel))).digest('hex');
}
Relevance

⭐⭐⭐ High

Team previously accepted wrapping readFileSync with try/catch and ENOENT handling in scripts.

PR-#50

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
read() and sha256() call fs.readFileSync() directly, and runChecks() calls them for multiple
required files under the (now user-supplied) root; any missing path throws before errors[] can
be populated and returned.

scripts/verify-sync.js[59-87]
scripts/verify-sync.js[95-155]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`runChecks(root)` is intentionally callable against fixture trees now, but missing or unreadable required files (ENOENT/EACCES/etc.) cause an immediate synchronous throw from `fs.readFileSync()`, preventing `runChecks()` from returning a complete structured `{ errors, info }` report.

### Issue Context
This behavior was less likely when `root` was effectively fixed to the repo checkout. With custom roots supported, partial fixtures or intentionally broken trees (or future callers) can hit missing paths.

### Fix Focus Areas
- Add safe file read helpers that catch fs errors and push descriptive messages into `errors` instead of throwing.
- Ensure all required reads/hashes in `runChecks()` use the safe helpers.
- Prefer continuing checks to collect as many inconsistencies as possible.

### Fix Focus Areas (code references)
- scripts/verify-sync.js[59-87]
- scripts/verify-sync.js[95-155]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Order-sensitive keys assertion 🐞 Bug ⚙ Maintainability
Description
TokenDefaultsShapeTest::test_source_and_hex_hint_groups_share_identical_keys() compares
array_keys() with assertSame(), which also asserts insertion order, not just key-set equality.
This can fail on harmless reordering even though the test comment describes only “identical keys”.
Code

tests-php/TokenDefaultsShapeTest.php[R40-48]

+	public function test_source_and_hex_hint_groups_share_identical_keys( $source_group, $hint_group ) {
+		$colors = Slashed_Token_Defaults::get_colors();
+		$this->assertArrayHasKey( $source_group, $colors );
+		$this->assertArrayHasKey( $hint_group, $colors );
+		$this->assertSame(
+			array_keys( $colors[ $source_group ] ),
+			array_keys( $colors[ $hint_group ] ),
+			"$hint_group keys must mirror $source_group"
+		);
Relevance

⭐⭐ Medium

No historical evidence found about order-insensitive key-set assertions in PHPUnit tests in this
repo.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new test uses assertSame(array_keys(...), array_keys(...)), which is order-sensitive, despite
the surrounding comment focusing on key equality (not ordering).

tests-php/TokenDefaultsShapeTest.php[34-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The test intends to ensure the source and *_hex_hints groups cover the same families, but it currently fails if the two arrays contain the same keys in different insertion order.

### Issue Context
Order can change during refactors (e.g., formatting/reordering defaults) without changing semantics, so enforcing order here increases brittleness.

### Fix Focus Areas
- Sort both `array_keys(...)` results before comparing, or use PHPUnit canonicalizing assertions.

### Fix Focus Areas (code references)
- tests-php/TokenDefaultsShapeTest.php[40-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread scripts/verify-sync.js
Comment on lines +59 to 65
function read(root, rel) {
return fs.readFileSync(path.join(root, rel), 'utf8');
}

function sha256(rel) {
return crypto.createHash('sha256').update(fs.readFileSync(path.join(ROOT, rel))).digest('hex');
function sha256(root, rel) {
return crypto.createHash('sha256').update(fs.readFileSync(path.join(root, rel))).digest('hex');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Runchecks throws on enoent 🐞 Bug ☼ Reliability

runChecks(root) now supports custom roots, but it still uses synchronous readFileSync()/hash
reads without try/catch, so a missing file under a fixture root aborts the entire check instead of
returning errors[]. This makes the new test/fixture-based usage less robust and can turn simple
drift into a hard crash.
Agent Prompt
### Issue description
`runChecks(root)` is intentionally callable against fixture trees now, but missing or unreadable required files (ENOENT/EACCES/etc.) cause an immediate synchronous throw from `fs.readFileSync()`, preventing `runChecks()` from returning a complete structured `{ errors, info }` report.

### Issue Context
This behavior was less likely when `root` was effectively fixed to the repo checkout. With custom roots supported, partial fixtures or intentionally broken trees (or future callers) can hit missing paths.

### Fix Focus Areas
- Add safe file read helpers that catch fs errors and push descriptive messages into `errors` instead of throwing.
- Ensure all required reads/hashes in `runChecks()` use the safe helpers.
- Prefer continuing checks to collect as many inconsistencies as possible.

### Fix Focus Areas (code references)
- scripts/verify-sync.js[59-87]
- scripts/verify-sync.js[95-155]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +40 to +48
public function test_source_and_hex_hint_groups_share_identical_keys( $source_group, $hint_group ) {
$colors = Slashed_Token_Defaults::get_colors();
$this->assertArrayHasKey( $source_group, $colors );
$this->assertArrayHasKey( $hint_group, $colors );
$this->assertSame(
array_keys( $colors[ $source_group ] ),
array_keys( $colors[ $hint_group ] ),
"$hint_group keys must mirror $source_group"
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

2. Order-sensitive keys assertion 🐞 Bug ⚙ Maintainability

TokenDefaultsShapeTest::test_source_and_hex_hint_groups_share_identical_keys() compares
array_keys() with assertSame(), which also asserts insertion order, not just key-set equality.
This can fail on harmless reordering even though the test comment describes only “identical keys”.
Agent Prompt
### Issue description
The test intends to ensure the source and *_hex_hints groups cover the same families, but it currently fails if the two arrays contain the same keys in different insertion order.

### Issue Context
Order can change during refactors (e.g., formatting/reordering defaults) without changing semantics, so enforcing order here increases brittleness.

### Fix Focus Areas
- Sort both `array_keys(...)` results before comparing, or use PHPUnit canonicalizing assertions.

### Fix Focus Areas (code references)
- tests-php/TokenDefaultsShapeTest.php[40-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

claude added 2 commits July 8, 2026 09:19
Extends the plain-PHP suite past the "pure logic only" boundary using
hand-rolled in-memory get_option/update_option/delete_option/apply_filters
stubs (in the spirit of the existing sanitize_key stub — no mocking framework,
no WordPress install), turning two previously WP-runtime-only classes into
covered ones:

- TokenStoreTest: overrides round-trip, corrupt-option recovery, plugin-
  settings default merge, retired-key stripping, and standalone-mode css_bundle.
- CssGeneratorEmissionTest: the store -> re-validate -> @layer emission path,
  proving the emitter drops unsafe/misnamed stored values, keeps has_overrides()
  in agreement, and expands scale knobs (with explicit tokens winning).

PHPUnit: 166 -> 180 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNzFE6py1whVTmrohL34Ra
The class/variable hint generators had no unit tests, so a future change to the
framework's CSS section-comment format or api-index.json shape would produce
wrong hints that the drift check can only flag as *changed*, never as *wrong*.

Extract the pure transforms and guard the CLI side effects behind a
main-module check (like verify-sync.js) so the modules are importable:
- gen-class-hints.js: export parseCss(src, category), applyCuratedHints(parsed),
  MANUAL_HINTS, OVERRIDE_HINTS.
- gen-variables-hints.js: export buildVariableHints(apiIndex).

Add tests against synthetic fixtures (no framework checkout needed):
- parseCss: section-desc → base+modifier inheritance, multi-section split,
  is-* capture, title fallback, comment-only and pre-section classes ignored.
- applyCuratedHints: override-wins / manual-fills-gap / manual-doesn't-override
  precedence, input not mutated.
- buildVariableHints: token+--sf- filtering, `--` stripping, category default,
  empty/malformed input tolerance.

CLI output is unchanged: `npm run check` still emits 67 class + 729 variable
hints and passes. JS suite: 151 -> 164 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNzFE6py1whVTmrohL34Ra
@jackgranatowski
jackgranatowski merged commit 4e20439 into main Jul 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants