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