# Summer Test

> Summer Framework testing — PostgresTestContainer setup with Flyway, WireMockServiceManager for stub services, blackbox test configuration and test-case JSON format, and test organization patterns.

- Skill: `taipt1504/summer-test` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add taipt1504/summer-test`
- Raw SKILL.md: https://api.skillmd.com/api/skills/taipt1504/summer-test/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: taipt1504 (https://skillmd.com/u/taipt1504)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/taipt1504/summer-test

---


# Summer Test — Containers, WireMock & Blackbox

**Gate:** Verify summer-core loaded and `io.f8a.summer:summer-platform` in build.gradle.

**Module:** `summer-test` | **Dependency:** `testImplementation 'io.f8a.summer:summer-test'`

## PostgresTestContainer

```java
@Testcontainers
class UserServiceIntegrationTest {

    @Container
    static PostgresTestContainer postgres = PostgresTestContainer.create()
        .withDatabaseName("testdb")
        .withUsername("testuser")
        .withPassword("testpass");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.r2dbc.url", postgres::getR2dbcUrl);
        registry.add("spring.r2dbc.username", postgres::getUsername);
        registry.add("spring.r2dbc.password", postgres::getPassword);
        registry.add("spring.flyway.url", postgres::getJdbcUrl);
        registry.add("spring.flyway.user", postgres::getUsername);
        registry.add("spring.flyway.password", postgres::getPassword);
    }

    @BeforeAll
    static void setup() {
        postgres.runFlywayMigrations("classpath:db/migration");
    }
}
```

Methods: `getJdbcUrl()`, `getR2dbcUrl()`, `getUsername()`, `getPassword()`, `runFlywayMigrations(path)`, `executeSqlScript(path)`, `createConnection()`. DB assertion utils (tableExists, getTableRowCount, executeUpdate, queryForString) → `references/blackbox-config.md` DatabaseTestUtils.

## WireMockServiceManager

```java
WireMockServiceManager wm = new WireMockServiceManager();
wm.startService("payment-service", 8081, "src/test/resources/blackbox/stubs/payment-service");
// ... tests ...
wm.stopAllServices();
```

## Blackbox Testing

Directory structure:
```
src/test/resources/blackbox/
├── blackbox_config.json      # Main config
├── stubs_config.json         # Stub-only config
├── stubs/                    # WireMock stubs per service
│   └── <service>/mappings/   # Request mappings
│   └── <service>/__files/    # Response templates
└── test-cases/               # Test case JSONs
```

### Test Implementation

```java
@SpringBootTest(webEnvironment = RANDOM_PORT)
@TestMethodOrder(OrderAnnotation.class)
public class UserServiceBlackboxTest {
    private static WireMockServiceManager serviceManager;

    @BeforeAll
    static void setUpAll() throws Exception {
        var config = BlackboxTestUtils.loadConfiguration("src/test/resources/blackbox/blackbox_config.json");
        serviceManager = new WireMockServiceManager();
        config.getStubs().getServices().stream()
            .filter(ServiceConfig::isEnabled)
            .forEach(s -> serviceManager.startService(s.getName(), s.getPort(), s.getStubsDataPath()));
    }

    @TestFactory
    Stream<DynamicTest> crudOperations() {
        return TestCaseUtils.loadTestCases("src/test/resources/blackbox/test-cases/user-service/crud-operations.json")
            .stream()
            .map(tc -> DynamicTest.dynamicTest(tc.getName(), () -> executeTestCase(tc)));
    }

    @AfterAll
    static void tearDown() { serviceManager.stopAllServices(); }
}
```

### Standalone Stub Runner

```java
WireMockServiceManager sm = new WireMockServiceManager();
var config = StubConfigurationUtils.loadStubsConfiguration("src/test/resources/blackbox/stubs_config.json");
StubConfigurationUtils.startAllStubServices(sm, config);
```

Gradle task: `./gradlew runStubs`

## Test Authentication (@AuthRoles-Protected Endpoints)

Construct a mock `X-Userinfo` header for blackbox tests against APISIX-secured endpoints:

```java
String userInfo = Base64.getEncoder().encodeToString(
    """{"sub":"test-user","preferred_username":"test","realm_access":{"roles":["user"]}}""".getBytes()
);
webTestClient.get().uri("/api/resource")
    .header("X-Userinfo", userInfo)
    .exchange()
    .expectStatus().isOk();
```

## RANDOM_PORT vs DEFINED_PORT

`RANDOM_PORT` binds to ephemeral port. `base_url` in `blackbox_config.json` only hardcodes 8080 when using `DEFINED_PORT`. With `RANDOM_PORT`, inject via `@LocalServerPort` and build URL dynamically.

Full config JSON format + test-case JSON schema → `references/blackbox-config.md`.

## Rules

- `PostgresTestContainer.create()` for integration tests — never hardcode JDBC/R2DBC URLs
- `@DynamicPropertySource` to wire container URLs — never static config files for test DB
- `RANDOM_PORT` + `@LocalServerPort` — never hardcode port 8080 in test config
- Always call `stopAllServices()` in `@AfterAll` — port leaks break CI
- `TestCaseUtils.loadTestCases()` for blackbox tests — enables JSON-driven test data

## Related Skills

- **summer-core** — Shared types (Member, ViewableException) used in test assertions
- **summer-security** — `@AuthRoles` testing with mock `X-Userinfo` header
- **testing-workflow** — General testing patterns, StepVerifier, WebTestClient
- **database-patterns** — R2DBC test patterns, Flyway migration testing

