Skip to content

Add GOPACSHandler day-ahead UFTP flow tests#42

Open
Miggets7 wants to merge 7 commits into
mainfrom
test/gopacs-handler-dayahead-flows
Open

Add GOPACSHandler day-ahead UFTP flow tests#42
Miggets7 wants to merge 7 commits into
mainfrom
test/gopacs-handler-dayahead-flows

Conversation

@Miggets7
Copy link
Copy Markdown
Contributor

@Miggets7 Miggets7 commented Jun 3, 2026

Summary

Adds test coverage for the GOPACSHandler day-ahead capacity-steering message flow on the AGR (EMS) side, closing the gap left by the UFTP V2→V3 participant migration (#40). The ems module previously had no tests for the GOPACS handler.

The spec drives real signed UFTP messages through the handler's actual entry point (processRawMessage) using libsodium crypto — no database, no container, no network. Inbound payloads are embedded as XML fixtures matching the real wire format (DSO nilsgrid.net → AGR openremote.io, v3.0.0, attribute-style). A small test-support constructor on GOPACSHandler skips remote config (OAuth client / private-key file) and JAX-RS deployment; a RecordingGOPACSHandler subclass captures outbound messages (both the library-generated responses and the handler-built FlexOffer); scheduled work runs inline for deterministic assertions.

Covered flows:

  • FlexRequest → asset updated from request ISPs (powerMaximumFlexRequest = importMax, powerMinimumFlexRequest = exportMax); replies with FlexRequestResponse (Accepted) then FlexOffer. The emitted FlexRequestResponse is asserted to match the reference message shape (result, referenced request id, swapped domains, version, conversation id).
  • FlexOfferResponse (Accepted and Rejected) → handled with no asset mutation and no reply.
  • FlexOrder → asset updated from order ISPs (currentPowerFlexRequest, plus the offtake powerLimitMaximumProfileFlexOrder / feed-in powerLimitMinimumProfileFlexOrder profile); replies with FlexOrderResponse (Accepted).
  • Out-of-scope drop → a validly-signed FlexRequest whose CongestionPoint differs from the contracted EAN is dropped: no asset mutation, no reply (the V3 re-scoping).

@Miggets7 Miggets7 added the Test Development of missing or unstable test label Jun 3, 2026
@Miggets7 Miggets7 requested a review from a team June 3, 2026 14:41
@wborn wborn requested a review from Copilot June 3, 2026 16:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds isolated Spock-based test coverage for the GOPACS UFTP day-ahead message flow on the EMS/AGR side by driving real signed UFTP v3.0.0 XML payloads through GOPACSHandler.processRawMessage, and introduces a test-support constructor to wire collaborators without container/OAuth/JAX-RS setup.

Changes:

  • Add GOPACSHandlerTest covering FlexRequest → (FlexRequestResponse + FlexOffer), FlexOfferResponse handling, FlexOrder flows, and out-of-scope congestion point dropping.
  • Add a protected test-support constructor on GOPACSHandler to allow exercising message handling without container wiring and remote configuration.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ems/src/test/groovy/org/openremote/extension/ems/manager/gopacs/GOPACSHandlerTest.groovy New Spock spec with signed-message fixtures and assertions for day-ahead GOPACS/UFTP flows.
ems/src/main/java/org/openremote/extension/ems/manager/gopacs/GOPACSHandler.java Adds a test-support constructor to instantiate the handler with injected services/executor/private key and without deployment/remote config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +246 to +250
this.client = null;
this.gopacsAddressBookResource = null;
this.gopacsAuthResource = null;
this.gopacsServerResource = null;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This path already degrades gracefully — no NPE is thrown.

  1. fetchBearerToken() wraps the gopacsAuthResource call in try/catch (Exception e). A null gopacsAuthResource raises an NPE, which is caught and the method returns "".

  2. getParticipantInformation() checks the blank token before touching the address book:

String authorization = fetchBearerToken();
if (authorization.isBlank()) {
    LOG.warning("Skipping participant lookup for " + domain + ": no OAuth2 bearer token available");
    return Optional.empty();
}
try (Response response = gopacsAddressBookResource.fetchParticipantByDomain(authorization, domain)) {

So even with participants unseeded: caught NPE → blank token → Optional.empty(), and gopacsAddressBookResource is never dereferenced. Additionally, the covered flows pre-seed participants, so the participants.containsKey(domain) cache returns first and fetchBearerToken() is not called at all.

Leaving the constructor as-is; no-op resource implementations would be unused plumbing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Test Development of missing or unstable test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants