Skip to content

Add IConstraintSetBuilder for programmatic constraint set construction#532

Merged
david-waltermire merged 6 commits intometaschema-framework:developfrom
david-waltermire:feature/constraint-set-builder
Dec 9, 2025
Merged

Add IConstraintSetBuilder for programmatic constraint set construction#532
david-waltermire merged 6 commits intometaschema-framework:developfrom
david-waltermire:feature/constraint-set-builder

Conversation

@david-waltermire
Copy link
Contributor

@david-waltermire david-waltermire commented Dec 9, 2025

Summary

  • Introduces a fluent builder API for creating IConstraintSet instances programmatically in tests
  • Eliminates the need for XMLBeans dependency when testing constraint-related functionality
  • Migrates ExternalConstraintsModulePostProcessorTest to use the new programmatic builder

New Classes

  • IConstraintSetBuilder: Main builder interface for constraint sets
  • IContextBuilder: Builder for constraint contexts with metapath targeting
  • ConstraintSetBuilder: Implementation that builds MetaConstraintSet
  • ContextBuilder: Implementation that builds MetaConstraintSet.Context

Design

The builder leverages existing constraint builders (IAllowedValuesConstraint.builder(), IMatchesConstraint.builder(), etc.) via a generic constraint() method that accepts any AbstractConstraintBuilder.

Example usage:

IConstraintSet constraintSet = mocking.constraintSet()
    .source(constraintSource)
    .context(ctx -> ctx
        .metapath("//*")
        .constraint(IAllowedValuesConstraint.builder()
            .source(constraintSource)
            .target(IMetapathExpression.compile("@value"))
            .allowedValue(IAllowedValue.of("value1", MarkupLine.fromMarkdown("Value #1"), null))))
    .build();

Test plan

  • New unit tests in ConstraintSetBuilderTest verify builder functionality
  • ExternalConstraintsModulePostProcessorTest migrated and passing
  • CI build passes (mvn install -PCI -Prelease)

Summary by CodeRabbit

  • Tests

    • Tests now construct external constraint sets programmatically (no XML loading), improving reliability and isolation.
    • Test method signatures simplified (removed checked exception declarations).
    • Added comprehensive tests covering allowed-values, matches, nested contexts, imports, multi-contexts, and other constraint scenarios.
  • New Features

    • Added fluent builders and context builders for composing constraint sets and nested contexts to simplify test setup.
    • Test utilities expose a convenience method to obtain constraint-set builders.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

📝 Walkthrough

Walkthrough

Adds test-support fluent builders and interfaces to programmatically construct metaschema constraint sets and contexts, integrates the builder via IModuleMockFactory, replaces XML-based loading in a post-processor test with programmatic construction, and adds unit tests for the new builder API. Test method signature no longer declares checked exceptions.

Changes

Cohort / File(s) Summary
Builder interfaces
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IConstraintSetBuilder.java, core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java
New public interfaces providing a fluent API to configure/build constraint sets and contexts (source(), imports(), context(), metapath(), constraint(), childContext(), build(), and a static builder factory).
Builder implementations
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java, core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java
New classes implementing the interfaces. ConstraintSetBuilder assembles a MetaConstraintSet from source, imports, and contexts and enforces source presence. ContextBuilder collects metapaths, constraints, and child contexts, dispatches supported constraint types into the model, builds nested contexts, and returns MetaConstraintSet.Context instances.
Test support factory
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IModuleMockFactory.java
Added default method constraintSet() returning IConstraintSetBuilder.builder() to expose the new builder from the factory.
Refactored test
core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java
Replaced XML/path-based external-constraints loading with programmatic construction of an IConstraintSet using the builders; aligned module source with the new constraint source; updated ExternalConstraintsModulePostProcessor invocation to accept the built set; removed throws MetaschemaException, IOException from the test method and removed XML loader/path imports; added imports for markup/metapath/collections.
New tests
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java
New JUnit tests covering constraint set creation, allowed-values and matches contexts, nested contexts, imports, and multiple-context scenarios using the builder API.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review ContextBuilder.addConstraint dispatch for correctness across constraint types and proper exception behavior for unsupported types.
  • Validate ConstraintSetBuilder.build enforces source presence and correctly aggregates imports and contexts.
  • Confirm ExternalConstraintsModulePostProcessorTest changes preserve previous semantics (module source alignment, constraints application).
  • Check nullability annotations, defensive copies, and thread-safety assumptions in builders.

Possibly related PRs

Poem

🐇 I hopped through code with nimble paws,
Built constraint gardens without old claws,
Metapaths dance in neat, planted rows,
Tests bloom bright where the builder goes,
A crunchy carrot for passing laws 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.42% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: introducing IConstraintSetBuilder for programmatic constraint set construction, which is the primary objective of the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1f93bd and 546a423.

📒 Files selected for processing (2)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.
🧬 Code graph analysis (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
🔇 Additional comments (6)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1)

21-55: Well-designed fluent builder interface.

The interface follows the fluent builder pattern correctly with:

  • Proper @NonNull annotations on all return types and parameters
  • Clear method naming (metapath, constraint, childContext)
  • Good use of Consumer<IContextBuilder> for nested context configuration
  • Comprehensive Javadoc documentation
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (5)

45-63: Clean builder initialization with proper nullability annotations.

The field declarations and constructor are well-structured. Using ArrayList for mutable lists during building and List.copyOf() later for immutability is a good pattern.


65-70: Good use of lazy compilation.

Using IMetapathExpression.lazyCompile() defers parsing overhead until the expression is actually evaluated, which is appropriate for test fixtures.


72-78: LGTM!

Eagerly building the constraint during configuration is appropriate since the builder is expected to be fully configured when passed.


89-121: Build method is well-structured with proper defensive copying.

The implementation correctly:

  1. Creates the constraint holder before populating it
  2. Uses List.copyOf(metapaths) for immutability
  3. Builds child contexts after parent creation to establish proper parent references
  4. Recursively assembles the context tree

140-160: Verify constraint type hierarchy for correct instanceof dispatch ordering.

The instanceof chain handles seven constraint types. If any of these interfaces have inheritance relationships (e.g., if IIndexConstraint extends IUniqueConstraint), the ordering matters—subtypes must be checked before supertypes to prevent incorrect dispatch. The current order should be verified against the interface definitions to ensure no inheritance relationships will cause early-termination issues.

The Javadoc note at lines 129-131 appropriately documents the need to update this method when adding new constraint types.


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

@david-waltermire david-waltermire added this to the v3.0.0 Milestone 1 milestone Dec 9, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (1)

51-78: Consider adding assertions to verify constraints were added.

The test only asserts assertNotNull(constraintSet) but doesn't verify that the allowed-values constraint was actually added to the context. Adding assertions to check the constraint set's contexts or the specific constraint would strengthen this test.

     // Then
     assertNotNull(constraintSet, "Constraint set should not be null");
+    // Optionally verify the context and constraint were added
+    // e.g., assertFalse(constraintSet.getTargetedConstraintsForScopedContext(...).isEmpty());
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb67ca8 and 746fbaa.

📒 Files selected for processing (7)
  • core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (2 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IConstraintSetBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IModuleMockFactory.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-08T16:52:33.199Z
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.

Applied to files:

  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java
  • core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java
🧬 Code graph analysis (3)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (3)
core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java (1)
  • MarkupLine (32-114)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/MockedModelTestSupport.java (1)
  • MockedModelTestSupport (15-25)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code
  • GitHub Check: Website
🔇 Additional comments (12)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IModuleMockFactory.java (1)

82-91: LGTM!

The new constraintSet() method follows the established pattern of other builder factory methods in this interface (flag(), field(), assembly(), module()). Clean delegation to the static builder factory.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IConstraintSetBuilder.java (1)

21-69: Well-designed fluent builder interface.

The API design is clean and follows the established patterns in the codebase. The use of Consumer<IContextBuilder> for context configuration enables flexible and readable constraint setup without exposing implementation details.

core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (2)

31-53: Good migration from XML to programmatic construction.

The inline comments documenting the original XML structure are helpful for understanding the intent. The test correctly builds a constraint set with an allowed-values constraint targeting @value and applies it to all nodes via the //* metapath.


55-80: Test verifies the integration correctly.

The test validates that ExternalConstraintsModulePostProcessor successfully applies external constraints to a module's assembly definition. The assertion checking definition.getConstraints().size() confirms the constraint was propagated.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (3)

34-49: LGTM - Good baseline test.

The test validates basic builder functionality including source assignment and empty imports. The assertions appropriately verify the expected state.


107-132: Nested context test covers an important scenario.

Testing hierarchical contexts (//parent with child child) validates the builder's ability to handle complex constraint structures. The pattern is useful for real-world constraint definitions.


134-156: Good test for imports functionality.

This test properly validates the import mechanism by creating an imported set, then a main set that references it, and verifying the relationship. The assertions are thorough.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1)

61-73: LGTM - Build logic is straightforward.

The build method properly validates that source is set (with a descriptive message), iterates through context builders to build them, and constructs the final MetaConstraintSet. The build(null) call for root-level contexts is appropriate.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1)

21-59: Well-designed fluent builder interface.

The interface provides a clean API with proper nullability annotations and generics that align with the AbstractConstraintBuilder pattern. The Consumer<IContextBuilder> approach for child contexts enables flexible nested configuration.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (3)

31-73: Clean fluent builder implementation.

The builder correctly implements the interface with proper fluent method chaining. The use of IMetapathExpression.lazyCompile with the source's static context and immediate build() on constraint builders integrates well with the existing constraint infrastructure.


75-107: Build method correctly constructs context hierarchy.

The method properly creates the constraint context with parent-child relationships and recursively builds nested contexts. The ObjectUtils.notNull(metapaths) wrapper on line 96 handles any nullability annotation requirements from the MetaConstraintSet.Context constructor.


109-122: Verify constraint type coverage against current framework constraint definitions.

The addConstraint method currently handles only 4 constraint types. Before extending support for additional types like ICardinalityConstraint, IIndexConstraint, or IUniqueConstraint, verify whether these types exist in the framework and are required for test scenarios by checking the constraint type registry in metaschema-module-constraints.xml.

Introduces a fluent builder API for creating IConstraintSet instances
programmatically in tests, eliminating the need for XMLBeans dependency
when testing constraint-related functionality.

New classes:
- IConstraintSetBuilder: Main builder interface for constraint sets
- IContextBuilder: Builder for constraint contexts with metapath targeting
- ConstraintSetBuilder: Implementation that builds MetaConstraintSet
- ContextBuilder: Implementation that builds MetaConstraintSet.Context

The builder leverages existing constraint builders (IAllowedValuesConstraint,
IMatchesConstraint, etc.) via a generic constraint() method that accepts
any AbstractConstraintBuilder.

Also migrates ExternalConstraintsModulePostProcessorTest to use the new
programmatic builder instead of XmlMetaConstraintLoader.
- Add descriptive error message when source() not called before context()
- Add Javadoc to addConstraint() private method
@david-waltermire david-waltermire force-pushed the feature/constraint-set-builder branch from 746fbaa to 346e3b3 Compare December 9, 2025 14:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)

1-135: Context building and constraint dispatch are sound; consider documenting supported constraint types

The implementation correctly compiles metapaths against the source’s static context, constructs an AssemblyConstraintSet per context, and recursively wires parent/child MetaConstraintSet.Context instances. The addConstraint helper currently supports a specific subset of constraint interfaces (IAllowedValuesConstraint, IMatchesConstraint, IExpectConstraint, IIndexHasKeyConstraint) and throws UnsupportedOperationException for anything else. That’s fine for the current test use, but it may surprise future callers who try to plug in additional constraint types.

Consider either:

  • Explicitly documenting this subset in the class-level Javadoc (and/or constraint(...) Javadoc), and/or
  • Adding a short comment/TODO in addConstraint reminding maintainers to extend the dispatch when new constraint types are introduced elsewhere.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 746fbaa and 346e3b3.

📒 Files selected for processing (7)
  • core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (2 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IConstraintSetBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IModuleMockFactory.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.
📚 Learning: 2025-12-08T16:52:33.199Z
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.

Applied to files:

  • core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java
🧬 Code graph analysis (4)
core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (3)
core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java (1)
  • MarkupLine (32-114)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/MockedModelTestSupport.java (1)
  • MockedModelTestSupport (15-25)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (2)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
🔇 Additional comments (5)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IConstraintSetBuilder.java (1)

1-70: Fluent constraint-set builder interface is clear and well-scoped

The interface cleanly exposes the minimal surface needed for test-time constraint-set construction (source, imports, contexts, build), and the static builder() entry point keeps usage consistent with other test-support builders. No issues from an API or nullability standpoint.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IModuleMockFactory.java (1)

83-91: constraintSet() helper is consistent with existing factory methods

The new default constraintSet() method matches the established pattern for other builders on this factory and cleanly exposes the new test-support API. No further changes needed.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ConstraintSetBuilder.java (1)

1-75: Constraint set builder implementation is correct; treat as single-use

The implementation correctly enforces that source is set before adding contexts or building, and delegates context construction cleanly to ContextBuilder. Given that imports and contexts are accumulated and never cleared, this builder should be treated as single-use (per constraint set) in tests—which matches how it’s used now. No mandatory changes from a correctness standpoint.

core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/IContextBuilder.java (1)

1-59: Context builder interface cleanly captures the intended usage

The API for setting metapaths, adding constraints via generic AbstractConstraintBuilder instances, and configuring child contexts is coherent and type-safe for the intended test-support use. It aligns well with the concrete ContextBuilder implementation.

core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java (1)

10-81: Programmatic constraint construction preserves original test semantics

The refactored test builds the external IConstraintSet and module entirely in code, closely mirroring the original XML context (//*) and allowed-values constraint on @value. Using MockedModelTestSupport.constraintSet() plus the module builder keeps everything in one place and removes IO/XML concerns while still validating that ExternalConstraintsModulePostProcessor applies a single constraint to assembly a. This looks correct and easier to maintain.

…traint types

- Add class-level Javadoc to ContextBuilder listing supported constraint
  types (IAllowedValuesConstraint, IMatchesConstraint, IExpectConstraint,
  IIndexHasKeyConstraint) and noting which types will throw
  UnsupportedOperationException
- Add source assertions to test methods and clarify that constraint
  application is tested in ExternalConstraintsModulePostProcessorTest
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (3)

70-76: Consider defaulting constraint source from the context to avoid fragile usage

AbstractConstraintBuilder.validate() requires a non-null source, so calling constraintBuilder.build() here will fail if the caller forgot to set .source(...) on the builder. That’s easy to miss in tests and leads to a runtime failure somewhat far from the call site.

Since this builder already captures a context-level source, consider setting it on the supplied builder before building, so the common case “just works”:

   public <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> IContextBuilder constraint(
       @NonNull AbstractConstraintBuilder<B, C> constraintBuilder) {
-    this.constraints.add(constraintBuilder.build());
+    // Default the source for convenience in tests; callers that care about a
+    // different source can set it explicitly before passing the builder.
+    constraintBuilder.source(source);
+    this.constraints.add(constraintBuilder.build());
     return this;
   }

This does overwrite any previously configured source on the builder, so if you rely on more granular sources, you may instead want to assert/document that a source must be set before calling constraint(...).


95-109: Avoid potential aliasing of metapaths between builder and built context

MetaConstraintSet.Context receives the metapaths list directly (wrapped only by ObjectUtils.notNull), so if the builder is mutated after build(...) (e.g., reused for another configuration), those mutations could leak into an already-built context unless MetaConstraintSet.Context makes its own defensive copy.

If contexts are meant to be immutable post-build, consider copying here:

-    MetaConstraintSet.Context context = new MetaConstraintSet.Context(
-        parent,
-        source,
-        ObjectUtils.notNull(metapaths),
-        modelConstrained);
+    MetaConstraintSet.Context context = new MetaConstraintSet.Context(
+        parent,
+        source,
+        new ArrayList<>(metapaths),
+        modelConstrained);

This keeps the builder reusable without depending on the internals of MetaConstraintSet.Context.


134-147: addConstraint dispatch is correct; remember to extend when new constraint types are added

The instanceof dispatch for IAllowedValuesConstraint, IMatchesConstraint, IExpectConstraint, and IIndexHasKeyConstraint matches the documented “supported types” list, and throwing UnsupportedOperationException for others is a good fail-fast behavior in tests.

As new constraint types are introduced (e.g., additional model constraints), this method will need to be extended to keep the test-support builder in sync with the core model. When you add new constraint types to the metaschema, that’s also the point where you typically update the constraint type lists in metaschema-module-constraints.xml and the spec docs; this helper should be updated alongside those changes. Based on learnings, ...

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 346e3b3 and bd0d18c.

📒 Files selected for processing (2)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.
🧬 Code graph analysis (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (2)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Website
  • GitHub Check: Code
🔇 Additional comments (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)

43-52: Overall builder structure looks solid for test support

The field layout (single ISource, plus lists for metapaths, constraints, and child contexts) provides a clear, minimal surface for constructing nested contexts, and the implementation stays cleanly in the test-support namespace without leaking into production code. No issues here.

Extend ContextBuilder to support the full set of constraint types:
- IAllowedValuesConstraint (existing)
- IMatchesConstraint (existing)
- IExpectConstraint (existing)
- IIndexHasKeyConstraint (existing)
- ICardinalityConstraint (new)
- IIndexConstraint (new)
- IUniqueConstraint (new)

Add comprehensive unit tests for each constraint type to verify
the builder correctly constructs constraint sets with all supported
constraint types.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (3)

73-79: Simplify constraint(...) generics for readability

The current signature

public <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> IContextBuilder constraint(
    @NonNull AbstractConstraintBuilder<B, C> constraintBuilder)

is more complex than needed since you only call build() and store the result as IConstraint. You could simplify to something like:

- public <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> IContextBuilder constraint(
-     @NonNull AbstractConstraintBuilder<B, C> constraintBuilder) {
+ public IContextBuilder constraint(
+     @NonNull AbstractConstraintBuilder<?, ? extends IConstraint> constraintBuilder) {
-   this.constraints.add(constraintBuilder.build());
+   this.constraints.add(constraintBuilder.build());
    return this;
  }

This keeps the API just as type-safe for callers while making it easier to read and maintain.


90-122: Consider defensively copying metapaths when building the context

build(...) passes the mutable metapaths list directly into MetaConstraintSet.Context:

MetaConstraintSet.Context context = new MetaConstraintSet.Context(
    parent,
    source,
    ObjectUtils.notNull(metapaths),
    modelConstrained);

If the same ContextBuilder were reused or modified after build, those changes would be visible through the already-built context. For a builder, it’s usually safer to decouple internal mutable state from the built object:

-    MetaConstraintSet.Context context = new MetaConstraintSet.Context(
-        parent,
-        source,
-        ObjectUtils.notNull(metapaths),
-        modelConstrained);
+    MetaConstraintSet.Context context = new MetaConstraintSet.Context(
+        parent,
+        source,
+        List.copyOf(ObjectUtils.notNull(metapaths)),
+        modelConstrained);

This keeps the built context immutable with respect to further builder mutations, without changing the external API.


124-157: Keep addConstraint and the documented supported constraint types in sync

addConstraint explicitly dispatches on each known constraint interface and throws UnsupportedOperationException for anything else. This matches the Javadoc list of supported types, but it also means:

  • Any new IConstraint subtype added to the model will cause this builder to fail at runtime until addConstraint is updated.
  • The Javadoc list (lines 34–44) and this if/else chain must be updated together to avoid divergence.

Given the project’s pattern of updating central constraint-type registries and docs when adding new constraint kinds, it would be good to treat this method as another place that must be kept in lockstep with the constraint model and documentation. You might consider adding a brief comment here to that effect to make future updates harder to miss. Based on learnings, this aligns with keeping constraint type lists and syntax docs updated when new constraints are introduced.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd0d18c and 6423f31.

📒 Files selected for processing (2)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/tests/ConstraintSetBuilderTest.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.
🧬 Code graph analysis (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (2)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
core/src/main/java/gov/nist/secauto/metaschema/core/util/ObjectUtils.java (1)
  • ObjectUtils (18-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code
  • GitHub Check: Website
🔇 Additional comments (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)

31-55: ContextBuilder documentation and structure are clear and aligned

The class Javadoc clearly enumerates the supported constraint types, and the field layout (source, metapaths, constraints, childContexts) matches the intended builder responsibilities for test contexts. This is a solid, focused test utility.

…tainability note

- Use List.copyOf() for defensive copy of metapaths when building context
  to ensure built context is immutable with respect to builder mutations
- Add note to addConstraint() Javadoc reminding maintainers to update
  this method when new constraint types are added to the model
- Remove unused ObjectUtils import
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (3)

72-78: Consider simplifying generics on constraint(...) for readability

The method’s type parameters <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> don’t provide benefits beyond what the parameter type already ensures, and they slightly increase visual noise for a test helper.

You could simplify the signature to reduce generics clutter while keeping type safety:

-  public <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> IContextBuilder constraint(
-      @NonNull AbstractConstraintBuilder<B, C> constraintBuilder) {
+  public IContextBuilder constraint(
+      @NonNull AbstractConstraintBuilder<?, ? extends IConstraint> constraintBuilder) {

This should still accept all existing constraint builders without impacting call sites.


80-87: Child context construction always reuses the same ISource

childContext currently always creates children with the same source as the parent, and build(...) wires them into a tree via the parent parameter. That’s perfectly fine for the current test scenarios where a single logical source is used.

If you later need tests that combine constraints from multiple sources in a single tree, you might consider an overload like:

public IContextBuilder childContext(@NonNull ISource childSource,
                                    @NonNull Consumer<IContextBuilder> childConfigurer)

so callers can explicitly vary the source per child when needed, while keeping the existing API as a convenience.

Also applies to: 96-121


123-160: Dispatcher in addConstraint is clear but may become a maintenance hotspot

The addConstraint method’s explicit instanceof chain is clear and matches the class Javadoc listing supported constraint types. As more constraint types are added, this is another place that must be updated in lockstep with the core constraint model and associated documentation (on top of the schema and spec updates already required). Based on learnings, this aligns with the broader need to keep constraint type lists in sync across code and docs.

If the core model ever gains either:

  • a generic addConstraint(IConstraint) on IModelConstrained, or
  • a visitor-style API on IConstraint,

you could centralize this dispatch there and have the builder delegate, avoiding another location that must be touched whenever a new constraint type is introduced.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6423f31 and a1f93bd.

📒 Files selected for processing (1)
  • core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: metaschema-framework/metaschema PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-08T16:52:33.199Z
Learning: When adding new constraint types, update the constraint type lists for `define-flag`, `define-field`, and `define-assembly` in `schema/metaschema/metaschema-module-constraints.xml` and add a new section with syntax table and examples in `website/content/specification/syntax/constraints.md`.
🧬 Code graph analysis (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)
core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java (1)
  • AbstractConstraintBuilder (36-310)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Website
  • GitHub Check: Code
🔇 Additional comments (1)
core/src/test/java/gov/nist/secauto/metaschema/core/testsupport/builder/ContextBuilder.java (1)

45-70: ContextBuilder core state and metapath handling look good

Constructor, field initialization, and metapath(String) are straightforward and appropriate for test support: source is final, collections are initialized eagerly, and using IMetapathExpression.lazyCompile with source.getStaticContext() is a good fit for on-demand compilation in tests. No changes needed here.

Replace named type parameters with wildcards since the method only
calls build() on the builder and doesn't need full type inference.

Before: <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint>
After:  AbstractConstraintBuilder<?, ? extends IConstraint>
@david-waltermire david-waltermire merged commit 226e13f into metaschema-framework:develop Dec 9, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 9, 2025
3 tasks
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