Skip to content

tools/constraint_checker: validate @cardinality is only on list fields#195

Open
SoundMatt wants to merge 1 commit into
COVESA:mainfrom
SoundMatt:fix/cardinality-list-only-validator
Open

tools/constraint_checker: validate @cardinality is only on list fields#195
SoundMatt wants to merge 1 commit into
COVESA:mainfrom
SoundMatt:fix/cardinality-list-only-validator

Conversation

@SoundMatt

Copy link
Copy Markdown

Closes the long-standing TODO at src/s2dm/exporters/utils/field.py:174.

@cardinality(min, max) describes the size of a list. On a non-list field the cardinality is already fixed by the GraphQL type itself (T is 0..1, T! is 1..1), so any @cardinality annotation is either redundant or contradicts the GraphQL non-null markers. Until now this misuse went unflagged — a user could write field: Int! @cardinality(min: 0, max: 5) and the constraint checker would happily report no errors.

Changes

  • src/s2dm/exporters/utils/field.py — replace the has_valid_cardinality() placeholder (always returned False) with a real implementation: returns True iff the field is list-typed or has no @cardinality directive.
  • src/s2dm/tools/constraint_checker.py — add check_cardinality_on_list_only() and wire it into ConstraintChecker.run() alongside the existing min <= max check.
  • tests/test_check_constraint.py — five new tests + two existing fixtures updated.

Test fixture updates

Two existing tests (test_cardinality_min_leq_max, test_cardinality_min_gt_max) used bar: Int @cardinality(...) which is invalid under the new rule. Both are switched to bar: [Int] so they continue to test what they were always meant to test (the min <= max constraint), without leaning on a misuse pattern as test scaffolding.

New tests

  • test_cardinality_on_nullable_scalar_is_flaggedbar: Int @cardinality(...) → error
  • test_cardinality_on_non_null_scalar_is_flaggedbar: Int! @cardinality(...) → error
  • test_cardinality_on_list_passes — all four list nullability flavours ([Int], [Int]!, [Int!], [Int!]!) → no error
  • test_no_cardinality_directive_passesbar: Int (no directive at all) → no error

Out of scope

Other potential cardinality validations — negative min/max, soft-warn on min > 0 for nullable lists, etc. — are intentionally left for follow-up PRs to keep this one focused on the specific contradiction the existing TODO calls out.

Local verification: 536 tests pass.

@Cardinality(min, max) describes the size of a list. On a non-list
field the cardinality is already fixed by the GraphQL type itself
(T is 0..1, T! is 1..1), so any @Cardinality annotation is either
redundant or contradicts the GraphQL non-null markers. Until now
this misuse went unflagged.

Implement has_valid_cardinality() in field.py (closing the long-
standing TODO) so it returns True iff the field is list-typed or
has no @Cardinality directive. Wire it into ConstraintChecker via
a new check_cardinality_on_list_only() method and call it from
run().

Two existing test fixtures used 'bar: Int @Cardinality(...)' which
is invalid under the new rule; both are switched to 'bar: [Int]'
so they continue to test the min<=max constraint they were always
meant to test. Five new tests cover the validator end-to-end:
- @Cardinality on a nullable scalar is flagged
- @Cardinality on a non-null scalar is flagged
- @Cardinality on lists of all four nullability flavours passes
- a scalar with no @Cardinality directive at all passes

Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
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