Skip to content

Address code review feedback for domain ports PR#259

Merged
leynos merged 1 commit intomainfrom
domain-ports-feedback
Dec 18, 2025
Merged

Address code review feedback for domain ports PR#259
leynos merged 1 commit intomainfrom
domain-ports-feedback

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Dec 18, 2025

  • Add schema constraints to UserSchema matching domain invariants:
    • id: format=uuid
    • display_name: minLength=3, maxLength=32, pattern for allowed chars
  • Add ErrorCode sync test verifying domain serialization matches schema
  • Consolidate OpenAPI tests: remove duplicates from doc.rs covered by BDD
  • Refactor remaining doc.rs tests to use structured Schema inspection
  • Define OpenAPI acronym on first use in exec plan documentation

🤖 Generated with Claude Code

Summary by Sourcery

Refine OpenAPI schema definitions and tests to better align with domain invariants and documentation.

New Features:

  • Tighten UserSchema OpenAPI constraints to reflect domain UUID and display name invariants.
  • Add a regression test ensuring ErrorCode domain serialization stays in sync with its OpenAPI schema representation.

Enhancements:

  • Simplify OpenAPI documentation tests to focus on schema field structure using direct schema inspection instead of JSON string matching.
  • Clarify execution plan documentation by expanding the OpenAPI acronym on first use.

Tests:

  • Remove redundant OpenAPI registration/response tests now covered by BDD, and add targeted schema field tests for Error and User objects.

- Add schema constraints to UserSchema matching domain invariants:
  - id: format=uuid
  - display_name: minLength=3, maxLength=32, pattern for allowed chars
- Add ErrorCode sync test verifying domain serialization matches schema
- Consolidate OpenAPI tests: remove duplicates from doc.rs covered by BDD
- Refactor remaining doc.rs tests to use structured Schema inspection
- Define OpenAPI acronym on first use in exec plan documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Dec 18, 2025

Reviewer's Guide

Refines OpenAPI-related schemas and tests to better align HTTP layer constraints with domain invariants, consolidates redundant OpenAPI tests in favor of BDD coverage, and tightens documentation wording around OpenAPI usage.

Class diagram for updated OpenAPI HTTP schemas and ErrorCode enum

classDiagram
    class UserSchema {
        +String id
        +String display_name
    }

    class ErrorSchema {
        +String code
        +String message
    }

    class ErrorCode {
        <<enum>>
        InvalidRequest
        Unauthorized
        Forbidden
        NotFound
        InternalError
    }

    ErrorSchema --> ErrorCode : uses
Loading

File-Level Changes

Change Details Files
Refactor OpenAPI schema tests to inspect structured schema objects instead of JSON string matching, while delegating registration/reference checks to existing BDD tests.
  • Remove tests that assert schema registration and response references via JSON string contains checks, now covered by backend/tests/openapi_schemas_bdd.rs.
  • Introduce a helper function that asserts a schema is an Object and contains a given field name using utoipa::openapi::schema::Schema and RefOr.
  • Update error schema test to fetch the Error schema from components.schemas and assert presence of code and message fields via the helper.
  • Update user schema test to fetch the User schema from components.schemas and assert presence of id and display_name fields via the helper.
backend/src/doc.rs
Tighten UserSchema OpenAPI constraints to mirror domain invariants and add a serialization sync test for ErrorCode.
  • Annotate UserSchema.id with OpenAPI attributes specifying String type, UUID format, and an example value, noting it matches the domain User invariant.
  • Annotate UserSchema.display_name with OpenAPI attributes specifying String type, length bounds (3–32), a regex pattern for allowed characters, and an example, documenting alignment with the DisplayName domain invariant.
  • Add a test that iterates over all ErrorCode domain variants and asserts their JSON-serialized strings match the expected snake_case names used in ErrorCodeSchema renames, ensuring schema stays in sync with domain.
  • Retain existing error and error schema tests while adding the new ErrorCode synchronization check to the test module.
backend/src/inbound/http/schemas.rs
Clarify documentation by expanding the OpenAPI acronym on first mention in the execution plan.
  • Update the exec plan bullet describing framework types in the domain to spell out OpenAPI as “Open API Specification” before using the acronym alongside Utoipa.
docs/execplans/phase-0-update-modules-to-use-domain-ports.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 18, 2025

Summary by CodeRabbit

Release Notes

  • Improvements
    • Enhanced API schema validation for user display names with stricter constraints (3–32 characters, alphanumeric characters, spaces, and underscores allowed).
    • Improved user ID schema documentation with explicit UUID format specification for better API clarity and validation consistency.

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

Walkthrough

Refactors test assertions within the documentation module to validate schema fields directly rather than inspecting serialised JSON. Enriches user schema definitions with UUID formatting and display name validation constraints (length and pattern). Adds a new test verifying error code serialisation consistency. Updates planning documentation with clarified OpenAPI terminology.

Changes

Cohort / File(s) Change Summary
Schema validation and test enhancements
backend/src/doc.rs, backend/src/inbound/http/schemas.rs
Replaces direct JSON string assertions with a field-assertion helper function (assert_object_schema_has_field). Adds UUID format metadata and example to UserSchema.id. Augments UserSchema.display_name with domain-driven constraints: min/max length (3–32), regex pattern (^[A-Za-z0-9_ ]+$), and example value. Introduces error_code_schema_matches_domain_serialization test to verify domain ErrorCode variants serialise to schema string keys.
Planning documentation update
docs/execplans/phase-0-update-modules-to-use-domain-ports.md
Minor textual refinement: expands "OpenAPI/Utoipa framework types in domain" heading to "OpenAPI (Open API Specification) / Utoipa framework types in domain" for clarity.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Check the test refactoring in backend/src/doc.rs to ensure the new field assertions remain equivalent in coverage to prior JSON checks
  • Validate the regex pattern and length constraints in UserSchema are production-ready and match domain requirements
  • Confirm the error code serialisation test covers all ErrorCode variants

Poem

🔍 Test assertions now find fields with grace,
Schemas bloom with UUID and space,
Constraints dance in patterns tight,
Domain rules now shine bright,
Error codes in perfect flight ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly relates to the main objective of addressing feedback from a prior domain ports PR, which encompasses all three file changes: schema constraint updates, test refactoring, and documentation clarification.
Description check ✅ Passed The description comprehensively outlines the changeset: schema constraints added to UserSchema, ErrorCode synchronisation test introduced, OpenAPI tests consolidated, doc.rs tests refactored, and documentation expanded—all matching the actual file changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch domain-ports-feedback

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21a1772 and 2128d12.

📒 Files selected for processing (3)
  • backend/src/doc.rs (1 hunks)
  • backend/src/inbound/http/schemas.rs (2 hunks)
  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{rs,ts,tsx,js,jsx,py,md}

📄 CodeRabbit inference engine (AGENTS.md)

Use consistent spelling and grammar with en-GB-oxendict ("-ize" / "-yse" / "-our") spelling and grammar, except for references to external APIs.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update documentation in docs/ when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve to ensure accuracy and currency.
Documentation must use en-GB-oxendict ("-ize" / "-yse" / "-our") spelling and grammar, with the exception of the "LICENSE" file which should remain unchanged.

Follow the Documentation style guide for clear, consistent documentation conventions

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns; code blocks at 120 columns; tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
**/*.{rs,ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,ts,tsx,js,jsx,py}: Comment why, not what—explain assumptions, edge cases, trade-offs, or complexity; do not echo the obvious.
Favour clarity over cleverness—be concise but prefer explicit over terse or obscure idioms; prioritise code that is easy to follow.
Use functions and composition to avoid repetition; prefer generators or comprehensions and declarative code to imperative repetition when readable.
Keep functions small and meaningful—single responsibility, clear purpose, and obey command/query segregation.
Name things precisely—use clear, descriptive variable and function names; for booleans, prefer names with is, has, or should.
Structure logically—each file should encapsulate a coherent module; group related code close together.
Illustrate with clear examples in function documentation demonstrating usage and outcome; for tests, omit examples where the example only reiterates test logic.
Keep file size manageable—no single code file may exceed 400 lines; break up long switch statements and move large test data blocks to external files.
New functionality or changes in behaviour must be fully validated by relevant unittests and behavioural tests before committing.
When fixing a bug, provide a unittest demonstrating the corrected behaviour to validate the fix and guard against regression.

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
**/*.{rs,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Group by feature, not layer—colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make fmt, make lint, and make test before committing Rust code; these wrap cargo fmt, cargo clippy, and cargo test.
Clippy warnings must be disallowed; fix any warnings emitted during tests in the code itself rather than silencing them.
For excessively long functions, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
For functions with too many parameters, group related parameters in meaningfully named structs.
Where a function returns a large error, consider using Arc to reduce the amount of data returned.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments, not before.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort; lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow when suppressing warnings.
Where a function is unused with specific features selected, use conditional compilation with #[cfg] or #[cfg_attr].
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer single-line versions of functions where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) }).
Write unit and behavioural tests for new Rust functionality; run both before and after making any change.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated tests with #[rstest(...)] parameterised cases in Rust.
Prefer mockall for mocks/stubs...

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
🧠 Learnings (2)
📚 Learning: 2025-12-14T12:57:20.659Z
Learnt from: leynos
Repo: leynos/wildside PR: 253
File: docs/wildside-pwa-design.md:28-30
Timestamp: 2025-12-14T12:57:20.659Z
Learning: In the leynos/wildside repository, documentation should follow en-GB-oxendict spelling standards that require -ize endings for localization-related terms: use 'localization', 'localizability', and 'localizable' (not -ise variants). Apply this guideline to all Markdown documentation files under the docs/ directory and any future docs as they are added.

Applied to files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
📚 Learning: 2025-12-14T12:57:30.806Z
Learnt from: leynos
Repo: leynos/wildside PR: 253
File: docs/wildside-pwa-data-model.md:156-158
Timestamp: 2025-12-14T12:57:30.806Z
Learning: Adopt en-GB-oxendict spelling in all Wildside documentation. Use -ize endings (e.g., localize, localization, localized) instead of -ise endings (e.g., localise) wherever the en-GB-oxendict style guide applies. Apply this consistently across all docs under the docs directory.

Applied to files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
🔍 Remote MCP Ref

Now let me search for more information about the test refactoring pattern and domain-driven design related to the PR:Now I have gathered sufficient context. Let me compile the findings into a focused summary for the PR review.

Summary of Additional Context for PR #259 Review

Based on comprehensive research of the relevant technologies and patterns, here is the additional context that would help in reviewing this PR:

1. Utoipa Schema Constraints Implementation

Utoipa supports max_length, min_length, and pattern attributes to define validation constraints for string types. The PR's changes align with Utoipa's standard practices: Examples show using #[schema(format = "base64", example = "cri4", min_length = 4, max_length = 6)] to specify constraints and formats.

Key validation point: The maximum length of a schema cannot be less than the minimum length—if a minimum length is specified, the maximum length must be greater than or equal to that minimum length. The PR sets min_length=3 and max_length=32 for display_name, which satisfies this requirement.

2. UUID Format in OpenAPI

The uuid format represents a Universally Unique IDentifier as defined in RFC4122. OpenAPI supports formats like "uuid" even though they may be undefined by the specification, and tools can use these formats for documentation purposes. Using format = "uuid" on the id field follows industry best practices for REST API documentation and enables better OpenAPI tooling support.

3. Domain-Driven Design with Ports & Adapters Context

The PR title references "domain ports," which aligns with hexagonal architecture principles. Hexagonal architecture isolates business logic from infrastructure code, with the application communicating with external components through interfaces called ports, using adapters to translate technical exchanges. Creating stronger types (like domain objects) for validation can introduce a place for validation and improves expressiveness of ports. The PR's schema constraints directly support this pattern by ensuring OpenAPI adapters accurately reflect domain invariants (3-32 char display names, UUID IDs).

4. Test Refactoring Approach

The PR consolidates tests by removing duplicates covered by external BDD tests and refactoring doc.rs tests to use structured schema inspection (assert_object_schema_has_field). This approach reduces brittle string-matching assertions in favor of targeted field validation, improving test maintainability.

5. ErrorCode Serialization Sync

The new test error_code_schema_matches_domain_serialization ensures that domain ErrorCode enum variants correctly serialize to their OpenAPI schema representations (e.g., "invalid_request"). This maintains consistency between the domain model and the OpenAPI contract adapter.,,,,

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (7)
docs/execplans/phase-0-update-modules-to-use-domain-ports.md (1)

13-14: LGTM - clear acronym definition.

Expanding "OpenAPI" on first use improves documentation clarity and follows best practices.

backend/src/doc.rs (3)

71-74: Good context for test scope boundaries.

The comment clarifies that registration and endpoint reference tests are handled by BDD tests, helping maintainers understand the test distribution.


85-96: Excellent refactoring to structured assertions.

The helper function replaces brittle JSON string matching with direct schema inspection. This improves test maintainability and provides clearer failure messages.


98-116: LGTM - focused field validation.

The refactored tests use the new helper to verify schema structure directly, which is more robust than string-based assertions.

backend/src/inbound/http/schemas.rs (3)

70-76: LGTM - UUID format correctly specified.

The schema now documents that id is a UUID with proper format annotation and a valid example, matching the domain invariant.


79-88: Verify pattern enforcement of "trimmed" invariant.

The comment states the domain invariant includes "trimmed", but the pattern ^[A-Za-z0-9_ ]+$ permits leading and trailing spaces (e.g., " Ada " would match). This could be intentional if:

  • The schema documents permissive input and the domain normalizes by trimming, or
  • An inconsistency where the pattern should enforce ^[A-Za-z0-9_]([A-Za-z0-9_ ]*[A-Za-z0-9_])?$ to match the trimmed invariant exactly.

Clarify whether the schema should enforce trimming or whether the comment should indicate that trimming is a domain-layer concern.

#!/bin/bash
# Verify how the domain DisplayName type handles whitespace

# Search for DisplayName validation or construction logic
ast-grep --pattern $'impl DisplayName {
  $$$
}'

# Search for display_name validation patterns
rg -nP -C5 'display.*name|DisplayName' --type=rust -g '!**/test/**' -g 'backend/src/domain/**'

158-184: Excellent synchronization test.

This test ensures domain ErrorCode serialization remains consistent with schema renames. The explicit mapping forces updates when new variants are added, preventing drift between domain and OpenAPI contract.


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

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • The display_name schema comment says the value is trimmed but the regex ^[A-Za-z0-9_ ]+$ still allows leading/trailing and multiple spaces; either tighten the pattern (e.g., disallow leading/trailing spaces) or relax the comment so it matches what the schema actually enforces.
  • The error_code_schema_matches_domain_serialization test won’t fail automatically when new ErrorCode variants are added (the array is non-exhaustive and doesn’t use pattern matching), so consider restructuring it with a match on ErrorCode to get compile-time exhaustiveness or at least adding a comment that this test must be updated whenever variants change.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `display_name` schema comment says the value is trimmed but the regex `^[A-Za-z0-9_ ]+$` still allows leading/trailing and multiple spaces; either tighten the pattern (e.g., disallow leading/trailing spaces) or relax the comment so it matches what the schema actually enforces.
- The `error_code_schema_matches_domain_serialization` test won’t fail automatically when new `ErrorCode` variants are added (the array is non-exhaustive and doesn’t use pattern matching), so consider restructuring it with a `match` on `ErrorCode` to get compile-time exhaustiveness or at least adding a comment that this test must be updated whenever variants change.

## Individual Comments

### Comment 1
<location> `backend/src/doc.rs:85-94` </location>
<code_context>
-            json.contains(&format!("#/components/schemas/{USER_SCHEMA_NAME}")),
-            "Responses should reference User schema"
-        );
+    /// Assert that an Object schema contains a field with the given name.
+    fn assert_object_schema_has_field(schema: &RefOr<Schema>, field: &str) {
+        match schema {
+            RefOr::T(Schema::Object(obj)) => {
+                assert!(
+                    obj.properties.contains_key(field),
+                    "schema should have field '{field}'"
+                );
+            }
+            _ => panic!("expected Object schema"),
+        }
     }
</code_context>

<issue_to_address>
**issue:** Handle referenced or composed schemas more defensively in the helper.

This helper will panic whenever the schema is a `$ref` or wrapped in a combinator (e.g., `AllOf`), which is a common pattern in `utoipa`. If `Error` or `User` are ever referenced or composed instead of inlined, these tests will fail despite a valid schema. Consider resolving `RefOr::Ref` (and other relevant variants) to an `Object` before checking `properties`, or at least include the schema name in the failure message so changes in representation are easier to diagnose.
</issue_to_address>

### Comment 2
<location> `backend/src/inbound/http/schemas.rs:78-88` </location>
<code_context>
     /// Display name shown to other users.
-    #[schema(value_type = String, example = "Ada Lovelace")]
+    ///
+    /// Matches the domain `DisplayName` invariant: trimmed, non-empty,
+    /// 3–32 characters, alphanumeric plus spaces and underscores.
+    #[schema(
+        value_type = String,
+        min_length = 3,
+        max_length = 32,
+        pattern = "^[A-Za-z0-9_ ]+$",
+        example = "Ada Lovelace"
+    )]
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The display_name pattern does not fully reflect the "trimmed" invariant.

The schema currently accepts values (e.g. leading/trailing or multiple consecutive spaces) that violate the `DisplayName` “trimmed” invariant. To keep the OpenAPI contract aligned with the domain, either tighten the regex to exclude those cases or adjust the comment to state that trimming is enforced in the domain layer, not by the schema itself.

```suggestion
    /// Display name shown to other users.
    ///
    /// Matches the domain `DisplayName` invariant: trimmed (no leading,
    /// trailing, or consecutive spaces), non-empty, 3–32 characters,
    /// alphanumeric plus spaces and underscores.
    #[schema(
        value_type = String,
        min_length = 3,
        max_length = 32,
        // One or more groups of `[A-Za-z0-9_]+` separated by single spaces:
        // - no leading/trailing spaces
        // - no consecutive spaces
        pattern = "^[A-Za-z0-9_]+(?: [A-Za-z0-9_]+)*$",
        example = "Ada Lovelace"
    )]
```
</issue_to_address>

### Comment 3
<location> `docs/execplans/phase-0-update-modules-to-use-domain-ports.md:13` </location>
<code_context>
    from `middleware/trace.rs`
-2. **OpenAPI/Utoipa framework types in domain**: Domain types derive `ToSchema`
-   from the `utoipa` crate
+2. **OpenAPI (Open API Specification) / Utoipa framework types in domain**: Domain
+   types derive `ToSchema` from the `utoipa` crate

</code_context>

<issue_to_address>
**suggestion (typo):** Use the correct, consistent spelling "OpenAPI Specification" in the parenthetical.

This parenthetical is the only place that uses "Open API" with a space. Please update it to "OpenAPI Specification" to match the official name and the rest of the document.

```suggestion
2. **OpenAPI (OpenAPI Specification) / Utoipa framework types in domain**: Domain
```
</issue_to_address>

### Comment 4
<location> `backend/src/inbound/http/schemas.rs:72` </location>
<code_context>
-    #[schema(value_type = String, example = "3fa85f64-5717-4562-b3fc-2c963f66afa6")]
+    ///
+    /// Matches the domain `User` invariant of being a UUID.
+    #[schema(
+        value_type = String,
+        format = "uuid",
</code_context>

<issue_to_address>
**issue (review_instructions):** Add tests that verify the new OpenAPI constraints on `UserSchema.id` and `UserSchema.display_name`, covering both behavioural and unit aspects.

You change the OpenAPI contract by adding `format = "uuid"`, `min_length`, `max_length`, and `pattern` to `UserSchema`, but you do not add tests that assert these constraints in the generated schema or at the behavioural level.

Add unit tests (e.g. alongside `openapi_user_schema_has_required_fields` in `backend/src/doc.rs`) that inspect the generated schema and assert:
- `id` has `format = "uuid"`.
- `display_name` has the configured `min_length`, `max_length`, and `pattern`.

Also add (or extend existing) behavioural/BDD tests to ensure the exposed OpenAPI document used by clients actually includes these constraints, so both the unit and behavioural test requirements are satisfied for this change in feature behaviour.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 5
<location> `backend/src/inbound/http/schemas.rs:158` </location>
<code_context>
         );
     }
+
+    /// Verify domain ErrorCode serialization matches schema renames.
+    ///
+    /// This test fails if a new domain variant is added without updating
</code_context>

<issue_to_address>
**nitpick (review_instructions):** Comment uses US spelling for “serialisation/serialise”; please switch to en-GB-oxendict forms in the doc and inline comments.

The new comments around `error_code_schema_matches_domain_serialization` use US-style forms:
- `serialization` in the doc comment
- `serialize` in the inline `//` comment

Per the guidelines, these should use en-GB-oxendict spelling, e.g. `serialisation` / `serialise`.

Please update both comments accordingly while keeping the identifiers and string literals (which don’t affect the spelling requirement) as they are.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.rs`

**Instructions:**
Comments must use en-GB-oxendict (-ize / -yse / -our) spelling and grammar.

</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread backend/src/doc.rs
Comment on lines +85 to +94
/// Assert that an Object schema contains a field with the given name.
fn assert_object_schema_has_field(schema: &RefOr<Schema>, field: &str) {
match schema {
RefOr::T(Schema::Object(obj)) => {
assert!(
obj.properties.contains_key(field),
"schema should have field '{field}'"
);
}
_ => panic!("expected Object schema"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue: Handle referenced or composed schemas more defensively in the helper.

This helper will panic whenever the schema is a $ref or wrapped in a combinator (e.g., AllOf), which is a common pattern in utoipa. If Error or User are ever referenced or composed instead of inlined, these tests will fail despite a valid schema. Consider resolving RefOr::Ref (and other relevant variants) to an Object before checking properties, or at least include the schema name in the failure message so changes in representation are easier to diagnose.

Comment on lines 78 to +88
/// Display name shown to other users.
#[schema(value_type = String, example = "Ada Lovelace")]
///
/// Matches the domain `DisplayName` invariant: trimmed, non-empty,
/// 3–32 characters, alphanumeric plus spaces and underscores.
#[schema(
value_type = String,
min_length = 3,
max_length = 32,
pattern = "^[A-Za-z0-9_ ]+$",
example = "Ada Lovelace"
)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): The display_name pattern does not fully reflect the "trimmed" invariant.

The schema currently accepts values (e.g. leading/trailing or multiple consecutive spaces) that violate the DisplayName “trimmed” invariant. To keep the OpenAPI contract aligned with the domain, either tighten the regex to exclude those cases or adjust the comment to state that trimming is enforced in the domain layer, not by the schema itself.

Suggested change
/// Display name shown to other users.
#[schema(value_type = String, example = "Ada Lovelace")]
///
/// Matches the domain `DisplayName` invariant: trimmed, non-empty,
/// 3–32 characters, alphanumeric plus spaces and underscores.
#[schema(
value_type = String,
min_length = 3,
max_length = 32,
pattern = "^[A-Za-z0-9_ ]+$",
example = "Ada Lovelace"
)]
/// Display name shown to other users.
///
/// Matches the domain `DisplayName` invariant: trimmed (no leading,
/// trailing, or consecutive spaces), non-empty, 3–32 characters,
/// alphanumeric plus spaces and underscores.
#[schema(
value_type = String,
min_length = 3,
max_length = 32,
// One or more groups of `[A-Za-z0-9_]+` separated by single spaces:
// - no leading/trailing spaces
// - no consecutive spaces
pattern = "^[A-Za-z0-9_]+(?: [A-Za-z0-9_]+)*$",
example = "Ada Lovelace"
)]

from `middleware/trace.rs`
2. **OpenAPI/Utoipa framework types in domain**: Domain types derive `ToSchema`
from the `utoipa` crate
2. **OpenAPI (Open API Specification) / Utoipa framework types in domain**: Domain
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (typo): Use the correct, consistent spelling "OpenAPI Specification" in the parenthetical.

This parenthetical is the only place that uses "Open API" with a space. Please update it to "OpenAPI Specification" to match the official name and the rest of the document.

Suggested change
2. **OpenAPI (Open API Specification) / Utoipa framework types in domain**: Domain
2. **OpenAPI (OpenAPI Specification) / Utoipa framework types in domain**: Domain

);
}

/// Verify domain ErrorCode serialization matches schema renames.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick (review_instructions): Comment uses US spelling for “serialisation/serialise”; please switch to en-GB-oxendict forms in the doc and inline comments.

The new comments around error_code_schema_matches_domain_serialization use US-style forms:

  • serialization in the doc comment
  • serialize in the inline // comment

Per the guidelines, these should use en-GB-oxendict spelling, e.g. serialisation / serialise.

Please update both comments accordingly while keeping the identifiers and string literals (which don’t affect the spelling requirement) as they are.

Review instructions:

Path patterns: **/*.rs

Instructions:
Comments must use en-GB-oxendict (-ize / -yse / -our) spelling and grammar.

Copy link
Copy Markdown
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/inbound/http/schemas.rs (1)

98-98: Fix spelling inconsistency to en-GB-oxendict.

Use "serializes" (with -ize) instead of "serialises" (with -ise) to match en-GB-oxendict spelling requirements and maintain consistency with lines 176 and 182.

🔎 Proposed fix
-        serde_json::to_string(&T::schema()).expect("schema serialises to JSON")
+        serde_json::to_string(&T::schema()).expect("schema serializes to JSON")
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21a1772 and 2128d12.

📒 Files selected for processing (3)
  • backend/src/doc.rs (1 hunks)
  • backend/src/inbound/http/schemas.rs (2 hunks)
  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{rs,ts,tsx,js,jsx,py,md}

📄 CodeRabbit inference engine (AGENTS.md)

Use consistent spelling and grammar with en-GB-oxendict ("-ize" / "-yse" / "-our") spelling and grammar, except for references to external APIs.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update documentation in docs/ when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve to ensure accuracy and currency.
Documentation must use en-GB-oxendict ("-ize" / "-yse" / "-our") spelling and grammar, with the exception of the "LICENSE" file which should remain unchanged.

Follow the Documentation style guide for clear, consistent documentation conventions

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns; code blocks at 120 columns; tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
**/*.{rs,ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,ts,tsx,js,jsx,py}: Comment why, not what—explain assumptions, edge cases, trade-offs, or complexity; do not echo the obvious.
Favour clarity over cleverness—be concise but prefer explicit over terse or obscure idioms; prioritise code that is easy to follow.
Use functions and composition to avoid repetition; prefer generators or comprehensions and declarative code to imperative repetition when readable.
Keep functions small and meaningful—single responsibility, clear purpose, and obey command/query segregation.
Name things precisely—use clear, descriptive variable and function names; for booleans, prefer names with is, has, or should.
Structure logically—each file should encapsulate a coherent module; group related code close together.
Illustrate with clear examples in function documentation demonstrating usage and outcome; for tests, omit examples where the example only reiterates test logic.
Keep file size manageable—no single code file may exceed 400 lines; break up long switch statements and move large test data blocks to external files.
New functionality or changes in behaviour must be fully validated by relevant unittests and behavioural tests before committing.
When fixing a bug, provide a unittest demonstrating the corrected behaviour to validate the fix and guard against regression.

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
**/*.{rs,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Group by feature, not layer—colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make fmt, make lint, and make test before committing Rust code; these wrap cargo fmt, cargo clippy, and cargo test.
Clippy warnings must be disallowed; fix any warnings emitted during tests in the code itself rather than silencing them.
For excessively long functions, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
For functions with too many parameters, group related parameters in meaningfully named structs.
Where a function returns a large error, consider using Arc to reduce the amount of data returned.
Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments, not before.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort; lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow when suppressing warnings.
Where a function is unused with specific features selected, use conditional compilation with #[cfg] or #[cfg_attr].
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer single-line versions of functions where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) }).
Write unit and behavioural tests for new Rust functionality; run both before and after making any change.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated tests with #[rstest(...)] parameterised cases in Rust.
Prefer mockall for mocks/stubs...

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • backend/src/doc.rs
  • backend/src/inbound/http/schemas.rs
🧠 Learnings (2)
📚 Learning: 2025-12-14T12:57:20.659Z
Learnt from: leynos
Repo: leynos/wildside PR: 253
File: docs/wildside-pwa-design.md:28-30
Timestamp: 2025-12-14T12:57:20.659Z
Learning: In the leynos/wildside repository, documentation should follow en-GB-oxendict spelling standards that require -ize endings for localization-related terms: use 'localization', 'localizability', and 'localizable' (not -ise variants). Apply this guideline to all Markdown documentation files under the docs/ directory and any future docs as they are added.

Applied to files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
📚 Learning: 2025-12-14T12:57:30.806Z
Learnt from: leynos
Repo: leynos/wildside PR: 253
File: docs/wildside-pwa-data-model.md:156-158
Timestamp: 2025-12-14T12:57:30.806Z
Learning: Adopt en-GB-oxendict spelling in all Wildside documentation. Use -ize endings (e.g., localize, localization, localized) instead of -ise endings (e.g., localise) wherever the en-GB-oxendict style guide applies. Apply this consistently across all docs under the docs directory.

Applied to files:

  • docs/execplans/phase-0-update-modules-to-use-domain-ports.md
🔍 Remote MCP Ref

Now let me search for more information about the test refactoring pattern and domain-driven design related to the PR:Now I have gathered sufficient context. Let me compile the findings into a focused summary for the PR review.

Summary of Additional Context for PR #259 Review

Based on comprehensive research of the relevant technologies and patterns, here is the additional context that would help in reviewing this PR:

1. Utoipa Schema Constraints Implementation

Utoipa supports max_length, min_length, and pattern attributes to define validation constraints for string types. The PR's changes align with Utoipa's standard practices: Examples show using #[schema(format = "base64", example = "cri4", min_length = 4, max_length = 6)] to specify constraints and formats.

Key validation point: The maximum length of a schema cannot be less than the minimum length—if a minimum length is specified, the maximum length must be greater than or equal to that minimum length. The PR sets min_length=3 and max_length=32 for display_name, which satisfies this requirement.

2. UUID Format in OpenAPI

The uuid format represents a Universally Unique IDentifier as defined in RFC4122. OpenAPI supports formats like "uuid" even though they may be undefined by the specification, and tools can use these formats for documentation purposes. Using format = "uuid" on the id field follows industry best practices for REST API documentation and enables better OpenAPI tooling support.

3. Domain-Driven Design with Ports & Adapters Context

The PR title references "domain ports," which aligns with hexagonal architecture principles. Hexagonal architecture isolates business logic from infrastructure code, with the application communicating with external components through interfaces called ports, using adapters to translate technical exchanges. Creating stronger types (like domain objects) for validation can introduce a place for validation and improves expressiveness of ports. The PR's schema constraints directly support this pattern by ensuring OpenAPI adapters accurately reflect domain invariants (3-32 char display names, UUID IDs).

4. Test Refactoring Approach

The PR consolidates tests by removing duplicates covered by external BDD tests and refactoring doc.rs tests to use structured schema inspection (assert_object_schema_has_field). This approach reduces brittle string-matching assertions in favor of targeted field validation, improving test maintainability.

5. ErrorCode Serialization Sync

The new test error_code_schema_matches_domain_serialization ensures that domain ErrorCode enum variants correctly serialize to their OpenAPI schema representations (e.g., "invalid_request"). This maintains consistency between the domain model and the OpenAPI contract adapter.,,,,

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (7)
docs/execplans/phase-0-update-modules-to-use-domain-ports.md (1)

13-14: LGTM - clear acronym definition.

Expanding "OpenAPI" on first use improves documentation clarity and follows best practices.

backend/src/doc.rs (3)

71-74: Good context for test scope boundaries.

The comment clarifies that registration and endpoint reference tests are handled by BDD tests, helping maintainers understand the test distribution.


85-96: Excellent refactoring to structured assertions.

The helper function replaces brittle JSON string matching with direct schema inspection. This improves test maintainability and provides clearer failure messages.


98-116: LGTM - focused field validation.

The refactored tests use the new helper to verify schema structure directly, which is more robust than string-based assertions.

backend/src/inbound/http/schemas.rs (3)

70-76: LGTM - UUID format correctly specified.

The schema now documents that id is a UUID with proper format annotation and a valid example, matching the domain invariant.


79-88: Verify pattern enforcement of "trimmed" invariant.

The comment states the domain invariant includes "trimmed", but the pattern ^[A-Za-z0-9_ ]+$ permits leading and trailing spaces (e.g., " Ada " would match). This could be intentional if:

  • The schema documents permissive input and the domain normalizes by trimming, or
  • An inconsistency where the pattern should enforce ^[A-Za-z0-9_]([A-Za-z0-9_ ]*[A-Za-z0-9_])?$ to match the trimmed invariant exactly.

Clarify whether the schema should enforce trimming or whether the comment should indicate that trimming is a domain-layer concern.

#!/bin/bash
# Verify how the domain DisplayName type handles whitespace

# Search for DisplayName validation or construction logic
ast-grep --pattern $'impl DisplayName {
  $$$
}'

# Search for display_name validation patterns
rg -nP -C5 'display.*name|DisplayName' --type=rust -g '!**/test/**' -g 'backend/src/domain/**'

158-184: Excellent synchronization test.

This test ensures domain ErrorCode serialization remains consistent with schema renames. The explicit mapping forces updates when new variants are added, preventing drift between domain and OpenAPI contract.

@leynos leynos merged commit 8a437a9 into main Dec 18, 2025
4 checks passed
@leynos leynos deleted the domain-ports-feedback branch December 18, 2025 22:11
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