Skip to content

Environments

An Environment describes the service boundaries that participate in an E2Engine test.

It defines:

  • which services E2Engine exposes;
  • whether each service is real or mocked;
  • where requests to real services are forwarded;
  • how mocked services respond;
  • HTTP request defaults;
  • and, for gRPC services, the protobuf contract E2Engine uses.

A test executes against an environment by sending traffic through the service addresses defined by that environment.

At a high level, an environment is a collection of services:

Environment
│
├── Service
│ ├── HTTP or gRPC
│ └── real or mocked
│
├── Service
│ ├── HTTP or gRPC
│ └── real or mocked
│
└── ...

For example, the E2Engine payment demo defines:

payment-demo
│
├── fraud
│ HTTP
│ mocked
│
├── account
│ gRPC
│ real
│
└── notification
gRPC
mocked

This topology is represented explicitly in the environment specification rather than being embedded in test orchestration code.

An environment is a versioned E2Engine resource:

kind: Environment
version: 1.0.0
name: payment-demo
description: dependencies for the E2Engine payment demo
spec:
services:
# ...
Field Type Required Description
kind string yes Resource kind. Must be Environment.
id string no E2Engine resource ID. Assigned when the resource is created.
name string yes Environment name. Between 3 and 200 characters.
version string yes Environment specification version. Must be a semantic version.
description string no Human-readable description. Between 3 and 2000 characters when specified.
spec object yes Environment-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 an environment 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.

spec.services defines the service boundaries available inside the environment.

spec:
services:
- id: account
kind: grpc
mode: real
address: 127.0.0.1:8082
grpc_target: 127.0.0.1:50051
# ...

An environment must contain at least one service.

Service IDs must be unique within the environment.

Service addresses must also be unique within the environment.

Field Type Required Description
id string yes Service identifier. Between 3 and 200 characters and unique within the environment.
kind string yes Service protocol: http or grpc.
mode string yes Service behavior: real or mocked.
address string yes Stable logical network address exposed inside the environment.
http_target string conditional Destination of a real HTTP service.
grpc_target string conditional Destination of a real gRPC service.
request_defaults object no Default request properties applied by the service.
fixtures array conditional Responses provided by a mocked service.
proto object conditional Protobuf definition for a gRPC service.

The combination of kind and mode determines which additional fields are valid.

Every service has an address.

For a real service, it also has a target.

For example:

- id: direct-real-http
kind: http
mode: real
address: 127.0.0.1:8083
http_target: http://127.0.0.1:9000

These two addresses have different roles.

Application / Test
│
│ http://127.0.0.1:8083
▼
E2Engine
│
│ forwards to
▼
http://127.0.0.1:9000
│
▼
Real service

address is the stable logical address through which the service participates in the environment.

http_target or grpc_target is the actual destination to which E2Engine forwards traffic for a real service.

Routing traffic through the environment allows E2Engine to observe service interactions during a test execution.

E2Engine currently supports two service kinds:

Kind Value Description
HTTP http HTTP service boundary.
gRPC grpc gRPC service boundary.

Each service must specify exactly one kind.

A service can operate in one of two modes:

Mode Value Description
Real real Traffic is forwarded to an actual service.
Mocked mocked E2Engine handles the service and returns configured fixture responses.

This allows one environment to combine real and controlled dependencies.

For example:

services:
- id: fraud
kind: http
mode: mocked
# ...
- id: account
kind: grpc
mode: real
# ...
- id: notification
kind: grpc
mode: mocked
# ...

A real HTTP service requires an http_target.

- id: direct-real-http
kind: http
mode: real
address: 127.0.0.1:8083
http_target: http://127.0.0.1:9000

http_target must be a valid HTTP URL.

A real HTTP service:

  • must define http_target;
  • must not define grpc_target;
  • must not define fixtures;
  • must not define proto.

Requests received on address are forwarded to http_target.

A mocked HTTP service is implemented by E2Engine using fixtures.

- id: fraud
kind: http
mode: mocked
address: 127.0.0.1:8081
fixtures:
- when:
http:
method: POST
path: /check
body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'
then:
http:
status: 200
headers:
Content-Type:
- application/json
body: '{"approved":true}'

A mocked service must define at least one fixture and must not define a target.

For HTTP services, every fixture must contain both:

when:
http:
# request matching
then:
http:
# response

gRPC fixture definitions are not allowed on an HTTP service.

when.http describes an HTTP request that a fixture can match.

when:
http:
method: POST
path: /check
headers:
Content-Type:
- application/json
body: '{"accountId":"acc-001"}'
Field Type Required Description
method string yes HTTP method to match.
path string yes Request path. Must be a valid path beginning with /.
headers map no Expected request headers. Each header can contain multiple values.
body string no Expected JSON request body.

Supported HTTP methods are:

GET
POST
PUT
DELETE
PATCH
OPTIONS
HEAD

When body is specified, it must contain valid JSON.

then.http defines the response returned by a mocked HTTP service.

then:
http:
status: 200
headers:
Content-Type:
- application/json
body: '{"approved":true}'
Field Type Required Description
status integer yes HTTP status code from 100 through 599.
headers map no Response headers. Each header can contain multiple values.
body string no JSON response body.

When body is specified, it must contain valid JSON.

A real gRPC service requires:

  • a grpc_target;
  • an external protobuf definition.

For example:

- 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

A real gRPC service:

  • must define grpc_target;
  • must not define http_target;
  • must not define fixtures;
  • must use an external protobuf definition.

An internal protobuf definition cannot be used for a real gRPC service.

Every gRPC service requires a proto definition.

Exactly one of the following must be configured:

proto:
external:

or:

proto:
internal:

They cannot be specified together.

An external definition refers to an existing protobuf contract.

A local .proto file can be used:

proto:
external:
file: ./proto/account.proto
service: account.v1.AccountService

Alternatively, a Buf module can be referenced:

proto:
external:
buf_module: example/module
service: example.v1.ExampleService

An external protobuf definition contains:

Field Type Required Description
file string conditional Path to a protobuf file.
buf_module string conditional Buf module containing the protobuf definition.
service string yes Fully qualified protobuf service name.

Exactly one of file or buf_module must be provided.

For mocked gRPC services, a small protobuf contract can be defined directly in the environment.

proto:
internal:
package: notification.v1
service: NotificationService
methods:
- name: Send
request:
fields:
- name: account_id
type: string
- name: payment_id
type: string
- name: amount
type: int64
- name: currency
type: string
response:
fields: []

An internal protobuf definition contains:

Field Type Required Description
package string yes Protobuf package name.
service string yes Service name.
methods array yes Methods exposed by the service. At least one method is required.

Each method contains a request and response message definition.

methods:
- name: Send
request:
fields:
- name: account_id
type: string
response:
fields: []
Field Type Required Description
name string yes Method name.
request object yes Request message definition.
response object yes Response message definition.

A request or response message contains zero or more fields:

fields:
- name: account_id
type: string
- name: amount
type: int64
- name: tags
type: string
repeated: true

Each field supports:

Field Type Required Description
name string yes Field name.
type string yes Scalar protobuf field type.
repeated boolean no Whether the field is repeated. Defaults to false.

Supported field types are:

string
bool
int32
int64
uint32
uint64
float
double
bytes

Internal protobuf definitions intentionally represent a focused subset of protobuf sufficient for defining lightweight mocked services.

A mocked gRPC service combines an external or internal protobuf definition with fixtures.

For example:

- id: notification
kind: grpc
mode: mocked
address: 127.0.0.1:8083
proto:
internal:
package: notification.v1
service: NotificationService
methods:
- name: Send
request:
fields:
- name: account_id
type: string
response:
fields: []
fixtures:
- when:
grpc:
method: Send
message:
account_id: acc-001
then:
grpc:
status: OK
message: {}

For gRPC services, every fixture must contain both:

when:
grpc:

and:

then:
grpc:

HTTP fixtures are not allowed on a gRPC service.

when.grpc describes the request to match.

when:
grpc:
method: Send
message:
account_id: acc-001
payment_id: payment-1
Field Type Required Description
method string yes gRPC method name.
message object no Request message fields to match.

then.grpc describes the response returned by the mocked service.

then:
grpc:
status: OK
message: {}
Field Type Required Description
status string no gRPC status.
message object no Response message fields.

Supported status values are:

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

If status is omitted, E2Engine uses the default successful gRPC behavior.

A service can define request defaults:

request_defaults:
headers:
Authorization:
- Bearer example-token

Currently, request defaults support HTTP-style headers:

Field Type Required Description
headers map no Default request headers, with one or more values per header.

Request defaults are useful when multiple requests to a service require the same request metadata.

The payment demo combines all of the main environment concepts:

kind: Environment
version: 1.0.0
name: payment-demo
description: dependencies for the E2Engine payment demo
spec:
services:
- id: fraud
kind: http
mode: mocked
address: 127.0.0.1:8081
fixtures:
- when:
http:
method: POST
path: /check
body: '{"accountId":"acc-001","amount":12500,"currency":"EUR"}'
then:
http:
status: 200
headers:
Content-Type:
- application/json
body: '{"approved":true}'
- 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: NotificationService
methods:
- name: Send
request:
fields:
- name: account_id
type: string
- name: payment_id
type: string
- name: amount
type: int64
- name: currency
type: string
response:
fields: []
fixtures:
- when:
grpc:
method: Send
message:
account_id: acc-001
payment_id: payment-1
amount: 12500
currency: EUR
then:
grpc:
status: OK
message: {}

The resulting environment can be viewed as:

payment-demo
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
fraud account notification
HTTP gRPC gRPC
mocked real mocked
│ │ │
fixture :50051 fixture

E2Engine validates environment specifications before they are used.

The main environment-level rules are:

  • kind must be Environment;
  • name must contain between 3 and 200 characters;
  • version must be a semantic version;
  • description, when present, must contain between 3 and 2000 characters;
  • an environment must contain at least one service;
  • service IDs must be unique;
  • service addresses must be unique.

For every service:

  • id must contain between 3 and 200 characters;
  • kind must be http or grpc;
  • mode must be real or mocked;
  • address must be a valid network address.

For real services:

  • a target is required;
  • HTTP services require http_target;
  • gRPC services require grpc_target;
  • the target must match the service kind;
  • fixtures are not allowed.

For mocked services:

  • at least one fixture is required;
  • http_target and grpc_target are not allowed.

For HTTP services:

  • protobuf definitions are not allowed;
  • fixtures must contain HTTP when and then definitions;
  • gRPC fixture definitions are not allowed;
  • fixture paths must be valid paths beginning with /.

For gRPC services:

  • a protobuf definition is required;
  • exactly one of external or internal must be specified;
  • real gRPC services cannot use an internal protobuf definition;
  • an internal definition must contain at least one method;
  • an external definition must specify exactly one of file or buf_module;
  • fixtures must contain gRPC when and then definitions;
  • HTTP fixture definitions are not allowed.

An environment answers:

What services participate in this test environment, and how should E2Engine handle them?

A Test answers a different question:

What behavior should be executed and verified inside that environment?

Keeping these concepts separate allows the same environment to be reused by multiple tests.

For example:

payment-demo
│
├── successful-payment
├── fraud-rejection
└── account-rejection

All three tests can execute against the same service topology while expressing different expected behavior.