Make setupSsl package-private so the test can call it directly, ensuring
tests exercise the real production code path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add RSA_4096 and EC_P256 alongside RSA_2048 and EC_P384
- Parameterize encrypted key tests (RSA-only, EC encrypted keys
are a pre-existing PemSslCredentials limitation)
- 14 test scenarios total
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add EC_P384 key type alongside RSA_2048
- Parameterize separateCertAndKeyFiles and combinedPemFile tests
- Write private keys in PKCS#8 format for EC compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use forPort(0) instead of TestSocketUtils for random port
- Replace manual poll loop with Awaitility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests mirror EdgeGrpcService.setupSsl() using PemSslCredentials:
- Separate cert and key files (existing behavior)
- Combined PEM file (cert + key)
- Encrypted private key with key_password
- Error when no private key found
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep privateKeyFile.pem as the default so existing users with
separate cert/key file configs are not affected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reuse PemSslCredentials (already handles combined PEM, separate files,
and encrypted keys) instead of duplicating PEM parsing logic.
Wire it into gRPC via GrpcSslContexts + KeyManagerFactory.
- Make private_key config optional (default empty) for combined PEM
- Add key_password config for encrypted private keys
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TimeseriesServiceNoSqlTest.shouldSaveEntryOfEachTypeWithTtl: await
tsService.save() with bounded .get(MAX_TIMEOUT, TimeUnit.SECONDS)
- EntityServiceTest.testFindTenantTelemetry: await timeseriesService.save()
and attributesService.save() with .get(TIMEOUT, TimeUnit.SECONDS) to
prevent both the race condition and an indefinite hang
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add update item version endpoint and service method (backend)
- Add update dialog component with confirm/updating/success/error states
- Add check for updates feature in installed items table
- Show update button on browse cards and detail dialog when update available
- Add route resolver for installed item infos shared across pages
- Improve deserialization: use ignoreUnknownFields, root cause error messages
- Refactor rule chain install to parse ruleChain and metadata separately
Variables reassigned in a loop cannot be captured in lambda expressions.
Introduce finalSavedDevice and finalTenantProfile as effectively final copies
before use in Awaitility.await() lambdas.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace magic number 5 with a named constant that explains intent:
small enough to force multiple pages and verify pagination loop correctness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Audit logs are saved asynchronously via executor.submit() in AuditLogServiceImpl.
Three tests were asserting log counts immediately after HTTP API calls, creating
a race condition where the last audit log write may not have completed yet.
Replace bare assertions with Awaitility.await().atMost(TIMEOUT, ...).untilAsserted()
in testAuditLogs, testAuditLogs_byTenantIdAndEntityId, and
testAuditLogs_byTenantIdAndEntityId_Sysadmin (confirmed broken: expected 2, got 1).
Also replace the hardcoded 10s timeout in testAuditLogsSysAdmin with the TIMEOUT constant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Full entity stack: IotHubInstalledItem with ID, descriptors, JPA entity, DAO (extends JpaAbstractDao), service
- Controller endpoints: install, list (paginated), list infos, get by itemId, delete
- Install flow saves descriptor with entity IDs, delete cascades to installed entities
- Frontend installed items page with search, pagination, type chips, action buttons
- Browse page shows "Installed" label on cards, detail dialog shows "Open details" for installed items
- Installed item infos fetched on browse page init to track installation state
EntityViewControllerTest was importing MQTT_PORT from AbstractMqttIntegrationTest,
a static final field initialized once per JVM. When running in the same Surefire
fork alongside other test classes that also use this constant (e.g. MqttGatewayRateLimitsTest,
DeviceEdgeTest), each class gets a different Spring context key but all try to bind
MqttTransportService to the same port, causing BindException.
Fix: define a private static MQTT_PORT/MQTT_URL directly in EntityViewControllerTest
so its Spring context gets its own independently allocated port.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests like testFindTenantsByTitle create 261 tenants and delete them via
deleteEntitiesAsync, which only waits for HTTP responses — not housekeeper
completion. Each tenant deletion submits ~30 TenantEntitiesDeletionHousekeeper
tasks (~7800 tasks total), which cascade further. The teardown's deleteTenant
then waits for lag==0 with a 90s timeout, which is insufficient for this
backlog and causes ConditionTimeoutException.
Fix: add awaitHousekeeperDrained() (5-min timeout) called at the start of
teardownWebTest so any pending housekeeper work from the test body drains
before per-tenant teardown deletions begin.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
doGet/doGetAsyncTyped assert HTTP 200 internally, so any non-200 response
throws AssertionError which Awaitility re-throws immediately instead of
continuing to poll.
Add .ignoreExceptions() to three additional await() polling loops that
call HTTP helpers:
- AbstractMqttV5ClientSparkplugAttributesTest: two doGetAsyncTyped calls
polling for attribute keys after NBIRTH/DBIRTH
- AbstractMqttAttributesIntegrationTest: doGetAsyncTyped polling for
attribute values after client publish
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
doGet(url, Class) asserts HTTP 200 internally, so when the Sparkplug
device hasn't been created yet the method throws AssertionError instead
of returning null. Awaitility propagates Error immediately rather than
continuing to poll, causing the test to fail after ~3 s instead of
retrying for up to 200 s.
Add .ignoreExceptions() to both await() calls in
connectClientWithCorrectAccessTokenWithNDEATHCreatedDevices and
connectClientWithCorrectAccessTokenWithNDEATHWithAliasCreatedDevices so
that a transient 404 is treated as "condition not yet met" and polling
continues as intended.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the MQTT transport closes the session on RPC delivery timeout,
the server re-queues the RPC asynchronously. The test was asserting
the RPC status immediately after the client disconnect latch fired,
before the server-side re-queuing had completed — so the status was
still SENT instead of the expected QUEUED.
Replace the direct doGet assertion with an Awaitility poll that waits
up to DEFAULT_WAIT_TIMEOUT_SECONDS for the status to become QUEUED.
Fixes flaky: MqttV5CloseTransportSessionOnRpcDeliveryTimeoutIntegrationTest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>