Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

E2E tests ls command #68

Merged
merged 2 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 251 additions & 0 deletions e2e/ls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package e2e

import (
"encoding/json"
"io"
"os"
"path/filepath"
"testing"
"time"

"github.com/NethermindEth/eigenlayer/internal/data"
"github.com/stretchr/testify/assert"
)

func TestLs_NoAVS(t *testing.T) {
// Test context
var (
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
nil,
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
assert.Equal(t, out, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT \n",
))
})
e2eTest.run()
}

func TestLs_NotRunning(t *testing.T) {
// Test context
var (
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
func(t *testing.T, eigenlayerPath string) error {
err := buildMockAvsImages(t)
if err != nil {
return err
}
return runCommand(t, eigenlayerPath, "install", "--profile", "option-returner", "--no-prompt", "--version", latestMockAVSVersion, "https://github.com/NethermindEth/mock-avs")
},
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
assert.Equal(t, out, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT \n"+
"mock-avs-default false unknown "+latestMockAVSVersion+" a7ca2dca2cc9 \n",
))
})
e2eTest.run()
}

func TestLs_RunningHealthy(t *testing.T) {
// Test context
var (
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
func(t *testing.T, eigenlayerPath string) error {
err := buildMockAvsImages(t)
if err != nil {
return err
}
err = runCommand(t, eigenlayerPath, "install", "--profile", "option-returner", "--yes", "--no-prompt", "--version", latestMockAVSVersion, "https://github.com/NethermindEth/mock-avs")
if err != nil {
return err
}
return waitHealthy(t, "option-returner", 8080, "eigenlayer", 3*time.Second)
},
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
assert.Equal(t, out, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT \n"+
"mock-avs-default true healthy "+latestMockAVSVersion+" a7ca2dca2cc9 \n",
))
})
e2eTest.run()
}

func TestLs_RunningPartiallyHealthy(t *testing.T) {
// Test context
var (
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
func(t *testing.T, eigenlayerPath string) error {
err := buildMockAvsImages(t)
if err != nil {
return err
}
err = runCommand(t, eigenlayerPath, "install", "--profile", "option-returner", "--yes", "--no-prompt", "--version", latestMockAVSVersion, "https://github.com/NethermindEth/mock-avs")
if err != nil {
return err
}
err = waitHealthy(t, "option-returner", 8080, "eigenlayer", 3*time.Second)
if err != nil {
return err
}
return changeHealthStatus(t, "option-returner", 8080, "eigenlayer", 206)
},
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
assert.Equal(t, out, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT \n"+
"mock-avs-default true partially healthy "+latestMockAVSVersion+" a7ca2dca2cc9 \n",
))
})
e2eTest.run()
}

func TestLs_RunningUnhealthy(t *testing.T) {
// Test context
var (
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
func(t *testing.T, eigenlayerPath string) error {
err := buildMockAvsImages(t)
if err != nil {
return err
}
err = runCommand(t, eigenlayerPath, "install", "--profile", "option-returner", "--yes", "--no-prompt", "--version", latestMockAVSVersion, "https://github.com/NethermindEth/mock-avs")
if err != nil {
return err
}
err = waitHealthy(t, "option-returner", 8080, "eigenlayer", 3*time.Second)
if err != nil {
return err
}
return changeHealthStatus(t, "option-returner", 8080, "eigenlayer", 503)
},
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
assert.Equal(t, out, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT \n"+
"mock-avs-default true unhealthy "+latestMockAVSVersion+" a7ca2dca2cc9 \n",
))
})
e2eTest.run()
}

func TestLs_Comment(t *testing.T) {
// Test context
var (
containerIP string
out []byte
lsErr error
)
e2eTest := newE2ETestCase(t,
func(t *testing.T, eigenlayerPath string) error {
err := buildMockAvsImages(t)
if err != nil {
return err
}
err = runCommand(t, eigenlayerPath, "install", "--profile", "option-returner", "--yes", "--no-prompt", "--version", latestMockAVSVersion, "https://github.com/NethermindEth/mock-avs")
if err != nil {
return err
}
err = waitHealthy(t, "option-returner", 8080, "eigenlayer", 3*time.Second)
if err != nil {
return err
}
containerIP, err = getContainerIPByName("option-returner", "eigenlayer")
if err != nil {
return err
}
err = changeHealthStatus(t, "option-returner", 8080, "eigenlayer", 503)
if err != nil {
return err
}
return changeStateAPIPort("mock-avs-default", "8081")
},
func(t *testing.T, eigenlayerPath string) {
out, lsErr = runCommandOutput(t, eigenlayerPath, "ls")
},
func(t *testing.T) {
assert.NoError(t, lsErr, "ls command should not return an error")
var ds string
for i := 0; i < len(containerIP)*2; i++ {
ds += " "
}
assert.Equal(t, []byte(
"AVS Instance ID RUNNING HEALTH VERSION COMMIT COMMENT"+ds+" \n"+
"mock-avs-default true unknown "+latestMockAVSVersion+" a7ca2dca2cc9 API container is running but health check failed: Get \"http://"+containerIP+":8081/eigen/node/health\": dial tcp "+containerIP+":8081: connect: connection refused \n",
), out)
})
e2eTest.run()
}

func changeStateAPIPort(instanceID string, port string) error {
dirPath, err := dataDirPath()
if err != nil {
return err
}
stateFile, err := os.OpenFile(filepath.Join(dirPath, "nodes", instanceID, "state.json"), os.O_RDONLY, 0o644)
if err != nil {
return err
}
stateData, err := io.ReadAll(stateFile)
if err != nil {
return err
}
err = stateFile.Close()
if err != nil {
return err
}
var instance data.Instance
err = json.Unmarshal(stateData, &instance)
if err != nil {
return err
}
instance.APITarget.Port = port
newStateJson, err := json.MarshalIndent(&instance, "", " ")
if err != nil {
return err
}
stateFile, err = os.OpenFile(filepath.Join(dirPath, "nodes", instanceID, "state.json"), os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
return err
}
err = stateFile.Truncate(0)
if err != nil {
return err
}
_, err = stateFile.Write(newStateJson)
if err != nil {
return err
}
return stateFile.Close()
}
48 changes: 47 additions & 1 deletion e2e/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,25 @@ import (
"path/filepath"
"strings"
"testing"
"time"

"github.com/NethermindEth/eigenlayer/internal/data"
"github.com/cenkalti/backoff"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)

func runCommand(t *testing.T, path string, args ...string) error {
_, err := runCommandOutput(t, path, args...)
return err
}

func runCommandOutput(t *testing.T, path string, args ...string) ([]byte, error) {
t.Helper()
t.Logf("Running command: %s %s", path, strings.Join(args, " "))
out, err := exec.Command(path, args...).CombinedOutput()
t.Logf("===== OUTPUT =====\n%s\n==================", out)
return err
return out, err
}

func buildMockAvsImages(t *testing.T) error {
Expand Down Expand Up @@ -204,3 +211,42 @@ func getAVSHealth(t *testing.T, url string) (int, error) {
}
return response.StatusCode, nil
}

func waitHealthy(t *testing.T, containerID string, port int, networkName string, timeout time.Duration) error {
containerIP, err := getContainerIPByName(containerID, networkName)
if err != nil {
return err
}
var (
timeOut time.Duration = 30 * time.Second
responseCode = -1
)
ctx, cancel := context.WithTimeout(context.Background(), timeOut)
defer cancel()
b := backoff.WithContext(backoff.NewExponentialBackOff(), ctx)
return backoff.Retry(func() error {
responseCode, err = getAVSHealth(t, fmt.Sprintf("http://%s:%d%s", containerIP, port, "/eigen/node/health"))
if err != nil {
return err
}
if responseCode != 200 {
return fmt.Errorf("expected response code %d, got %d", 200, responseCode)
}
return nil
}, b)
}

func changeHealthStatus(t *testing.T, containerID string, port int, networkName string, healthStatus int) error {
containerIP, err := getContainerIPByName(containerID, networkName)
if err != nil {
return err
}
response, err := http.Post(fmt.Sprintf("http://%s:%d/health/%d", containerIP, port, healthStatus), "", nil)
if err != nil {
return err
}
if response.StatusCode != 200 {
return fmt.Errorf("expected response code %d, got %d", 200, response.StatusCode)
}
return nil
}