Skip to content

Commit c2f4244

Browse files
authored
V2 Framework draft (#1236)
* V2 Framework PoC
1 parent 2116c4f commit c2f4244

File tree

49 files changed

+9970
-10
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+9970
-10
lines changed

framework/.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
### Go template
2+
# If you prefer the allow list template instead of the deny list, see community template:
3+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4+
#
5+
# Binaries for programs and plugins
6+
*.exe
7+
*.exe~
8+
*.dll
9+
*.so
10+
*.dylib
11+
12+
# Test binary, built with `go test -c`
13+
*.test
14+
15+
# Output of the go coverage tool, specifically when used with LiteIDE
16+
*.out
17+
18+
# Dependency directories (remove the comment below to include it)
19+
# vendor/
20+
21+
# Go workspace file
22+
go.work
23+
24+
.envrc
25+
promtail-config.yml
26+
27+
# CL node
28+
components/clnode/config.toml
29+
components/clnode/secrets.toml
30+
components/clnode/overrides.toml
31+
components/clnode/secrets-overrides.toml
32+
components/clnode/user-overrides.toml
33+
components/clnode/user-secrets-overrides.toml
34+

framework/CACHING.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## Component caching
2+
3+
We use component caching to accelerate test development and enforce idempotent test actions.
4+
5+
Each component is designed to be pure with defined inputs/outputs.
6+
7+
You can use an environment variable to skip deployment steps and use cached outputs if your infrastructure is already running (locally or remotely):
8+
9+
```
10+
export CTF_CONFIGS=smoke-cache.toml
11+
export CTF_USE_CACHED_OUTPUTS=true
12+
```
13+
14+
### Using remote components
15+
16+
Because components are decoupled through outputs, you can use a cached config and switch outputs to any deployed infrastructure, such as staging. This allows you to reuse the same testing logic for behavior validation.
17+
18+
Example:
19+
```
20+
[blockchain_a.out]
21+
chain_id = '31337'
22+
23+
[[blockchain_a.out.nodes]]
24+
ws_url = 'ws://127.0.0.1:33447'
25+
http_url = 'http://127.0.0.1:33447'
26+
docker_internal_ws_url = 'ws://anvil-3716a:8900'
27+
docker_internal_http_url = 'http://anvil-3716a:8900'
28+
```

framework/COMPONENTS.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## Developing Components
2+
3+
To build a scalable framework that enables the reuse of our product deployments (contracts or services in Docker), we need to establish a clear component structure.
4+
```
5+
package mycomponent
6+
7+
import (
8+
"fmt"
9+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
10+
)
11+
12+
type Input struct {
13+
// inputs fields that component exposes for configuration
14+
...
15+
// outputs are embedded into inputs so framework can automatically save them
16+
Out *Output `toml:"out"`
17+
}
18+
19+
type Output struct {
20+
// outputs that will be dumped to config and cached
21+
}
22+
23+
24+
func NewComponent(input *Input) (*Output, error) {
25+
if input.Out != nil && framework.UseCache() {
26+
return input.Out, nil
27+
}
28+
29+
// component logic here
30+
// deploy a docker container(s)
31+
// or deploy a set of smart contracts
32+
33+
input.Out = &Output{...}
34+
return out, nil
35+
}
36+
```
37+
Each component can define inputs and outputs, following these rules:
38+
39+
- Outputs should be included within inputs.
40+
- If your component is used for side effects output can be omitted.
41+
- `if input.Out != nil && framework.UseCache()` should be added if you'd like to use caching
42+
43+
### Docker components good practices for [testcontainers-go](https://golang.testcontainers.org/):
44+
- `ContainerRequest` must contain labels, network and alias required for local observability stack and deployment isolation
45+
```
46+
Labels: framework.DefaultTCLabels(),
47+
Networks: []string{framework.DefaultNetworkName},
48+
NetworkAliases: map[string][]string{
49+
framework.DefaultNetworkName: {containerName},
50+
},
51+
```
52+
- In order to copy files into container use `framework.WriteTmpFile(data string, fileName string)`
53+
```
54+
userSecretsOverridesFile, err := WriteTmpFile(in.Node.UserSecretsOverrides, "user-secrets-overrides.toml")
55+
if err != nil {
56+
return nil, err
57+
}
58+
```
59+
- Output of docker component must contain all the URLs component exposes for access, both for internal docker usage and external test (host) usage
60+
```
61+
host, err := framework.GetHost(c)
62+
if err != nil {
63+
return nil, err
64+
}
65+
mp, err := c.MappedPort(ctx, nat.Port(bindPort))
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
return &NodeOut{
71+
DockerURL: fmt.Sprintf("http://%s:%s", containerName, in.Node.Port),
72+
HostURL: fmt.Sprintf("http://%s:%s", host, mp.Port()),
73+
}, nil
74+
```

framework/CONFIGURATION.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
## Test Configuration
2+
3+
In our framework, components control the configuration. Each component defines an Input that you embed into your test configuration. We automatically ensure consistency between TOML and struct definitions during validation.
4+
5+
An example of your component configuration:
6+
```
7+
// Input is a blockchain network configuration params declared by the component
8+
type Input struct {
9+
Type string `toml:"type" validate:"required,oneof=anvil geth" envconfig:"net_type"`
10+
Image string `toml:"image" validate:"required"`
11+
Tag string `toml:"tag" validate:"required"`
12+
Port string `toml:"port" validate:"required"`
13+
ChainID string `toml:"chain_id" validate:"required"`
14+
DockerCmdParamsOverrides []string `toml:"docker_cmd_params"`
15+
Out *Output `toml:"out"`
16+
}
17+
```
18+
19+
How you use it in tests:
20+
```
21+
type Config struct {
22+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
23+
}
24+
25+
func TestDON(t *testing.T) {
26+
in, err := framework.Load[Config](t)
27+
require.NoError(t, err)
28+
29+
// deploy component
30+
bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
31+
require.NoError(t, err)
32+
```
33+
34+
In `TOML`:
35+
```
36+
[blockchain_a]
37+
chain_id = "31337"
38+
docker_cmd_params = ["--block-time=1"]
39+
image = "f4hrenh9it/foundry"
40+
port = "8500"
41+
tag = "latest"
42+
type = "anvil"
43+
```
44+
45+
### Best practices for configuration and validation
46+
- Avoid stateful types (e.g., loggers, clients) in your config.
47+
- All `input` fields should include validate: "required", ensuring consistency between TOML and struct definitions.
48+
- Add extra validation rules for URLs or "one-of" variants. Learn more here: go-playground/validator.
49+
50+
### Overriding configuration
51+
To override any configuration, we merge multiple files into a single struct.
52+
53+
You can specify multiple file paths using `CTF_CONFIGS=path1,path2,path3`.
54+
55+
The framework will apply these configurations from right to left.

framework/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Chainlink Testing Framework Harness
2+
3+
<!-- TOC -->
4+
* [CLI](./cmd/README.md)
5+
* [Components](./COMPONENTS.md)
6+
* [Configuration](./CONFIGURATION.md)
7+
* [Caching](./CACHING.md)
8+
* [Local Observability Stack](./cmd/observability/README.md)
9+
* [Examples](https://github.com/smartcontractkit/chainlink/tree/8e8597aa14c39c48ed4b3261f6080fa43b5d7cd0/e2e/capabilities)
10+
<!-- TOC -->
11+
12+
This module includes the CTFv2 harness, a lightweight, modular, and data-driven framework designed for combining off-chain and on-chain components while implementing best practices for end-to-end system-level testing:
13+
14+
- **Non-nil configuration**: All test variables must have defaults, automatic validation.
15+
- **Component isolation**: Components are decoupled via input/output structs, without exposing internal details.
16+
- **Modular configuration**: No arcane knowledge of framework settings is required; the config is simply a reflection of the components being used in the test. Components declare their own configuration—'what you see is what you get.'
17+
- **Replaceability and extensibility**: Since components are decoupled via outputs, any deployment component can be swapped with a real service without altering the test code.
18+
- **Integrated observability stack**: use `ctf obs up` to spin up a local observability stack.
19+
- **Connectivity**: seamless connection of production-ready components and local components using [testcontainers-go networking](https://golang.testcontainers.org/features/networking/#exposing-host-ports-to-the-container)."
20+
21+
22+
### Environment variables (Tests, when using in Go code)
23+
| Name | Description | Possible values | Default | Required? |
24+
|:----------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------:|-------------------------:|:-------:|:------------------------:|
25+
| CTF_CONFIGS | Path(s) to test config files. <br/>Can be more than one, ex.: smoke.toml,smoke_1.toml,smoke_2.toml.<br/>First filepath will hold all the merged values | Any valid TOML file path | ||
26+
| CTF_LOG_LEVEL | Harness log level | `info`, `debug`, `trace` | `info` | 🚫 |
27+
| CTF_LOKI_STREAM | Streams all components logs to `Loki`, see params below | `true`, `false` | `false` | 🚫 |
28+
| LOKI_URL | URL to `Loki` push api, should be like`${host}/loki/api/v1/push` | URL | - | If you use `Loki` then ✅ |
29+
| LOKI_TENANT_ID | Streams all components logs to `Loki`, see params below | `true`, `false` | - | If you use `Loki` then ✅ |
30+
| TESTCONTAINERS_RYUK_DISABLED | Testcontainers-Go reaper container, removes all the containers after the test exit | `true`, `false` | `false` | 🚫 |
31+
| CTF_USE_CACHED_OUTPUTS | Use cached `.out` fields from `*-cache.toml` config | `true`, `false` | `false` | 🚫 |
32+
| RESTY_DEBUG | Log all Resty client HTTP calls | `true`, `false` | `false` | 🚫 |

0 commit comments

Comments
 (0)