Skip to content

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.

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
└── gRPC

For 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: /ok

This test expresses two expectations:

GET /ok
│
├── response must be 200
│
└── direct-real-http must receive GET /ok

The response and the interactions that produced it are both part of the test model.

A test is a versioned E2Engine resource:

kind: Test
version: 1.0.0
name: successful-payment
description: payment succeeds and all downstream services are called as expected
spec:
tags:
- smoke
request:
# ...
expect:
# ...
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.

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
- payments

Each 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: smoke
account-rejection ─┘

A test suite can then select the smoke tag.

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 expectation
gRPC request → gRPC response expectation

An HTTP request with a gRPC response expectation, or vice versa, is invalid.

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"}'
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:

GET
POST
PUT
DELETE
PATCH
OPTIONS
HEAD

The URL must:

  • use the http or https scheme;
  • contain a host.

When body is specified, it must contain valid JSON.

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-001

This keeps the base URL and request parameters explicit in the test model.

Headers support one or more values:

headers:
Accept:
- application/json
X-Request-Source:
- e2engine

HTTP 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.

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"}'
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: 200

while a more specific test can verify both status and body:

expect:
http:
status: 201
body: '{"status":"completed"}'

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: EUR
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.

gRPC metadata supports one or more values for each key:

metadata:
x-request-source:
- e2engine

The gRPC request message is represented as structured data:

message:
account_id: acc-001
amount: 12500
currency: EUR

The message is interpreted according to the protobuf service definition available to the execution environment.

For a gRPC test, expect.grpc defines the expected gRPC result.

expect:
grpc:
status: OK
message:
transaction_id: txn-1
Field Type Required Description
status string yes Expected gRPC status.
message object no Expected response message fields.

Supported status values are:

OK
Canceled
Unknown
InvalidArgument
DeadlineExceeded
NotFound
AlreadyExists
PermissionDenied
ResourceExhausted
FailedPrecondition
Aborted
OutOfRange
Unimplemented
Internal
Unavailable
DataLoss
Unauthenticated

A test that only cares about successful completion can use:

expect:
grpc:
status: OK

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: Debit

Each call expectation refers to a service by its environment service ID.

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.

count controls how many matching interactions are expected.

For example:

- service_id: fraud
count: 1
http:
method: POST
path: /check

requires exactly one matching call.

A count of zero expresses an important negative assertion:

- service_id: notification
count: 0
grpc:
service: notification.v1.NotificationService
method: Send

This means:

The notification service must not receive a matching Send call.

When count is omitted:

- service_id: direct-real-http
http:
method: GET
path: /ok

the 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.

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"}'
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:

GET
POST
PUT
DELETE
PATCH
OPTIONS
HEAD

When body is specified, it must contain valid JSON.

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: EUR
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: 201

asks:

What should the caller observe?

A call expectation:

calls:
- service_id: account
count: 1
grpc:
service: account.v1.AccountService
method: Debit

asks:

What should happen at this service boundary?

Together, they let a test describe behavior across a distributed system:

Test
│
│ request
▼
Application
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
Fraud Account Notification
│ │ │
└──────────┼──────────┘
│
▼
Response

The test can verify both the final response and the interactions that occurred—or did not occur—along the way.

The successful payment test from the E2Engine demo combines an HTTP request, an HTTP response expectation, and HTTP and gRPC call expectations:

kind: Test
version: 1.0.0
name: successful-payment
description: 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: EUR

This test describes the expected behavior as one model:

POST /payments
│
├── response: 201
│
├── fraud.Check × 1
├── account.AccountService.Debit × 1
└── notification.Send × 1

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: Test
version: 1.0.0
name: fraud-rejection
description: 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: Send

The behavior being verified is therefore:

POST /payments
│
▼
Fraud × 1
│
│ rejected
▼
Account × 0
│
▼
Notification × 0
Response: 422

The absence of the Account and Notification calls is part of the expected behavior, not merely a side effect of the test.

E2Engine validates test specifications before they are used.

The main resource-level rules are:

  • kind must be Test;
  • name must contain between 3 and 200 characters;
  • version must 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:

  • request must contain exactly one of http or grpc;
  • expect must contain exactly one of http or grpc;
  • the request and response expectation must use the same protocol.

For HTTP requests:

  • method must be one of the supported HTTP methods;
  • url must be present;
  • the URL scheme must be http or https;
  • the URL must contain a host;
  • body, when present, must contain valid JSON.

For HTTP response expectations:

  • status must be between 100 and 599;
  • body, when present, must contain valid JSON.

For gRPC requests:

  • target must be a valid network address;
  • service must be present;
  • method must be present.

For gRPC response expectations:

  • status must be one of the supported gRPC status values.

For service call expectations:

  • service_id must contain between 3 and 200 characters;
  • count, when present, must be zero or greater;
  • exactly one of http or grpc must be specified.

For HTTP call expectations:

  • method must be one of the supported HTTP methods;
  • path must be present;
  • body, when present, must contain valid JSON.

For gRPC call expectations:

  • service must be present;
  • method must be present.

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 Test

The same environment can support multiple tests describing different behaviors.

The environment defines the topology.

The test defines the behavior.

The execution brings them together.