Skip to content

Commit

Permalink
feat: Add Echo App end-to-end test
Browse files Browse the repository at this point in the history
  • Loading branch information
fmoura committed Apr 11, 2024
1 parent 16ba201 commit ded8e77
Show file tree
Hide file tree
Showing 17 changed files with 731 additions and 113 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


Check failure on line 9 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / assess-code-quality

Multiple consecutive blank lines

CHANGELOG.md:9 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md012.md
## [unreleased]

### Added

- Added Rollups end-to-end test using Echo Dapp

## [1.4.0] 2024-04-09

### Added
Expand All @@ -14,7 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added verification to ensure the Cartesi Machine snapshot hash matches the template hash from the CartesiDApp contract
- Added support for `CARTESI_AUTH_PRIVATE_KEY` and `CARTESI_AUTH_PRIVATE_KEY_FILE`
- Added `CARTESI_AUTH_KIND` environment variable to select the blockchain authetication method
- Added structured logging with slog. Colored logs can now be enabled with `CARTESI_LOG_PRETTY` environment variable.
- Added structured logging with slog. Colored logs can now be enabled with `CARTESI_LOG_PRETTY` environment variable


Check failure on line 26 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / assess-code-quality

Multiple consecutive blank lines

CHANGELOG.md:26 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md012.md
### Changed

Expand Down
24 changes: 23 additions & 1 deletion cmd/cartesi-rollups-cli/root/deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ package deps
import (
"context"
"log/slog"
"os"
"os/signal"
"syscall"

"github.com/cartesi/rollups-node/internal/deps"
"github.com/lmittmann/tint"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
)

Expand All @@ -24,6 +27,7 @@ const examples = `# Run all deps:
cartesi-rollups-cli run-deps`

var depsConfig = deps.NewDefaultDepsConfig()
var verbose = false

func init() {
Cmd.Flags().StringVar(&depsConfig.Postgres.DockerImage, "postgres-docker-image",
Expand All @@ -44,13 +48,31 @@ func init() {

Cmd.Flags().StringVar(&depsConfig.Devnet.Port, "devnet-mapped-port",
deps.DefaultDevnetPort,
"devnet local listening port number")
"Devnet local listening port number")

Cmd.Flags().StringVar(&depsConfig.Devnet.BlockTime, "devnet-block-time",
deps.DefaultBlockTime,
"Devnet mining block time")

Cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "verbose logs")
}

func run(cmd *cobra.Command, args []string) {
ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

if verbose {
// setup log
opts := &tint.Options{
Level: slog.LevelDebug,
AddSource: true,
NoColor: false || !isatty.IsTerminal(os.Stdout.Fd()),
}
handler := tint.NewHandler(os.Stdout, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
}

depsContainers, err := deps.Run(ctx, *depsConfig)
cobra.CheckErr(err)

Expand Down
32 changes: 7 additions & 25 deletions cmd/cartesi-rollups-cli/root/increasetime/increasetime.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
package increasetime

import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"context"
"log/slog"

"github.com/cartesi/rollups-node/pkg/ethutil"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -39,24 +37,8 @@ func init() {
}

func run(cmd *cobra.Command, args []string) {
client := &http.Client{}
var data = strings.NewReader(`{
"id":1337,
"jsonrpc":"2.0",
"method":"evm_increaseTime",
"params":[` + strconv.Itoa(time) + `]
}`)

req, err := http.NewRequest("POST", anvilEndpoint, data)
cobra.CheckErr(err)

req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
cobra.CheckErr(err)

defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
cobra.CheckErr(err)

fmt.Printf("%s\n", bodyText)

cobra.CheckErr(ethutil.AdvanceDevnetTime(context.Background(), anvilEndpoint, time))

slog.Info("Ok")
}
2 changes: 1 addition & 1 deletion cmd/cartesi-rollups-node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func main() {
slog.Info("Starting the Cartesi Rollups Node", "version", buildVersion, "config", config)

// create the node supervisor
supervisor, err := node.Setup(ctx, config)
supervisor, err := node.Setup(ctx, config, "")
if err != nil {
slog.Error("Node exited with an error", "error", err)
os.Exit(1)
Expand Down
4 changes: 3 additions & 1 deletion docs/cli/cartesi-rollups-cli_run-deps.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ cartesi-rollups-cli run-deps
### Options

```
--devnet-block-time string Devnet mining block time (default "1")
--devnet-docker-image string Devnet docker image name (default "cartesi/rollups-node-devnet:devel")
--devnet-mapped-port string devnet local listening port number (default "8545")
--devnet-mapped-port string Devnet local listening port number (default "8545")
-h, --help help for run-deps
--postgres-docker-image string Postgress docker image name (default "postgres:16-alpine")
--postgres-mapped-port string Postgres local listening port number (default "5432")
--postgres-password string Postgres password (default "password")
-v, --verbose verbose logs
```

### SEE ALSO
Expand Down
146 changes: 113 additions & 33 deletions internal/deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package deps
import (
"context"
"fmt"
"io"
"log/slog"
"strings"
"sync"
Expand All @@ -17,16 +18,25 @@ import (
)

const (
DefaultPostgresDockerImage = "postgres:16-alpine"
DefaultPostgresPort = "5432"
DefaultPostgresPassword = "password"
DefaultDevnetDockerImage = "cartesi/rollups-node-devnet:devel"
DefaultDevnetPort = "8545"
DefaultPostgresDatabase = "postgres"
DefaultPostgresDockerImage = "postgres:16-alpine"
DefaultPostgresPort = "5432"
DefaultPostgresUser = "postgres"
DefaultPostgresPassword = "password"
DefaultDevnetDockerImage = "cartesi/rollups-node-devnet:devel"
DefaultDevnetPort = "8545"
DefaultBlockTime = "1"
DefaultBlockToWaitForOnStartup = "21"

numPostgresCheckReadyAttempts = 2
pollInterval = 5 * time.Second
)

const (
postgresKey = iota
devnetKey
)

// Struct to hold Node dependencies containers configurations
type DepsConfig struct {
Postgres *PostgresConfig
Expand All @@ -40,8 +50,10 @@ type PostgresConfig struct {
}

type DevnetConfig struct {
DockerImage string
Port string
DockerImage string
Port string
BlockTime string
BlockToWaitForOnStartup string
}

// Builds a DepsConfig struct with default values
Expand All @@ -55,30 +67,88 @@ func NewDefaultDepsConfig() *DepsConfig {
&DevnetConfig{
DefaultDevnetDockerImage,
DefaultDevnetPort,
DefaultBlockTime,
DefaultBlockToWaitForOnStartup,
},
}
}

// Struct to represent the Node dependencies containers
type DepsContainers struct {
containers []testcontainers.Container
containers map[int]testcontainers.Container
//Literal copies lock value from waitGroup as sync.WaitGroup contains sync.noCopy
waitGroup *sync.WaitGroup
}

func (depContainers *DepsContainers) DevnetLogs(ctx context.Context) (io.ReadCloser, error) {
container, ok := depContainers.containers[devnetKey]
if !ok {
return nil, fmt.Errorf("Container Devnet is not present")
}
reader, err := container.Logs(ctx)
if err != nil {
return nil, fmt.Errorf("Error retrieving logs from Devnet Container : %w", err)
}
return reader, nil
}

func (depContainers *DepsContainers) PostgresLogs(ctx context.Context) (io.ReadCloser, error) {
container, ok := depContainers.containers[postgresKey]
if !ok {
return nil, fmt.Errorf("Container Postgres is not present")
}
reader, err := container.Logs(ctx)
if err != nil {
return nil, fmt.Errorf("Error retrieving logs from Postgres Container : %w", err)
}
return reader, nil
}

func (depContainers *DepsContainers) DevnetEndpoint(
ctx context.Context,
protocol string,
) (string, error) {
container, ok := depContainers.containers[devnetKey]
if !ok {
return "", fmt.Errorf("Container Devnet is not present")
}
endpoint, err := container.Endpoint(ctx, protocol)
if err != nil {
return "", fmt.Errorf("Error retrieving endpoint from Devnet Container : %w", err)
}
return endpoint, nil
}

func (depContainers *DepsContainers) PostgresEndpoint(
ctx context.Context,
protocol string,
) (string, error) {

container, ok := depContainers.containers[postgresKey]
if !ok {
return "", fmt.Errorf("Container Postgres is not present")
}
endpoint, err := container.Endpoint(ctx, protocol)
if err != nil {
return "", fmt.Errorf("Error retrieving endpoint from Postgres Container : %w", err)
}
return endpoint, nil
}

// debugLogging implements the testcontainers.Logging interface by printing the log to slog.Debug.
type debugLogging struct{}

func (d debugLogging) Printf(format string, v ...interface{}) {
slog.Debug(fmt.Sprintf(format, v...))
}

func createHook(waitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHooks {
func createHook(finishedWaitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHooks {
return []testcontainers.ContainerLifecycleHooks{
{

PostTerminates: []testcontainers.ContainerHook{
func(ctx context.Context, container testcontainers.Container) error {
waitGroup.Done()
finishedWaitGroup.Done()
return nil
},
},
Expand All @@ -90,25 +160,29 @@ func createHook(waitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHo
// The returned DepContainers struct can be used to gracefully
// terminate the containers using the Terminate method
func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
debugLogger := debugLogging{}
var waitGroup sync.WaitGroup

containers := []testcontainers.Container{}
debugLogger := debugLogging{}
var finishedWaitGroup sync.WaitGroup
containers := make(map[int]testcontainers.Container)
if depsConfig.Postgres != nil {
// wait strategy copied from testcontainers docs
postgresWaitStrategy := wait.ForLog("database system is ready to accept connections").
WithOccurrence(numPostgresCheckReadyAttempts).
WithPollInterval(pollInterval)

postgresExposedPorts := "5432/tcp"
if depsConfig.Postgres.Port != "" {
postgresExposedPorts = strings.Join([]string{
depsConfig.Postgres.Port, ":", postgresExposedPorts}, "")
}
postgresReq := testcontainers.ContainerRequest{
Image: depsConfig.Postgres.DockerImage,
ExposedPorts: []string{strings.Join([]string{
depsConfig.Postgres.Port, ":5432/tcp"}, "")},
WaitingFor: postgresWaitStrategy,
Name: "rollups-node-dep-postgres",
Image: depsConfig.Postgres.DockerImage,
ExposedPorts: []string{postgresExposedPorts},
WaitingFor: postgresWaitStrategy,
Env: map[string]string{
"POSTGRES_PASSWORD": depsConfig.Postgres.Password,
},
LifecycleHooks: createHook(&waitGroup),
LifecycleHooks: createHook(&finishedWaitGroup),
}
postgres, err := testcontainers.GenericContainer(
ctx,
Expand All @@ -121,20 +195,24 @@ func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
if err != nil {
return nil, err
}
waitGroup.Add(1)
containers = append(containers, postgres)
finishedWaitGroup.Add(1)
containers[postgresKey] = postgres
}

if depsConfig.Devnet != nil {

devnetExposedPort := "8545/tcp"
if depsConfig.Devnet.Port != "" {
devnetExposedPort = strings.Join([]string{
depsConfig.Devnet.Port, ":", devnetExposedPort}, "")
}
devNetReq := testcontainers.ContainerRequest{
Image: depsConfig.Devnet.DockerImage,
ExposedPorts: []string{strings.Join([]string{depsConfig.Devnet.Port, ":8545/tcp"}, "")},
WaitingFor: wait.ForLog("Listening on 0.0.0.0:8545"),
Name: "rollups-node-dep-devnet",
Env: map[string]string{
"ANVIL_IP_ADDR": "0.0.0.0",
},
LifecycleHooks: createHook(&waitGroup),
ExposedPorts: []string{devnetExposedPort},
WaitingFor: wait.ForLog("Block Number: " + depsConfig.Devnet.BlockToWaitForOnStartup),
Cmd: []string{"anvil", "--block-time",
depsConfig.Devnet.BlockTime, "--load-state", "/usr/share/devnet/anvil_state.json"},
LifecycleHooks: createHook(&finishedWaitGroup),
}
devnet, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: devNetReq,
Expand All @@ -144,25 +222,27 @@ func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
if err != nil {
return nil, err
}
waitGroup.Add(1)
containers = append(containers, devnet)
finishedWaitGroup.Add(1)
containers[devnetKey] = devnet
}

if len(containers) < 1 {
return nil, fmt.Errorf("configuration is empty")
}
return &DepsContainers{containers, &waitGroup}, nil

return &DepsContainers{containers: containers,
waitGroup: &finishedWaitGroup,
}, nil
}

// Terminate terminates all dependencies containers. This method waits for all the containers
// to terminate or gives an error if it fails to terminate one of the containers
func Terminate(ctx context.Context, depContainers *DepsContainers) error {

for _, depContainer := range depContainers.containers {
terr := depContainer.Terminate(ctx)
if terr != nil {
return terr
}
}
depContainers.waitGroup.Wait()
return nil
}
Loading

0 comments on commit ded8e77

Please sign in to comment.