hackathon/docs/testing/test-package-structure.md
Christoph Wagner 5b658e2468 docs: add architectural review and requirement refinement verification
Complete architectural analysis and requirement traceability improvements:

  1. Architecture Review Report (NEW)
     - Independent architectural review identifying 15 issues
     - 5 critical issues: security (no TLS), buffer inadequacy, performance
       bottleneck, missing circuit breaker, inefficient backoff
     - 5 major issues: no metrics, no graceful shutdown, missing rate limiting,
       no backpressure, low test coverage
     - Overall architecture score: 6.5/10
     - Recommendation: DO NOT DEPLOY until critical issues resolved
     - Detailed analysis with code examples and effort estimates

  2. Requirement Refinement Verification (NEW)
     - Verified Req-FR-25, Req-NFR-7, Req-NFR-8 refinement status
     - Added 12 missing Req-FR-25 references to architecture documents
     - Confirmed 24 Req-NFR-7 references (health check endpoint)
     - Confirmed 26 Req-NFR-8 references (health check content)
     - 100% traceability for all three requirements

  3. Architecture Documentation Updates
     - system-architecture.md: Added 4 Req-FR-25 references for data transmission
     - java-package-structure.md: Added 8 Req-FR-25 references across components
     - Updated DataTransmissionService, GrpcStreamPort, GrpcStreamingAdapter,
       DataConsumerService with proper requirement annotations

  Files changed:
  - docs/ARCHITECTURE_REVIEW_REPORT.md (NEW)
  - docs/REQUIREMENT_REFINEMENT_VERIFICATION.md (NEW)
  - docs/architecture/system-architecture.md (4 additions)
  - docs/architecture/java-package-structure.md (8 additions)

  All 62 requirements now have complete bidirectional traceability with
  documented architectural concerns and critical issues identified for resolution.
2025-11-19 11:06:02 +01:00

775 lines
24 KiB
Markdown

# Test Package Structure
## Overview
This document defines the complete test package organization, test class structure, and mock server setup for the Log Data Collector system.
## Test Source Directory Structure
```
src/test/
├── java/
│ └── com/
│ └── logcollector/
│ ├── unit/ # Unit Tests (75% of suite)
│ │ ├── config/
│ │ │ ├── ConfigurationLoaderTest.java
│ │ │ ├── YamlParserTest.java
│ │ │ └── ValidationServiceTest.java
│ │ ├── serialization/
│ │ │ ├── DataSerializerTest.java
│ │ │ ├── JsonSerializerTest.java
│ │ │ └── ProtobufSerializerTest.java
│ │ ├── buffer/
│ │ │ ├── CircularBufferTest.java
│ │ │ ├── BufferOverflowHandlerTest.java
│ │ │ └── BufferThreadSafetyTest.java
│ │ ├── retry/
│ │ │ ├── RetryMechanismTest.java
│ │ │ ├── ExponentialBackoffTest.java
│ │ │ └── RetryPolicyTest.java
│ │ ├── health/
│ │ │ ├── HealthCheckEndpointTest.java
│ │ │ ├── HttpHealthCheckTest.java
│ │ │ └── GrpcHealthCheckTest.java
│ │ ├── collector/
│ │ │ ├── HttpCollectorTest.java
│ │ │ ├── EndpointSchedulerTest.java
│ │ │ └── ResponseParserTest.java
│ │ ├── transmitter/
│ │ │ ├── GrpcTransmitterTest.java
│ │ │ ├── ConnectionManagerTest.java
│ │ │ └── TransmissionQueueTest.java
│ │ └── startup/
│ │ ├── StartupSequenceTest.java
│ │ ├── ComponentInitializerTest.java
│ │ └── DependencyResolverTest.java
│ │
│ ├── integration/ # Integration Tests (20% of suite)
│ │ ├── collector/
│ │ │ ├── HttpCollectionIntegrationTest.java
│ │ │ └── MultiEndpointIntegrationTest.java
│ │ ├── transmitter/
│ │ │ ├── GrpcTransmissionIntegrationTest.java
│ │ │ └── ReconnectionIntegrationTest.java
│ │ ├── e2e/
│ │ │ ├── EndToEndDataFlowTest.java
│ │ │ └── BackpressureIntegrationTest.java
│ │ ├── config/
│ │ │ ├── ConfigurationFileIntegrationTest.java
│ │ │ └── ConfigurationReloadIntegrationTest.java
│ │ └── buffer/
│ │ ├── CircularBufferIntegrationTest.java
│ │ └── BufferPerformanceIntegrationTest.java
│ │
│ ├── performance/ # Performance Tests
│ │ ├── PerformanceConcurrentEndpointsTest.java
│ │ ├── PerformanceMemoryUsageTest.java
│ │ ├── PerformanceVirtualThreadTest.java
│ │ └── PerformanceStartupTimeTest.java
│ │
│ ├── reliability/ # Reliability Tests
│ │ ├── ReliabilityStartupSequenceTest.java
│ │ ├── ReliabilityGrpcRetryTest.java
│ │ ├── ReliabilityHttpFailureTest.java
│ │ ├── ReliabilityBufferOverflowTest.java
│ │ └── ReliabilityPartialFailureTest.java
│ │
│ ├── compliance/ # Compliance Tests
│ │ ├── ComplianceErrorDetectionTest.java
│ │ ├── ComplianceIso9001Test.java
│ │ ├── ComplianceEn50716Test.java
│ │ └── ComplianceAuditLoggingTest.java
│ │
│ └── util/ # Test Utilities
│ ├── mock/
│ │ ├── HttpMockServerSetup.java
│ │ ├── GrpcMockServerSetup.java
│ │ └── MockClockProvider.java
│ ├── builder/
│ │ ├── TestDataFactory.java
│ │ ├── TestConfigurationBuilder.java
│ │ ├── TestEndpointBuilder.java
│ │ └── TestLogEntryBuilder.java
│ ├── assertion/
│ │ ├── CustomAssertions.java
│ │ └── PerformanceAssertions.java
│ └── extension/
│ ├── MockServerExtension.java
│ ├── GrpcServerExtension.java
│ └── PerformanceTestExtension.java
└── resources/
├── config/
│ ├── test-config.yaml # Valid test configuration
│ ├── test-config-invalid.yaml # Invalid format
│ ├── test-config-minimal.yaml # Minimal valid config
│ └── test-config-maximal.yaml # Maximum complexity config
├── data/
│ ├── sample-log-entries.json # Sample log data
│ └── large-payload.json # Large payload test data
├── wiremock/
│ ├── mappings/ # WireMock stub mappings
│ │ ├── health-check-success.json
│ │ ├── health-check-failure.json
│ │ ├── log-endpoint-success.json
│ │ └── log-endpoint-timeout.json
│ └── __files/ # WireMock response files
│ ├── sample-response.json
│ └── error-response.json
├── proto/
│ └── test-log-data.proto # Test Protocol Buffer definitions
├── logback-test.xml # Test logging configuration
└── junit-platform.properties # JUnit configuration
```
## Test Class Templates
### Unit Test Template
```java
package com.logcollector.unit.config;
import org.junit.jupiter.api.*;
import org.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* Unit tests for ConfigurationLoader component.
*
* @validates Req-FR-11 - Configuration file detection
* @validates Req-FR-12 - Configuration parsing
* @validates Req-FR-13 - Configuration validation
* @validates Req-Norm-3 - Error detection
*/
@DisplayName("Configuration Loader Unit Tests")
@Tag("unit")
@Tag("config")
class ConfigurationLoaderTest {
@Mock
private FileSystem fileSystem;
@Mock
private YamlParser yamlParser;
@InjectMocks
private ConfigurationLoader configurationLoader;
private AutoCloseable mocks;
@BeforeEach
void setUp() {
mocks = MockitoAnnotations.openMocks(this);
}
@AfterEach
void tearDown() throws Exception {
mocks.close();
}
@Nested
@DisplayName("Configuration File Detection")
class FileDetectionTests {
@Test
@DisplayName("should detect config file when file exists")
void shouldDetectConfigFile_whenFileExists() {
// Arrange
when(fileSystem.exists("/etc/logcollector/config.yaml"))
.thenReturn(true);
// Act
boolean result = configurationLoader.detectConfigFile();
// Assert
assertThat(result).isTrue();
verify(fileSystem).exists("/etc/logcollector/config.yaml");
}
@Test
@DisplayName("should throw exception when file not found")
void shouldThrowException_whenFileNotFound() {
// Arrange
when(fileSystem.exists(anyString())).thenReturn(false);
// Act & Assert
assertThatThrownBy(() -> configurationLoader.load())
.isInstanceOf(ConfigurationNotFoundException.class)
.hasMessageContaining("Configuration file not found");
}
}
@Nested
@DisplayName("Configuration Parsing")
class ParsingTests {
// Parsing test methods...
}
@Nested
@DisplayName("Configuration Validation")
class ValidationTests {
// Validation test methods...
}
}
```
### Integration Test Template
```java
package com.logcollector.integration.collector;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.logcollector.util.extension.MockServerExtension;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.assertj.core.api.Assertions.*;
/**
* Integration tests for HTTP collection with mock server.
*
* @validates Req-Test-1 - Mock HTTP server
* @validates Req-FR-14 - HTTP endpoint collection
* @validates Req-FR-15 - Response parsing
*/
@DisplayName("HTTP Collection Integration Tests")
@Tag("integration")
@Tag("http")
@ExtendWith(MockServerExtension.class)
class HttpCollectionIntegrationTest {
private WireMockServer wireMockServer;
private HttpCollector httpCollector;
@BeforeEach
void setUp(WireMockServer server) {
this.wireMockServer = server;
this.httpCollector = new HttpCollector(
"http://localhost:" + server.port()
);
}
@Test
@DisplayName("should collect from mock endpoint when server running")
void shouldCollectFromMockEndpoint_whenServerRunning() {
// Arrange
wireMockServer.stubFor(get(urlEqualTo("/logs"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("sample-response.json")));
// Act
LogData result = httpCollector.collect("/logs");
// Assert
assertThat(result).isNotNull();
assertThat(result.getEntries()).isNotEmpty();
// Verify interaction
wireMockServer.verify(1, getRequestedFor(urlEqualTo("/logs")));
}
@Test
@DisplayName("should handle multiple endpoints concurrently")
void shouldHandleMultipleEndpoints_concurrently() {
// Test implementation...
}
}
```
### Performance Test Template
```java
package com.logcollector.performance;
import org.junit.jupiter.api.*;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.*;
/**
* Performance tests for concurrent endpoint handling.
*
* @validates Req-NFR-1 - 1000 concurrent endpoints support
*/
@DisplayName("Performance: Concurrent Endpoints")
@Tag("performance")
class PerformanceConcurrentEndpointsTest {
@Test
@DisplayName("should handle 1000 endpoints concurrently")
@Timeout(value = 60, unit = TimeUnit.SECONDS)
void shouldHandle1000Endpoints_concurrently() throws Exception {
// Arrange
List<String> endpoints = IntStream.range(0, 1000)
.mapToObj(i -> "http://endpoint-" + i + ".test/logs")
.collect(Collectors.toList());
HttpCollector collector = new HttpCollector();
// Act
long startTime = System.nanoTime();
List<CompletableFuture<LogData>> futures = endpoints.stream()
.map(collector::collectAsync)
.toList();
List<LogData> results = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
).thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.toList()
).get();
long duration = System.nanoTime() - startTime;
// Assert
assertThat(results).hasSize(1000);
assertThat(duration).isLessThan(TimeUnit.MINUTES.toNanos(1));
// Report metrics
System.out.printf("Collected 1000 endpoints in %.2f seconds%n",
duration / 1_000_000_000.0);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public void benchmarkEndpointCollection() {
// JMH benchmark implementation...
}
}
```
## Mock Server Setup
### WireMock HTTP Server
```java
package com.logcollector.util.mock;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
/**
* Mock HTTP server setup for testing HTTP collection.
*
* @validates Req-Test-1 - Mock HTTP server requirement
*/
public class HttpMockServerSetup {
private final WireMockServer server;
public HttpMockServerSetup() {
this.server = new WireMockServer(
WireMockConfiguration.options()
.dynamicPort()
.usingFilesUnderClasspath("wiremock")
);
}
public void start() {
server.start();
configureDefaultStubs();
}
public void stop() {
server.stop();
}
public int getPort() {
return server.port();
}
public WireMockServer getServer() {
return server;
}
private void configureDefaultStubs() {
// Health check success
server.stubFor(get(urlEqualTo("/health"))
.willReturn(aResponse()
.withStatus(200)
.withBody("{\"status\":\"UP\"}")));
// Log endpoint success
server.stubFor(get(urlPathMatching("/logs.*"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("sample-response.json")));
// Simulate timeout
server.stubFor(get(urlEqualTo("/slow"))
.willReturn(aResponse()
.withStatus(200)
.withFixedDelay(10000)));
// Simulate error
server.stubFor(get(urlEqualTo("/error"))
.willReturn(aResponse()
.withStatus(500)
.withBody("{\"error\":\"Internal Server Error\"}")));
}
}
```
### gRPC Mock Server
```java
package com.logcollector.util.mock;
import io.grpc.*;
import io.grpc.inprocess.*;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
/**
* Mock gRPC server setup for testing transmission.
*
* @validates Req-Test-2 - Mock gRPC server requirement
*/
public class GrpcMockServerSetup {
private final String serverName;
private final Server server;
private final MockLogDataService mockService;
public GrpcMockServerSetup() {
this.serverName = InProcessServerBuilder.generateName();
this.mockService = new MockLogDataService();
this.server = InProcessServerBuilder
.forName(serverName)
.directExecutor()
.addService(mockService)
.build();
}
public void start() throws IOException {
server.start();
}
public void stop() {
server.shutdownNow();
}
public String getServerName() {
return serverName;
}
public MockLogDataService getMockService() {
return mockService;
}
public ManagedChannel createChannel() {
return InProcessChannelBuilder
.forName(serverName)
.directExecutor()
.build();
}
/**
* Mock implementation of LogDataService for testing.
*/
public static class MockLogDataService
extends LogDataServiceGrpc.LogDataServiceImplBase {
private int callCount = 0;
private boolean shouldFail = false;
@Override
public void sendLogData(
LogDataRequest request,
StreamObserver<LogDataResponse> responseObserver) {
callCount++;
if (shouldFail) {
responseObserver.onError(
Status.UNAVAILABLE
.withDescription("Mock failure")
.asRuntimeException()
);
return;
}
LogDataResponse response = LogDataResponse.newBuilder()
.setSuccess(true)
.setMessageId(UUID.randomUUID().toString())
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
public int getCallCount() {
return callCount;
}
public void setShouldFail(boolean shouldFail) {
this.shouldFail = shouldFail;
}
public void reset() {
callCount = 0;
shouldFail = false;
}
}
}
```
## Test Data Builders
### Test Configuration Builder
```java
package com.logcollector.util.builder;
import com.logcollector.config.Configuration;
import com.logcollector.config.EndpointConfig;
import com.logcollector.config.GrpcConfig;
import java.util.*;
/**
* Fluent builder for test Configuration objects.
*/
public class TestConfigurationBuilder {
private List<EndpointConfig> endpoints = new ArrayList<>();
private String grpcHost = "localhost";
private int grpcPort = 9090;
private int bufferSize = 10000;
private int retryMaxAttempts = 3;
private int retryBaseDelay = 100;
public static TestConfigurationBuilder aConfiguration() {
return new TestConfigurationBuilder();
}
public TestConfigurationBuilder withEndpoint(EndpointConfig endpoint) {
this.endpoints.add(endpoint);
return this;
}
public TestConfigurationBuilder withEndpoints(int count) {
for (int i = 0; i < count; i++) {
endpoints.add(TestEndpointBuilder.anEndpoint()
.withUrl("http://endpoint-" + i + ".test/logs")
.withSchedule("0/30 * * * * ?")
.build());
}
return this;
}
public TestConfigurationBuilder withGrpcHost(String host) {
this.grpcHost = host;
return this;
}
public TestConfigurationBuilder withGrpcPort(int port) {
this.grpcPort = port;
return this;
}
public TestConfigurationBuilder withBufferSize(int size) {
this.bufferSize = size;
return this;
}
public TestConfigurationBuilder withRetryConfig(int maxAttempts, int baseDelay) {
this.retryMaxAttempts = maxAttempts;
this.retryBaseDelay = baseDelay;
return this;
}
public Configuration build() {
return Configuration.builder()
.endpoints(endpoints)
.grpcConfig(GrpcConfig.builder()
.host(grpcHost)
.port(grpcPort)
.build())
.bufferSize(bufferSize)
.retryMaxAttempts(retryMaxAttempts)
.retryBaseDelayMs(retryBaseDelay)
.build();
}
}
```
## JUnit Extensions
### Mock Server Extension
```java
package com.logcollector.util.extension;
import com.logcollector.util.mock.HttpMockServerSetup;
import org.junit.jupiter.api.extension.*;
/**
* JUnit 5 extension for WireMock server lifecycle management.
*/
public class MockServerExtension implements BeforeEachCallback, AfterEachCallback {
private static final String SERVER_KEY = "mockServer";
@Override
public void beforeEach(ExtensionContext context) throws Exception {
HttpMockServerSetup server = new HttpMockServerSetup();
server.start();
context.getStore(ExtensionContext.Namespace.GLOBAL)
.put(SERVER_KEY, server);
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
HttpMockServerSetup server = context.getStore(
ExtensionContext.Namespace.GLOBAL
).get(SERVER_KEY, HttpMockServerSetup.class);
if (server != null) {
server.stop();
}
}
}
```
## Test Resource Files
### test-config.yaml
```yaml
# Valid test configuration
endpoints:
- url: "http://endpoint1.test/logs"
schedule: "0/30 * * * * ?"
timeout: 5000
- url: "http://endpoint2.test/logs"
schedule: "0/60 * * * * ?"
timeout: 5000
grpc:
host: "localhost"
port: 9090
tls: false
buffer:
size: 10000
overflow: "overwrite"
retry:
maxAttempts: 3
baseDelayMs: 100
maxDelayMs: 5000
```
### logback-test.xml
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<!-- Reduce noise from test frameworks -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="io.grpc" level="WARN"/>
<logger name="com.github.tomakehurst.wiremock" level="WARN"/>
</configuration>
```
## Maven Test Configuration
### pom.xml (Test Section)
```xml
<build>
<testSourceDirectory>src/test/java</testSourceDirectory>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<!-- Surefire for unit tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/integration/**</exclude>
<exclude>**/performance/**</exclude>
</excludes>
<parallel>classes</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>
<!-- Failsafe for integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<includes>
<include>**/integration/**/*Test.java</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!-- Performance testing profile -->
<profile>
<id>performance-tests</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/performance/**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
```
---
**Version**: 1.0
**Last Updated**: 2025-11-19
**Author**: Test Strategist Agent
**Status**: Complete - Test Infrastructure Defined