Skip to content

Do not enforce uniqueItems when it is set to false - #577

Open
gaoflow wants to merge 1 commit into
voxpupuli:masterfrom
gaoflow:fix-unique-items-false-ignored
Open

Do not enforce uniqueItems when it is set to false#577
gaoflow wants to merge 1 commit into
voxpupuli:masterfrom
gaoflow:fix-unique-items-false-ignored

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 27, 2026

Copy link
Copy Markdown

Three-way control on [1, 1] against master: uniqueItems: true is correctly invalid, the keyword absent is correctly valid, and uniqueItems: false is wrongly invalid — same in draft2, draft3, draft4 and draft6. The cause is that Validator#validate dispatches an attribute whenever its keyword is present in the schema, and UniqueItemsAttribute.validate never reads current_schema.schema['uniqueItems'], so presence alone enforces the constraint; draft-04 §5.3.4.2 ("If this keyword has boolean value false, the instance validates successfully") and §5.3.4.3, which makes false the default, say it must be a no-op, as do draft-03 §5.15 and draft-06 §6.13. That matters in practice because an explicit false is the normal way to override an inherited uniqueItems: true, which is exactly the report in #461. The fix reads the value and returns early when it is falsy — the metaschema already constrains uniqueItems to a boolean, so false is the only value whose behaviour changes.

This survived because the official test suite does cover it — 36 cases across draft3/4/6, every one named uniqueItems=false … — but test/common_test_suite_test.rb builds its test methods from Dir[...] at class-definition time, and test/test-suite is empty in a plain checkout, so the file contributes nothing:

$ bundle exec ruby -Ilib -I. test/common_test_suite_test.rb
Finished in 0.04736s
0 tests, 0 assertions, 0 failures, 0 errors, 0 skips

.github/workflows/test.yml checks out without submodules:, and .gitmodules still points at git://github.com/…, which GitHub switched off in 2022 (git ls-remote on it hangs). I've left all of that alone since #387 is already open on it and it's your call — and no unit-test fixture had ever set uniqueItems to anything but true, so nothing else caught it either.

I ran bundle exec rake test (383 → 396 tests, 2359 → 2551 assertions, 0 failures) and bundle exec rubocop clean; reverting only the lib/ change while keeping the new tests fails 8 of them, and disabling the keyword outright fails the pre-existing test_unique_items in all four drafts. Cloning the pinned submodule commit 637f0ac locally and running draft3/4/6 through it takes failures from 161 to 125 with errors unchanged at 92 — exactly the 36 uniqueItems cases, and no other suite file's count moves. I also audited every other attribute class for the same "ignores its own keyword value" defect: all 38 keyword/class pairs registered across drafts 1–6, plus a true/false/absent differential over every boolean-valued keyword (additionalProperties, additionalItems, exclusiveMaximum/exclusiveMinimum, maximumCanEqual/minimumCanEqual, draft3 required, draft1/2 optional, draft6 propertyNames) — all of them read their value correctly, so uniqueItems is the only offender rather than a pattern needing a wider change.

The tests go into the shared ArrayValidation::UniqueItemsTests, which draft2/3/4 already include; I also included that module in Draft6Test, which had no uniqueItems coverage at all.

Closes #461.

Validator#validate dispatches an attribute whenever its keyword is present
in the schema, so UniqueItemsAttribute ran for `uniqueItems: false` just as
it did for `true` -- it never read the keyword's value. Per draft3 5.15,
draft4 5.3.4 and draft6 6.13, false is the default and imposes no
constraint, so a schema that explicitly opts out (or that overrides an
inherited `uniqueItems: true`) was rejecting valid data.

Read the value in the attribute and return early when it is falsy. This is
the smallest possible behaviour change: the metaschema already constrains
uniqueItems to a boolean, so only `false` is affected.

Adds the false and omitted cases to the shared UniqueItemsTests module,
which draft2, draft3 and draft4 already include, and includes that module
in the draft6 tests, which had no uniqueItems coverage at all.
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.

Attribute uniqueItem: false should not trigger validation

1 participant