diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index c593851295..230cc5badd 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -127,6 +127,28 @@ jobs: TRAVIS_PULL_REQUEST_BRANCH: ${{ github.head_ref }} TRAVIS_BRANCH: ${{ github.ref_name }} + mesh-checker: + name: Run Mesh API integration tests (amd64) + needs: build + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - *checkout-harmony + - name: Download and extract harmony binaries + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 + with: + name: harmony-binaries-amd64 + path: go/src/github.com/harmony-one/harmony/bin + - *prepare-harmony-binaries + + - name: Run Mesh API checker + run: bash ./test/localnet.sh mesh + working-directory: go/src/github.com/harmony-one/harmony + env: + HARMONY_BINARY_BUILDER: "true" + HARMONY_TEST_REF: 6afe7cdc1ecdb920d1c9a19d1b1ca912d3a590ab + LOCALNET_ARCH: amd64 + pyhmy-checker: name: Run pyhmy tests (${{ matrix.arch }}) needs: build diff --git a/Makefile b/Makefile index 5e19468b56..0502aa9d6c 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ SHELL := bash EPOCH_TO_WAIT ?=5 EXTRA_NODES_FILE ?="./test/configs/local-extra-nodes.txt" -.PHONY: all help libs exe race trace-pointer debug debug-ext debug-kill test test-all test-go test-integration test-rpc test-rpc-attach test-rosetta test-rosetta-attach test-pyhmy test-api test-api-attach linux_static deb_init deb_build deb debpub_dev debpub_prod rpm_init rpm_build rpm rpmpub_dev rpmpub_prod clean distclean docker go-vet go-test docker build_localnet_validator protofiles travis_go_checker travis_rpc_checker travis_rosetta_checker debug-start-log debug-stop-log debug-restart-log debug-delete-log +.PHONY: all help libs exe race trace-pointer debug debug-ext debug-kill test test-all test-go test-integration test-rpc test-rpc-attach test-mesh test-mesh-attach test-rosetta test-rosetta-attach test-pyhmy test-api test-api-attach linux_static deb_init deb_build deb debpub_dev debpub_prod rpm_init rpm_build rpm rpmpub_dev rpmpub_prod clean distclean docker go-vet go-test docker build_localnet_validator protofiles travis_go_checker travis_rpc_checker travis_rosetta_checker debug-start-log debug-stop-log debug-restart-log debug-delete-log all: libs bash ./scripts/go_executable_build.sh -S @@ -29,12 +29,14 @@ help: @echo "test - run native Go checks and unit tests" @echo "test-all - run native Go checks and all localnet integration suites" @echo "test-go - run the go test (with go lint, fmt, imports, mod, and generate checks)" - @echo "test-integration - run RPC, Rosetta, and pyhmy localnet tests" + @echo "test-integration - run RPC, Mesh API, and pyhmy localnet tests" @echo "test-rpc - run the rpc tests" @echo "test-pyhmy - run the pyhmy tests" @echo "test-rpc-attach - attach onto the rpc testing docker container for inspection" - @echo "test-rosetta - run the rosetta tests" - @echo "test-rosetta-attach - attach onto the rosetta testing docker container for inspection" + @echo "test-mesh - run the Mesh API tests" + @echo "test-mesh-attach - attach onto the Mesh API testing docker container for inspection" + @echo "test-rosetta - alias for test-mesh" + @echo "test-rosetta-attach - alias for test-mesh-attach" @echo "linux_static - static build the harmony binary & bootnode along with the MCL & BLS libs (for linux)" @echo "linux_static_quick - static build the harmony binary & bootnode more quickly without recompiling dependencies (for linux)" @echo "linux_static_cross_build - cross-compile static Linux binaries for Harmony and Bootnode from macOS" @@ -153,7 +155,7 @@ test-go: bash ./test/go.sh test-integration: - bash ./test/localnet.sh rpc rosetta pyhmy + bash ./test/localnet.sh rpc mesh pyhmy test-rpc: bash ./test/rpc.sh run @@ -164,11 +166,15 @@ test-pyhmy: test-rpc-attach: bash ./test/rpc.sh attach -test-rosetta: - bash ./test/rosetta.sh run +test-mesh: + bash ./test/mesh.sh run -test-rosetta-attach: - bash ./test/rosetta.sh attach +test-mesh-attach: + bash ./test/mesh.sh attach + +test-rosetta: test-mesh + +test-rosetta-attach: test-mesh-attach linux_static: bash ./scripts/go_executable_build.sh -s diff --git a/README.md b/README.md index df73df32ba..c9d9e1a6d5 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Note that all test Docker containers bind several ports to the host machine for * `9798` - Shard 1 Rosetta (for an explorer) * `9899` - Shard 0 WS for an explorer * `9898` - Shard 1 WS for an explorer -> This allows you to use curl, hmy CLI, postman, rosetta-cli, etc... on your host machine to play with or probe the localnet that was used for the test. +> This allows you to use curl, hmy CLI, Postman, the Mesh CLI, etc. on your host machine to play with or probe the localnet that was used for the test. ### Go tests To run this test, do: @@ -192,18 +192,18 @@ make test-rpc-attach > the current block height of localnet. Reference the documentation for the CLI [here](https://docs.harmony.one/home/wallets/harmony-cli) > for more details & commands. -### Rosetta tests +### Mesh API tests To run this test, do: ```bash -make test-rosetta +make test-mesh ``` -This test starts a localnet (within the Docker container), **ensures it reaches a consensus**, and runs the Construction & Data API checks using the [rosetta-cli](https://github.com/coinbase/rosetta-cli). +This test starts a localnet (within the Docker container), **ensures it reaches a consensus**, and runs the Construction & Data API checks using the [Coinbase Mesh CLI](https://github.com/coinbase/mesh-cli). The pinned v0.10.4 release still installs its official binary as `rosetta-cli`. This test also acts as a preliminary integration test (more thorough tests are done on the testnets). > The config for this test can be found [here](https://github.com/harmony-one/harmony-test/blob/master/localnet/configs/localnet_rosetta_test_s0.json) & [here](https://github.com/harmony-one/harmony-test/blob/master/localnet/configs/localnet_rosetta_test_s1.json) Similar to the RPC tests, if you wish to debug further with the localnet after the tests are done, open a new shell and run: ```bash -make test-rosetta-attach +make test-mesh-attach ``` ## License diff --git a/hmy/tracer.go b/hmy/tracer.go index 1e6e1f6877..6289a3df30 100644 --- a/hmy/tracer.go +++ b/hmy/tracer.go @@ -777,6 +777,8 @@ func (hmy *Harmony) traceTx(ctx context.Context, message core.Message, txctx *tr case *native.ParityBlockTracer: rs, err := tracer.GetParityResult() return rs, err + case *native.RosettaBlockTracer: + return tracer.GetRosettaResult() case tracers.Tracer: return tracer.GetResult() diff --git a/hmy/tracers/native/rosetta.go b/hmy/tracers/native/rosetta.go new file mode 100644 index 0000000000..28ce17f388 --- /dev/null +++ b/hmy/tracers/native/rosetta.go @@ -0,0 +1,131 @@ +package native + +import ( + "encoding/json" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/hmy/tracers" +) + +func init() { + register("RosettaBlockTracer", newRosettaTracer) +} + +func newRosettaTracer(_ *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { + return &RosettaBlockTracer{ParityBlockTracer: &ParityBlockTracer{}}, nil +} + +// RosettaBlockTracer converts Parity-style call traces and Harmony staking +// balance movements into the operation format consumed by the Rosetta service. +type RosettaBlockTracer struct { + *ParityBlockTracer + + logs []*tracers.RosettaLogItem + frameStart []int +} + +func (rbt *RosettaBlockTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + rbt.logs = nil + rbt.frameStart = []int{0} + rbt.ParityBlockTracer.CaptureStart(env, from, to, create, input, gas, value) +} + +func (rbt *RosettaBlockTracer) CaptureEnd(output []byte, gasUsed uint64, duration time.Duration, err error) { + rbt.markCurrentFrame(err) + rbt.ParityBlockTracer.CaptureEnd(output, gasUsed, duration, err) +} + +func (rbt *RosettaBlockTracer) CaptureEnter(_ vm.OpCode, _ common.Address, _ common.Address, _ []byte, _ uint64, _ *big.Int) { + rbt.frameStart = append(rbt.frameStart, len(rbt.logs)) +} + +func (rbt *RosettaBlockTracer) CaptureExit(_ []byte, _ uint64, err error) { + rbt.markCurrentFrame(err) +} + +func (rbt *RosettaBlockTracer) markCurrentFrame(err error) { + if len(rbt.frameStart) == 0 { + return + } + last := len(rbt.frameStart) - 1 + start := rbt.frameStart[last] + rbt.frameStart = rbt.frameStart[:last] + if err == nil { + return + } + for _, log := range rbt.logs[start:] { + log.IsSuccess = false + log.Reverted = true + } +} + +func (rbt *RosettaBlockTracer) formatAction(depth []int, parentErr error, ac *action) *tracers.RosettaLogItem { + value := ac.value + if value == nil { + value = new(big.Int) + } + return &tracers.RosettaLogItem{ + IsSuccess: ac.err == nil, + Reverted: parentErr != nil || ac.err != nil, + OP: ac.op, + Depth: depth, + From: &vm.RosettaLogAddressItem{Account: &ac.from}, + To: &vm.RosettaLogAddressItem{Account: &ac.to}, + Value: value, + } +} + +// AddRosettaLog records Harmony-specific balance movements that are not EVM +// calls, such as staking operations. +func (rbt *RosettaBlockTracer) AddRosettaLog(op vm.OpCode, from, to *vm.RosettaLogAddressItem, value *big.Int) { + if value == nil { + value = new(big.Int) + } else { + value = new(big.Int).Set(value) + } + rbt.logs = append(rbt.logs, &tracers.RosettaLogItem{ + IsSuccess: true, + OP: op, + Depth: []int{}, + From: from, + To: to, + Value: value, + }) +} + +// GetRosettaResult returns the typed trace used internally by the Rosetta API. +func (rbt *RosettaBlockTracer) GetRosettaResult() ([]*tracers.RosettaLogItem, error) { + results := make([]*tracers.RosettaLogItem, 0, len(rbt.logs)) + if rbt.cur != nil { + root := &rbt.cur.action + var finalize func(*action, error, []int) + finalize = func(ac *action, parentErr error, depth []int) { + results = append(results, rbt.formatAction(depth, parentErr, ac)) + nextErr := parentErr + if ac.err != nil { + nextErr = ac.err + } + for i, subAction := range ac.subCalls { + subDepth := append(append([]int(nil), depth...), i) + finalize(subAction, nextErr, subDepth) + } + } + for i, subAction := range root.subCalls { + finalize(subAction, root.err, []int{i}) + } + } + return append(results, rbt.logs...), nil +} + +// GetResult implements tracers.Tracer. Rosetta callers use GetRosettaResult to +// retain the typed internal representation. +func (rbt *RosettaBlockTracer) GetResult() (json.RawMessage, error) { + result, err := rbt.GetRosettaResult() + if err != nil { + return nil, err + } + return json.Marshal(result) +} diff --git a/hmy/tracers/native/rosetta_test.go b/hmy/tracers/native/rosetta_test.go new file mode 100644 index 0000000000..a856b34f40 --- /dev/null +++ b/hmy/tracers/native/rosetta_test.go @@ -0,0 +1,108 @@ +package native_test + +import ( + "errors" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/hmy/tracers" + "github.com/harmony-one/harmony/hmy/tracers/native" +) + +func TestRosettaBlockTracerRegistered(t *testing.T) { + tracer, err := tracers.New("RosettaBlockTracer", new(tracers.Context), nil) + if err != nil { + t.Fatalf("RosettaBlockTracer lookup failed: %v", err) + } + rosettaTracer, ok := tracer.(*native.RosettaBlockTracer) + if !ok { + t.Fatalf("unexpected RosettaBlockTracer type: %T", tracer) + } + if _, ok := tracer.(vm.RosettaTracer); !ok { + t.Fatalf("RosettaBlockTracer does not implement vm.RosettaTracer: %T", tracer) + } + + from := common.HexToAddress("0x1") + to := common.HexToAddress("0x2") + value := big.NewInt(3) + rosettaTracer.AddRosettaLog( + vm.CALL, + &vm.RosettaLogAddressItem{Account: &from}, + &vm.RosettaLogAddressItem{Account: &to}, + value, + ) + result, err := rosettaTracer.GetRosettaResult() + if err != nil { + t.Fatalf("RosettaBlockTracer result failed: %v", err) + } + if len(result) != 1 { + t.Fatalf("unexpected result length: got %d, want 1", len(result)) + } + want := &tracers.RosettaLogItem{ + IsSuccess: true, + OP: vm.CALL, + Depth: []int{}, + From: &vm.RosettaLogAddressItem{Account: &from}, + To: &vm.RosettaLogAddressItem{Account: &to}, + Value: value, + } + if !reflect.DeepEqual(result[0], want) { + t.Fatalf("unexpected result: got %#v, want %#v", result[0], want) + } +} + +func TestRosettaBlockTracerRevertsSyntheticLogsWithTransaction(t *testing.T) { + genericTracer, err := tracers.New("RosettaBlockTracer", new(tracers.Context), nil) + if err != nil { + t.Fatalf("RosettaBlockTracer lookup failed: %v", err) + } + tracer := genericTracer.(*native.RosettaBlockTracer) + tracer.CaptureEnter(vm.CALL, common.Address{}, common.Address{}, nil, 0, new(big.Int)) + tracer.CaptureEnter(vm.CALL, common.Address{}, common.Address{}, nil, 0, new(big.Int)) + tracer.AddRosettaLog(vm.CALL, nil, nil, big.NewInt(1)) + tracer.CaptureExit(nil, 0, nil) + tracer.CaptureExit(nil, 0, errors.New("execution reverted")) + + result, err := tracer.GetRosettaResult() + if err != nil { + t.Fatalf("RosettaBlockTracer result failed: %v", err) + } + if len(result) != 1 { + t.Fatalf("unexpected result length: got %d, want 1", len(result)) + } + if result[0].IsSuccess || !result[0].Reverted { + t.Fatalf("synthetic log did not inherit transaction revert: %#v", result[0]) + } +} + +func TestRosettaBlockTracerLimitsRevertToCurrentFrame(t *testing.T) { + genericTracer, err := tracers.New("RosettaBlockTracer", new(tracers.Context), nil) + if err != nil { + t.Fatalf("RosettaBlockTracer lookup failed: %v", err) + } + tracer := genericTracer.(*native.RosettaBlockTracer) + + tracer.CaptureEnter(vm.CALL, common.Address{}, common.Address{}, nil, 0, new(big.Int)) + tracer.AddRosettaLog(vm.CALL, nil, nil, big.NewInt(1)) + tracer.CaptureExit(nil, 0, errors.New("execution reverted")) + tracer.CaptureEnter(vm.CALL, common.Address{}, common.Address{}, nil, 0, new(big.Int)) + tracer.AddRosettaLog(vm.CALL, nil, nil, big.NewInt(2)) + tracer.CaptureExit(nil, 0, nil) + + result, err := tracer.GetRosettaResult() + if err != nil { + t.Fatalf("RosettaBlockTracer result failed: %v", err) + } + if len(result) != 2 { + t.Fatalf("unexpected result length: got %d, want 2", len(result)) + } + if result[0].IsSuccess || !result[0].Reverted { + t.Fatalf("failed frame log was not reverted: %#v", result[0]) + } + if !result[1].IsSuccess || result[1].Reverted { + t.Fatalf("successful sibling log inherited revert: %#v", result[1]) + } +} diff --git a/test/all.sh b/test/all.sh index 3cbc75961f..985f066302 100644 --- a/test/all.sh +++ b/test/all.sh @@ -5,4 +5,4 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" bash "$DIR/kill_node.sh" bash "$DIR/go.sh" -bash "$DIR/localnet.sh" rpc rosetta pyhmy +bash "$DIR/localnet.sh" rpc mesh pyhmy diff --git a/test/localnet.Dockerfile b/test/localnet.Dockerfile index 4a288f1801..6603e31212 100644 --- a/test/localnet.Dockerfile +++ b/test/localnet.Dockerfile @@ -4,7 +4,7 @@ ARG MAIN_REPO_BRANCH=dev ARG MAIN_REPO_ORG=harmony-one ARG APT_SNAPSHOT=20260729T000000Z ARG PYHMY_REF=5aeb8601fa174c734f9091619520cf3160b04a16 -ARG MESH_CLI_REF=bbbd759336a0912853d8b10e84a3731c7a0b99d3 +ARG MESH_CLI_REF=8bdb815048e51fe0f6b821308070cc5c4b97073f ARG HMY_VERSION=v2026.0.0 ARG HMY_AMD64_SHA256=489682b069817dce437ac1cbff6b9b564f167019e026c71dfe413405034f9ccd ARG HMY_ARM64_SHA256=dd3aff0fd8971adc0e4ed11fc41791e86767b2a71d13f4a0b12c68f6a904b662 @@ -55,7 +55,9 @@ RUN set -euo pipefail && \ git remote add origin https://github.com/coinbase/mesh-cli.git && \ git fetch -q --depth=1 origin "$MESH_CLI_REF" && \ git checkout -q --detach FETCH_HEAD && \ - make install > /dev/null + make install > /dev/null && \ + command -v rosetta-cli && \ + test "$(rosetta-cli version)" = v0.10.4 WORKDIR "$GOPATH/src/github.com/harmony-one/harmony-test/localnet" COPY scripts/ scripts/ @@ -63,7 +65,14 @@ COPY rpc_tests/ rpc_tests/ COPY configs/ configs/ COPY requirements.txt requirements.txt -RUN printf '%s\n' \ +RUN sed -i '2a set -o pipefail' scripts/run.sh && \ + sed -i 's/>> output_rossetta.log 2>&1/2>\&1 | tee -a output_mesh.log/' scripts/run.sh && \ + sed -i 's/> output_rossetta.log 2>&1/2>\&1 | tee output_mesh.log/' scripts/run.sh && \ + test "$(grep -c 'rosetta-cli check:' scripts/run.sh)" -eq 4 && \ + test "$(grep -c 'output_mesh.log' scripts/run.sh)" -eq 4 && \ + test "$(grep -c '^set -o pipefail$' scripts/run.sh)" -eq 1 && \ + ! grep -q 'output_rossetta.log' scripts/run.sh && \ + printf '%s\n' \ 'annotated-types==0.8.0' \ 'attrs==26.1.0' \ 'bitarray==3.9.2' \ diff --git a/test/localnet.sh b/test/localnet.sh index 04341c811b..eb93cffb9e 100755 --- a/test/localnet.sh +++ b/test/localnet.sh @@ -25,7 +25,8 @@ Usage: test/localnet.sh [--keep] MODE [MODE...] Modes: rpc Run RPC integration tests pyhmy Run pyhmy tests - rosetta Run Rosetta tests + mesh Run Mesh API integration tests + rosetta Alias for mesh Environment: HARMONY_TEST_REF harmony-test branch, tag, or commit (default: pinned commit) @@ -54,7 +55,7 @@ trap cleanup EXIT while (($#)); do case "$1" in --keep) KEEP=true ;; - rpc|pyhmy|rosetta) MODES+=("$1") ;; + rpc|pyhmy|mesh|rosetta) MODES+=("$1") ;; -h|--help) usage; exit 0 ;; *) echo "Unknown localnet test mode: $1" >&2; usage >&2; exit 2 ;; esac @@ -144,7 +145,7 @@ for mode in "${MODES[@]}"; do case "$mode" in rpc) flags=(-B -n) ;; pyhmy) flags=(-B -p) ;; - rosetta) flags=(-B -r) ;; + mesh|rosetta) flags=(-B -r) ;; esac run_args=(run) diff --git a/test/localnet_runner_test.sh b/test/localnet_runner_test.sh index 0c4d45598a..d8c0e2b843 100755 --- a/test/localnet_runner_test.sh +++ b/test/localnet_runner_test.sh @@ -29,8 +29,9 @@ assert_not_contains "docker pull" "$ROOT/test/all.sh" assert_not_contains "docker pull" "$ROOT/test/go.sh" assert_not_contains "docker pull" "$ROOT/test/rpc.sh" assert_not_contains "docker pull" "$ROOT/test/rosetta.sh" +assert_not_contains "docker pull" "$ROOT/test/mesh.sh" assert_not_contains "docker" "$ROOT/test/go.sh" -assert_contains 'localnet.sh" rpc rosetta pyhmy' "$ROOT/test/all.sh" +assert_contains 'localnet.sh" rpc mesh pyhmy' "$ROOT/test/all.sh" assert_contains '6afe7cdc1ecdb920d1c9a19d1b1ca912d3a590ab' "$ROOT/test/localnet.sh" assert_contains 'test: test-go' "$ROOT/Makefile" assert_contains 'test-integration:' "$ROOT/Makefile" @@ -38,6 +39,7 @@ assert_contains 'test-all:' "$ROOT/Makefile" bash "$ROOT/test/rpc.sh" >/dev/null || fail "rpc.sh without arguments must show usage" bash "$ROOT/test/rosetta.sh" >/dev/null || fail "rosetta.sh without arguments must show usage" +bash "$ROOT/test/mesh.sh" >/dev/null || fail "mesh.sh without arguments must show usage" mkdir -p "$TMPDIR_TEST/source" git -C "$TMPDIR_TEST/source" init -q @@ -79,7 +81,7 @@ HARMONY_RUN_ROOT="$TMPDIR_TEST/run-root" \ LOCALNET_ARCH=arm64 \ LOCALNET_IMAGE="harmony-localnet-test:test" \ PATH="$TMPDIR_TEST/bin:$PATH" \ - "$ROOT/test/localnet.sh" rpc rosetta pyhmy + "$ROOT/test/localnet.sh" rpc mesh pyhmy [[ $(grep -c '^built$' "$TMPDIR_TEST/builder.log") -eq 1 ]] || fail "Linux binaries must be prepared once" [[ $(grep -c '^build ' "$TMPDIR_TEST/docker.log") -eq 1 ]] || fail "image must be built exactly once" @@ -95,7 +97,23 @@ assert_not_contains "pull harmonyone/localnet-test" "$TMPDIR_TEST/docker.log" assert_contains 'GOFLAGS=-buildvcs=false -mod=mod' "$ROOT/test/build_linux_binaries.sh" assert_contains 'FROM golang:1.24.2@sha256:' "$ROOT/test/localnet.Dockerfile" assert_contains 'PYHMY_REF=5aeb8601fa174c734f9091619520cf3160b04a16' "$ROOT/test/localnet.Dockerfile" -assert_contains 'MESH_CLI_REF=bbbd759336a0912853d8b10e84a3731c7a0b99d3' "$ROOT/test/localnet.Dockerfile" +assert_contains 'MESH_CLI_REF=8bdb815048e51fe0f6b821308070cc5c4b97073f' "$ROOT/test/localnet.Dockerfile" +assert_contains 'https://github.com/coinbase/mesh-cli.git' "$ROOT/test/localnet.Dockerfile" +assert_contains 'command -v rosetta-cli' "$ROOT/test/localnet.Dockerfile" +assert_contains 'rosetta-cli version' "$ROOT/test/localnet.Dockerfile" +assert_contains '= v0.10.4' "$ROOT/test/localnet.Dockerfile" +assert_not_contains "sed -i 's/rosetta-cli/mesh-cli/g' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains "sed -i '2a set -o pipefail' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains 'tee output_mesh.log' "$ROOT/test/localnet.Dockerfile" +assert_contains 'tee -a output_mesh.log' "$ROOT/test/localnet.Dockerfile" +assert_contains "grep -c 'rosetta-cli check:' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains "grep -c 'output_mesh.log' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains "grep -c '^set -o pipefail$' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains "! grep -q 'output_rossetta.log' scripts/run.sh" "$ROOT/test/localnet.Dockerfile" +assert_contains 'mesh-checker:' "$ROOT/.github/workflows/ci-pr.yaml" +assert_contains 'localnet.sh mesh' "$ROOT/.github/workflows/ci-pr.yaml" +assert_contains 'HARMONY_TEST_REF: 6afe7cdc1ecdb920d1c9a19d1b1ca912d3a590ab' "$ROOT/.github/workflows/ci-pr.yaml" +assert_not_contains 'HARMONY_TEST_DIR:' "$ROOT/.github/workflows/ci-pr.yaml" assert_contains 'setuptools==80.9.0' "$ROOT/test/localnet.Dockerfile" assert_contains '--no-build-isolation' "$ROOT/test/localnet.Dockerfile" diff --git a/test/mesh.sh b/test/mesh.sh new file mode 100755 index 0000000000..7269eabc3c --- /dev/null +++ b/test/mesh.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +docker_name="harmony-localnet-test" + +case ${1:-} in +run) + bash "$DIR/kill_node.sh" + exec bash "$DIR/localnet.sh" --keep mesh + ;; +attach) + docker exec -it "$docker_name" /bin/bash + ;; +*) + echo " +Mesh API tests + +Param: Help: +run Run the Mesh API tests +attach Attach to the Mesh API testing container +" + exit 0 + ;; +esac