Tests
A Test describes behavior to execute and verify inside an E2Engine environment.
It defines:
- the request E2Engine executes;
- the expected response;
- expected interactions with environment services;
- and optional tags used to organize tests.
A test is independent of a particular environment. The environment is selected when the test is executed.
The test model
Section titled “The test model”At a high level, a test consists of a request and its expectations:
Test │ ├── Request │ └── HTTP or gRPC │ └── Expect ├── Response │ └── HTTP or gRPC │ └── Service calls ├── HTTP └── gRPCFor example:
spec: request: http: method: GET url: http://127.0.0.1:8083/ok
expect: http: status: 200
calls: - service_id: direct-real-http http: method: GET path: /okThis test expresses two expectations:
GET /ok │ ├── response must be 200 │ └── direct-real-http must receive GET /okThe response and the interactions that produced it are both part of the test model.
Test resource
Section titled “Test resource”A test is a versioned E2Engine resource:
kind: Testversion: 1.0.0name: successful-paymentdescription: payment succeeds and all downstream services are called as expected
spec: tags: - smoke
request: # ...
expect: # ...Resource fields
Section titled “Resource fields”| Field | Type | Required | Description |
|---|---|---|---|
kind |
string | yes | Resource kind. Must be Test. |
id |
string | no | E2Engine resource ID. Assigned when the resource is created. |
name |
string | yes | Test name. Between 3 and 200 characters. |
version |
string | yes | Test specification version. Must be a semantic version. |
description |
string | no | Human-readable description. Between 3 and 2000 characters when specified. |
spec |
object | yes | Test-specific configuration. |
created_at |
timestamp | no | Creation timestamp assigned to the stored resource. |
updated_at |
timestamp | no | Last-update timestamp assigned to the stored resource. |
When defining a test in a file, you normally provide kind, name, version, description, and spec.
Fields such as id, created_at, and updated_at belong to the stored resource and are managed by E2Engine.
Test specification
Section titled “Test specification”A test specification contains:
spec: tags: - smoke
request: # ...
expect: # ...| Field | Type | Required | Description |
|---|---|---|---|
tags |
array | no | Tags used to organize and select tests. |
request |
object | yes | Request E2Engine executes. |
expect |
object | yes | Expected response and service interactions. |
Tags provide a lightweight way to classify tests:
tags: - smoke - paymentsEach tag must contain between 1 and 200 characters.
Tags within a test must be unique.
Tags can be used by test suites to select groups of tests without listing every test individually.
For example:
successful-payment ─┐fraud-rejection ├── tag: smokeaccount-rejection ─┘A test suite can then select the smoke tag.
Request
Section titled “Request”request describes the operation E2Engine executes.
A request uses exactly one protocol:
request: http:or:
request: grpc:HTTP and gRPC cannot be specified together.
The protocol used by expect must match the request protocol.
For example:
HTTP request → HTTP response expectationgRPC request → gRPC response expectationAn HTTP request with a gRPC response expectation, or vice versa, is invalid.
HTTP requests
Section titled “HTTP requests”An HTTP request defines the method and URL, with optional query parameters, headers, and body.
request: http: method: POST url: http://127.0.0.1:8080/payments headers: Content-Type: - application/json body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'HTTP request fields
Section titled “HTTP request fields”| Field | Type | Required | Description |
|---|---|---|---|
method |
string | yes | HTTP method. |
url |
string | yes | Absolute HTTP or HTTPS URL. |
query |
map | no | Query parameters added to the request. |
headers |
map | no | Request headers. Each header can contain multiple values. |
body |
string | no | JSON request body. |
Supported methods are:
GETPOSTPUTDELETEPATCHOPTIONSHEADThe URL must:
- use the
httporhttpsscheme; - contain a host.
When body is specified, it must contain valid JSON.
Query parameters
Section titled “Query parameters”Query parameters can be defined separately from the URL:
request: http: method: GET url: http://127.0.0.1:8080/payments query: status: completed account: acc-001This keeps the base URL and request parameters explicit in the test model.
Headers
Section titled “Headers”Headers support one or more values:
headers: Accept: - application/json X-Request-Source: - e2engineHTTP request bodies are represented as JSON strings:
body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'If a body is present, it must contain valid JSON.
HTTP response expectations
Section titled “HTTP response expectations”For an HTTP test, expect.http defines the expected response.
expect: http: status: 201 body: '{"paymentId":"payment-1","accountId":"acc-001","amount":12500,"currency":"EUR","status":"completed"}'HTTP expectation fields
Section titled “HTTP expectation fields”| Field | Type | Required | Description |
|---|---|---|---|
status |
integer | yes | Expected HTTP status code, from 100 through 599. |
body |
string | no | Expected JSON response body. |
When body is specified, it must contain valid JSON.
A minimal HTTP expectation can therefore be:
expect: http: status: 200while a more specific test can verify both status and body:
expect: http: status: 201 body: '{"status":"completed"}'gRPC requests
Section titled “gRPC requests”A gRPC request specifies the target, service, method, and optionally metadata and message fields.
request: grpc: target: 127.0.0.1:8082 service: account.v1.AccountService method: Debit
message: account_id: acc-001 amount: 12500 currency: EURgRPC request fields
Section titled “gRPC request fields”| Field | Type | Required | Description |
|---|---|---|---|
target |
string | yes | Network address of the gRPC endpoint. |
service |
string | yes | Fully qualified gRPC service name. |
method |
string | yes | gRPC method name. |
metadata |
map | no | gRPC request metadata. Each key can contain multiple values. |
message |
object | no | Request message fields. |
The target must be a valid network address.
Metadata
Section titled “Metadata”gRPC metadata supports one or more values for each key:
metadata: x-request-source: - e2engineMessage
Section titled “Message”The gRPC request message is represented as structured data:
message: account_id: acc-001 amount: 12500 currency: EURThe message is interpreted according to the protobuf service definition available to the execution environment.
gRPC response expectations
Section titled “gRPC response expectations”For a gRPC test, expect.grpc defines the expected gRPC result.
expect: grpc: status: OK
message: transaction_id: txn-1gRPC expectation fields
Section titled “gRPC expectation fields”| Field | Type | Required | Description |
|---|---|---|---|
status |
string | yes | Expected gRPC status. |
message |
object | no | Expected response message fields. |
Supported status values are:
OKCanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedFailedPreconditionAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticatedA test that only cares about successful completion can use:
expect: grpc: status: OKService call expectations
Section titled “Service call expectations”Response assertions describe what the test caller observes.
Call expectations describe what E2Engine expects to observe at service boundaries inside the environment.
They are defined under:
expect: calls: # ...For example:
expect: calls: - service_id: fraud count: 1 http: method: POST path: /check
- service_id: account count: 1 grpc: service: account.v1.AccountService method: DebitEach call expectation refers to a service by its environment service ID.
Call expectation fields
Section titled “Call expectation fields”| Field | Type | Required | Description |
|---|---|---|---|
service_id |
string | yes | ID of the environment service to observe. Between 3 and 200 characters. |
count |
integer | no | Expected number of matching calls. Must be zero or greater. |
http |
object | conditional | Expected HTTP interaction. |
grpc |
object | conditional | Expected gRPC interaction. |
Exactly one of http or grpc must be specified for each call expectation.
Call counts
Section titled “Call counts”count controls how many matching interactions are expected.
For example:
- service_id: fraud count: 1 http: method: POST path: /checkrequires exactly one matching call.
A count of zero expresses an important negative assertion:
- service_id: notification count: 0 grpc: service: notification.v1.NotificationService method: SendThis means:
The notification service must not receive a matching
Sendcall.
When count is omitted:
- service_id: direct-real-http http: method: GET path: /okthe expectation requires at least one matching call.
This is useful when the interaction must occur but its exact number is not important to the behavior being tested.
HTTP call expectations
Section titled “HTTP call expectations”An HTTP call expectation can match the method, path, query parameters, headers, and body of an observed request.
calls: - service_id: fraud count: 1
http: method: POST path: /check
headers: Content-Type: - application/json
body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'HTTP call expectation fields
Section titled “HTTP call expectation fields”| Field | Type | Required | Description |
|---|---|---|---|
method |
string | yes | HTTP method to match. |
path |
string | yes | Request path to match. |
query |
map | no | Query parameters to match. Each parameter can contain multiple values. |
headers |
map | no | Request headers to match. Each header can contain multiple values. |
body |
string | no | JSON request body to match. |
Supported methods are:
GETPOSTPUTDELETEPATCHOPTIONSHEADWhen body is specified, it must contain valid JSON.
gRPC call expectations
Section titled “gRPC call expectations”A gRPC call expectation matches an observed gRPC service interaction.
calls: - service_id: account count: 1
grpc: service: account.v1.AccountService method: Debit
message: account_id: acc-001 amount: 12500 currency: EURgRPC call expectation fields
Section titled “gRPC call expectation fields”| Field | Type | Required | Description |
|---|---|---|---|
service |
string | yes | gRPC service name to match. |
method |
string | yes | gRPC method name to match. |
metadata |
map | no | gRPC metadata to match. Each key can contain multiple values. |
message |
object | no | Request message fields to match. |
The service and method identify the gRPC operation.
Optional metadata and message fields allow the expectation to describe the interaction in more detail.
Response expectations and call expectations
Section titled “Response expectations and call expectations”These two forms of expectation answer different questions.
A response expectation:
expect: http: status: 201asks:
What should the caller observe?
A call expectation:
calls: - service_id: account count: 1 grpc: service: account.v1.AccountService method: Debitasks:
What should happen at this service boundary?
Together, they let a test describe behavior across a distributed system:
Test │ │ request ▼ Application │ ┌──────────┼──────────┐ │ │ │ ▼ ▼ ▼ Fraud Account Notification │ │ │ └──────────┼──────────┘ │ ▼ ResponseThe test can verify both the final response and the interactions that occurred—or did not occur—along the way.
Complete HTTP example
Section titled “Complete HTTP example”The successful payment test from the E2Engine demo combines an HTTP request, an HTTP response expectation, and HTTP and gRPC call expectations:
kind: Testversion: 1.0.0name: successful-paymentdescription: payment succeeds and all downstream services are called as expected
spec: tags: - smoke
request: http: method: POST url: http://127.0.0.1:8080/payments headers: Content-Type: - application/json body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'
expect: http: status: 201 body: '{"paymentId":"payment-1","accountId":"acc-001","amount":12500,"currency":"EUR","status":"completed"}'
calls: - service_id: fraud count: 1 http: method: POST path: /check body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'
- service_id: account count: 1 grpc: service: account.v1.AccountService method: Debit message: account_id: acc-001 amount: 12500 currency: EUR
- service_id: notification count: 1 grpc: service: notification.v1.NotificationService method: Send message: account_id: acc-001 payment_id: payment-1 amount: 12500 currency: EURThis test describes the expected behavior as one model:
POST /payments │ ├── response: 201 │ ├── fraud.Check × 1 ├── account.AccountService.Debit × 1 └── notification.Send × 1Negative interaction expectations
Section titled “Negative interaction expectations”Call expectations are also useful for describing paths where some interactions must not happen.
The fraud-rejection test from the demo expects the request to stop after the fraud service rejects it:
kind: Testversion: 1.0.0name: fraud-rejectiondescription: payment is rejected when the fraud service rejects it
spec: tags: - smoke
request: http: method: POST url: http://127.0.0.1:8080/payments headers: Content-Type: - application/json body: '{"accountId":"acc-003","amount":12500,"currency":"EUR"}'
expect: http: status: 422 body: '{"error":"payment rejected"}'
calls: - service_id: fraud count: 1 http: method: POST path: /check body: '{"accountId":"acc-003","amount":12500,"currency":"EUR"}'
- service_id: account count: 0 grpc: service: account.v1.AccountService method: Debit
- service_id: notification count: 0 grpc: service: notification.v1.NotificationService method: SendThe behavior being verified is therefore:
POST /payments │ ▼ Fraud × 1 │ │ rejected ▼ Account × 0 │ ▼ Notification × 0
Response: 422The absence of the Account and Notification calls is part of the expected behavior, not merely a side effect of the test.
Validation rules
Section titled “Validation rules”E2Engine validates test specifications before they are used.
The main resource-level rules are:
kindmust beTest;namemust contain between 3 and 200 characters;versionmust be a semantic version;description, when present, must contain between 3 and 2000 characters.
For tags:
- each tag must contain between 1 and 200 characters;
- tags within a test must be unique.
For the request and response protocol:
requestmust contain exactly one ofhttporgrpc;expectmust contain exactly one ofhttporgrpc;- the request and response expectation must use the same protocol.
For HTTP requests:
methodmust be one of the supported HTTP methods;urlmust be present;- the URL scheme must be
httporhttps; - the URL must contain a host;
body, when present, must contain valid JSON.
For HTTP response expectations:
statusmust be between100and599;body, when present, must contain valid JSON.
For gRPC requests:
targetmust be a valid network address;servicemust be present;methodmust be present.
For gRPC response expectations:
statusmust be one of the supported gRPC status values.
For service call expectations:
service_idmust contain between 3 and 200 characters;count, when present, must be zero or greater;- exactly one of
httporgrpcmust be specified.
For HTTP call expectations:
methodmust be one of the supported HTTP methods;pathmust be present;body, when present, must contain valid JSON.
For gRPC call expectations:
servicemust be present;methodmust be present.
Tests and environments
Section titled “Tests and environments”A test answers:
What behavior should E2Engine execute and verify?
An Environment answers:
What service boundaries participate in that execution, and how should E2Engine handle them?
The two resources are intentionally separate.
For example:
payment-demo Environment │ ┌─────────────┼─────────────┐ │ │ │ ▼ ▼ ▼successful-payment fraud-rejection account-rejection Test Test TestThe same environment can support multiple tests describing different behaviors.
The environment defines the topology.
The test defines the behavior.
The execution brings them together.

