Production-grade, multi-layer test framework for ClearCore — a Java 21 / Spring Boot 3.2 payment authorization and settlement engine.
ClearCore exposes a payment API (POST /api/v1/transactions/authorize, capture, reverse) backed by PostgreSQL, Redis (rate limiting + velocity), and Kafka (event stream). This framework validates correctness, fraud rule enforcement, latency SLAs, and double-spend prevention under concurrent load.
clearcore-test-suite/
├── python/ # Primary suite
│ ├── tests/
│ │ ├── conftest.py # Per-suite dedicated cards, client, fixtures
│ │ ├── test_authorization.py # Happy path, all decline codes, idempotency
│ │ ├── test_lifecycle.py # authorize → capture → reverse full flow
│ │ ├── test_contracts.py # JSON schema validation on every endpoint
│ │ ├── test_fraud_rules.py # Velocity, amount threshold, merchant rules
│ │ └── test_rate_limits.py # 429 triggering, Retry-After, burst behavior
│ ├── schemas/
│ │ ├── authorize_response.json
│ │ ├── transaction.json
│ │ └── error_response.json
│ ├── pytest.ini
│ └── requirements.txt
│
├── java/ # Concurrency + latency layer
│ └── src/test/
│ ├── java/com/clearcore/tests/
│ │ ├── TestConfig.java # Shared base config, payload factories
│ │ ├── ConcurrencyTest.java # 50-thread double-spend, concurrent capture/refund
│ │ └── LatencySlaTest.java # p50/p95/p99 over 1000 requests
│ └── groovy/com/clearcore/tests/
│ └── AuthorizationSpec.groovy # Spock data-driven decline-code coverage
│
├── docker-compose.yml # ClearCore + Postgres + Redis + Kafka
└── README.md
| Suite | Layer | Tests | Status | What it covers |
|---|---|---|---|---|
| Authorization | Python | 20 | ✅ Passing | Happy path, decline codes 51/54/82/14/61/62, idempotency deduplication |
| Lifecycle | Python | 15 | ✅ Passing | Full flow: authorize → capture → reverse. Partial capture, double-reverse rejection, state machine violations |
| Contracts | Python | 10 | ✅ Passing | JSON schema validation on every endpoint. Field types, required fields, enum values |
| Fraud rules | Python | 12 | ✅ Passing | Velocity limit (N txns/min), amount threshold (fraud score), merchant category rules |
| Rate limits | Python | 3 | ✅ Passing | Burst → 429, sequential rate limit verification |
| Concurrency | Java | 4 | 50-thread overdraw via CountDownLatch, concurrent capture/refund deduplication | |
| Latency SLA | Java | 5 | p95 < 200ms, p99 < 500ms, p50 < 100ms over 1000 sequential requests | |
| Authorization (Spock) | Groovy | 14 | Data-driven decline codes via @Unroll, idempotency, contract assertions |
Python layer
- pytest 8.3, requests, jsonschema, allure-pytest, pytest-xdist, pytest-timeout
Java layer
- REST Assured 5.5, JUnit 5, AssertJ, Awaitility, Allure JUnit5
Groovy / Spock
- Spock Framework 2.4-M4, GMavenPlus 3.0
Infrastructure
- Docker Compose (ClearCore + PostgreSQL 16 + Redis 7 + Kafka)
- Docker + Docker Compose
- Python 3.12+
- Java 21, Maven 3.9+
- ClearCore cloned at
../ClearCore
# From this repo root
docker compose up -d --build
# Wait for ClearCore to be healthy (~30s)
curl -s http://localhost:8080/actuator/healthAfter each container restart, run:
# Fix VARCHAR column (ClearCore schema uses VARCHAR(10) — needs widening for test data)
docker exec clearcore-test-suite-postgres-1 psql -U clearcore -d clearcore \
-c "ALTER TABLE transactions ALTER COLUMN response_code TYPE VARCHAR(50); \
ALTER TABLE transactions ALTER COLUMN status TYPE VARCHAR(50);"
# Flush Redis velocity counters between runs
docker exec clearcore-test-suite-redis-1 redis-cli FLUSHALL
# Seed per-suite test cards
docker cp sql/insert_test_cards.sql clearcore-test-suite-postgres-1:/tmp/
docker exec clearcore-test-suite-postgres-1 psql -U clearcore -d clearcore -f /tmp/insert_test_cards.sqlcd python
pip install -r requirements.txt
# Run all tests
pytest -v
# Run a single suite
pytest tests/test_authorization.py -v
pytest tests/test_fraud_rules.py -v
# With Allure report
pytest --alluredir=allure-results
allure serve allure-results# Requires JDK 21 (Groovy 4.0 incompatible with JDK 25+)
# Run via Docker to avoid local JDK version conflicts:
docker run --rm --network clearcore-test-suite_default \
-v "$(pwd)/java:/app" -w /app eclipse-temurin:21-jdk-alpine \
sh -c "apk add --no-cache maven && mvn test -Dgmavenplus.skip=true"The most important test in the framework — validates ClearCore's optimistic locking prevents double-spend under concurrent load.
CountDownLatch startGun = new CountDownLatch(1);
for (int i = 0; i < 50; i++) {
pool.submit(() -> {
startGun.await(); // synchronized start
var response = jsonRequest().body(payload)
.post("/api/v1/transactions/authorize");
if ("00".equals(response.jsonPath().getString("data.responseCode")))
approvals.incrementAndGet();
});
}
startGun.countDown(); // release all 50 simultaneously
assertThat(approvals.get()).isEqualTo(1); // exactly one winsWithout @Version optimistic locking on ClearCore's Transaction entity, multiple threads win the balance check simultaneously → double-spend. With it, only one thread wins the CAS on the DB row; all others get an optimistic lock exception → decline code 51.
// 1000 sequential requests after 50-request warmup
Arrays.sort(times);
long p95 = times[(int)(SAMPLE_SIZE * 0.95)];
assertThat(p95).isLessThan(P95_SLA_MS); // 200msPercentile from a sorted array: index N * 0.95 is p95. Hard CI gate — catches slow queries, missing indexes, GC pressure before production.
# Fire 50 rapid transactions on a dedicated card
for _ in range(50):
client.authorize(make_payload(card_id=CARD_VEL, amount=1000))
# Next request must decline — velocity limit exceeded
res = client.authorize(make_payload(card_id=CARD_VEL, amount=1000))
assert res.status_code == 422
assert res.json()["data"]["responseCode"] in ("62", "51")Each suite uses a dedicated card UUID to avoid cross-suite velocity interference. The velocity counter lives in Redis; FLUSHALL between runs resets it.
def test_authorize_response_schema(self, client, valid_card, authorize_schema):
res = client.authorize(valid_card)
assert res.status_code == 201
validate(instance=res.json()["data"], schema=authorize_schema)Schema files in schemas/ define required fields, types, and enum values. Fails immediately on any structural regression in ClearCore's response format.
| Issue | Cause | Status |
|---|---|---|
AuthorizationSpec.groovy — Connection refused |
Spock tests hardcode localhost; inside Docker container, localhost ≠ clearcore |
Fix requires editing .groovy file with JDK 21 |
| Groovy compilation fails locally | JDK 25 installed locally; Groovy 4.0 supports max JDK 21 (class file major version 69 error) |
Run via eclipse-temurin:21-jdk-alpine Docker container |
response_code VARCHAR(10) |
ClearCore schema column too narrow for some response codes | Run the ALTER TABLE command after each container restart (see setup above) |
| Velocity limit in concurrency tests | 50 concurrent requests on one card exceed the 50/min velocity limit | Known — requires a card exempted from velocity checks |
Per-suite dedicated cards — each test suite has its own card UUID seeded in the DB. This prevents velocity counters in Redis from bleeding across suites. The conftest.py constants (CARD_AUTH, CARD_LIFECYCLE, etc.) map to UUIDs in sql/insert_test_cards.sql.
data envelope unwrapping — ClearCore wraps all responses in {"success": true, "data": {...}}. The unwrap() helper in conftest.py normalizes this so test assertions work directly on the business object.
Sequential rate limit test — the rate limit suite fires requests sequentially (not via pytest-xdist) to avoid thread-scheduling noise confusing the burst count.
VELOCITY_LIMIT=50 in ClearCore — the default application.yml velocity limit was raised from 5 to 50 to allow the fraud rule tests to exercise the threshold without hitting it in setup. This change lives in the ClearCore repo.