Skip to content

Pin composition-law edge cases via <method>Examples() - #25

Merged
rasuvaeff merged 1 commit into
masterfrom
test/property-examples
Aug 10, 2026
Merged

Pin composition-law edge cases via <method>Examples()#25
rasuvaeff merged 1 commit into
masterfrom
test/property-examples

Conversation

@rasuvaeff

Copy link
Copy Markdown
Owner

Audit of the property-testing-core public API showed that the <method>Examples() mechanism — fixed argument tuples run before the random phase — has zero usage across the monorepo, despite being explicitly recommended in the root AGENTS.md (Principles: "where there is property, there are examples"). This PR is the first pilot.

What

Adds 28 deterministic edge cases across all 8 properties of `CompositionLawsPropertyTest`, plus a shared `operandCatalog()` helper for the named operands (`active` / `inactive` / `pricey` / `all` / `none` / `notActive` / `doubleNeg`):

Property Examples Shape
`andIsCommutative` 6 identity, NOT-wrapped left/right, double negation, disjoint (empty ∩), universal vs empty
`andIsAssociative` 3 all-empty parens, NOT-wrapped middle, double negation
`orIsCommutative` 3 identity, NOT-wrapped left, disjoint union
`orIsAssociative` 2 all-empty union, NOT-wrapped middle
`andIsIdempotent` 3 empty, NOT-wrapped, double negation
`deMorganAnd` 4 both empty (truth-table extreme), both universal, NOT-wrapped both, double negation left
`deMorganOr` 4 same shape as `deMorganAnd`
`distributive` 3 empty factor, b == c, NOT-wrapped a

Why

Mutation proof (issue #22) showed that a broken `QueryBuildingVisitor::visitNot()` can pass random search when both sides of a De Morgan law use the broken NOT identically — the random phase hits the same regression on both sides of the equality and sees no diff. The hand-written `SqliteIntegrationTest::notNegatesCondition` caught it; the property did not.

Examples close that gap deterministically: each known-dangerous shape runs before the random phase, so the test fails on a named example instead of waiting for ~100 random trials to maybe hit it.

Local verification

  1. `composer build` green: 258 tests, +28 assertions (1096 → 1124 on the Integration suite alone, 2518 → 2546 total — exactly one assertion per added example).
  2. Injected a deliberately broken `visitNot()` (early return + skip subquery, mirrors the issue Prove composition-law properties detect a broken NOT visitor #22 mutation): `deMorganAnd` fails on the 4th example `'double negation left'` — assertion history `✓ array(0) ✓ array(5) ✓ array(3) ✗ array(0) vs array(5)`. No random trials needed.
  3. Reverted the injection; `composer build` green again.

Scope

  • `CompositionLawsPropertyTest` only — pilot. If the pattern lands cleanly, follow-up PRs can extend it to `yii3-workflow` (`WorkflowStatefulPropertyTest`, idempotency no-op), `circuit-breaker` (`InMemoryStorageTest`, half-open probe), `yii3-ab-testing` (`WeightedHashAssignmentStrategyTest`, bucket boundary).
  • The body of each property is unchanged; `operandCatalog()` is private and test-only.

The CompositionLawsPropertyTest relied entirely on the random phase to
hit edge cases (NOT-wrapped operands, double negation, empty-result
intersections). Mutation proof on issue #22 already showed that a
broken QueryBuildingVisitor::visitNot() can pass random search when
both sides of a law use the broken NOT identically. Examples run before
the random phase and pin the shapes that broke then.

Added 28 deterministic edge cases across all 8 properties plus a shared
operandCatalog() helper for the named operands (active/inactive/pricey/
all/none/notActive/doubleNeg). Verified locally: a deliberately broken
visitNot() (early return + identity) fails deMorganAndExamples on the
4th example instead of relying on random search to find it.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 24 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3bfbed2-d6c1-40e7-9f13-979072d30296

📥 Commits

Reviewing files that changed from the base of the PR and between 78f0820 and 7b3104c.

📒 Files selected for processing (1)
  • tests/Integration/CompositionLawsPropertyTest.php

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.

@rasuvaeff
rasuvaeff merged commit bb5e895 into master Aug 10, 2026
10 checks passed
@rasuvaeff
rasuvaeff deleted the test/property-examples branch August 10, 2026 17:59
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