Skip to content

Introduce application service implementations and document service contracts#9

Merged
koder95 merged 10 commits into
mainfrom
codex/implement-service-interfaces-in-impl-package
May 7, 2026
Merged

Introduce application service implementations and document service contracts#9
koder95 merged 10 commits into
mainfrom
codex/implement-service-interfaces-in-impl-package

Conversation

@koder95
Copy link
Copy Markdown
Contributor

@koder95 koder95 commented Apr 22, 2026

Motivation

  • Surface application-level service contracts in the core module and provide concrete implementations that orchestrate domain ports and encapsulate business validation and error mapping.

Description

  • Update README.md to document service contracts, package layout changes, request model timestamp, and to list available application services and ports.
  • Add pl.vtt.wpi.core.application.service.impl implementations: AdminPasswordServiceImpl, DebugServiceImpl, DeviceInfoServiceImpl, NetworkConfigurationServiceImpl, PixelProgramServiceImpl, RebootServiceImpl, RuntimeDataServiceImpl, and UserManagementServiceImpl.
  • Service implementations validate inputs, convert DTOs/models as needed, and translate InputPortException/OutputPortException into appropriate runtime or domain exceptions while delegating I/O to InputPort/OutputPort interfaces.
  • PixelProgramServiceImpl provides full list-management operations (getAll/insert/set/get/save/update/remove) and UserManagementServiceImpl implements create/changePassword/remove with existence checks and password validation.

Testing

  • Ran the automated test suite with mvn test which executes the JUnit Jupiter 5 unit tests.
  • The automated tests completed successfully.

Codex Task

Summary by CodeRabbit

Release Notes

  • New Features

    • Multiple application services fully implemented with robust error handling, including admin password reset, device info, network configuration, pixel programs, reboot, runtime data, and user management capabilities.
  • Documentation

    • Architecture documentation expanded to clarify service-level contracts and port-based communication patterns.
  • Tests

    • Comprehensive test coverage added for service implementations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a comprehensive application service layer with eight new service implementations (AdminPasswordService, DebugService, DeviceInfoService, NetworkConfigurationService, PixelProgramService, RebootService, RuntimeDataService, UserManagementService), introduces an exception hierarchy, adds domain DTOs (AdminPasswordResetRequest, UserCreateRequest), updates port contracts, and expands test coverage across all new services while updating the README documentation.

Changes

Application Service Layer Implementation

Layer / File(s) Summary
Exception Hierarchy
src/main/java/pl/vtt/wpi/core/application/exception/ApplicationServiceException.java, AdminPasswordResetException.java, AuthenticationServiceUnavailableException.java, DebugDataAccessException.java, DeviceInfoReadException.java, DeviceRebootException.java, NetworkConfigurationException.java, PixelProgramOperationException.java, RuntimeDataOperationException.java, UserManagementOperationException.java
Introduces an abstract ApplicationServiceException base class with message and cause constructors, followed by nine domain-specific exception subclasses (AdminPasswordReset, AuthenticationServiceUnavailable, DebugDataAccess, DeviceInfoRead, DeviceReboot, NetworkConfiguration, PixelProgramOperation, RuntimeDataOperation, UserManagementOperation), each with serialVersionUID and dual constructors.
Domain Data Shapes & Port Contracts
src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java, UserCreateRequest.java, src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java
Adds two immutable DTOs (AdminPasswordResetRequest with secureKey and passwordDto; UserCreateRequest with user and passwordDto). Updates UserCreateInputPort from InputPort<User> to InputPort<UserCreateRequest> with expanded null-validation logic.
Service Interface Contracts
src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java, DebugService.java, DeviceInfoService.java, LoginService.java, NetworkConfigurationService.java, PixelProgramService.java, RebootService.java, RuntimeDataService.java, UserManagementService.java
Updates interface method signatures to declare checked exceptions: AdminPasswordService#resetPassword, DebugService#(pollLogs|peekLogs|getCurrentState), DeviceInfoService#read, LoginService#login, NetworkConfigurationService#config, PixelProgramService#(getAll|insert|get|save|update|remove), RebootService#reboot, RuntimeDataService#(read|set), and UserManagementService#(createUser|changePassword|removeUser). Removes RuntimeDataService#update.
Service Implementations
src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java, DebugServiceImpl.java, DeviceInfoServiceImpl.java, NetworkConfigurationServiceImpl.java, PixelProgramServiceImpl.java, RebootServiceImpl.java, RuntimeDataServiceImpl.java, UserManagementServiceImpl.java, LoginServiceImpl.java
Eight new service implementations orchestrating domain ports (InputPort/OutputPort) with consistent validation patterns: null checks, blank-string validation, business-logic assertions (e.g., user existence, pixel-program index matching). Exception translation from port-layer exceptions (InputPortException/OutputPortException) into service-layer exceptions with preserved cause chains. LoginServiceImpl updated to throw AuthenticationServiceUnavailableException alongside IncorrectUsernameOrPasswordException. PixelProgramServiceImpl uses ReentrantLock for concurrent read-modify-write safety on pixel-program mutations. UserManagementServiceImpl uses ReentrantLock for user creation/removal consistency checks.
Comprehensive Test Coverage
src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java, DebugServiceImplTest.java, DeviceInfoServiceImplTest.java, NetworkConfigurationServiceImplTest.java, PixelProgramServiceImplTest.java, RebootServiceImplTest.java, RuntimeDataServiceImplTest.java, UserManagementServiceImplTest.java, LoginServiceImplTest.java, src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java
JUnit 5 test classes validate happy-path execution (correct port delegation, request formation, state persistence), exception wrapping (InputPortException/OutputPortException translation with message and cause preservation), validation failures (null/blank rejections, existence checks), and complex workflows (PixelProgramServiceImpl multi-operation locking, RuntimeDataServiceImpl pixel-program reference validation). LoginServiceImplTest and UserCreateInputPortTest updated to exercise new exception types and UserCreateRequest DTO structure.
Documentation
README.md
Architecture documentation restructured to reflect application.service / domain.port contracts, with infrastructure layer covering request/response handling. Request model section updated with timestamp and HTTP method fields. Client guidance shifts from single LoginService to multiple application service interfaces. Domain ports clarified as port contracts plus endpoint-specific implementations. Test documentation condensed to focus on port-level behavior and application-service orchestration.

Sequence Diagram(s)

Service implementations follow a consistent request-response pattern through domain ports. Here is a representative flow for a typical service operation (e.g., AdminPasswordService):

sequenceDiagram
    participant Client
    participant AdminPasswordServiceImpl
    participant InputPort
    participant Infrastructure

    Client->>AdminPasswordServiceImpl: resetPassword(secureKey, passwordDto)
    activate AdminPasswordServiceImpl
    AdminPasswordServiceImpl->>AdminPasswordServiceImpl: validateInputs()
    alt Validation fails
        AdminPasswordServiceImpl-->>Client: AdminPasswordResetException
    else Validation succeeds
        AdminPasswordServiceImpl->>AdminPasswordServiceImpl: buildRequest(secureKey, passwordDto)
        AdminPasswordServiceImpl->>InputPort: send(AdminPasswordResetRequest)
        activate InputPort
        InputPort->>Infrastructure: create & send HTTP request
        activate Infrastructure
        Infrastructure-->>InputPort: response/exception
        deactivate Infrastructure
        InputPort-->>AdminPasswordServiceImpl: success or InputPortException
        deactivate InputPort
        alt Port throws InputPortException
            AdminPasswordServiceImpl-->>Client: AdminPasswordResetException(cause=InputPortException)
        else Port succeeds
            AdminPasswordServiceImpl-->>Client: (void)
        end
    end
    deactivate AdminPasswordServiceImpl
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

The PR introduces eight new service implementations with heterogeneous error-handling patterns, synchronized operations (ReentrantLock in PixelProgramServiceImpl and UserManagementServiceImpl), complex validation chains (RuntimeDataServiceImpl pixel-program existence checks), and cross-cutting exception-translation logic. Each service exhibits distinct orchestration semantics (PixelProgramServiceImpl defensive-copy returns and list-based mutations; UserManagementServiceImpl concurrent user checks; RuntimeDataServiceImpl multi-port validation). Comprehensive test coverage across all implementations and port changes (UserCreateInputPort signature shift) demands careful reasoning about backward compatibility. Nine new exception classes with similar structure reduce per-class cognitive load but necessitate systematic exception-contract validation across service interfaces.

Possibly related PRs

  • VerbaTechTeam/wpi-core#6: Both PRs update LoginServiceImplTest exception handling and AuthorizationHolder cleanup behavior to support new authentication failure scenarios.
  • VerbaTechTeam/wpi-core#8: Main PR changes UserCreateInputPort from InputPort<User> to InputPort<UserCreateRequest>, directly overlapping with prior introduction of that port.
  • VerbaTechTeam/wpi-core#7: Both PRs introduce UserCreateRequest, AdminPasswordResetRequest DTOs and corresponding service implementations orchestrating these new domain models.

Poem

🐰 Eight services bloom where ports do dance,
Exception chains in grand expanse,
With locks and checks and validation keen,
The cleanest app layer ever seen!
Hop forth and code with joy profound! 🌿✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately summarizes the main objective of the pull request: introducing application service implementations and documenting their contracts.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/implement-service-interfaces-in-impl-package

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 764ee3cc26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@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: 6

🧹 Nitpick comments (2)
src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java (2)

62-67: Minor DRY: consolidate the "pixel program not found" lookup.

get(int) (lines 62–67) and findListIndex(...) (lines 127–135) duplicate the same search and identical exception message. You could have get(int) delegate through findListIndex (e.g., return programs.get(findListIndex(programs, index));) to keep the not-found semantics in one place.

Also applies to: 127-135

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 62 - 67, get(int) duplicates the search and exception logic already
implemented in findListIndex(List<PixelProgram>, int); change get(int) to call
loadPrograms(), store the list in a variable (e.g., programs) and return
programs.get(findListIndex(programs, index)), reusing findListIndex to throw
PixelProgramNotFoundException with the existing message; ensure findListIndex is
accessible from get (adjust visibility if needed) and remove the duplicated
stream/filter code from get(int).

41-45: Prefer a more specific unchecked exception over raw RuntimeException.

Wrapping port failures in bare new RuntimeException(...) at lines 44, 88, 103, and 123 is a code smell — callers can't distinguish infrastructure failures from programmer errors, and it's harder to catch selectively. Consider IllegalStateException (standard, unchecked) or a dedicated application-layer unchecked exception consistent with the other new service implementations in this package.

Also applies to: 85-89, 100-104, 117-125

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 41 - 45, Replace the raw RuntimeException wrappers around port
failures in PixelProgramServiceImpl (the catch blocks that wrap calls such as
set(current), find(...), update(...), delete(...)) with a more specific
unchecked exception; either throw IllegalStateException with the original
exception as the cause or throw the same application-layer unchecked exception
used by other services (e.g., ApplicationServiceException or
PixelProgramServiceException if one exists) so callers can distinguish
infrastructure errors; update each catch that currently does `throw new
RuntimeException("...", e)` to `throw new IllegalStateException("Cannot
<operation> pixel programs", e)` or the project-specific unchecked type and keep
the original message and cause.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 53-69: The fenced code block containing the directory tree
(starting with "pl.vtt.wpi.core") triggers MD040; update its opening fence from
``` to a language-hint fence like ```text or ```plaintext so the block is
treated as plain text; locate the block in README.md (the one with the directory
tree and comments such as "AuthorizationHolder (thread-local auth state)") and
replace the fence accordingly.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java`:
- Around line 22-27: Update the null-check block in AdminPasswordServiceImpl
that validates the incoming PasswordDto to also reject empty or whitespace-only
passwords and confirmations (use passwordDto.password().isBlank() and
passwordDto.passwordConfirmation().isBlank() or equivalent) and throw an
IllegalArgumentException with a clear message; keep the subsequent equality
check (password().equals(passwordConfirmation())) but ensure both fields are
validated for blank values first so blank admin passwords are blocked just like
user password validation.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java`:
- Around line 27-36: pollLogs() currently calls peekLogs() then
logsDeleteInputPort.send(null), which can lose entries appended between the read
(LOGS_READ via peekLogs()) and the delete (LOGS_DELETE via logsDeleteInputPort);
add an atomic read-and-clear input port (e.g., logsReadAndClearInputPort /
method name readAndClearLogs()) and use it from pollLogs(), or if an atomic
endpoint cannot be added, update pollLogs() and its JavaDoc to explicitly mark
it as best-effort (non-atomic) and document the race window; also update any
callers/tests to expect non-guaranteed drain if you choose the documentation
approach.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`:
- Around line 35-46: In PixelProgramServiceImpl.insert(List<PixelProgram>
pixelPrograms) move the null/empty guard to the top so you return immediately
before calling getAll(); currently getAll() is invoked even for null/empty
inputs (causing unnecessary I/O and possible exceptions), so check "if
(pixelPrograms == null || pixelPrograms.isEmpty()) return;" first, then call
getAll(), addAll(pixelPrograms), and call set(current) catching
DataInconsistencyException as before.
- Around line 35-125: The class PixelProgramServiceImpl has unguarded
read-modify-write races in methods insert, save, update, and remove (and any
other methods that call getAll()/loadPrograms() followed by set()), so either
document a single-threaded/external-synchronization contract in the class
Javadoc or make the operations atomic by adding a per-instance lock and using it
to guard all mutation sequences; specifically add a private final Lock (or
synchronized block) used around insert(List<PixelProgram>),
save(List<RgbColor>), update(int,List<RgbColor>), remove(int), and any other
code paths that call getAll()/loadPrograms() then set(), and ensure
set(List<PixelProgram>) and loadPrograms() are called inside that lock; also
update the class-level Javadoc to state the threading model and requirements (or
mention that InputPort/OutputPort must provide atomicity) so callers know
whether external synchronization is required.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java`:
- Around line 18-48: The createUser flow currently discards the initial password
because createUser(User user, PasswordDto passwordDto) validates passwordDto but
calls userCreateInputPort.send(user); change the port payload to a composite
request: introduce a record UserCreateRequest(User user, PasswordDto
passwordDto) and update the field and constructor signature from InputPort<User>
userCreateInputPort to InputPort<UserCreateRequest> userCreateInputPort; then in
createUser(...) send new UserCreateRequest(user, passwordDto) instead of user
and adjust any callers/implementations of the port to accept the new
UserCreateRequest type (also update the Objects.requireNonNull message to
reflect the new parameter name).

---

Nitpick comments:
In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`:
- Around line 62-67: get(int) duplicates the search and exception logic already
implemented in findListIndex(List<PixelProgram>, int); change get(int) to call
loadPrograms(), store the list in a variable (e.g., programs) and return
programs.get(findListIndex(programs, index)), reusing findListIndex to throw
PixelProgramNotFoundException with the existing message; ensure findListIndex is
accessible from get (adjust visibility if needed) and remove the duplicated
stream/filter code from get(int).
- Around line 41-45: Replace the raw RuntimeException wrappers around port
failures in PixelProgramServiceImpl (the catch blocks that wrap calls such as
set(current), find(...), update(...), delete(...)) with a more specific
unchecked exception; either throw IllegalStateException with the original
exception as the cause or throw the same application-layer unchecked exception
used by other services (e.g., ApplicationServiceException or
PixelProgramServiceException if one exists) so callers can distinguish
infrastructure errors; update each catch that currently does `throw new
RuntimeException("...", e)` to `throw new IllegalStateException("Cannot
<operation> pixel programs", e)` or the project-specific unchecked type and keep
the original message and cause.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 98b1fa4e-7bc9-4cca-a268-eb20df402d09

📥 Commits

Reviewing files that changed from the base of the PR and between d0a85b2 and 764ee3c.

📒 Files selected for processing (9)
  • README.md
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java

Comment thread README.md Outdated
Copy link
Copy Markdown

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

🧹 Nitpick comments (5)
README.md (1)

154-154: Minor: the unit-test description is now narrower than the actual coverage.

The updated sentence mentions only "core port behaviors and login service logic", but this PR in fact broadens coverage to include UserCreateInputPort (with the new UserCreateRequest DTO) and other application services. Consider rewording to cover port + application-service coverage more generally, so the doc doesn't under-sell what's there.

✏️ Proposed tweak
-Unit tests are written with JUnit Jupiter 5 and currently include coverage for core port behaviors and login service logic.
+Unit tests are written with JUnit Jupiter 5 and currently include coverage for core port behaviors and application service logic.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 154, Update the README sentence about unit-test coverage
to reflect the broader scope: replace the narrow phrase "core port behaviors and
login service logic" with wording that mentions both port-level and
application-service tests (e.g., include UserCreateInputPort and the new
UserCreateRequest DTO plus other application services); locate the sentence in
README.md that currently describes JUnit Jupiter 5 coverage and rephrase it to
indicate port + application-service coverage so the documentation accurately
reflects tests for UserCreateInputPort, UserCreateRequest, login service, and
other service-level behaviors.
src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java (2)

90-110: Minor: the returned saved reference may mislead on IllegalStateException.

When set(programs) on line 103 fails (and is wrapped into IllegalStateException with the DataInconsistencyException cause), the method exits via the catch block without ever returning the saved value — good — but a caller reading only the Javadoc/interface contract might still reasonably assume "if save returned normally, the program is persisted; if it threw, nothing was persisted". That assumption holds here, so no action is strictly required. One small readability tweak: use getAll()'s defensive copy plus a local mutation rather than programs.add(saved) directly, so the intent (mutation happens on a scratch list, not on state shared with anything) is obvious. Optional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 90 - 110, The save method currently mutates the list returned by
getAll() which can be confusing; change it to operate on a local mutable copy:
call getAll(), wrap the result into a new ArrayList (e.g. List<PixelProgram>
programs = new ArrayList<>(getAll())), add the new PixelProgram(saved) to that
local list, then call set(programs) as before; keep the existing mutationLock,
exception handling, and use of PixelProgram constructor and List.copyOf for
pixelProgram unchanged so semantics remain identical but intent is clearer.

62-77: Minor: List.copyOf will throw a raw NPE on null elements instead of DataInconsistencyException.

set explicitly converts a null list into DataInconsistencyException("Pixel programs cannot be null"), but List.copyOf(pixelPrograms) on line 69 throws a plain NullPointerException if any element is null — that NPE escapes the try { ... } catch (InputPortException ...) and surfaces to the caller as an untyped NullPointerException. If the intent (per the PR summary "validates non-null input elements") is to treat null elements as a consistency issue, consider validating elements up-front and throwing DataInconsistencyException for symmetry:

♻️ Proposed tweak
         if (pixelPrograms == null) {
             throw new DataInconsistencyException("Pixel programs cannot be null");
         }
+        for (PixelProgram program : pixelPrograms) {
+            if (program == null) {
+                throw new DataInconsistencyException("Pixel program element cannot be null");
+            }
+        }
         try {
             pixelProgramsInputPort.send(List.copyOf(pixelPrograms));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 62 - 77, The set method uses List.copyOf(pixelPrograms) which will
throw a raw NullPointerException if any element is null; before calling
List.copyOf in PixelProgramServiceImpl.set, validate that pixelPrograms contains
no null elements and if any are found throw a DataInconsistencyException("Pixel
programs cannot contain null elements") so the caller gets a consistent
exception; keep the existing mutationLock and existing InputPortException
handling around pixelProgramsInputPort.send(List.copyOf(pixelPrograms)).
src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java (1)

23-33: Optional: consider narrowing the catch (Exception e) to avoid double-wrapping.

Because requestFactory.create(...) / requestSender.send(...) can themselves throw InputPortException (the declared sender exception type in other ports), catch (Exception e) will re-wrap an already-typed InputPortException inside another InputPortException with cause = original. That's functionally harmless here but loses the original message/type on the outer layer. Either catch RuntimeException (since RequestSender.send doesn't actually declare a checked throw in this codebase) or catch specific exceptions explicitly. Low priority — existing behavior matches other input ports, so feel free to leave as-is for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java`
around lines 23 - 33, The catch-all in UserCreateInputPort.send currently
catches Exception and wraps it in a new InputPortException, which double-wraps
if the cause is already an InputPortException; modify the error handling in
send(UserCreateRequest) so you either catch RuntimeException (or specific
checked exceptions thrown by requestFactory.create/requestSender.send) or detect
and rethrow an existing InputPortException (i.e., if (e instanceof
InputPortException) throw (InputPortException)e;) before wrapping, referencing
the send method in UserCreateInputPort and the calls to requestFactory.create
and requestSender.send to locate the change.
src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java (1)

55-62: Minor: test name and scope don't fully match the new validation surface.

send_nullUser_throwsInputPortException only exercises the fully-null UserCreateRequest path. The production guard at UserCreateInputPort#send additionally rejects null for obj.user() and obj.passwordDto() independently, but neither partial-null case is covered here. Consider either renaming this test (e.g. send_nullRequest_throwsInputPortException) and adding two more cases for new UserCreateRequest(null, passwordDto) and new UserCreateRequest(user, null), to lock in the expanded validation behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java`
around lines 55 - 62, The test name and coverage should match the expanded
validation in UserCreateInputPort#send: either rename
send_nullUser_throwsInputPortException to
send_nullRequest_throwsInputPortException, or (preferred) add two new tests that
assert InputPortException is thrown for partial-null cases by calling
port.send(new UserCreateRequest(null, validPasswordDto)) and port.send(new
UserCreateRequest(validUser, null)); keep the existing null-request test intact,
use the same assertion pattern (assertThrows(InputPortException.class, ...)) and
assert the expected message text to ensure the validations for obj.user() and
obj.passwordDto() are enforced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@README.md`:
- Line 154: Update the README sentence about unit-test coverage to reflect the
broader scope: replace the narrow phrase "core port behaviors and login service
logic" with wording that mentions both port-level and application-service tests
(e.g., include UserCreateInputPort and the new UserCreateRequest DTO plus other
application services); locate the sentence in README.md that currently describes
JUnit Jupiter 5 coverage and rephrase it to indicate port + application-service
coverage so the documentation accurately reflects tests for UserCreateInputPort,
UserCreateRequest, login service, and other service-level behaviors.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`:
- Around line 90-110: The save method currently mutates the list returned by
getAll() which can be confusing; change it to operate on a local mutable copy:
call getAll(), wrap the result into a new ArrayList (e.g. List<PixelProgram>
programs = new ArrayList<>(getAll())), add the new PixelProgram(saved) to that
local list, then call set(programs) as before; keep the existing mutationLock,
exception handling, and use of PixelProgram constructor and List.copyOf for
pixelProgram unchanged so semantics remain identical but intent is clearer.
- Around line 62-77: The set method uses List.copyOf(pixelPrograms) which will
throw a raw NullPointerException if any element is null; before calling
List.copyOf in PixelProgramServiceImpl.set, validate that pixelPrograms contains
no null elements and if any are found throw a DataInconsistencyException("Pixel
programs cannot contain null elements") so the caller gets a consistent
exception; keep the existing mutationLock and existing InputPortException
handling around pixelProgramsInputPort.send(List.copyOf(pixelPrograms)).

In `@src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java`:
- Around line 23-33: The catch-all in UserCreateInputPort.send currently catches
Exception and wraps it in a new InputPortException, which double-wraps if the
cause is already an InputPortException; modify the error handling in
send(UserCreateRequest) so you either catch RuntimeException (or specific
checked exceptions thrown by requestFactory.create/requestSender.send) or detect
and rethrow an existing InputPortException (i.e., if (e instanceof
InputPortException) throw (InputPortException)e;) before wrapping, referencing
the send method in UserCreateInputPort and the calls to requestFactory.create
and requestSender.send to locate the change.

In `@src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java`:
- Around line 55-62: The test name and coverage should match the expanded
validation in UserCreateInputPort#send: either rename
send_nullUser_throwsInputPortException to
send_nullRequest_throwsInputPortException, or (preferred) add two new tests that
assert InputPortException is thrown for partial-null cases by calling
port.send(new UserCreateRequest(null, validPasswordDto)) and port.send(new
UserCreateRequest(validUser, null)); keep the existing null-request test intact,
use the same assertion pattern (assertThrows(InputPortException.class, ...)) and
assert the expected message text to ensure the validations for obj.user() and
obj.passwordDto() are enforced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54b3dc4d-32d3-419d-8a03-31d070c1f955

📥 Commits

Reviewing files that changed from the base of the PR and between 764ee3c and b879984.

📒 Files selected for processing (9)
  • README.md
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/domain/dto/UserCreateRequest.java
  • src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java
  • src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (7)
src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java (1)

10-21: Consider expanding coverage for the other branches of NetworkConfigurationServiceImpl.

The mapping assertion is good, but the test only exercises a single happy path. The class exposes a config(WifiConfig) overload and the config(AccessPointConfig) method throws IllegalArgumentException when passed null — neither is covered. Adding small tests for these cases would lock in the contract and guard against regressions.

🧪 Suggested additional tests
     `@Test`
     void config_accessPointConfig_isMappedToWifiConfig() {
         AtomicReference<WifiConfig> sent = new AtomicReference<>();
         NetworkConfigurationServiceImpl service = new NetworkConfigurationServiceImpl(sent::set);

         service.config(new AccessPointConfig("ap", "pass"));

         assertEquals(new WifiConfig("ap", "pass"), sent.get());
     }
+
+    `@Test`
+    void config_nullAccessPointConfig_throws() {
+        NetworkConfigurationServiceImpl service = new NetworkConfigurationServiceImpl(c -> {});
+        assertThrows(IllegalArgumentException.class,
+                () -> service.config((AccessPointConfig) null));
+    }
+
+    `@Test`
+    void config_wifiConfig_isForwardedAsIs() {
+        AtomicReference<WifiConfig> sent = new AtomicReference<>();
+        NetworkConfigurationServiceImpl service = new NetworkConfigurationServiceImpl(sent::set);
+        WifiConfig cfg = new WifiConfig("ap", "pass");
+
+        service.config(cfg);
+
+        assertEquals(cfg, sent.get());
+    }

(You'd also need import static org.junit.jupiter.api.Assertions.assertThrows;.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java`
around lines 10 - 21, Add unit tests for the uncovered branches in
NetworkConfigurationServiceImpl: (1) a test that calls config(WifiConfig) and
verifies the same WifiConfig is forwarded to the provided Consumer (similar to
the existing AccessPointConfig test but passing a WifiConfig directly), and (2)
a test that asserts config((AccessPointConfig) null) throws
IllegalArgumentException using assertThrows; reference the class/method names
NetworkConfigurationServiceImpl, config(WifiConfig), config(AccessPointConfig)
and the exception IllegalArgumentException, and add the static import for
assertThrows from org.junit.jupiter.api.Assertions.
src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java (1)

27-35: Prefer a dedicated catch over an instanceof pattern inside catch (Exception).

The instanceof re-throw pattern works but is unusual; a dedicated catch (InputPortException e) ahead of the generic catch is more idiomatic and avoids the broad-catch-then-rethrow dance. It also makes the intent (propagate domain exceptions, wrap everything else) obvious to readers.

♻️ Suggested cleanup
         try {
             Request<UserCreateRequest> request = requestFactory.create(obj, Method.POST, RequestTarget.USERS_CREATE);
             requestSender.send(request);
-        } catch (Exception e) {
-            if (e instanceof InputPortException inputPortException) {
-                throw inputPortException;
-            }
+        } catch (InputPortException e) {
+            throw e;
+        } catch (Exception e) {
             throw new InputPortException("Cannot create user", e);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java`
around lines 27 - 35, In the create flow in UserCreateInputPort (around the
requestFactory.create(...) and requestSender.send(...) calls) replace the broad
catch(Exception) with two dedicated catches: first catch InputPortException and
rethrow it unchanged, then catch Exception and wrap it in a new
InputPortException("Cannot create user", e); remove the instanceof pattern
inside the catch and keep the original behavior of propagating
InputPortException while wrapping all other exceptions.
src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java (1)

17-64: LGTM — consider broadening coverage in a follow-up.

Tests correctly exercise the three main branches (success forwarding, existing-user conflict, blank password). The assertEquals(new UserCreateRequest(user, passwordDto), sent.get()) assumes record-based value equality on UserCreateRequest, which holds for the DTO as written.

Given that UserManagementServiceImpl also handles null user, password/confirmation mismatch, InputPortExceptionRuntimeException wrapping, changePassword success path, and removeUser, it would be worth adding tests for those paths in a follow-up to prevent regressions as the port contracts evolve.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java`
around lines 17 - 64, Add unit tests to broaden coverage for
UserManagementServiceImpl: add tests for createUser(null) to assert the expected
null-user handling, createUser when password/confirmation mismatch to assert the
appropriate exception, simulate the input port throwing InputPortException and
verify it is wrapped as a RuntimeException by createUser, add a success-path
test for changePassword verifying the forwarded PasswordDto and no exception,
and add tests for removeUser verifying the forwarded removal request/behavior;
reference the methods and DTOs UserManagementServiceImpl#createUser,
UserManagementServiceImpl#changePassword, UserManagementServiceImpl#removeUser,
PasswordDto, UserCreateRequest and exceptions UserAlreadyExistsException,
InvalidPasswordException, InputPortException/RuntimeException when implementing
these tests.
src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java (1)

11-32: LGTM — optional: also assert the cause is preserved.

The failure test could additionally verify exception.getCause() instanceof IllegalStateException to lock in the cause-preservation contract implemented by RebootServiceImpl (which unwraps InputPortException.getCause()). Not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java`
around lines 11 - 32, Update the failing-path test
reboot_portFailure_wrapsException in RebootServiceImplTest to also assert that
the wrapped runtime exception preserves the original cause: after capturing the
RuntimeException from assertThrows(service::reboot), add an assertion that
exception.getCause() is an instance of IllegalStateException (or equals the
original cause), referencing the test method reboot_portFailure_wrapsException
and the captured exception variable to lock in the cause-preservation contract
of RebootServiceImpl.
src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java (2)

62-80: Validate inputs before acquiring the lock.

Null/null-element validation here does not touch shared state, so taking mutationLock prior to the checks needlessly serializes invalid calls. Consider moving the two validation blocks above mutationLock.lock().

♻️ Proposed fix
     `@Override`
     public void set(List<PixelProgram> pixelPrograms) throws DataInconsistencyException {
+        if (pixelPrograms == null) {
+            throw new DataInconsistencyException("Pixel programs cannot be null");
+        }
+        if (pixelPrograms.stream().anyMatch(Objects::isNull)) {
+            throw new DataInconsistencyException("Pixel programs cannot contain null elements");
+        }
         mutationLock.lock();
         try {
-            if (pixelPrograms == null) {
-                throw new DataInconsistencyException("Pixel programs cannot be null");
-            }
-            if (pixelPrograms.stream().anyMatch(Objects::isNull)) {
-                throw new DataInconsistencyException("Pixel programs cannot contain null elements");
-            }
             try {
                 pixelProgramsInputPort.send(List.copyOf(pixelPrograms));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 62 - 80, The null and null-element validations in
PixelProgramServiceImpl.set are performed while holding mutationLock, but they
don't access shared state—move the two checks (pixelPrograms == null and
pixelPrograms.stream().anyMatch(Objects::isNull)) to before calling
mutationLock.lock() so invalid inputs are rejected without serializing callers;
keep the mutationLock around the
pixelProgramsInputPort.send(List.copyOf(pixelPrograms)) call and the
InputPortException handling so the mutation remains properly synchronized.

94-102: Redundant defensive copy.

getAll() already returns new ArrayList<>(loadPrograms()), so wrapping it again in new ArrayList<>(...) creates two copies. Minor.

♻️ Proposed fix
-            List<PixelProgram> programs = new ArrayList<>(getAll());
+            List<PixelProgram> programs = getAll();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 94 - 102, The save method creates an unnecessary defensive copy by
doing new ArrayList<>(getAll()) even though getAll() already returns a new
ArrayList from loadPrograms(); inside PixelProgramServiceImpl.save replace the
double-copy with a single reference to the list returned by getAll() (e.g.,
List<PixelProgram> programs = getAll()) so you avoid creating two ArrayList
instances while preserving the ability to mutate the local programs list under
the existing mutationLock.
src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java (1)

11-33: Consider expanding branch coverage.

Current tests cover the happy path and blank-password branch. The production resetPassword additionally throws on: null secureKey, blank secureKey, null passwordDto (or null fields), and password/confirmation mismatch. A parameterized test (or a few extra cases) would lock in those branches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java`
around lines 11 - 33, Tests only exercise the happy path and blank-password case
for AdminPasswordServiceImpl.resetPassword; add tests that cover the remaining
validation branches: null secureKey, blank secureKey, null passwordDto,
passwordDto with null fields, and password/confirmation mismatch so
IllegalArgumentException is thrown as in production. Create additional unit
tests (or a `@ParameterizedTest`) that call AdminPasswordServiceImpl.resetPassword
with these inputs and assert assertThrows(IllegalArgumentException.class), and
keep one positive test that verifies the AdminPasswordResetRequest is sent
(using the AtomicReference/InputPort approach already present).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`:
- Around line 133-145: The remove method currently propagates
DataInconsistencyException while insert/save/update wrap it; change remove(int
index) in PixelProgramServiceImpl to catch DataInconsistencyException thrown by
set(programs) and rethrow as an IllegalStateException (matching
insert/save/update), and update the service interface signature to drop
DataInconsistencyException from remove's throws clause so callers no longer see
the checked exception; keep the same locking logic (mutationLock) and still
return the removed PixelProgram.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImplTest.java`:
- Around line 16-23: The test set_nullElement_throwsDataInconsistencyException
never reaches PixelProgramServiceImpl.set(...) because List.of(null) throws NPE;
change the test to use a list factory that allows nulls (e.g.,
Collections.singletonList(null) or Arrays.asList((PixelProgram) null)) so the
null element is passed into PixelProgramServiceImpl.set(...) and
assertThrows(DataInconsistencyException.class, () -> service.set(...)) can
observe the DataInconsistencyException thrown by the null-element validation in
PixelProgramServiceImpl.

---

Nitpick comments:
In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`:
- Around line 62-80: The null and null-element validations in
PixelProgramServiceImpl.set are performed while holding mutationLock, but they
don't access shared state—move the two checks (pixelPrograms == null and
pixelPrograms.stream().anyMatch(Objects::isNull)) to before calling
mutationLock.lock() so invalid inputs are rejected without serializing callers;
keep the mutationLock around the
pixelProgramsInputPort.send(List.copyOf(pixelPrograms)) call and the
InputPortException handling so the mutation remains properly synchronized.
- Around line 94-102: The save method creates an unnecessary defensive copy by
doing new ArrayList<>(getAll()) even though getAll() already returns a new
ArrayList from loadPrograms(); inside PixelProgramServiceImpl.save replace the
double-copy with a single reference to the list returned by getAll() (e.g.,
List<PixelProgram> programs = getAll()) so you avoid creating two ArrayList
instances while preserving the ability to mutate the local programs list under
the existing mutationLock.

In `@src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java`:
- Around line 27-35: In the create flow in UserCreateInputPort (around the
requestFactory.create(...) and requestSender.send(...) calls) replace the broad
catch(Exception) with two dedicated catches: first catch InputPortException and
rethrow it unchanged, then catch Exception and wrap it in a new
InputPortException("Cannot create user", e); remove the instanceof pattern
inside the catch and keep the original behavior of propagating
InputPortException while wrapping all other exceptions.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java`:
- Around line 11-33: Tests only exercise the happy path and blank-password case
for AdminPasswordServiceImpl.resetPassword; add tests that cover the remaining
validation branches: null secureKey, blank secureKey, null passwordDto,
passwordDto with null fields, and password/confirmation mismatch so
IllegalArgumentException is thrown as in production. Create additional unit
tests (or a `@ParameterizedTest`) that call AdminPasswordServiceImpl.resetPassword
with these inputs and assert assertThrows(IllegalArgumentException.class), and
keep one positive test that verifies the AdminPasswordResetRequest is sent
(using the AtomicReference/InputPort approach already present).

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java`:
- Around line 10-21: Add unit tests for the uncovered branches in
NetworkConfigurationServiceImpl: (1) a test that calls config(WifiConfig) and
verifies the same WifiConfig is forwarded to the provided Consumer (similar to
the existing AccessPointConfig test but passing a WifiConfig directly), and (2)
a test that asserts config((AccessPointConfig) null) throws
IllegalArgumentException using assertThrows; reference the class/method names
NetworkConfigurationServiceImpl, config(WifiConfig), config(AccessPointConfig)
and the exception IllegalArgumentException, and add the static import for
assertThrows from org.junit.jupiter.api.Assertions.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java`:
- Around line 11-32: Update the failing-path test
reboot_portFailure_wrapsException in RebootServiceImplTest to also assert that
the wrapped runtime exception preserves the original cause: after capturing the
RuntimeException from assertThrows(service::reboot), add an assertion that
exception.getCause() is an instance of IllegalStateException (or equals the
original cause), referencing the test method reboot_portFailure_wrapsException
and the captured exception variable to lock in the cause-preservation contract
of RebootServiceImpl.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java`:
- Around line 17-64: Add unit tests to broaden coverage for
UserManagementServiceImpl: add tests for createUser(null) to assert the expected
null-user handling, createUser when password/confirmation mismatch to assert the
appropriate exception, simulate the input port throwing InputPortException and
verify it is wrapped as a RuntimeException by createUser, add a success-path
test for changePassword verifying the forwarded PasswordDto and no exception,
and add tests for removeUser verifying the forwarded removal request/behavior;
reference the methods and DTOs UserManagementServiceImpl#createUser,
UserManagementServiceImpl#changePassword, UserManagementServiceImpl#removeUser,
PasswordDto, UserCreateRequest and exceptions UserAlreadyExistsException,
InvalidPasswordException, InputPortException/RuntimeException when implementing
these tests.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57661681-3e2a-4afe-90ba-ce34c5b61e62

📥 Commits

Reviewing files that changed from the base of the PR and between b879984 and 4f892a9.

📒 Files selected for processing (14)
  • README.md
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/domain/port/input/UserCreateInputPort.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/domain/port/UserCreateInputPortTest.java
💤 Files with no reviewable changes (1)
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • README.md

Comment on lines +133 to +145
@Override
public PixelProgram remove(int index) throws PixelProgramNotFoundException, DataInconsistencyException {
mutationLock.lock();
try {
List<PixelProgram> programs = getAll();
int removeIndex = findListIndex(programs, index);
PixelProgram removed = programs.remove(removeIndex);
set(programs);
return removed;
} finally {
mutationLock.unlock();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the PixelProgramService interface signature for remove() to know which direction to align.
fd -a 'PixelProgramService.java' --type f | xargs -I{} sh -c 'echo "=== {} ==="; cat -n "{}"'

Repository: VerbaTechTeam/wpi-core

Length of output: 1303


🏁 Script executed:

cat -n /home/jailuser/git/src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java | head -150

Repository: VerbaTechTeam/wpi-core

Length of output: 7038


Inconsistent exception handling: remove propagates DataInconsistencyException, peers wrap it.

insert, save, and update all catch DataInconsistencyException from set(...) and rewrap it as IllegalStateException, while remove propagates it uncaught. This inconsistency breaks the symmetry of the mutation API and leaks an extra checked exception to callers of remove only.

To align with the other mutation methods, wrap the exception in remove and update the interface signature to remove DataInconsistencyException from its throws clause.

♻️ Proposed fix (wrap for consistency)
     `@Override`
-    public PixelProgram remove(int index) throws PixelProgramNotFoundException, DataInconsistencyException {
+    public PixelProgram remove(int index) throws PixelProgramNotFoundException {
         mutationLock.lock();
         try {
             List<PixelProgram> programs = getAll();
             int removeIndex = findListIndex(programs, index);
             PixelProgram removed = programs.remove(removeIndex);
             set(programs);
             return removed;
+        } catch (DataInconsistencyException e) {
+            throw new IllegalStateException("Cannot remove pixel program", e);
         } finally {
             mutationLock.unlock();
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public PixelProgram remove(int index) throws PixelProgramNotFoundException, DataInconsistencyException {
mutationLock.lock();
try {
List<PixelProgram> programs = getAll();
int removeIndex = findListIndex(programs, index);
PixelProgram removed = programs.remove(removeIndex);
set(programs);
return removed;
} finally {
mutationLock.unlock();
}
}
`@Override`
public PixelProgram remove(int index) throws PixelProgramNotFoundException {
mutationLock.lock();
try {
List<PixelProgram> programs = getAll();
int removeIndex = findListIndex(programs, index);
PixelProgram removed = programs.remove(removeIndex);
set(programs);
return removed;
} catch (DataInconsistencyException e) {
throw new IllegalStateException("Cannot remove pixel program", e);
} finally {
mutationLock.unlock();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java`
around lines 133 - 145, The remove method currently propagates
DataInconsistencyException while insert/save/update wrap it; change remove(int
index) in PixelProgramServiceImpl to catch DataInconsistencyException thrown by
set(programs) and rethrow as an IllegalStateException (matching
insert/save/update), and update the service interface signature to drop
DataInconsistencyException from remove's throws clause so callers no longer see
the checked exception; keep the same locking logic (mutationLock) and still
return the removed PixelProgram.

Copy link
Copy Markdown

@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: 4

🧹 Nitpick comments (11)
src/main/java/pl/vtt/wpi/core/application/exception/DebugServiceException.java (1)

3-11: Consider adding serialVersionUID and brief Javadoc.

Exception implements Serializable, so declaring an explicit serialVersionUID avoids compiler warnings and prevents accidental serialization incompatibilities if the class evolves. A short Javadoc explaining when this exception is thrown (e.g., wrapping InputPortException/OutputPortException from debug operations) would also help consumers handle it appropriately.

♻️ Proposed refinement
-public class DebugServiceException extends Exception {
-    public DebugServiceException(String message) {
-        super(message);
-    }
-
-    public DebugServiceException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
+/**
+ * Thrown by {`@code` DebugService} operations when an underlying input/output port
+ * fails (e.g., reading logs or current state, clearing logs).
+ */
+public class DebugServiceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DebugServiceException(String message) {
+        super(message);
+    }
+
+    public DebugServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/DebugServiceException.java`
around lines 3 - 11, Add a brief Javadoc to the DebugServiceException class
describing when it is thrown (e.g., to wrap
InputPortException/OutputPortException from debug operations) and add an
explicit serialVersionUID field to avoid serialization warnings and
compatibility issues; specifically, update the DebugServiceException class to
include a short Javadoc above the class declaration and a private static final
long serialVersionUID = 1L (or a generated UID) while leaving the existing
constructors (DebugServiceException(String) and DebugServiceException(String,
Throwable)) intact.
src/main/java/pl/vtt/wpi/core/application/exception/RebootServiceException.java (1)

3-11: Optional: add a serialVersionUID.

Exception is Serializable, so without an explicit serialVersionUID the JVM-generated default will silently change if the class shape ever evolves, breaking deserialization across versions. Most lint/IDE configurations warn about this for new exception types.

♻️ Suggested addition
 public class RebootServiceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
     public RebootServiceException(String message) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/RebootServiceException.java`
around lines 3 - 11, The RebootServiceException class lacks an explicit
serialVersionUID which can break serialization compatibility; add a private
static final long serialVersionUID field to the RebootServiceException class
(e.g., in class RebootServiceException) with a chosen constant value to satisfy
the Serializable contract and silence IDE/lint warnings while preserving
deserialization stability across versions.
src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java (1)

21-24: Consider preserving the full exception chain instead of unwrapping getCause().

Throwable cause = e.getCause() == null ? e : e.getCause(); drops the InputPortException frame from the chain whenever a cause is present, which makes diagnostics in logs lose the port-layer context. Since RebootServiceException already carries its own message, simply passing e as the cause preserves the full chain (RebootServiceExceptionInputPortException → underlying cause) without harm.

♻️ Suggested change
-        } catch (InputPortException e) {
-            Throwable cause = e.getCause() == null ? e : e.getCause();
-            throw new RebootServiceException("Cannot reboot device", cause);
-        }
+        } catch (InputPortException e) {
+            throw new RebootServiceException("Cannot reboot device", e);
+        }

Note: this would also require adjusting the test that asserts on the wrapped cause (if any) accordingly. Per the snippet, RebootServiceImplTest#reboot_portFailure_wrapsException only asserts the message, so this change should be test-compatible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java`
around lines 21 - 24, Replace the current unwrapping logic in RebootServiceImpl
that computes Throwable cause = e.getCause() == null ? e : e.getCause(); and
then throws new RebootServiceException("Cannot reboot device", cause) with
simply passing the original InputPortException as the cause (throw new
RebootServiceException("Cannot reboot device", e)); this preserves the full
exception chain (RebootServiceException → InputPortException → underlying cause)
while keeping the existing message; update any tests that assert on the exact
wrapped cause if necessary (e.g.,
RebootServiceImplTest#reboot_portFailure_wrapsException).
src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoServiceException.java (1)

3-11: Optional: consider a shared service-exception base and a serialVersionUID.

A couple of nice-to-haves for the new checked-exception family being introduced across the service layer:

  1. Since several near-identical exception classes are being added in this PR (DeviceInfoServiceException, PixelProgramServiceException, UserManagementServiceException, …), a common abstract base (e.g., ApplicationServiceException extends Exception) would let callers catch the family generically and remove constructor boilerplate from each subclass.
  2. Exception is Serializable, so omitting serialVersionUID produces a compiler/IDE warning and makes serial form depend on the auto-generated UID. Adding an explicit constant is cheap insurance.
♻️ Suggested tweak
 package pl.vtt.wpi.core.application.exception;

-public class DeviceInfoServiceException extends Exception {
+public class DeviceInfoServiceException extends ApplicationServiceException {
+    private static final long serialVersionUID = 1L;
+
     public DeviceInfoServiceException(String message) {
         super(message);
     }

     public DeviceInfoServiceException(String message, Throwable cause) {
         super(message, cause);
     }
 }

With a shared base such as:

package pl.vtt.wpi.core.application.exception;

public abstract class ApplicationServiceException extends Exception {
    private static final long serialVersionUID = 1L;

    protected ApplicationServiceException(String message) { super(message); }
    protected ApplicationServiceException(String message, Throwable cause) { super(message, cause); }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoServiceException.java`
around lines 3 - 11, Introduce a shared abstract base exception class (e.g.,
ApplicationServiceException) that extends Exception, declares a private static
final long serialVersionUID, and provides protected constructors (message and
message+cause); change DeviceInfoServiceException to extend this new
ApplicationServiceException and add its own serialVersionUID constant as well
(do similarly for other service exceptions like PixelProgramServiceException and
UserManagementServiceException) to remove constructor duplication and silence
serialization warnings.
src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationServiceException.java (1)

3-11: LGTM!

Standard checked-exception pattern with the two most useful constructors (message and message + cause). Naming and package placement (application.exception) are consistent with the new application-service exception family introduced in this PR.

Optional nit: since Exception implements Serializable, you may want to declare a serialVersionUID to silence serialization-lint warnings and stabilize the serialized form across JVMs. Not a blocker.

♻️ Optional: add serialVersionUID
 public class NetworkConfigurationServiceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
     public NetworkConfigurationServiceException(String message) {
         super(message);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationServiceException.java`
around lines 3 - 11, Add a serialVersionUID field to
NetworkConfigurationServiceException to satisfy serialization linting and
stabilize its serialized form; specifically, inside the
NetworkConfigurationServiceException class (alongside the existing constructors)
declare a private static final long serialVersionUID with any fixed long literal
(e.g., 1L) so that the class explicitly defines its serialization identifier.
src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java (1)

5-5: Nit: import ordering.

pl.vtt.wpi.core.application.exception.NetworkConfigurationServiceException is grouped after pl.vtt.wpi.core.domain.model.* imports, which breaks lexical ordering within the same root package. If your project enforces sorted imports (Checkstyle / IDE formatter), this will likely be flagged. Minor — feel free to ignore if no formatter rule is configured.

♻️ Suggested ordering
-import pl.vtt.wpi.core.domain.model.device.AccessPointConfig;
-import pl.vtt.wpi.core.domain.model.device.WifiConfig;
-import pl.vtt.wpi.core.application.exception.NetworkConfigurationServiceException;
+import pl.vtt.wpi.core.application.exception.NetworkConfigurationServiceException;
+import pl.vtt.wpi.core.domain.model.device.AccessPointConfig;
+import pl.vtt.wpi.core.domain.model.device.WifiConfig;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java`
at line 5, Import ordering is out of lexical order: move the import of
NetworkConfigurationServiceException so imports under the same root are sorted;
specifically, place
pl.vtt.wpi.core.application.exception.NetworkConfigurationServiceException
before the pl.vtt.wpi.core.domain.model.* imports in
NetworkConfigurationService.java (adjust other nearby imports if necessary) to
satisfy the project's sorted-import convention.
src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java (1)

59-75: Test name and @DisplayName no longer reflect what is being tested.

The method name runtime_exception_clears_authorization and the display name "Tests cleanup when auth port throws runtime exception" were accurate when the production code wrapped non-domain failures in a RuntimeException. After this PR, the port throws an OutputPortException (checked) wrapping an IllegalStateException, and the assertion is now on the checked LoginServiceException. Renaming would make the test intent clearer for future readers.

♻️ Proposed rename
-    `@DisplayName`("Tests cleanup when auth port throws runtime exception")
-    void runtime_exception_clears_authorization() {
+    `@DisplayName`("Tests cleanup when auth port wraps a non-domain failure in OutputPortException")
+    void output_port_failure_clears_authorization() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java`
around lines 59 - 75, Rename the test method
runtime_exception_clears_authorization and its `@DisplayName` to reflect that the
port now throws a checked OutputPortException and the service translates it into
a LoginServiceException; update the method name (e.g.,
output_port_exception_clears_authorization or
login_service_throws_loginserviceexception_and_clears_authorization) and change
the `@DisplayName` to something like "Tests cleanup when OutputPortException
causes LoginServiceException", keeping assertions on LoginServiceException,
OutputPortException wrapping IllegalStateException, and the
AuthorizationHolder.get() null check unchanged; locate these in
LoginServiceImplTest where OutputPort, OutputPortException,
LoginServiceException, and AuthorizationHolder are used.
src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java (1)

11-11: Consider extracting AdminPasswordResetRequest to a top-level DTO.

Per the AI summary, UserCreateRequest was introduced as a top-level DTO under pl.vtt.wpi.core.domain.dto to parameterize UserCreateInputPort. Keeping AdminPasswordResetRequest as a public nested record inside AdminPasswordServiceImpl couples the input-port generic type to the implementation class symbol (e.g., adapters wiring this port must import AdminPasswordServiceImpl.AdminPasswordResetRequest). Extracting it for parity would make the port wiring cleaner and decouple adapters from the concrete service class.

♻️ Sketch of the refactor
-public class AdminPasswordServiceImpl implements AdminPasswordService {
-    private final InputPort<AdminPasswordResetRequest> adminPasswordResetInputPort;
+public class AdminPasswordServiceImpl implements AdminPasswordService {
+    private final InputPort<AdminPasswordResetRequest> adminPasswordResetInputPort;
@@
-    public record AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) {
-    }
 }

Then add a new top-level file e.g. src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java:

package pl.vtt.wpi.core.domain.dto;

public record AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) {
}

…and update the import in this file accordingly.

Also applies to: 40-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java`
at line 11, AdminPasswordResetRequest is defined as a public nested record
inside AdminPasswordServiceImpl which forces consumers to reference
AdminPasswordServiceImpl.AdminPasswordResetRequest; extract it to a top-level
DTO (e.g., package pl.vtt.wpi.core.domain.dto) as public record
AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) and replace
all usages/imports of AdminPasswordServiceImpl.AdminPasswordResetRequest with
the new top-level AdminPasswordResetRequest, including the
InputPort<AdminPasswordResetRequest> field in AdminPasswordServiceImpl and any
adapter wiring that imports the nested type.
src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java (1)

16-58: Solid coverage of set(); consider adding read() tests too.

The three scenarios for set() (null input, missing pixel program with NoSuchElementException cause, happy path) align well with the implementation. The PR description and AI summary indicate read() also now declares throws RuntimeDataServiceException and wraps OutputPortException. Adding a couple of tests for read() (success path and OutputPortException → RuntimeDataServiceException translation) would round out coverage of the new contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java`
around lines 16 - 58, Add two unit tests for RuntimeDataServiceImpl.read(): one
success test that configures the service (RuntimeDataServiceImpl) so its read()
call returns a known RuntimeData and asserts the returned value equals that
instance, and one error-translation test that makes the underlying output port
throw an OutputPortException (or configure the read supplier to throw) and
asserts read() throws RuntimeDataServiceException with the OutputPortException
as the cause; reference RuntimeDataServiceImpl.read(),
RuntimeDataServiceException, and OutputPortException when locating where to hook
the mocks/suppliers and use a concrete RuntimeData instance for the success
case.
src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java (1)

12-34: Consider also covering the InputPortException → AdminPasswordServiceException path.

Current tests cover the happy path and a blank-password validation failure. The exception-translation branch in AdminPasswordServiceImpl (lines 32-37) — including cause unwrapping when e.getCause() is non-null — isn't exercised. A small additional test using an InputPort lambda that throws InputPortException would lock in this contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java`
around lines 12 - 34, Add a unit test that verifies
AdminPasswordServiceImpl.resetPassword translates an InputPortException into an
AdminPasswordServiceException (including the case where
InputPortException.getCause() is non-null); create an AdminPasswordServiceImpl
with an InputPort implementation that throws new InputPortException("msg") and
another variant that throws new InputPortException("msg", new
RuntimeException("cause")), call service.resetPassword("secure-key", valid
PasswordDto) and assert that an AdminPasswordServiceException is thrown and that
the original cause/message is preserved/accessible via getCause()/getMessage()
as appropriate; reference AdminPasswordServiceImpl, InputPort,
InputPortException, and AdminPasswordServiceException when locating where to add
the test.
src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java (1)

72-74: Optional: prefer Objects.equals for the index comparison.

program.index() == runtimeData.pixelProgram() is currently safe (the null branch is short-circuited at L61), but it relies on auto-unboxing the Integer returned by pixelProgram(). A future refactor that removes the early null guard would silently introduce an NPE here. Using Objects.equals(program.index(), runtimeData.pixelProgram()) (or explicit runtimeData.pixelProgram().intValue()) makes the intent and null-safety explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java`
around lines 72 - 74, Replace the direct == comparison between boxed integers
with a null-safe equality check: in the stream anyMatch where you currently test
program != null && program.index() == runtimeData.pixelProgram(), change the
comparison to use Objects.equals(program.index(), runtimeData.pixelProgram())
(or call runtimeData.pixelProgram().intValue() if you prefer explicit unboxing)
so the check is explicit about null-safety; update the import if needed and keep
the surrounding null guard for pixelPrograms and program.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImpl.java`:
- Around line 29-32: The current catch only handles
IncorrectUsernameOrPasswordException and LoginServiceException, so unchecked
exceptions from authPort.load() can bypass AuthorizationHolder.clear(); modify
LoginServiceImpl to guarantee cleanup by wrapping the authPort.load()/post-load
logic in a try/finally that calls AuthorizationHolder.clear() on failure (or add
an additional catch for RuntimeException that calls AuthorizationHolder.clear()
and rethrows); ensure the finally/catch covers all exceptions so
AuthorizationHolder.clear() is always invoked before propagating errors from
authPort.load() or other unexpected failures.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java`:
- Around line 64-70: The catch block around pixelProgramsOutputPort.load()
should not convert an infrastructure OutputPortException into a
DataInconsistencyException; change it to rethrow a RuntimeDataServiceException
with the caught cause (keep the original message/context), and update any method
signatures that declare throwing DataInconsistencyException (e.g., the set
method and validatePixelProgramExists) so they only throw
DataInconsistencyException for the genuine "pixel program does not exist" path
while port failures propagate as RuntimeDataServiceException; locate and modify
the load call handling in RuntimeDataServiceImpl (and adjust
validatePixelProgramExists and set declarations/usages) accordingly.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java`:
- Around line 38-47: In createUser, move the null-check for the User before
calling validatePassword so that a null user triggers
UserManagementServiceException("User cannot be null") immediately; specifically,
in UserManagementServiceImpl.update createUser(...) perform the if (user ==
null) guard first, then call validatePassword(passwordDto), then check existence
and throw UserAlreadyExistsException as before to ensure argument shape is
validated before content.
- Around line 44-49: The read-then-write sequence around loadUsers() and
userCreateRequestInputPort.send() has a race; guard the entire uniqueness
check+send with a ReentrantLock (similar to PixelProgramServiceImpl) by adding a
private final ReentrantLock (e.g., userLock) and wrapping the
loadUsers().stream().anyMatch(...) check and subsequent
userCreateRequestInputPort.send(new UserCreateRequest(...)) call inside
userLock.lock()/unlock() (use try/finally), and apply the same locking pattern
to removeUser() around its load/delete sequence; alternatively, if you choose
not to add a lock, add a clear javadoc on createUser/removeUser stating that
username uniqueness is enforced at the storage/port layer and must be atomic.

---

Nitpick comments:
In
`@src/main/java/pl/vtt/wpi/core/application/exception/DebugServiceException.java`:
- Around line 3-11: Add a brief Javadoc to the DebugServiceException class
describing when it is thrown (e.g., to wrap
InputPortException/OutputPortException from debug operations) and add an
explicit serialVersionUID field to avoid serialization warnings and
compatibility issues; specifically, update the DebugServiceException class to
include a short Javadoc above the class declaration and a private static final
long serialVersionUID = 1L (or a generated UID) while leaving the existing
constructors (DebugServiceException(String) and DebugServiceException(String,
Throwable)) intact.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoServiceException.java`:
- Around line 3-11: Introduce a shared abstract base exception class (e.g.,
ApplicationServiceException) that extends Exception, declares a private static
final long serialVersionUID, and provides protected constructors (message and
message+cause); change DeviceInfoServiceException to extend this new
ApplicationServiceException and add its own serialVersionUID constant as well
(do similarly for other service exceptions like PixelProgramServiceException and
UserManagementServiceException) to remove constructor duplication and silence
serialization warnings.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationServiceException.java`:
- Around line 3-11: Add a serialVersionUID field to
NetworkConfigurationServiceException to satisfy serialization linting and
stabilize its serialized form; specifically, inside the
NetworkConfigurationServiceException class (alongside the existing constructors)
declare a private static final long serialVersionUID with any fixed long literal
(e.g., 1L) so that the class explicitly defines its serialization identifier.

In
`@src/main/java/pl/vtt/wpi/core/application/exception/RebootServiceException.java`:
- Around line 3-11: The RebootServiceException class lacks an explicit
serialVersionUID which can break serialization compatibility; add a private
static final long serialVersionUID field to the RebootServiceException class
(e.g., in class RebootServiceException) with a chosen constant value to satisfy
the Serializable contract and silence IDE/lint warnings while preserving
deserialization stability across versions.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java`:
- Line 11: AdminPasswordResetRequest is defined as a public nested record inside
AdminPasswordServiceImpl which forces consumers to reference
AdminPasswordServiceImpl.AdminPasswordResetRequest; extract it to a top-level
DTO (e.g., package pl.vtt.wpi.core.domain.dto) as public record
AdminPasswordResetRequest(String secureKey, PasswordDto passwordDto) and replace
all usages/imports of AdminPasswordServiceImpl.AdminPasswordResetRequest with
the new top-level AdminPasswordResetRequest, including the
InputPort<AdminPasswordResetRequest> field in AdminPasswordServiceImpl and any
adapter wiring that imports the nested type.

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java`:
- Around line 21-24: Replace the current unwrapping logic in RebootServiceImpl
that computes Throwable cause = e.getCause() == null ? e : e.getCause(); and
then throws new RebootServiceException("Cannot reboot device", cause) with
simply passing the original InputPortException as the cause (throw new
RebootServiceException("Cannot reboot device", e)); this preserves the full
exception chain (RebootServiceException → InputPortException → underlying cause)
while keeping the existing message; update any tests that assert on the exact
wrapped cause if necessary (e.g.,
RebootServiceImplTest#reboot_portFailure_wrapsException).

In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java`:
- Around line 72-74: Replace the direct == comparison between boxed integers
with a null-safe equality check: in the stream anyMatch where you currently test
program != null && program.index() == runtimeData.pixelProgram(), change the
comparison to use Objects.equals(program.index(), runtimeData.pixelProgram())
(or call runtimeData.pixelProgram().intValue() if you prefer explicit unboxing)
so the check is explicit about null-safety; update the import if needed and keep
the surrounding null guard for pixelPrograms and program.

In
`@src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java`:
- Line 5: Import ordering is out of lexical order: move the import of
NetworkConfigurationServiceException so imports under the same root are sorted;
specifically, place
pl.vtt.wpi.core.application.exception.NetworkConfigurationServiceException
before the pl.vtt.wpi.core.domain.model.* imports in
NetworkConfigurationService.java (adjust other nearby imports if necessary) to
satisfy the project's sorted-import convention.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java`:
- Around line 12-34: Add a unit test that verifies
AdminPasswordServiceImpl.resetPassword translates an InputPortException into an
AdminPasswordServiceException (including the case where
InputPortException.getCause() is non-null); create an AdminPasswordServiceImpl
with an InputPort implementation that throws new InputPortException("msg") and
another variant that throws new InputPortException("msg", new
RuntimeException("cause")), call service.resetPassword("secure-key", valid
PasswordDto) and assert that an AdminPasswordServiceException is thrown and that
the original cause/message is preserved/accessible via getCause()/getMessage()
as appropriate; reference AdminPasswordServiceImpl, InputPort,
InputPortException, and AdminPasswordServiceException when locating where to add
the test.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java`:
- Around line 59-75: Rename the test method
runtime_exception_clears_authorization and its `@DisplayName` to reflect that the
port now throws a checked OutputPortException and the service translates it into
a LoginServiceException; update the method name (e.g.,
output_port_exception_clears_authorization or
login_service_throws_loginserviceexception_and_clears_authorization) and change
the `@DisplayName` to something like "Tests cleanup when OutputPortException
causes LoginServiceException", keeping assertions on LoginServiceException,
OutputPortException wrapping IllegalStateException, and the
AuthorizationHolder.get() null check unchanged; locate these in
LoginServiceImplTest where OutputPort, OutputPortException,
LoginServiceException, and AuthorizationHolder are used.

In
`@src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java`:
- Around line 16-58: Add two unit tests for RuntimeDataServiceImpl.read(): one
success test that configures the service (RuntimeDataServiceImpl) so its read()
call returns a known RuntimeData and asserts the returned value equals that
instance, and one error-translation test that makes the underlying output port
throw an OutputPortException (or configure the read supplier to throw) and
asserts read() throws RuntimeDataServiceException with the OutputPortException
as the cause; reference RuntimeDataServiceImpl.read(),
RuntimeDataServiceException, and OutputPortException when locating where to hook
the mocks/suppliers and use a concrete RuntimeData instance for the success
case.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58ae5834-6e5c-47dd-ad3b-13926486fc7f

📥 Commits

Reviewing files that changed from the base of the PR and between 4f892a9 and d716514.

📒 Files selected for processing (36)
  • src/main/java/pl/vtt/wpi/core/application/exception/AdminPasswordServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DebugServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/LoginServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/PixelProgramServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/RebootServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/RuntimeDataServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/UserManagementServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
  • src/main/java/pl/vtt/wpi/core/application/service/DeviceInfoService.java
  • src/main/java/pl/vtt/wpi/core/application/service/LoginService.java
  • src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java
  • src/main/java/pl/vtt/wpi/core/application/service/PixelProgramService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RebootService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java
  • src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java
✅ Files skipped from review due to trivial changes (4)
  • src/main/java/pl/vtt/wpi/core/application/exception/LoginServiceException.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java

Comment thread src/main/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImpl.java Outdated
@koder95
Copy link
Copy Markdown
Contributor Author

koder95 commented Apr 27, 2026

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad17cba7c2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

try {
List<PixelProgram> programs = new ArrayList<>(getAll());
int nextIndex = programs.stream()
.map(PixelProgram::index)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip null entries before deriving next pixel-program index

In save, programs.stream().map(PixelProgram::index) will throw a NullPointerException when the loaded list contains a null element. This class already accounts for null entries elsewhere (findListIndex), so a null coming from pixelProgramsOutputPort.load() is a realistic corrupted-data path; in that case save fails with an unchecked exception instead of the declared PixelProgramOperationException, breaking the service contract for callers.

Useful? React with 👍 / 👎.

validatePassword(passwordDto);
userLock.lock();
try {
boolean exists = loadUsers().stream().anyMatch(existingUser -> existingUser.username().equals(user.username()));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard null user entries during existence checks

Both createUser and removeUser dereference existingUser.username() directly while scanning loadUsers(). If the output port returns a list containing a null entry, this path throws NullPointerException (not wrapped), so callers receive an unexpected runtime failure instead of UserManagementOperationException/domain exceptions. Adding a null filter (or validating loadUsers output) would preserve the checked-exception contract.

Useful? React with 👍 / 👎.

@koder95
Copy link
Copy Markdown
Contributor Author

koder95 commented May 7, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java (1)

23-24: ⚡ Quick win

OutputPortException context is silently dropped when a nested cause exists.

When e.getCause() != null, cause is set to e.getCause() directly, bypassing the OutputPortException itself. Any message or port-specific context carried by the OutputPortException is discarded, and DeviceInfoReadException.getCause() points straight to the root cause. If a caller (or log line) chains through getCause() it will never see the OutputPortException layer.

The more conventional approach is to always chain the caught exception, letting the full stack be traversable:

♻️ Proposed fix
-        } catch (OutputPortException e) {
-            Throwable cause = e.getCause() == null ? e : e.getCause();
-            throw new DeviceInfoReadException("Cannot read device info", cause);
+        } catch (OutputPortException e) {
+            throw new DeviceInfoReadException("Cannot read device info", e);
         }
🤖 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
`@src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java`
around lines 23 - 24, The current catch in DeviceInfoServiceImpl replaces the
caught OutputPortException context by using e.getCause() when present, losing
the OutputPortException layer; change the throw to always chain the caught
exception e (the OutputPortException) as the cause when constructing
DeviceInfoReadException so the full exception chain and port-specific context
remain accessible (i.e., stop substituting e.getCause() and pass e as the cause
to the DeviceInfoReadException).
🤖 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.

Nitpick comments:
In
`@src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java`:
- Around line 23-24: The current catch in DeviceInfoServiceImpl replaces the
caught OutputPortException context by using e.getCause() when present, losing
the OutputPortException layer; change the throw to always chain the caught
exception e (the OutputPortException) as the cause when constructing
DeviceInfoReadException so the full exception chain and port-specific context
remain accessible (i.e., stop substituting e.getCause() and pass e as the cause
to the DeviceInfoReadException).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3de476ef-f913-426f-8fc9-a165a76992db

📥 Commits

Reviewing files that changed from the base of the PR and between d716514 and ad17cba.

📒 Files selected for processing (35)
  • src/main/java/pl/vtt/wpi/core/application/exception/AdminPasswordResetException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/ApplicationServiceException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/AuthenticationServiceUnavailableException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DebugDataAccessException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoReadException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DeviceRebootException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/PixelProgramOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/RuntimeDataOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/UserManagementOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/service/AdminPasswordService.java
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
  • src/main/java/pl/vtt/wpi/core/application/service/DeviceInfoService.java
  • src/main/java/pl/vtt/wpi/core/application/service/LoginService.java
  • src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java
  • src/main/java/pl/vtt/wpi/core/application/service/PixelProgramService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RebootService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java
  • src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java
✅ Files skipped from review due to trivial changes (12)
  • src/main/java/pl/vtt/wpi/core/application/exception/ApplicationServiceException.java
  • src/main/java/pl/vtt/wpi/core/domain/dto/AdminPasswordResetRequest.java
  • src/main/java/pl/vtt/wpi/core/application/exception/UserManagementOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/AuthenticationServiceUnavailableException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/NetworkConfigurationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DeviceRebootException.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DebugDataAccessException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/DeviceInfoReadException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/PixelProgramOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/exception/RuntimeDataOperationException.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/NetworkConfigurationServiceImpl.java
🚧 Files skipped from review as they are similar to previous changes (20)
  • src/main/java/pl/vtt/wpi/core/application/service/DeviceInfoService.java
  • src/main/java/pl/vtt/wpi/core/application/service/RebootService.java
  • src/main/java/pl/vtt/wpi/core/application/service/NetworkConfigurationService.java
  • src/main/java/pl/vtt/wpi/core/application/service/LoginService.java
  • src/main/java/pl/vtt/wpi/core/application/service/DebugService.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImplTest.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/DeviceInfoServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/UserManagementService.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImpl.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RebootServiceImpl.java
  • src/test/java/pl/vtt/wpi/core/application/service/impl/AdminPasswordServiceImplTest.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/LoginServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/UserManagementServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/DebugServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/PixelProgramServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/PixelProgramService.java
  • src/main/java/pl/vtt/wpi/core/application/service/impl/RuntimeDataServiceImpl.java
  • src/main/java/pl/vtt/wpi/core/application/service/RuntimeDataService.java

@koder95 koder95 merged commit 386e4a5 into main May 7, 2026
3 checks passed
@koder95 koder95 deleted the codex/implement-service-interfaces-in-impl-package branch May 7, 2026 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant