Skip to content

feat(test-fixtures): add shared domain-write fixture catalog - #63

Merged
kazemek merged 4 commits into
mainfrom
feat/shared-domain-write-fixtures
Aug 12, 2026
Merged

kazemek merged 4 commits into
mainfrom
feat/shared-domain-write-fixtures

Conversation

@kazemek

@kazemek kazemek commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Implements Phase 2.13 — Shared Domain Write Test Fixtures: one version-neutral flat domain-to-resource write fixture catalog in jsonapi-java-test-fixtures, consumed by Jackson-major contract tests.

  • Moves the 8 annotated write models (Article, ArticleWithSet, BlogWithJsonProperty, Comment, ConventionalId, Person, Tag, SamplePojo) into the new @NullMarked io.github.kazemek.jsonapi.testfixtures.domainwrite package, with the contract @Nullable members; deletes the 7 records from the jackson3 testmodel package and repoints every adapter-local spec/helper import (behavior unchanged).
  • Adds the DomainWriteScenarios catalog (14 scenarios; all()/byId(String)), the DomainWriteOperation/DomainWriteInput/DomainWriteOutcome/DomainWriteComparisonPolicy types, and Jackson 3 ResourceMapperSpec migrated onto it with a full-catalog coverage assertion (executedScenarioIds == catalogScenarioIds, @Stepwise for deterministic ordering).
  • Adds the TestFixturesDependencyRulesSpec ArchUnit rule (ADR-010) enforcing the major-neutral boundary, amends ADR-010's allowlists, and updates module docs + root README.
  • Relaxed contract (design decision): the catalog is a living, locally-consistent surface that grows by addition — no closed-inventory pins, index-based matrix, or exclusion-manifest machinery. Local invariants (per-entry operation/input/envelope/outcome/policy consistency, complete outcomes, valid comparison policies, fresh unmodifiable inputs) are enforced by DomainWriteScenariosCatalogSpec. The Jackson 2 full-catalog coverage requirement is pinned contractually in the (refined, Not started) Phase 2.18 milestone.
  • Review feedback addressed: unmodifiable shared tag collection (with regression test), strict to-many unordered-policy validation, deterministic coverage ordering, envelope-constant and member-array reuse, milestone wording, and two small jackson3 main-source refactors (S3398 method-placement) plus S1192 literal dedup surfaced by Sonar.

Verification

  • ./gradlew spotlessApply / spotlessCheck — pass
  • ./gradlew clean build — pass (incl. ArchUnit + catalog-integrity + coverage specs)
  • SonarCloud PR Quality Gate — OK (new-code coverage 100%, zero issues in the new-code period); S1192 and both S3398 findings fixed and CLOSED/FIXED

Notes

  • Milestone status: phase-2-13 is Complete (final fresh-context review skipped by maintainer decision; the review loop's last finding was fixed and verified closed). phase-2-18 (Jackson 2 mandate) refined and plan-reviewed (Pass).
  • No commits touch published API behavior; jackson3 main changes are behavior-preserving refactors.

The shared catalog keeps Jackson-major write mapping verifiable without
per-major fixture copies or closed-inventory pins; Jackson 2 must run
the full catalog per the Phase 2.18 milestone.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 49 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3924bf55-909b-4dd7-be3f-d02ae88a1bcd

📥 Commits

Reviewing files that changed from the base of the PR and between f16e878 and 633cb1a.

📒 Files selected for processing (1)
  • jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenariosCatalogSpec.groovy

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e253fe72-b5aa-4780-9f23-b2f84455c766

📥 Commits

Reviewing files that changed from the base of the PR and between ebbeb1d and f16e878.

📒 Files selected for processing (6)
  • .agentWork/milestones/README.md
  • .agentWork/milestones/phase-2-13-shared-domain-write-fixtures.md
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperSpec.groovy
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java
  • jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenariosCatalogSpec.groovy
🚧 Files skipped from review as they are similar to previous changes (4)
  • .agentWork/milestones/README.md
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java
  • jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenariosCatalogSpec.groovy

📝 Walkthrough

Walkthrough

The PR moves domain-write fixtures into a shared version-neutral module, adds an extensible scenario catalog with integrity checks, and updates Jackson 3 tests to execute the catalog. Documentation, dependency rules, and Jackson 2 milestone requirements are also updated.

Changes

Shared domain-write catalog

Layer / File(s) Summary
Fixture and scenario contracts
jsonapi-java-test-fixtures/..., gradle/libs.versions.toml
Adds shared domain models, typed inputs, operations, outcomes, comparison policies, and scenario records.
Catalog construction and validation
jsonapi-java-test-fixtures/..., docs/adr/...
Adds stable-ID catalog access, expected-result builders, catalog invariants, and dependency-boundary tests.
Jackson 3 catalog execution
jsonapi-java-jackson3/...
Replaces individual resource-mapping cases with full catalog execution, operation dispatch, outcome handling, and semantic comparisons.
Catalog documentation and roadmap alignment
.agentWork/..., README.md, jsonapi-java-test-fixtures/README.md
Updates catalog ownership, adapter-local test rules, Jackson 2 requirements, project documentation, and dependency aliases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a shared domain-write fixture catalog for test fixtures.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/shared-domain-write-fixtures

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.

@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.

Actionable comments posted: 5

🧹 Nitpick comments (2)
jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java (1)

203-221: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated envelope literals into constants.

The envelope at Lines 207-210 and the expected document at Lines 216-218 repeat the same Links, Meta, and JsonApiObject values. The scenario passes only when both sides stay identical. An edit to one side alone produces a silent expectation change.

Bind the three values once and reuse them.

♻️ Proposed refactor
+  private static final Links ENVELOPE_LINKS = Links.ofLinks(Collections.singletonMap("self", null));
+
+  private static final Meta ENVELOPE_META = Meta.of(Map.of("key", "value"));
+
+  private static final JsonApiObject ENVELOPE_JSONAPI = JsonApiObject.ofVersion("1.1");

Then use them in the scenario:

-              new DocumentEnvelope(
-                  Links.ofLinks(Collections.singletonMap("self", null)),
-                  Meta.of(Map.of("key", "value")),
-                  JsonApiObject.ofVersion("1.1")),
+              new DocumentEnvelope(ENVELOPE_LINKS, ENVELOPE_META, ENVELOPE_JSONAPI),
               DomainWriteOutcome.document(
                   new JsonApiDocument(
                       new DocumentData.SingleResource(
                           articleResource("1", "T", "B", List.of(), null)),
                       null,
-                      Meta.of(Map.of("key", "value")),
-                      JsonApiObject.ofVersion("1.1"),
-                      Links.ofLinks(Collections.singletonMap("self", null)),
+                      ENVELOPE_META,
+                      ENVELOPE_JSONAPI,
+                      ENVELOPE_LINKS,
                       null,
                       Map.of())),

Declare the constants before SCENARIOS so static initialization order is correct.

🤖 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
`@jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java`
around lines 203 - 221, In DomainWriteScenarios, extract the shared Links, Meta,
and JsonApiObject values used by the “toDocument with envelope passes links,
meta, and jsonapi” scenario into constants declared before SCENARIOS, then reuse
those constants in both the DocumentEnvelope input and expected JsonApiDocument.
jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java (1)

265-281: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: build the member array once per property.

from calls findAnnotationAnywhere three times, and each call allocates a new four-element array. Resolve the members once in from and pass the array to each lookup. The behavior does not change.

♻️ Proposed refactor
     static RoleAnnotations from(BeanPropertyDefinition propertyDefinition) {
+      AnnotatedMember[] members = {
+        propertyDefinition.getField(),
+        propertyDefinition.getGetter(),
+        propertyDefinition.getSetter(),
+        propertyDefinition.getConstructorParameter()
+      };
       return new RoleAnnotations(
-          findAnnotationAnywhere(propertyDefinition, JsonApiId.class),
-          findAnnotationAnywhere(propertyDefinition, JsonApiAttribute.class),
-          findAnnotationAnywhere(propertyDefinition, JsonApiRelationship.class));
+          findAnnotationAnywhere(members, JsonApiId.class),
+          findAnnotationAnywhere(members, JsonApiAttribute.class),
+          findAnnotationAnywhere(members, JsonApiRelationship.class));
     }
 
     private static <A extends Annotation> `@Nullable` A findAnnotationAnywhere(
-        BeanPropertyDefinition propertyDefinition, Class<A> annotationClass) {
-      for (AnnotatedMember member :
-          new AnnotatedMember[] {
-            propertyDefinition.getField(),
-            propertyDefinition.getGetter(),
-            propertyDefinition.getSetter(),
-            propertyDefinition.getConstructorParameter()
-          }) {
+        AnnotatedMember[] members, Class<A> annotationClass) {
+      for (AnnotatedMember member : members) {
         // Jackson's getAnnotation is not `@Nullable-annotated`; use hasAnnotation as the presence
         // check.
         if (member != null && member.hasAnnotation(annotationClass)) {
           return member.getAnnotation(annotationClass);
         }
       }
       return null;
     }

Run spotlessApply and then spotlessCheck if you apply this change.

As per coding guidelines: "Run Spotless formatting when Spotless-covered files or formatter configuration changes: `spotlessApply` followed by `spotlessCheck`."
🤖 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
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java`
around lines 265 - 281, Update from to construct the four-member AnnotatedMember
array once per property and pass it to each annotation lookup instead of
recreating it in findAnnotationAnywhere. Adjust findAnnotationAnywhere to accept
the precomputed members while preserving its existing lookup order and behavior;
run Spotless formatting and verification afterward.

Source: Coding guidelines

🤖 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.

Inline comments:
In @.agentWork/milestones/phase-2-13-shared-domain-write-fixtures.md:
- Around line 39-45: Correct the catalog description around DomainWriteScenarios
to remove the claim that expected outcomes are derived from a Jackson 3 mapper.
State instead that the catalog defines explicit ResourceObject and
JsonApiDocument expectations, while preserving the surrounding explanation about
catalog growth and adapter-suite discovery.

In
`@jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperSpec.groovy`:
- Around line 26-52: Remove the shared executedScenarioIds state and separate
coverage feature from ResourceMapperSpec. Keep the full-catalog assertion
mandatory by validating the scenario IDs within the same parameterized feature
that invokes each DomainWriteScenarios entry, using the deterministic scenario
data available there.

In `@jsonapi-java-test-fixtures/README.md`:
- Around line 39-41: Update the Phase 2.13 wording in the README to describe the
flat write catalog as a deliverable rather than completed, since the milestone
remains In progress; preserve the version-neutral and later-phase fixture
statements.

In
`@jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java`:
- Around line 37-38: Update the TAGS_SET declaration in DomainWriteScenarios to
wrap the LinkedHashSet with an unmodifiable Set while preserving insertion order
and the existing tag values. Keep the shared set contents unchanged so mutation
attempts fail fast.

In
`@jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenariosCatalogSpec.groovy`:
- Around line 144-146: Update the DocumentData.ResourceCollection branch in
linkageIsCollection so it considers only resources declaring relationshipName
and requires every such resource to have collection linkage, using an all-style
validation rather than any-style acceptance. Preserve the existing behavior for
non-collection data.

---

Nitpick comments:
In
`@jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java`:
- Around line 265-281: Update from to construct the four-member AnnotatedMember
array once per property and pass it to each annotation lookup instead of
recreating it in findAnnotationAnywhere. Adjust findAnnotationAnywhere to accept
the precomputed members while preserving its existing lookup order and behavior;
run Spotless formatting and verification afterward.

In
`@jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java`:
- Around line 203-221: In DomainWriteScenarios, extract the shared Links, Meta,
and JsonApiObject values used by the “toDocument with envelope passes links,
meta, and jsonapi” scenario into constants declared before SCENARIOS, then reuse
those constants in both the DocumentEnvelope input and expected JsonApiDocument.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fd37ebc-c48e-4c55-8c39-14dba85e70c6

📥 Commits

Reviewing files that changed from the base of the PR and between 7d99226 and ebbeb1d.

📒 Files selected for processing (45)
  • .agentWork/milestones/README.md
  • .agentWork/milestones/phase-2-13-shared-domain-write-fixtures.md
  • .agentWork/milestones/phase-2-18-jackson2-domain-resource-mapping.md
  • README.md
  • docs/adr/010-architectural-tests.md
  • gradle/libs.versions.toml
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/JsonApiDomainDocument.java
  • jsonapi-java-jackson3/src/main/java/io/github/kazemek/jsonapi/jackson3/internal/MappingDefinitionResolver.java
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/CompoundSerializationSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/DomainDocumentReaderSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/IdentifierConversionSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceBinderSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperIsolationSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMapperSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/ResourceMappingJacksonFeaturesSpec.groovy
  • jsonapi-java-jackson3/src/test/groovy/io/github/kazemek/jsonapi/jackson3/SparseFieldsetSpec.groovy
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/AccessCountingArticle.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/AccessCountingFieldsetArticle.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithArray.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithOptionalRelationship.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ArticleWithRenamedAuthor.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/BaseComment.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ConflictArticle.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/ModeratedComment.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Person.java
  • jsonapi-java-test-fixtures/README.md
  • jsonapi-java-test-fixtures/build.gradle.kts
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/Article.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/ArticleWithSet.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/BlogWithJsonProperty.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/Comment.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/ConventionalId.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteComparisonPolicy.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteInput.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteOperation.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteOutcome.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenario.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenarios.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/Person.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/SamplePojo.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/Tag.java
  • jsonapi-java-test-fixtures/src/main/java/io/github/kazemek/jsonapi/testfixtures/domainwrite/package-info.java
  • jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/architecture/TestFixturesDependencyRulesSpec.groovy
  • jsonapi-java-test-fixtures/src/test/groovy/io/github/kazemek/jsonapi/testfixtures/domainwrite/DomainWriteScenariosCatalogSpec.groovy
💤 Files with no reviewable changes (2)
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Comment.java
  • jsonapi-java-jackson3/src/test/java/io/github/kazemek/jsonapi/jackson3/testmodel/Person.java

Comment thread .agentWork/milestones/phase-2-13-shared-domain-write-fixtures.md Outdated
Comment thread jsonapi-java-test-fixtures/README.md
Addresses PR review findings: unmodifiable shared tag collection, strict
to-many unordered-policy validation, deterministic coverage ordering, and
envelope/member-array reuse.
Invoke every scenario supplier, exercise the SamplePojo bean surface, and
cover the byId/outcome rejection branches to close the new-code coverage
gap on the shared catalog.
@sonarqubecloud

Copy link
Copy Markdown

@kazemek
kazemek merged commit 22358a8 into main Aug 12, 2026
3 checks passed
@kazemek
kazemek deleted the feat/shared-domain-write-fixtures branch August 12, 2026 08:00
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