-
Notifications
You must be signed in to change notification settings - Fork 0
feat(phase2): cross-service saga integration tests (STA-13) #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,242 @@ | ||||||||||||||||||||||||
| # Phase 2 Integration Test — Cross-Service Saga (S1 + S2 + S6) | ||||||||||||||||||||||||
| # | ||||||||||||||||||||||||
| # Usage: | ||||||||||||||||||||||||
| # ./scripts/run-phase2-tests.sh | ||||||||||||||||||||||||
| # | ||||||||||||||||||||||||
| # Or manually: | ||||||||||||||||||||||||
| # ./gradlew :payment-orchestrator:payment-orchestrator:jibDockerBuild \ | ||||||||||||||||||||||||
| # :compliance-travel-rule:compliance-travel-rule:jibDockerBuild \ | ||||||||||||||||||||||||
| # :fx-liquidity-engine:fx-liquidity-engine:jibDockerBuild | ||||||||||||||||||||||||
| # docker compose -f docker-compose.phase2-test.yml up -d | ||||||||||||||||||||||||
| # ./gradlew :phase2-integration-tests:test | ||||||||||||||||||||||||
| # docker compose -f docker-compose.phase2-test.yml down -v | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| services: | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # ── Infrastructure ───────────────────────────────────────────── | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| postgres: | ||||||||||||||||||||||||
| image: postgres:18-alpine | ||||||||||||||||||||||||
| container_name: p2t-postgres | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "5432:5432" | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| POSTGRES_USER: dev | ||||||||||||||||||||||||
| POSTGRES_PASSWORD: dev | ||||||||||||||||||||||||
| POSTGRES_DB: postgres | ||||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||||
| - ./infra/local/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "pg_isready -U dev"] | ||||||||||||||||||||||||
| interval: 5s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 10 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| timescaledb: | ||||||||||||||||||||||||
| image: timescale/timescaledb:latest-pg17 | ||||||||||||||||||||||||
| container_name: p2t-timescaledb | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "5433:5432" | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| POSTGRES_USER: dev | ||||||||||||||||||||||||
| POSTGRES_PASSWORD: dev | ||||||||||||||||||||||||
| POSTGRES_DB: fx_rates | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "pg_isready -U dev"] | ||||||||||||||||||||||||
| interval: 5s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 10 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| redis: | ||||||||||||||||||||||||
| image: redis:8-alpine | ||||||||||||||||||||||||
| container_name: p2t-redis | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "6379:6379" | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD", "redis-cli", "ping"] | ||||||||||||||||||||||||
| interval: 5s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 10 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| redpanda: | ||||||||||||||||||||||||
| image: docker.redpanda.com/redpandadata/redpanda:latest | ||||||||||||||||||||||||
| container_name: p2t-redpanda | ||||||||||||||||||||||||
| command: | ||||||||||||||||||||||||
| - redpanda | ||||||||||||||||||||||||
| - start | ||||||||||||||||||||||||
| - --smp=1 | ||||||||||||||||||||||||
| - --memory=512M | ||||||||||||||||||||||||
| - --overprovisioned | ||||||||||||||||||||||||
| - --kafka-addr=internal://0.0.0.0:29092,external://0.0.0.0:9092 | ||||||||||||||||||||||||
| - --advertise-kafka-addr=internal://redpanda:29092,external://localhost:9092 | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "9092:9092" | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "rpk cluster health | grep -q 'Healthy:.*true'"] | ||||||||||||||||||||||||
| interval: 5s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 20 | ||||||||||||||||||||||||
| start_period: 15s | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| kafka-init: | ||||||||||||||||||||||||
| image: docker.redpanda.com/redpandadata/redpanda:latest | ||||||||||||||||||||||||
| container_name: p2t-kafka-init | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| redpanda: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| entrypoint: ["/bin/bash", "-c"] | ||||||||||||||||||||||||
| command: | | ||||||||||||||||||||||||
| " | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create payment.initiated --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create compliance.result --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create fx.rate.locked --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create payment.completed --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create payment.failed --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create audit.event --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| rpk --brokers redpanda:29092 topic create merchant.activated --partitions 3 --replicas 1 && | ||||||||||||||||||||||||
| echo 'All topics created' | ||||||||||||||||||||||||
| " | ||||||||||||||||||||||||
| restart: on-failure | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| temporal: | ||||||||||||||||||||||||
| image: temporalio/auto-setup:latest | ||||||||||||||||||||||||
| container_name: p2t-temporal | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "7233:7233" | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| DB: postgres12 | ||||||||||||||||||||||||
| DB_PORT: 5432 | ||||||||||||||||||||||||
| POSTGRES_USER: dev | ||||||||||||||||||||||||
| POSTGRES_PWD: dev | ||||||||||||||||||||||||
| POSTGRES_SEEDS: postgres | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| postgres: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "temporal operator cluster health 2>/dev/null | grep -q 'OK' || exit 0"] | ||||||||||||||||||||||||
| interval: 15s | ||||||||||||||||||||||||
| timeout: 10s | ||||||||||||||||||||||||
| retries: 15 | ||||||||||||||||||||||||
|
Comment on lines
+115
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Temporal healthcheck always succeeds. The 🐛 Proposed fix healthcheck:
- test: ["CMD-SHELL", "temporal operator cluster health 2>/dev/null | grep -q 'OK' || exit 0"]
+ test: ["CMD-SHELL", "temporal operator cluster health 2>&1 | grep -q 'SERVING'"]
interval: 15s
timeout: 10s
retries: 15
+ start_period: 30s📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| temporal-ui: | ||||||||||||||||||||||||
| image: temporalio/ui:latest | ||||||||||||||||||||||||
| container_name: p2t-temporal-ui | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "8233:8080" | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| TEMPORAL_ADDRESS: temporal:7233 | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| - temporal | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| wiremock: | ||||||||||||||||||||||||
| image: wiremock/wiremock:latest | ||||||||||||||||||||||||
| container_name: p2t-wiremock | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "4444:8080" | ||||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||||
| - ./infra/local/wiremock/mappings:/home/wiremock/mappings | ||||||||||||||||||||||||
| - ./infra/local/wiremock/__files:/home/wiremock/__files | ||||||||||||||||||||||||
| command: --verbose --global-response-templating | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # ── Application Services ─────────────────────────────────────── | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| compliance-travel-rule: | ||||||||||||||||||||||||
| image: stablebridge/compliance-travel-rule:latest | ||||||||||||||||||||||||
| container_name: p2t-compliance | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| postgres: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redis: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redpanda: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| wiremock: | ||||||||||||||||||||||||
| condition: service_started | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| JAVA_TOOL_OPTIONS: "-Xms256m -Xmx512m" | ||||||||||||||||||||||||
| DB_URL: "jdbc:postgresql://postgres:5432/s2_compliance" | ||||||||||||||||||||||||
| DB_USER: dev | ||||||||||||||||||||||||
| DB_PASS: dev | ||||||||||||||||||||||||
| KAFKA_BROKERS: "redpanda:29092" | ||||||||||||||||||||||||
| SPRING_DATA_REDIS_HOST: redis | ||||||||||||||||||||||||
| SPRING_DATA_REDIS_PORT: "6379" | ||||||||||||||||||||||||
| APP_SECURITY_ENABLED: "false" | ||||||||||||||||||||||||
| # Use WorldCheck adapter → WireMock for sanctions testing | ||||||||||||||||||||||||
| APP_SANCTIONS_PROVIDER: world-check | ||||||||||||||||||||||||
| APP_SANCTIONS_WORLDCHECK_BASEURL: "http://wiremock:8080/v2" | ||||||||||||||||||||||||
| APP_SANCTIONS_WORLDCHECK_APIKEY: "test-api-key" | ||||||||||||||||||||||||
| APP_SANCTIONS_WORLDCHECK_TIMEOUTSECONDS: "30" | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "8083:8083" | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "wget -q --spider http://localhost:8083/compliance/actuator/health || exit 1"] | ||||||||||||||||||||||||
| interval: 10s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 30 | ||||||||||||||||||||||||
| start_period: 30s | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| fx-liquidity-engine: | ||||||||||||||||||||||||
| image: stablebridge/fx-liquidity-engine:latest | ||||||||||||||||||||||||
| container_name: p2t-fx-engine | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| timescaledb: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redis: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redpanda: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| JAVA_TOOL_OPTIONS: "-Xms256m -Xmx512m" | ||||||||||||||||||||||||
| DB_URL: "jdbc:postgresql://timescaledb:5432/fx_rates" | ||||||||||||||||||||||||
| DB_USER: dev | ||||||||||||||||||||||||
| DB_PASS: dev | ||||||||||||||||||||||||
| KAFKA_BROKERS: "redpanda:29092" | ||||||||||||||||||||||||
| REDIS_HOST: redis | ||||||||||||||||||||||||
| REDIS_PORT: "6379" | ||||||||||||||||||||||||
| APP_SECURITY_ENABLED: "false" | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "8084:8084" | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "wget -q --spider http://localhost:8084/fx/actuator/health || exit 1"] | ||||||||||||||||||||||||
| interval: 10s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 30 | ||||||||||||||||||||||||
| start_period: 30s | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| payment-orchestrator: | ||||||||||||||||||||||||
| image: stablebridge/payment-orchestrator:latest | ||||||||||||||||||||||||
| container_name: p2t-orchestrator | ||||||||||||||||||||||||
| depends_on: | ||||||||||||||||||||||||
| postgres: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redis: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| redpanda: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| temporal: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| compliance-travel-rule: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| fx-liquidity-engine: | ||||||||||||||||||||||||
| condition: service_healthy | ||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||
| JAVA_TOOL_OPTIONS: "-Xms256m -Xmx512m" | ||||||||||||||||||||||||
| DB_URL: "jdbc:postgresql://postgres:5432/s1_payment_orchestrator" | ||||||||||||||||||||||||
| DB_USER: dev | ||||||||||||||||||||||||
| DB_PASS: dev | ||||||||||||||||||||||||
| KAFKA_BROKERS: "redpanda:29092" | ||||||||||||||||||||||||
| SPRING_DATA_REDIS_HOST: redis | ||||||||||||||||||||||||
| SPRING_DATA_REDIS_PORT: "6379" | ||||||||||||||||||||||||
| TEMPORAL_ADDRESS: "temporal:7233" | ||||||||||||||||||||||||
| TEMPORAL_NAMESPACE: default | ||||||||||||||||||||||||
| COMPLIANCE_SERVICE_URL: "http://compliance-travel-rule:8083/compliance" | ||||||||||||||||||||||||
| FX_SERVICE_URL: "http://fx-liquidity-engine:8084/fx" | ||||||||||||||||||||||||
| APP_SECURITY_ENABLED: "false" | ||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||
| - "8082:8082" | ||||||||||||||||||||||||
| healthcheck: | ||||||||||||||||||||||||
| test: ["CMD-SHELL", "wget -q --spider http://localhost:8082/orchestrator/actuator/health || exit 1"] | ||||||||||||||||||||||||
| interval: 10s | ||||||||||||||||||||||||
| timeout: 5s | ||||||||||||||||||||||||
| retries: 30 | ||||||||||||||||||||||||
| start_period: 30s | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||
| package com.stablecoin.payments.orchestrator.infrastructure.config; | ||||||||||
|
|
||||||||||
| import feign.RequestInterceptor; | ||||||||||
| import feign.RequestTemplate; | ||||||||||
| import org.springframework.stereotype.Component; | ||||||||||
|
|
||||||||||
| import java.util.UUID; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Feign interceptor that adds an Idempotency-Key header to all outgoing requests. | ||||||||||
| * Required by S2 Compliance Service which rejects POST requests without this header. | ||||||||||
| */ | ||||||||||
|
Comment on lines
+9
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Interceptor applies to all HTTP methods, not just POST/PUT. The comment notes S2 requires this for POST, but the interceptor injects the header on GET, DELETE, etc. as well. Low impact but adds unnecessary overhead and noise in logs/traces. ✂️ Limit to state-changing methods `@Override`
public void apply(RequestTemplate template) {
+ if (template.method() == null ||
+ (!template.method().equals("POST") && !template.method().equals("PUT"))) {
+ return;
+ }
if (!template.headers().containsKey(IDEMPOTENCY_KEY_HEADER)) {
template.header(IDEMPOTENCY_KEY_HEADER, UUID.randomUUID().toString());
}
}Also applies to: 19-22 🤖 Prompt for AI Agents |
||||||||||
| @Component | ||||||||||
| public class FeignIdempotencyInterceptor implements RequestInterceptor { | ||||||||||
|
|
||||||||||
| private static final String IDEMPOTENCY_KEY_HEADER = "Idempotency-Key"; | ||||||||||
|
|
||||||||||
| @Override | ||||||||||
| public void apply(RequestTemplate template) { | ||||||||||
| if (!template.headers().containsKey(IDEMPOTENCY_KEY_HEADER)) { | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Header lookup may be case-sensitive. HTTP headers are case-insensitive per RFC 7230, but 🔧 Safer case-insensitive check- if (!template.headers().containsKey(IDEMPOTENCY_KEY_HEADER)) {
+ boolean hasKey = template.headers().keySet().stream()
+ .anyMatch(h -> h.equalsIgnoreCase(IDEMPOTENCY_KEY_HEADER));
+ if (!hasKey) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| template.header(IDEMPOTENCY_KEY_HEADER, UUID.randomUUID().toString()); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idempotency breaks on Feign retries. When Feign retries a failed request (timeout, 5xx), the Consider propagating the idempotency key from request context (e.g., MDC, ThreadLocal, or a request-scoped bean) so retries reuse the same key. 🛠️ Sketch of context-aware approach+import org.slf4j.MDC;
+
`@Override`
public void apply(RequestTemplate template) {
if (!template.headers().containsKey(IDEMPOTENCY_KEY_HEADER)) {
- template.header(IDEMPOTENCY_KEY_HEADER, UUID.randomUUID().toString());
+ String contextKey = MDC.get("idempotencyKey");
+ String key = (contextKey != null) ? contextKey : UUID.randomUUID().toString();
+ template.header(IDEMPOTENCY_KEY_HEADER, key);
}
}Upstream controller/entry point sets 🤖 Prompt for AI Agents |
||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,35 @@ | ||||||||||||||||||||||||||||
| package com.stablecoin.payments.orchestrator.infrastructure.config; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import org.apache.kafka.clients.producer.ProducerConfig; | ||||||||||||||||||||||||||||
| import org.apache.kafka.common.serialization.StringSerializer; | ||||||||||||||||||||||||||||
| import org.springframework.beans.factory.annotation.Value; | ||||||||||||||||||||||||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Bean; | ||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Configuration; | ||||||||||||||||||||||||||||
| import org.springframework.kafka.core.DefaultKafkaProducerFactory; | ||||||||||||||||||||||||||||
| import org.springframework.kafka.core.KafkaTemplate; | ||||||||||||||||||||||||||||
| import org.springframework.kafka.core.ProducerFactory; | ||||||||||||||||||||||||||||
| import org.springframework.kafka.support.serializer.JsonSerializer; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @Configuration | ||||||||||||||||||||||||||||
| public class KafkaProducerConfig { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||
| @ConditionalOnMissingBean | ||||||||||||||||||||||||||||
| public ProducerFactory<String, Object> producerFactory( | ||||||||||||||||||||||||||||
| @Value("${spring.kafka.bootstrap-servers}") String bootstrapServers) { | ||||||||||||||||||||||||||||
| return new DefaultKafkaProducerFactory<>(Map.of( | ||||||||||||||||||||||||||||
| ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, | ||||||||||||||||||||||||||||
| ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class, | ||||||||||||||||||||||||||||
| ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider adding producer reliability settings. For a payment orchestration system, missing ♻️ Proposed enhancement return new DefaultKafkaProducerFactory<>(Map.of(
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers,
ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
- ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class));
+ ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class,
+ ProducerConfig.ACKS_CONFIG, "all",
+ ProducerConfig.RETRIES_CONFIG, 3,
+ ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||
| @ConditionalOnMissingBean | ||||||||||||||||||||||||||||
| public KafkaTemplate<String, Object> kafkaTemplate( | ||||||||||||||||||||||||||||
| ProducerFactory<String, Object> producerFactory) { | ||||||||||||||||||||||||||||
| return new KafkaTemplate<>(producerFactory); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,45 @@ | ||||||
| plugins { | ||||||
| java | ||||||
| } | ||||||
|
|
||||||
| val temporalVersion: String by project | ||||||
| val wiremockVersion: String by project | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused property declaration.
🧹 Proposed fix val temporalVersion: String by project
-val wiremockVersion: String by project📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| dependencies { | ||||||
| // API modules — DTOs for request/response deserialization | ||||||
| testImplementation(project(":payment-orchestrator:payment-orchestrator-api")) | ||||||
| testImplementation(project(":compliance-travel-rule:compliance-travel-rule-api")) | ||||||
| testImplementation(project(":fx-liquidity-engine:fx-liquidity-engine-api")) | ||||||
|
|
||||||
| // Temporal SDK — query workflow results | ||||||
| testImplementation("io.temporal:temporal-sdk:$temporalVersion") | ||||||
|
|
||||||
| // Jackson 3 — JSON parsing (JSR-310 built into core in Jackson 3) | ||||||
| testImplementation("tools.jackson.core:jackson-databind") | ||||||
|
Comment on lines
+17
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check what Jackson version is used elsewhere in the project
rg -n "jackson" --type=kotlin -g "*.gradle.kts" | head -20
# Check version catalog for Jackson
fd "libs.versions.toml" --exec cat {} 2>/dev/null | grep -i jacksonRepository: Puneethkumarck/stablebridge-platform Length of output: 1124 🏁 Script executed: #!/bin/bash
# Find the full context of the Jackson dependency in question
cat -n phase2-integration-tests/build.gradle.kts | sed -n '1,30p'
# Search for version catalog files in the project root
fd "versions.toml|gradle.toml|settings.gradle.kts" --max-depth 3
# Check if Jackson 3.x is used anywhere else
rg -n "tools\.jackson" --type=kotlin -g "*.gradle.kts"
# Look for any explicit version specifications for the problematic dependency
rg -n "jackson-databind" --type=kotlinRepository: Puneethkumarck/stablebridge-platform Length of output: 1609 🏁 Script executed: #!/bin/bash
# Check for dependency management or constraints
cat -n phase2-integration-tests/build.gradle.kts | sed -n '1,50p'
# Check if there's a root gradle file with dependencyManagement
fd "build.gradle.kts" --max-depth 1 -x head -50 {}
# Search for any dependency constraints or exclusions
rg -n "exclude|constraint|dependencyManagement" --type=kotlin phase2-integration-tests/Repository: Puneethkumarck/stablebridge-platform Length of output: 3487 Remove Jackson 3.x or exclude Jackson 2.x transitively — classpath conflict detected. The explicit 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| // Kafka — event verification | ||||||
| testImplementation("org.apache.kafka:kafka-clients") | ||||||
|
|
||||||
| // Test framework | ||||||
| testImplementation("org.junit.jupiter:junit-jupiter") | ||||||
| testImplementation("org.assertj:assertj-core") | ||||||
| testImplementation("org.awaitility:awaitility") | ||||||
|
|
||||||
| // SLF4J for logging in tests | ||||||
| testImplementation("org.slf4j:slf4j-api") | ||||||
| testRuntimeOnly("ch.qos.logback:logback-classic") | ||||||
| testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||||||
| } | ||||||
|
|
||||||
| tasks.test { | ||||||
| useJUnitPlatform { | ||||||
| excludeTags("load") | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| tasks.register<Test>("loadTest") { | ||||||
| useJUnitPlatform { | ||||||
| includeTags("load") | ||||||
| } | ||||||
| maxHeapSize = "1g" | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider pinning image versions for reproducibility.
Using
:latestfor redpanda, temporal, and wiremock images can cause flaky tests when upstream releases break compatibility. Pin to specific versions for deterministic builds.🤖 Prompt for AI Agents