Fraud Service
Controlled responses determine whether a payment should continue through the flow.
:8081Demo
A small payment system demonstrates HTTP and gRPC, real and mocked dependencies, response verification, downstream interaction expectations, and the same declarative test model across local CLI and Docker/CI execution.
The system
The Payment API is the system under test. It calls all three downstream dependencies through E2Engine, allowing E2Engine to mock or proxy them while observing the interactions.
Controlled responses determine whether a payment should continue through the flow.
:8081E2Engine observes and proxies the call to a real gRPC service using an external protobuf contract.
:8082 → :50051E2Engine provides the gRPC service from a protobuf contract defined directly in the environment.
:8083Payment flow
Check whether the payment is approved.
Debit the account through the real gRPC service.
Save the completed payment.
Send the payment notification.
Return the final HTTP result.
Environment
The environment defines which services participate, whether each dependency is real or mocked, and where E2Engine exposes or forwards each service boundary.
Response fixtures
External protobuf
Internal protobuf
kind: Environment
version: 1.0.0
name: payment-demo
spec:
services:
- id: fraud
kind: http
mode: mocked
address: 127.0.0.1:8081
fixtures:
- when:
http:
method: POST
path: /check
then:
http:
status: 200
- id: account
kind: grpc
mode: real
address: 127.0.0.1:8082
grpc_target: 127.0.0.1:50051
proto:
external:
file: ./proto/account.proto
service: account.v1.AccountService
- id: notification
kind: grpc
mode: mocked
address: 127.0.0.1:8083
proto:
internal:
package: notification.v1
service: NotificationServiceThe Docker/CI variant uses the same logical topology with container addresses: E2Engine binds the service boundaries to0.0.0.0, forwards Account calls toaccount:50051, and the Payment API callse2engine:8081, e2engine:8082, ande2engine:8083.
Tests
Each test describes the external result and the downstream interactions that should — or should not — happen.
| Test | Fraud | Account | Notification | HTTP |
|---|---|---|---|---|
| successful-paymentcomplete payment flow | ✓ 1 call | ✓ 1 · OK | ✓ 1 call | ✓ 201 |
| fraud-rejectionstop after fraud check | ✓ 1 call | ✓ 0 calls | ✓ 0 calls | ✓ 422 |
| account-rejectionstop after account debit | ✓ 1 call | ✓ 1 · FailedPrecondition | ✓ 0 calls | ✓ 422 |
A verified zero is part of the assertion. For example,fraud-rejection requires that neither Account nor Notification is called.
Expected behavior
Compare the successful path with an early rejection. The test specification describes both what the API returns and what should happen across service boundaries.
successful-paymentexpect:
http:
status: 201
body: '{"paymentId":"payment-1", ...}'
calls:
- service_id: fraud
count: 1
http:
method: POST
path: /check
- service_id: account
count: 1
grpc:
method: Debit
- service_id: notification
count: 1
grpc:
method: Sendfraud-rejectionexpect:
http:
status: 422
body: '{"error":"payment rejected"}'
calls:
- service_id: fraud
count: 1
http:
method: POST
path: /check
- service_id: account
count: 0
grpc:
method: Debit
- service_id: notification
count: 0
grpc:
method: SendExecution
Both demo modes create the Environment, the three Tests, and thesmoke TestSuite. The suite then executes the same declarative model against the running application.
$ e2engine create env ./e2engine/payment-demo.env.yml
$ e2engine create test ./e2engine/successful-payment.test.yml
$ e2engine create test ./e2engine/fraud-rejection.test.yml
$ e2engine create test ./e2engine/account-rejection.test.yml
$ e2engine create ts ./e2engine/smoke.ts.yml
$ e2engine run ts smoke payment-demo
$ e2engine get testsuiteexecutions
$ e2engine get testexecutionsResult
/payments201 Created✓Expected interactions
POST /check1 / 1AccountService.Debit1 / 1NotificationService.Send1 / 1Simplified presentation of one TestExecution in the TestSuiteExecution.
status: passed
environment_name: payment-demo
test_name: successful-payment
summary:
response:
http:
status_code: 201
calls:
- service_id: fraud
http:
request:
method: POST
path: /check
- service_id: account
grpc:
request:
rpc: /account.v1.AccountService/Debit
- service_id: notification
grpc:
request:
rpc: /notification.v1.NotificationService/SendNegative paths
In the fraud-rejection test, the API returns 422 after the first dependency rejects the payment. E2Engine verifies that the remaining services were never called.
The model
The same structured model connects what participates in the test, what should happen, the execution itself, and what actually happened.
What participates
What should happen
Run the system
What actually happened
What moves into the model?
The application remains ordinary application code. E2Engine takes responsibility for describing the controlled test environment and expected distributed behavior.
Run it
Run the same Environment, Tests, and TestSuite locally with an installed E2Engine CLI or entirely through Docker using the published E2Engine image.
make demo-cliBuilds and starts the Account service and Payment API locally, then executes the suite using the installed e2enginecommand.
make demo-ciBuilds Linux service images, creates an isolated Docker network, runs E2Engine from ghcr.io/e2engine/cli:0.1.0, verifies the TestSuiteExecution with check tse, and cleans up afterward.
Try it