Phase 2 Tests: SSL profile bean tests and Properties binding tests #133

Open
opened 2026-07-06 19:25:54 +00:00 by Polly · 1 comment
Member

Description

When working on the portainer-automation module, I want comprehensive SSL profile bean tests and Properties binding tests, so I can ensure the SSL configuration and property bindings work correctly under various conditions.

Acceptance Criteria

  • GIVEN a valid SSL configuration profile WHEN the context loads THEN all SSL beans are correctly initialized with expected values
  • GIVEN missing or partial SSL properties WHEN the context loads THEN appropriate default values are applied or graceful degradation occurs
  • GIVEN property binding configurations WHEN properties are loaded THEN all fields are correctly bound from application properties
  • GIVEN invalid property values WHEN binding occurs THEN appropriate validation errors are raised

Technical Notes

  • Build upon the existing test infrastructure from Phase 1
  • Cover edge cases: missing cert paths, invalid keystore formats, expired cert scenarios
  • Use Spring Boot test slicing where appropriate

Dependencies

  • Blocks: Phase 2 integration

Effort: S (2-5 days)

## Description When working on the portainer-automation module, I want comprehensive SSL profile bean tests and Properties binding tests, so I can ensure the SSL configuration and property bindings work correctly under various conditions. ## Acceptance Criteria - GIVEN a valid SSL configuration profile WHEN the context loads THEN all SSL beans are correctly initialized with expected values - GIVEN missing or partial SSL properties WHEN the context loads THEN appropriate default values are applied or graceful degradation occurs - GIVEN property binding configurations WHEN properties are loaded THEN all fields are correctly bound from application properties - GIVEN invalid property values WHEN binding occurs THEN appropriate validation errors are raised ## Technical Notes - Build upon the existing test infrastructure from Phase 1 - Cover edge cases: missing cert paths, invalid keystore formats, expired cert scenarios - Use Spring Boot test slicing where appropriate ## Dependencies - Blocks: Phase 2 integration ## Effort: S (2-5 days)
Polly added the
effort:s
test
labels 2026-07-06 19:26:00 +00:00
Member

Architecture Review — #133

Context Reviewed

I reviewed the full repository structure and the test branch where Phase 1 code lives. Here's my analysis:

1. Phase 1 Dependency (Important!)

The classes under test (PortainerClientConfig, PortainerAutomationProperties) currently exist only on the test branch — they are not on main. Before Phase 2 tests can be implemented, Phase 1 code must be merged to main (or you branch from test). This is a hard dependency.

2. SSL Profile Bean Tests (PortainerClientConfig)

What needs testing:

Profile Bean Behavior
@Profile("local") portainerRestTemplateLocal Trust-all SSL (self-signed certs via Cloudflare Tunnel)
@Profile("!local") portainerRestTemplate Standard JVM truststore (internal Docker network)
Always portainerApiClient Wires properties + RestTemplate into PortainerApiClient

Technical approach:

  • Use @SpringBootTest + @ActiveProfiles("local") / @ActiveProfiles("!local") in separate test classes
  • For "local", verify the RestTemplate uses a trust-all SSLContext — best checked via ReflectionTestUtils on the HttpComponentsClientHttpRequestFactory
  • For "!local", verify it uses the default HttpComponentsClientHttpRequestFactory (no custom SSL)
  • Test the portainerApiClient bean wiring — verify it's constructed with the correct RestTemplate and properties

Potential concern: The @Profile("local") bean manually constructs a trust-all SSLContext using raw TrustManager and SSLConnectionSocketFactory from Apache HttpClient5. In a test, you may want to factor out the SSL creation into a package-private method for easier unit testing (or use @TestConfiguration overrides).

3. Properties Binding Tests (PortainerAutomationProperties)

Binding hierarchy to test:

portainer.base-url          # → baseUrl (String)
portainer.api-key           # → apiKey (String) — from ${PORTAINER_API_KEY:}
portainer.endpoint-id       # → endpointId (Long)
portainer.service.api-key   # → service.apiKey (String, nested class)

Test scenarios needed:

Scenario Approach
Happy path — all props set @TestPropertySource(properties = {"portainer.base-url=...", ...})
Missing api-key with default Verify empty string fallback from PORTAINER_API_KEY:
Invalid endpoint-id (non-numeric) Verify BindingResult / failure analysis
Null base URL Verify empty/fallback behavior
Nested Service class binding Verify portainer.service.api-key binds correctly
Trailing slash on base-url Verify PortainerApiClient strips it

Recommendation: Use @SpringBootTest(classes = {PortainerAutomationProperties.class}) with @EnableConfigurationProperties(PortainerAutomationProperties.class) and @TestPropertySource for fine-grained control. Avoid loading the full context.

4. Test Dependencies

Ensure portainer-automation/build.gradle includes (already present on test branch):

testImplementation 'org.springframework.boot:spring-boot-starter-test'

No additional test deps needed beyond what's already declared.

5. Architecture Notes

  • The PortainerClientConfig mixes @Profile and @Bean method injection (portainerApiClient depends on both RestTemplate beans via the same bean name). This works because Spring resolves by parameter type, not name, but it's worth noting in tests.
  • Consider adding @ConditionalOnMissingBean guards if this config might be excluded in tests.

6. Missing Test File Locations

Based on the existing test branch structure:

portainer-automation/src/test/java/com/hithomelabs/portainer/config/
├── PortainerClientConfigTest.java        # SSL profile tests
└── PortainerAutomationPropertiesTest.java # Properties binding tests

Summary: Good scope for ~3 story points. The main dependency is Phase 1 being merged. Consider extracting the SSL context construction for better testability.

## Architecture Review — #133 ### Context Reviewed I reviewed the full repository structure and the `test` branch where Phase 1 code lives. Here's my analysis: ### 1. Phase 1 Dependency (Important!) The classes under test (`PortainerClientConfig`, `PortainerAutomationProperties`) currently exist **only on the `test` branch** — they are not on `main`. Before Phase 2 tests can be implemented, Phase 1 code must be merged to `main` (or you branch from `test`). This is a hard dependency. ### 2. SSL Profile Bean Tests (`PortainerClientConfig`) **What needs testing:** | Profile | Bean | Behavior | |---------|------|----------| | `@Profile("local")` | `portainerRestTemplateLocal` | Trust-all SSL (self-signed certs via Cloudflare Tunnel) | | `@Profile("!local")` | `portainerRestTemplate` | Standard JVM truststore (internal Docker network) | | Always | `portainerApiClient` | Wires properties + RestTemplate into `PortainerApiClient` | **Technical approach:** - Use `@SpringBootTest` + `@ActiveProfiles("local")` / `@ActiveProfiles("!local")` in separate test classes - For `"local"`, verify the `RestTemplate` uses a trust-all `SSLContext` — best checked via `ReflectionTestUtils` on the `HttpComponentsClientHttpRequestFactory` - For `"!local"`, verify it uses the default `HttpComponentsClientHttpRequestFactory` (no custom SSL) - Test the `portainerApiClient` bean wiring — verify it's constructed with the correct `RestTemplate` and properties **Potential concern:** The `@Profile("local")` bean manually constructs a trust-all `SSLContext` using raw `TrustManager` and `SSLConnectionSocketFactory` from Apache HttpClient5. In a test, you may want to factor out the SSL creation into a package-private method for easier unit testing (or use `@TestConfiguration` overrides). ### 3. Properties Binding Tests (`PortainerAutomationProperties`) **Binding hierarchy to test:** ```yaml portainer.base-url # → baseUrl (String) portainer.api-key # → apiKey (String) — from ${PORTAINER_API_KEY:} portainer.endpoint-id # → endpointId (Long) portainer.service.api-key # → service.apiKey (String, nested class) ``` **Test scenarios needed:** | Scenario | Approach | |----------|----------| | Happy path — all props set | `@TestPropertySource(properties = {"portainer.base-url=...", ...})` | | Missing `api-key` with default | Verify empty string fallback from `PORTAINER_API_KEY:` | | Invalid `endpoint-id` (non-numeric) | Verify `BindingResult` / failure analysis | | Null base URL | Verify empty/fallback behavior | | Nested `Service` class binding | Verify `portainer.service.api-key` binds correctly | | Trailing slash on base-url | Verify `PortainerApiClient` strips it | **Recommendation:** Use `@SpringBootTest(classes = {PortainerAutomationProperties.class})` with `@EnableConfigurationProperties(PortainerAutomationProperties.class)` and `@TestPropertySource` for fine-grained control. Avoid loading the full context. ### 4. Test Dependencies Ensure `portainer-automation/build.gradle` includes (already present on `test` branch): ```groovy testImplementation 'org.springframework.boot:spring-boot-starter-test' ``` No additional test deps needed beyond what's already declared. ### 5. Architecture Notes - The `PortainerClientConfig` mixes `@Profile` and `@Bean` method injection (`portainerApiClient` depends on both `RestTemplate` beans via the same bean name). This works because Spring resolves by parameter type, not name, but it's worth noting in tests. - Consider adding `@ConditionalOnMissingBean` guards if this config might be excluded in tests. ### 6. Missing Test File Locations Based on the existing `test` branch structure: ``` portainer-automation/src/test/java/com/hithomelabs/portainer/config/ ├── PortainerClientConfigTest.java # SSL profile tests └── PortainerAutomationPropertiesTest.java # Properties binding tests ``` --- **Summary:** Good scope for ~3 story points. The main dependency is Phase 1 being merged. Consider extracting the SSL context construction for better testability.
Archie added the
architect:complete
label 2026-07-06 19:28:11 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Hithomelabs/CFTunnels#133
No description provided.