Skip to content

Test Documentation ​

Test Strategy ​

MetaOne uses a layered test approach:

LayerFrameworkScope
Unit testsJUnit 5 + MockitoIndividual service/component logic
Integration testsSpring Boot Test + H2Full Spring context, in-memory DB
End-to-end testsSpring Boot TestFull install/enable/invoke/event lifecycle
Webhook testsMockMvc + MockitoHTTP-level controller tests

All tests run with the test Spring profile, which uses H2 in PostgreSQL compatibility mode — no external database is required.

Running Tests ​

bash
# Full test suite (all modules)
mvn test

# Single module
mvn -pl metaone-core test
mvn -pl crm-extension test
mvn -pl chatwoot-extension test

# Single test class
mvn -pl metaone-core -Dtest=ExtensionInstallerServiceTest test
mvn -pl metaone-core -Dtest=CommonResponseDTOTest test
mvn -pl metaone-core -Dtest=InProcessEventGatewayTest test

# Skip tests during build
mvn -DskipTests package

⚠️ Known issue: Full mvn test may fail on Spring context startup in some environments. Run mvn -pl metaone-core -Dtest=ExtensionInstallerServiceTest test as the reliable baseline. See TODOS.md P1.

Test Coverage by Area ​

Extension Lifecycle (metaone-core) ​

Test ClassWhat It Covers
ExtensionInstallerServiceTestInstall, uninstall, enable, disable flows; status transitions; error handling
ExtensionLifecycleE2ETestFull end-to-end: install → enable → invoke capability → disable → uninstall
ExtensionRuntimeServiceImplTestPF4J plugin load/unload, health checks
ExtensionRuntimeBootstrapTestPlugin bootstrap on startup
ExtensionInstallerEventHookTestEvent subscription hooks during install/enable/disable
ExtensionUpdaterServiceImplTestVersion update flow; rollback on failure
ExtensionHealthCheckServiceImplTestHealth check pass/fail scenarios
CompatibilityServiceImplTestPlatform version compatibility checks

Security & Policy (metaone-core) ​

Test ClassWhat It Covers
ExtensionPolicyEvaluatorImplTestInstall policy: blocked categories, duplicate installs
SignatureVerificationServiceImplTestArtifact signature validation (pass/fail/missing)

Event System (metaone-core) ​

Test ClassWhat It Covers
InProcessEventGatewayTestAsync/sync dispatch, @EventHandler discovery, typed payload deserialization, WorkspaceContext propagation

Configuration (metaone-core) ​

Test ClassWhat It Covers
ConfigEncryptionServiceTestAES-256-GCM encrypt/decrypt, key rotation
ExtensionConfigServiceImplTestCRUD config values, secret masking, required field validation
ExtensionConfigIntegrationTestFull config round-trip: save → retrieve → validate → enable

HTTP Layer (metaone-core) ​

Test ClassWhat It Covers
ExtensionWebhookControllerTestWebhook routing, WebhookAuthenticationException → HTTP 401, missing handler → 404
CommonResponseDTOTestResponse wrapper construction, null field exclusion

Data Layer (metaone-core) ​

Test ClassWhat It Covers
WorkspaceDataSourceRegistryTestWorkspace DataSource pool creation, eviction, max-pool enforcement
WorkspaceMembershipRepositoryTestMembership queries, cascade behavior

Chatwoot Extension (chatwoot-extension) ​

Test ClassWhat It Covers
ChatwootWebhookHandlerTestHMAC-SHA256 validation, event type routing, WebhookAuthenticationException on bad signature
ChatwootWidgetHandlerTestWidget config and identity token capability handlers

Test Configuration ​

Tests use src/test/resources/application-test.yaml:

yaml
spring:
  datasource:
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
    driver-class-name: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop
    database-platform: org.hibernate.dialect.H2Dialect

H2's PostgreSQL compatibility mode handles most PostgreSQL-specific syntax. Schema is created fresh for each test run and dropped on exit.

Test Patterns ​

Mocking External Dependencies ​

Services with external side effects (runtime deploy, health checks) are mocked in tests:

java
@MockitoBean
private ExtensionRuntimeService extensionRuntimeService;

@MockitoBean
private ExtensionHealthCheckService healthCheckService;

@BeforeEach
void setUp() {
    doNothing().when(extensionRuntimeService).deploy(any(), any(), any());
    doNothing().when(healthCheckService).assertHealthy(anyString(), any());
}

Integration Test Pattern ​

java
@SpringBootTest
@ActiveProfiles("test")
@Transactional                    // rolls back after each test
class ExtensionInstallerServiceTest {

    @Autowired ExtensionInstallerService installerService;
    @Autowired WorkspaceRepository workspaceRepository;

    @Test
    void testInstallExtension() {
        Workspace ws = workspaceRepository.save(...);
        ExtensionInstallation result = installerService.install("crm-extension", "1.0.0", ws.getId());
        assertEquals(ExtensionStatus.INSTALLED, result.getStatus());
    }
}

Known Coverage Gaps ​

The following areas have zero test coverage and are tracked as tech debt:

AreaTracked In
CrmService.publishOpportunityEventTODOS P2
NotificationEventListener.onNotificationTODOS P2
NotificationEventListener.resolveChannelTODOS P2
ChatService.sendSystemMessageTODOS P2
ChatService.getOrCreateDealsChannel (race condition path)TODOS P2

Starting point for new coverage: Follow the pattern in InProcessEventGatewayTest for event dispatch testing. For the getOrCreateDealsChannel race condition, spin two threads and verify idempotency via DataIntegrityViolationException handling.

Quality Gates ​

Before merging, ensure:

  • [ ] mvn -DskipTests package succeeds (compilation + assembly)
  • [ ] All tests in the changed module pass: mvn -pl <module> test
  • [ ] No new RuntimeException thrown directly in service code (use typed exceptions)
  • [ ] New capability handlers return CapabilityResult<T> (not raw types)
  • [ ] CHANGELOG.md updated for user-facing changes

MetaOne Platform Documentation