Skip to content

Commit 2885ddc

Browse files
committed
add blockscout, refactor CLI
1 parent 2eb1370 commit 2885ddc

30 files changed

+1292
-128
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- [Profiling](framework/observability/profiling.md)
2323
- [Traces]()
2424
- [Debugger]()
25+
- [Blockscout](framework/observability/blockscout.md)
2526
- [Components](framework/components/overview.md)
2627
- [Blockchains](framework/components/blockchains/overview.md)
2728
- [Anvil](framework/components/blockchains/anvil.md)

book/src/framework.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

book/src/framework/getting_started.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Getting started
22

3+
## Test setup
4+
35
To start writing tests create a directory for your project with `go.mod` and pull the framework
46
```
57
go get github.com/smartcontractkit/chainlink-testing-framework/framework
@@ -14,12 +16,6 @@ mv ~/go/bin/cmd ~/go/bin/ctf
1416
```
1517
More CLI [docs](./cli.md)
1618

17-
18-
Spin up your local obserability stack
19-
```
20-
ctf obs up
21-
```
22-
2319
Create an `.envrc` file and put common parameters there (you can use [direnv](https://direnv.net/) to sync them more easily)
2420
```
2521
export CTF_LOG_LEVEL=info
@@ -30,4 +26,18 @@ export CTF_CONFIGS=smoke.toml
3026
export PRIVATE_KEY="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
3127
```
3228

33-
Now you are ready to write your [first test](./first_test.md)
29+
Now you are ready to write your [first test](./first_test.md)
30+
31+
## Tools setup (Optional)
32+
33+
This setup is optional, and it explains how to setup a local observability stack for on-chain and off-chain components.
34+
35+
Spin up your local obserability stack (Grafana LGTM)
36+
```
37+
ctf obs up
38+
```
39+
40+
Spin up your `Blockscout` stack
41+
```
42+
ctf bs up
43+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Blockscout
2+
3+
You can use local [Blockscout](https://www.blockscout.com/) instance to debug EVM smart contracts.
4+
5+
```
6+
ctf bs up
7+
```
8+
Your `Blockscout` instance is up on [localhost](http://localhost)
9+
10+
To remove it, we also clean up all Blockscout databases to prevent stale data when restarting your tests.
11+
```
12+
ctf bs down
13+
```
14+
15+
<div class="warning">
16+
17+
Blockscout isn’t ideal for local, ephemeral environments, as it won’t re-index blocks and transactions on test reruns. The easiest approach is to set up Blockscout first, initialize the test environment, switch to the [cache](../components/caching.md) config, and run tests without restarting RPC nodes.
18+
19+
Otherwise, use `ctf bs r` each time you restart your test with a fresh docker environment.
20+
</div>
21+
22+
<div class="warning">
23+
24+
Blockscout integration is still WIP, for now Blockscout reads only one node that is on `:8545`, all our blockchain implementation expose this port by default.
25+
</div>

book/src/overview.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
The Chainlink Testing Framework (CTF) is a blockchain development framework written in Go.
44

5-
Its primary purpose is to help chainlink developers create extensive integration, e2e, performance, and chaos tests to ensure the stability of the chainlink project.
5+
Its primary purpose is to help Chainlink developers create extensive integration, e2e, performance, and chaos tests to ensure the stability of the chainlink project.
66

77
It can also be helpful to those who just want to use chainlink oracles in their projects to help test their contracts, or even for those that aren't using chainlink.
88

9+
This documentation is for:
10+
- Developers looking to write end-to-end tests
11+
- Quality Assurance Engineers aiming to test locally
12+
13+
To get started with writing tests, refer to the [Framework](./framework/getting_started.md) chapter, where we guide you from basic to more complex scenarios.
14+
915
[Repository](https://github.com/smartcontractkit/chainlink-testing-framework) contains two major pieces:
1016
- [Framework](framework/overview.md)
1117
- [Libraries](libraries.md)

framework/cmd/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
blockscout/services/blockscout-db-data
2+
blockscout/services/logs
3+
blockscout/services/redis-data
4+
blockscout/services/stats-db-data

framework/cmd/blockscout.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
6+
"path/filepath"
7+
)
8+
9+
func blockscoutUp() error {
10+
framework.L.Info().Msg("Creating local Blockscout stack")
11+
if err := extractAllFiles("observability"); err != nil {
12+
return err
13+
}
14+
err := runCommand("bash", "-c", fmt.Sprintf(`
15+
cd %s && \
16+
docker compose up -d
17+
`, "blockscout"))
18+
if err != nil {
19+
return err
20+
}
21+
framework.L.Info().Msg("Done")
22+
fmt.Println()
23+
framework.L.Info().Msgf("Blockscout is up at: %s", "http://localhost")
24+
return nil
25+
}
26+
27+
func blockscoutDown() error {
28+
framework.L.Info().Msg("Removing local Blockscout stack")
29+
err := runCommand("bash", "-c", fmt.Sprintf(`
30+
cd %s && \
31+
docker compose down -v
32+
`, "blockscout"))
33+
if err != nil {
34+
return err
35+
}
36+
err = runCommand("bash", "-c", fmt.Sprintf(`
37+
cd %s && \
38+
rm -rf blockscout-db-data && \
39+
rm -rf logs && \
40+
rm -rf redis-data && \
41+
rm -rf stats-db-data
42+
`, filepath.Join("blockscout", "services")))
43+
framework.L.Info().Msg("Done")
44+
return nil
45+
}

framework/cmd/docker.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/rs/zerolog"
6+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
)
11+
12+
func cleanDockerResources() error {
13+
framework.L.Info().Str("label", "framework=ctf").Msg("Cleaning up docker containers")
14+
// Bash command for removing Docker containers and networks with "framework=ctf" label
15+
cmd := exec.Command("bash", "-c", `
16+
docker ps -aq --filter "label=framework=ctf" | xargs -r docker rm -f && \
17+
docker network ls --filter "label=framework=ctf" -q | xargs -r docker network rm
18+
`)
19+
framework.L.Debug().Msg("Running command")
20+
if framework.L.GetLevel() == zerolog.DebugLevel {
21+
fmt.Println(cmd.String())
22+
}
23+
output, err := cmd.CombinedOutput()
24+
if err != nil {
25+
return fmt.Errorf("error running clean command: %s", string(output))
26+
}
27+
framework.L.Info().Msgf("Done")
28+
return nil
29+
}
30+
31+
// BuildDocker runs Docker commands to set up a local registry, build an image, and push it.
32+
func BuildDocker(dockerfile string, buildContext string, imageName string) error {
33+
registryRunning := isContainerRunning("local-registry")
34+
if registryRunning {
35+
fmt.Println("Local registry container is already running.")
36+
} else {
37+
framework.L.Info().Msg("Starting local registry container...")
38+
err := runCommand("docker", "run", "-d", "-p", "5050:5000", "--name", "local-registry", "registry:2")
39+
if err != nil {
40+
return fmt.Errorf("failed to start local registry: %w", err)
41+
}
42+
framework.L.Info().Msg("Local registry started")
43+
}
44+
45+
img := fmt.Sprintf("localhost:5050/%s:latest", imageName)
46+
framework.L.Info().Str("DockerFile", dockerfile).Str("Context", buildContext).Msg("Building Docker image")
47+
err := runCommand("docker", "build", "-t", fmt.Sprintf("localhost:5050/%s:latest", imageName), "-f", dockerfile, buildContext)
48+
if err != nil {
49+
return fmt.Errorf("failed to build Docker image: %w", err)
50+
}
51+
framework.L.Info().Msg("Docker image built successfully")
52+
53+
framework.L.Info().Str("Image", img).Msg("Pushing Docker image to local registry")
54+
fmt.Println("Pushing Docker image to local registry...")
55+
err = runCommand("docker", "push", img)
56+
if err != nil {
57+
return fmt.Errorf("failed to push Docker image: %w", err)
58+
}
59+
framework.L.Info().Msg("Docker image pushed successfully")
60+
return nil
61+
}
62+
63+
// isContainerRunning checks if a Docker container with the given name is running.
64+
func isContainerRunning(containerName string) bool {
65+
cmd := exec.Command("docker", "ps", "--filter", fmt.Sprintf("name=%s", containerName), "--format", "{{.Names}}")
66+
output, err := cmd.Output()
67+
if err != nil {
68+
return false
69+
}
70+
return strings.Contains(string(output), containerName)
71+
}
72+
73+
// runCommand executes a command and prints the output.
74+
func runCommand(name string, args ...string) error {
75+
cmd := exec.Command(name, args...)
76+
cmd.Stdout = os.Stdout
77+
cmd.Stderr = os.Stderr
78+
return cmd.Run()
79+
}

0 commit comments

Comments
 (0)