From 9be4ae4c878b0d9bc2d502a5be4045963d112e39 Mon Sep 17 00:00:00 2001 From: Orpheus Lummis Date: Wed, 22 Mar 2023 21:30:54 -0400 Subject: [PATCH] WIP --- tests/integration/cli/client_peerid_test.go | 14 +- tests/integration/cli/client_ping_test.go | 23 +- tests/integration/cli/client_query_test.go | 63 +-- tests/integration/cli/client_schema_test.go | 47 +- tests/integration/cli/dump_test.go | 44 +- tests/integration/cli/init_test.go | 79 ++-- tests/integration/cli/log_config_test.go | 82 ---- tests/integration/cli/root_test.go | 42 +- tests/integration/cli/start_test.go | 52 ++- tests/integration/cli/utils.go | 460 +++----------------- tests/integration/cli/utils_test.go | 73 ---- tests/integration/cli/version_test.go | 4 +- 12 files changed, 209 insertions(+), 774 deletions(-) delete mode 100644 tests/integration/cli/utils_test.go diff --git a/tests/integration/cli/client_peerid_test.go b/tests/integration/cli/client_peerid_test.go index 8971899dc2..ca2110276f 100644 --- a/tests/integration/cli/client_peerid_test.go +++ b/tests/integration/cli/client_peerid_test.go @@ -17,9 +17,10 @@ import ( ) func TestPeerID(t *testing.T) { - stopDefra := runDefraNode(t) + conf := NewDefraNodeDefaultConfig(t) + stopDefra := runDefraNode(t, conf) - stdout, stderr, loglines := runDefraCommand(t, []string{"client", "peerid"}) + stdout, _ := runDefraCommand(t, conf, []string{"client", "peerid"}) defraLogLines := stopDefra() @@ -27,8 +28,11 @@ func TestPeerID(t *testing.T) { assert.NotContains(t, line, "ERROR") } - t.Log("stdout:", stdout) - t.Log("stderr:", stderr) - t.Log("loglines:", loglines) + assertContainsSubstring(t, stdout, "peerID") +} +func TestPeerIDWithNoHost(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{"client", "peerid"}) + assertContainsSubstring(t, stderr, "failed to request peer ID") } diff --git a/tests/integration/cli/client_ping_test.go b/tests/integration/cli/client_ping_test.go index d09320ba9e..1fe6af5b22 100644 --- a/tests/integration/cli/client_ping_test.go +++ b/tests/integration/cli/client_ping_test.go @@ -20,9 +20,10 @@ import ( ) func TestPingSimple(t *testing.T) { - stopDefra := runDefraNode(t) + conf := NewDefraNodeDefaultConfig(t) + stopDefra := runDefraNode(t, conf) - stdout, _, _ := runDefraCommand(t, []string{"client", "ping"}) + stdout, _ := runDefraCommand(t, conf, []string{"client", "ping"}) nodeLog := stopDefra() @@ -33,9 +34,9 @@ func TestPingSimple(t *testing.T) { } func TestPingCommandToInvalidHost(t *testing.T) { - stopDefra := runDefraNode(t) - - _, stderr, _ := runDefraCommand(t, []string{"client", "ping", "--url", "'1!2:3!4'"}) + conf := NewDefraNodeDefaultConfig(t) + stopDefra := runDefraNode(t, conf) + _, stderr := runDefraCommand(t, conf, []string{"client", "ping", "--url", "'1!2:3!4'"}) nodeLog := stopDefra() @@ -52,13 +53,7 @@ func TestPingCommandToInvalidHost(t *testing.T) { } func TestPingCommandNoHost(t *testing.T) { - _, _, loglines := runDefraCommand(t, []string{"client", "ping", "--url", "localhost:9876"}) - - // for some line in stderr to contain the error message - for _, line := range loglines { - if strings.Contains(line, "connection refused") { - return - } - } - t.Error("expected error message not found in stderr") + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{"client", "ping", "--url", "localhost:56788"}) //WIP + assertContainsSubstring(t, stderr, "failed to send ping") } diff --git a/tests/integration/cli/client_query_test.go b/tests/integration/cli/client_query_test.go index f8324fb08b..f176cb0003 100644 --- a/tests/integration/cli/client_query_test.go +++ b/tests/integration/cli/client_query_test.go @@ -14,62 +14,15 @@ import ( "testing" ) -// good request func TestRequestSimple(t *testing.T) { - // var err error + conf := NewDefraNodeDefaultConfig(t) + stopDefra := runDefraNode(t, conf) - // have defradb running + stdout, _ := runDefraCommand(t, conf, []string{"client", "query", + "query IntrospectionQuery {__schema {queryType { name }}}", + }) + nodeLog := stopDefra() - // do request - // requestCmd := exec.Command("defradb", "client", "query", "query { ... }") - // requestCmd.Env = append(os.Environ(), "DEFRADB_API_URL=http://localhost:8080") - // requestCmd.Stdout = os.Stdout - // requestCmd.Stderr = os.Stderr - // err := requestCmd.Run() - // if err != nil { - // t.Fatal(err) - // } - - // cfg := newTestConfig(t) - // ctx := context.Background() - // defraCmd := cli.NewDefraCommand(cfg) - // defraCmd.RootCmd.SetArgs([]string{"client", "query", "query { ... }"}) - // defraCmd.SetArgs([]string{"query { ... }"}) - // err = rCmd.Execute() - - // defraCmd := cli.NewDefraCommand(cfg) - - // err = executeWithArgs(t, defraCmd, []string{"client", "query", "query { ... }"}) - // assert.NoError(t, err) - // it should error because defra is not running - // we want defra to be running wiht some given parameters + assertContainsSubstring(t, stdout, "Query") + assertNotContainsSubstring(t, nodeLog, "ERROR") } - -// bad request - -// basic test -// func TestOK(t *testing.T) { -// stop := startDefra(t) - -// cfg := config.DefaultConfig() -// cmd := newDefraCmd(t, cfg, []string{"client", "ping"}) -// out, err := captureStderr(t, func() error { -// return cmd.Execute(context.Background()) -// }) -// assert.NoError(t, err) -// for _, line := range out { -// t.Log("LINE", line) -// } -// defraOut := stop() -// for _, line := range defraOut { -// t.Log("DEFRAOUT LINE", line) -// } -// t.Fail() -// } - -// on the server side we have a log entry that is -// 2023-03-13T13:07:28.473-0400, INFO, defra.http, Request, {"Method": "GET", "Path": "/api/v0/ping", "Status": 200, "LengthBytes": 28, "ElapsedTime": "11.792µs"} - -// on the client side we have -// 2023-03-13T13:07:28.472-0400, INFO, defra.cli, Sending ping... -// 2023-03-13T13:07:28.473-0400, INFO, defra.cli, pong diff --git a/tests/integration/cli/client_schema_test.go b/tests/integration/cli/client_schema_test.go index 29b29a33f3..6016f0dfc6 100644 --- a/tests/integration/cli/client_schema_test.go +++ b/tests/integration/cli/client_schema_test.go @@ -11,45 +11,48 @@ package clitest import ( - "io/ioutil" "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" ) -// defradb client schema - -/* -Example: add from file: - defradb client schema add -f schema.graphql - -Example: add from stdin: - cat schema.graphql | defradb client schema add - -*/ - func TestAddSchemaFromFile(t *testing.T) { - conf, stopDefra := runDefraNode(t) - const fname = "schema.graphql" + tmpdir := t.TempDir() + conf := NewDefraNodeDefaultConfig(t) + stopDefra := runDefraNode(t, conf) - err := ioutil.WriteFile(fname, []byte(` + fname := filepath.Join(tmpdir, "schema.graphql") + err := os.WriteFile(fname, []byte(` type User { id: ID name: String } `), 0644) assert.NoError(t, err) - defer func() { - err := os.Remove(fname) - assert.NoError(t, err) - }() - stdout, _, _ := runDefraCommand(t, []string{"client", "schema", "add", "-f", "schema.graphql"}) + stdout, _ := runDefraCommand(t, conf, []string{"client", "schema", "add", "-f", fname}) nodeLog := stopDefra() assert.Contains(t, stdout, `{"data":{"result":"success"}}`) - for _, line := range nodeLog { - assert.NotContains(t, line, "ERROR") - } + assertNotContainsSubstring(t, nodeLog, "ERROR") } + +// func TestAddSchemaFromPipe(t *testing.T) { +// conf := NewDefraNodeDefaultConfig(t) +// stopDefra := runDefraNode(t, conf) + +// stdout, _, _ := runDefraCommandWithPipe(t, conf, +// []string{"client", "schema", "add", "-"}, +// "type User {id: ID}", +// ) + +// nodeLog := stopDefra() + +// assert.Contains(t, stdout, `{"data":{"result":"success"}}`) +// for _, line := range nodeLog { +// assert.NotContains(t, line, "ERROR") +// } +// } diff --git a/tests/integration/cli/dump_test.go b/tests/integration/cli/dump_test.go index 6bb34bbb40..ac3477af62 100644 --- a/tests/integration/cli/dump_test.go +++ b/tests/integration/cli/dump_test.go @@ -10,50 +10,12 @@ package clitest -/* - import ( - "bufio" - "os" "testing" - - "github.com/sourcenetwork/defradb/cli" ) -// Execution error, {"Error": "badger store does not exist at /Users/o/.defradb/data. Try with an existing directory"} - -func TestServerDump(t *testing.T) { - lines := capture(t, func() { - cmd := cli.MakeCommandTree() - tmpdir := t.TempDir() - cmd.SetArgs([]string{"init", tmpdir}) - cmd.Execute() - cmd.SetArgs([]string{"--rootdir", tmpdir, "server-dump"}) - cmd.Execute() - - // tmpdir := t.TempDir() - // initCmd := cli.MakeInitCommand() - // initCmd.SetArgs([]string{tmpdir}) - // initCmd.Execute() - - // serverDumpCmd := cli.MakeServerDumpCmd() - // serverDumpCmd.Execute() - }) - t.Log(lines) - t.Fail() - // Dumping DB state - // keys are like - // /db/blocks/ - // /db/data/ - // /db/system/ - // /peers/peers/ - // /providers/ -} - func TestServerDumpMemoryErrs(t *testing.T) { - // we expect the following error: - // Execution error, {"Error": "server-side dump is only supported for the Badger datastore"} + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{"server-dump", "--store", "memory"}) + assertContainsSubstring(t, stderr, "server-side dump is only supported for the Badger datastore") } - - -*/ diff --git a/tests/integration/cli/init_test.go b/tests/integration/cli/init_test.go index 0284d956ee..8da20dfe1a 100644 --- a/tests/integration/cli/init_test.go +++ b/tests/integration/cli/init_test.go @@ -10,55 +10,42 @@ package clitest -/* +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/sourcenetwork/defradb/config" +) + // Executing init command creates valid config file. func TestCLIInitCommand(t *testing.T) { - tmpdir := t.TempDir() - logLines := captureStderr(t, func() { - defraCmd := cli.NewDefraCommand(config.NewWithDefaults()) - defraCmd.RootCmd.SetArgs([]string{"init", "--rootdir", tmpdir}) - assert.NoError(t, defraCmd.Execute(context.Background())) - }) - t.Log(logLines) - assert.Contains(t, parseLogLine(logLines[0]), "Initialized configuration file at "+tmpdir+"/"+config.DefaultConfigFileName) - - cfg := config.NewWithDefaults() - cfg.Rootdir = tmpdir - assert.NoError(t, cfg.Load(true)) + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{"init", conf.rootDir}) + cfgfilePath := filepath.Join(conf.rootDir, config.DefaultConfigFileName) + assertContainsSubstring(t, stderr, "Created config file at "+cfgfilePath) + if !assert.FileExists(t, cfgfilePath) { + t.Fatal("Config file not created") + } } -// Executing init command twice leads to error. -// func TestCLIInitCommandTwiceErrors(t *testing.T) { -// tmpdir := t.TempDir() -// logLines := captureStderr(t, func() { -// rootCmd := cli.NewDefraCommand(config.NewWithDefaults()).RootCmd -// rootCmd.SetArgs([]string{"init", tmpdir}) -// assert.NoError(t, rootCmd.Execute()) -// }) -// assert.Contains(t, parseLogLine(logLines[0]), "Initialized configuration file at "+tmpdir+"/"+config.DefaultConfigFileName) -// logLines = captureStderr(t, func() { -// rootCmd := cli.NewDefraCommand(config.NewWithDefaults()).RootCmd -// rootCmd.SetArgs([]string{"init", tmpdir}) -// assert.NoError(t, rootCmd.Execute()) // TBD: should this be an error? -// }) -// assert.Contains(t, parseLogLine(logLines[0]), "Configuration file already exists at "+tmpdir+"/"+config.DefaultConfigFileName+". Consider using --reinitialize") -// } +func TestCLIInitCommandTwiceErrors(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + cfgfilePath := filepath.Join(conf.rootDir, config.DefaultConfigFileName) + _, stderr := runDefraCommand(t, conf, []string{"init", conf.rootDir}) + assertContainsSubstring(t, stderr, "Created config file at "+cfgfilePath) + _, stderr = runDefraCommand(t, conf, []string{"init", conf.rootDir}) + assertContainsSubstring(t, stderr, "Configuration file already exists at "+cfgfilePath) +} // Executing init command twice, but second time reinitializing. -// func TestInitCommandTwiceReinitalize(t *testing.T) { -// tmpdir := t.TempDir() -// defraCommand := cli.NewDefraCommand(config.NewWithDefaults()) -// logLines := captureStderr(t, func() { -// defraCommand.RootCmd.SetArgs([]string{"init", "--rootdir", tmpdir}) -// assert.NoError(t, defraCommand.Execute(context.Background())) -// }) -// t.Log(logLines) -// assert.Contains(t, parseLogLine(logLines[0]), "Initialized configuration file at "+tmpdir+"/"+config.DefaultConfigFileName) -// // logLines = captureStderr(t, func() { -// // rootCmd.SetArgs([]string{"init", tmpdir, "--reinitialize"}) -// // assert.NoError(t, rootCmd.Execute()) -// // }) -// // assert.Contains(t, parseLogLine(logLines[0]), "Reinitialized configuration file at "+tmpdir+"/"+config.DefaultConfigFileName) -// } - -*/ +func TestInitCommandTwiceReinitalize(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + cfgfilePath := filepath.Join(conf.rootDir, config.DefaultConfigFileName) + _, stderr := runDefraCommand(t, conf, []string{"init", conf.rootDir}) + assertContainsSubstring(t, stderr, "Created config file at "+cfgfilePath) + _, stderr = runDefraCommand(t, conf, []string{"init", conf.rootDir, "--reinitialize"}) + assertContainsSubstring(t, stderr, "Deleted config file at "+cfgfilePath) + assertContainsSubstring(t, stderr, "Created config file at "+cfgfilePath) +} diff --git a/tests/integration/cli/log_config_test.go b/tests/integration/cli/log_config_test.go index d4f3e5d6a2..b50f4f007c 100644 --- a/tests/integration/cli/log_config_test.go +++ b/tests/integration/cli/log_config_test.go @@ -9,85 +9,3 @@ // licenses/APL.txt. package clitest - -// const ( -// testLogger1 = "testLogger1" -// testLogger2 = "testLogger2" -// testLogger3 = "testLogger3" -// ) - -/* -func TestCLILogsToStderrGivenNamedLogLevel(t *testing.T) { - ctx := context.Background() - logLines := captureLogLines( - t, - func() { - // set the log levels - // general: error - // testLogger1: debug - // testLogger2: info - os.Args = append(os.Args, "--loglevel") - os.Args = append(os.Args, fmt.Sprintf("%s,%s=debug,%s=info", "error", testLogger1, testLogger2)) - }, - func() { - log1.Error(ctx, "error") - log1.Debug(ctx, "debug") - log2.Info(ctx, "info") - log3.Debug(ctx, "debug") // wont print, as logger3 will use global level defined above as 'error' - log3.Info(ctx, "info") // wont print, as logger3 will use global level defined above as 'error' - }, - ) - - assert.Len(t, logLines, 3) -} - -func captureLogLines(t *testing.T, setup func(), predicate func()) []string { - r, w, err := os.Pipe() - if err != nil { - t.Fatal(err) - } - stderr := os.Stderr - os.Stderr = w - defer func() { - os.Stderr = stderr - }() - - directory := t.TempDir() - - // Set the default logger output path to a file in the temp dir - // so that production logs don't polute and confuse the tests - // os.Args = append(os.Args, "--logoutput", directory+"/log.txt") - os.Args = append(os.Args, "init", directory) - - setup() - cli.Execute() - predicate() - log1.Flush() - log2.Flush() - log3.Flush() - - w.Close() - var buf bytes.Buffer - io.Copy(&buf, r) - logLines, err := parseLines(&buf) - if err != nil { - t.Fatal(err) - } - - return logLines -} - -func parseLines(r io.Reader) ([]string, error) { - fileScanner := bufio.NewScanner(r) - - fileScanner.Split(bufio.ScanLines) - - logLines := []string{} - for fileScanner.Scan() { - logLines = append(logLines, fileScanner.Text()) - } - - return logLines, nil -} - -*/ diff --git a/tests/integration/cli/root_test.go b/tests/integration/cli/root_test.go index 42cc8b8666..11d3e04a7d 100644 --- a/tests/integration/cli/root_test.go +++ b/tests/integration/cli/root_test.go @@ -11,43 +11,21 @@ package clitest import ( - "context" "testing" "github.com/stretchr/testify/assert" - - "github.com/sourcenetwork/defradb/cli" - "github.com/sourcenetwork/defradb/config" ) func TestRootCommandEmptyRootDir(t *testing.T) { - defraCmd := cli.NewDefraCommand(config.DefaultConfig()) - tmpdir := t.TempDir() - defraCmd.RootCmd.SetArgs([]string{"--rootdir", tmpdir}) - assert.NoError(t, defraCmd.Execute(context.Background())) - assert.Equal(t, config.DefaultConfig(), defraCmd.Cfg) + conf := NewDefraNodeDefaultConfig(t) + stdout, _ := runDefraCommand(t, conf, []string{}) + assert.Contains(t, stdout, "Usage:") } -// func TestRootCommandRootDirWithDefaultConfig(t *testing.T) { -// defraCmd := cli.NewDefraCommand(config.DefaultConfig()) -// tmpdir := t.TempDir() -// cfg := config.DefaultConfig() -// cfg.Rootdir = tmpdir -// assert.NoError(t, cfg.CreateRootDir()) -// defraCmd.RootCmd.SetArgs([]string{"--rootdir", tmpdir}) -// assert.NoError(t, defraCmd.Execute(context.Background())) -// assert.Equal(t, config.DefaultConfig(), defraCmd.Cfg) -// } - -// // loaded config is non-default -// func TestRootCommandRootDirWithNonDefaultConfig(t *testing.T) { -// tmpdir := t.TempDir() -// cfg := config.DefaultConfig() -// cfg.Rootdir = tmpdir -// defraCmd := cli.NewDefraCommand(cfg) -// defraCmd.Cfg.Datastore.Store = "memory" -// assert.NoError(t, defraCmd.Cfg.ConfigFileWrite()) -// defraCmd.RootCmd.SetArgs([]string{"--rootdir", tmpdir}) -// assert.NoError(t, defraCmd.Execute(context.Background())) -// assert.Equal(t, "memory", defraCmd.Cfg.Datastore.Store) -// } +func TestRootCommandRootDirWithDefaultConfig(t *testing.T) { + conf := DefraNodeConfig{ + logPath: t.TempDir(), + } + stdout, _ := runDefraCommand(t, conf, []string{}) + assert.Contains(t, stdout, "Usage:") +} diff --git a/tests/integration/cli/start_test.go b/tests/integration/cli/start_test.go index 63fd671f0d..d9b2ac7e82 100644 --- a/tests/integration/cli/start_test.go +++ b/tests/integration/cli/start_test.go @@ -10,20 +10,44 @@ package clitest -/* -func TestWithFrameworkStartDoubleFailsBecauseOfRootdirLock(t *testing.T) { - cfg := config.NewWithDefaults() - testCfg := newRandomTestConfig(t) - cfg.Rootdir = testCfg.RootDir - defraCmd := cli.NewDefraCommand(cfg) - // defer(releasePortsOfCommand(defraCmd)) - logLines := testFrameworkWithBackgroundNodeExec(t, testCfg, func() { - defraCmd.RootCmd.SetArgs([]string{"start"}) - err := defraCmd.Execute(context.Background()) - assert.Error(t, err) +import ( + "fmt" + "testing" +) + +func TestStartCommand(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{ + "start", + "--url", fmt.Sprintf("localhost:%d", conf.portAPI), + "--tcpaddr", fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", conf.portGRPC), + }) + assertContainsSubstring(t, stderr, "Starting DefraDB service...") + assertNotContainsSubstring(t, stderr, "Error") +} + +func TestStartCommandWithTLSIncomplete(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{ + "start", + "--tls", + "--url", fmt.Sprintf("localhost:%d", conf.portAPI), + "--tcpaddr", fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", conf.portGRPC), + }) + assertContainsSubstring(t, stderr, "Starting DefraDB service...") + assertContainsSubstring(t, stderr, "Error") +} + +func TestStartCommandWithStoreMemory(t *testing.T) { + conf := NewDefraNodeDefaultConfig(t) + _, stderr := runDefraCommand(t, conf, []string{ + "start", "--store", "memory", + "--url", fmt.Sprintf("localhost:%d", conf.portAPI), + "--tcpaddr", fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", conf.portGRPC), }) - expectedError := fmt.Sprintf("{\"Error\": \"failed to open datastore: Cannot acquire directory lock on \\\"%v/data\\\". Another process is using this Badger database. error: resource temporarily unavailable\"}", testCfg.RootDir) - assert.Contains(t, parseLogLine(logLines[2]), expectedError) + assertContainsSubstring(t, stderr, "Starting DefraDB service...") + assertContainsSubstring(t, stderr, "Building new memory store") + assertNotContainsSubstring(t, stderr, "Error") } -*/ +// no-p2p diff --git a/tests/integration/cli/utils.go b/tests/integration/cli/utils.go index 2a6885d2b6..3fc2e6a4e0 100644 --- a/tests/integration/cli/utils.go +++ b/tests/integration/cli/utils.go @@ -9,9 +9,7 @@ // licenses/APL.txt. /* -WIP explanatory how the test framework works - -A system to generate unique test configurations. +Package clitest provides a testing framework for the Defra CLI, along with CLI integration tests. */ package clitest @@ -19,7 +17,6 @@ import ( "bufio" "bytes" "context" - "crypto/sha256" "errors" "fmt" "io" @@ -27,7 +24,6 @@ import ( "net" "os" "path/filepath" - "strconv" "strings" "testing" "time" @@ -38,81 +34,83 @@ import ( "github.com/sourcenetwork/defradb/config" ) -// var log = logging.MustNewLogger("defra.tests.integration.cli") - -// perhaps the server should returns its outputted lines too - -// uses a configurable rare ports to avoid conflict +const COMMAND_DELAY = 2 -type defraNodeConfig struct { - // the root directory of the node - rootDir string - // the log file path - logFile string - // the API port - portAPI int - // the GRPC port +type DefraNodeConfig struct { + rootDir string + logPath string + portAPI int portGRPC int } -// Runs a defradb node in a goroutine, and returns a function to cancel it, -// which also returns the node's execution log lines. -func runDefraNode(t *testing.T) (defraNodeConfig, func() []string) { +func NewDefraNodeDefaultConfig(t *testing.T) DefraNodeConfig { t.Helper() - - tmpdir := t.TempDir() - logFilepath := fmt.Sprintf("%x.log", sha256.Sum256([]byte(tmpdir))) - cfg := config.DefaultConfig() - ctx, cancel := context.WithCancel(context.Background()) - portAPI, err := findFreePortInRange(49152, 65535) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } portGRPC, err := findFreePortInRange(49152, 65535) - assert.NoError(t, err) - - conf := defraNodeConfig{ + if err != nil { + t.Fatal(err) + } + return DefraNodeConfig{ rootDir: t.TempDir(), - logFile: filepath.Join(t.TempDir(), fmt.Sprintf("%x.log", sha256.Sum256([]byte(t.TempDir())))), + logPath: "", portAPI: portAPI, portGRPC: portGRPC, } +} + +// runDefraNode runs a defra node in a separate goroutine and returns a stopping function +// which also returns the node's execution log lines. +func runDefraNode(t *testing.T, conf DefraNodeConfig) func() []string { + t.Helper() + + if conf.logPath == "" { + conf.logPath = filepath.Join(t.TempDir(), "defra.log") + } + + var args []string + if conf.rootDir != "" { + args = append(args, "--rootdir", conf.rootDir) + } + if conf.portAPI != 0 { + args = append(args, "--url", fmt.Sprintf("localhost:%d", conf.portAPI)) + } + if conf.portGRPC != 0 { + args = append(args, "--tcpaddr", fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", conf.portGRPC)) + } + args = append(args, "--logoutput", conf.logPath) + cfg := config.DefaultConfig() + ctx, cancel := context.WithCancel(context.Background()) go func() { defraCmd := cli.NewDefraCommand(cfg) - defraCmd.RootCmd.SetArgs([]string{ - "--rootdir", tmpdir, - "--logoutput", logFilepath, - "--url", "127.0.0.1:" + strconv.Itoa(portAPI), - "--tcpaddr", "/ip4/0.0.0.0/tcp/" + strconv.Itoa(portGRPC), - "start", - }) + defraCmd.RootCmd.SetArgs( + append([]string{"start"}, args...), + ) err := defraCmd.Execute(ctx) assert.NoError(t, err) }() time.Sleep(1 * time.Second) // time buffer for it to start cancelAndOutput := func() []string { cancel() - lines, err := readLoglines(t, logFilepath) - assert.NoError(t, err) - err = os.Remove(logFilepath) + lines, err := readLoglines(t, conf.logPath) assert.NoError(t, err) return lines } - return conf, cancelAndOutput + return cancelAndOutput } -func runDefraCommand(t *testing.T, args []string, conf defraNodeConfig) (stdout, stderr []string, loglines []string) { +func runDefraCommand(t *testing.T, conf DefraNodeConfig, args []string) (stdout, stderr []string) { t.Helper() - tmpdir := t.TempDir() - logFile := fmt.Sprintf("%x.log", sha256.Sum256([]byte(tmpdir))) - logFilePath := filepath.Join(tmpdir, logFile) cfg := config.DefaultConfig() args = append([]string{ - "--rootdir", tmpdir, - "--logoutput", logFilePath, + "--rootdir", conf.rootDir, + "--url", fmt.Sprintf("localhost:%d", conf.portAPI), }, args...) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), COMMAND_DELAY*time.Second) defer cancel() stdout, stderr = captureOutput(func() { @@ -121,18 +119,9 @@ func runDefraCommand(t *testing.T, args []string, conf defraNodeConfig) (stdout, defraCmd.RootCmd.SetArgs(args) _ = defraCmd.Execute(ctx) }) - - loglines, err := readLoglines(t, logFilePath) - if err == nil { - _ = os.Remove(logFilePath) - } - - // WIPP - t.Log("🔥 stdout", stdout) - t.Log("🔥 stderr", stderr) - t.Log("🔥 loglines", loglines) - - return stdout, stderr, loglines + t.Log("⭐️ stdout", stdout) //WIP + t.Log("⭐️ stderr", stderr) //WIP + return stdout, stderr } func readLoglines(t *testing.T, fpath string) ([]string, error) { @@ -212,332 +201,25 @@ func findFreePortInRange(minPort, maxPort int) (int, error) { return 0, errors.New("unable to find a free port") } -// func fixtureEnvKeyValue(t *testing.T, key, value string) { -// t.Helper() -// if err := os.Setenv(key, value); err != nil { -// t.Fatal(err) -// } -// t.Cleanup(func() { -// if err := os.Unsetenv(key); err != nil { -// t.Fatal(err) -// } -// }) -// } - -// // Runs the defradb commands, using a temporary rootdir and logoutput, with -// // the given args. Returns stdout, stderr, and log lines. -// func runDefraCommand(t *testing.T, args []string) (stdout []string, stderr []string, loglines []string) { -// t.Helper() -// tmpdir := t.TempDir() -// logFilepath := fmt.Sprintf("%x.log", sha256.Sum256([]byte(tmpdir))) -// cfg := config.DefaultConfig() -// args = append([]string{ -// "--rootdir", tmpdir, -// "--logoutput", logFilepath, -// }, args...) - -// // Saving original stdout and stderr -// oldStdout := os.Stdout -// oldStderr := os.Stderr - -// rStdout, wStdout, _ := os.Pipe() -// rStderr, wStderr, _ := os.Pipe() - -// os.Stdout = wStdout -// os.Stderr = wStderr - -// ctx, cancel := context.WithCancel(context.Background()) - -// go func() { -// defer cancel() -// defraCmd := cli.NewDefraCommand(cfg) -// t.Log("executing defra command with args", args) -// defraCmd.RootCmd.SetArgs(args) -// err := defraCmd.Execute(ctx) -// assert.NoError(t, err) -// }() - -// time.Sleep(1 * time.Second) // time buffer for it to run - -// // Restore original stdout and stderr -// wStdout.Close() -// wStderr.Close() -// os.Stdout = oldStdout -// os.Stderr = oldStderr - -// var stdoutBuf, stderrBuf bytes.Buffer -// io.Copy(&stdoutBuf, rStdout) -// io.Copy(&stderrBuf, rStderr) - -// stdout = bytes.Split(stdoutBuf.Bytes(), []byte("\n")) -// stderr = bytes.Split(stderrBuf.Bytes(), []byte("\n")) - -// loglines = readLoglines(t, logFilepath) -// err := os.Remove(logFilepath) -// assert.NoError(t, err) - -// return -// } - -// // Runs the defradb commands, using a temporary rootdir and logoutput, with -// // the given args. Returns the log lines. -// func runDefraCommand(t *testing.T, args []string) ([]string, []string, []string) { -// t.Helper() -// tmpdir := t.TempDir() -// logFilepath := fmt.Sprintf("%x.log", sha256.Sum256([]byte(tmpdir))) -// cfg := config.DefaultConfig() -// args = append([]string{ -// "--rootdir", tmpdir, -// "--logoutput", logFilepath, -// }, args...) -// go func() { -// defraCmd := cli.NewDefraCommand(cfg) -// t.Log("executing defra command with args", args) -// defraCmd.RootCmd.SetArgs(args) -// err := defraCmd.Execute(context.Background()) -// assert.NoError(t, err) -// }() -// time.Sleep(1 * time.Second) // time buffer for it to run -// loglines := readLoglines(t, logFilepath) -// err := os.Remove(logFilepath) -// assert.NoError(t, err) -// return (stdout, stderr, loglines) -// } - -// // Capture the stdout and stderr of a function, and return them as lines. -// func captureOutput(f func()) (stdout []string, stderr []string) { -// // Saving original stdout and stderr -// oldStdout := os.Stdout -// oldStderr := os.Stderr - -// rStdout, wStdout, _ := os.Pipe() -// rStderr, wStderr, _ := os.Pipe() - -// os.Stdout = wStdout -// os.Stderr = wStderr - -// // Run the function with captured output -// f() - -// // Restore original stdout and stderr -// wStdout.Close() -// wStderr.Close() -// os.Stdout = oldStdout -// os.Stderr = oldStderr - -// var stdoutBuf, stderrBuf bytes.Buffer -// io.Copy(&stdoutBuf, rStdout) -// io.Copy(&stderrBuf, rStderr) - -// stdout = bytes.Split(stdoutBuf.Bytes(), []byte("\n")) -// stderr = bytes.Split(stderrBuf.Bytes(), []byte("\n")) - -// return -// } - -// // Capture the stdout and stderr of a function, and return them as lines. -// func captureOutput(ctx context.Context, f func()) (stdout []string, stderr []string) { -// // Saving original stdout and stderr -// oldStdout := os.Stdout -// oldStderr := os.Stderr - -// rStdout, wStdout, _ := os.Pipe() -// rStderr, wStderr, _ := os.Pipe() - -// os.Stdout = wStdout -// os.Stderr = wStderr - -// done := make(chan struct{}) - -// // Run the function with captured output in a goroutine -// go func() { -// f() -// close(done) -// }() - -// // Wait for the function to complete or the context to be cancelled -// select { -// case <-done: -// case <-ctx.Done(): -// } - -// // Restore original stdout and stderr -// wStdout.Close() -// wStderr.Close() -// os.Stdout = oldStdout -// os.Stderr = oldStderr - -// var stdoutBuf, stderrBuf bytes.Buffer -// io.Copy(&stdoutBuf, rStdout) -// io.Copy(&stderrBuf, rStderr) - -// stdout = bytes.Split(stdoutBuf.Bytes(), []byte("\n")) -// stderr = bytes.Split(stderrBuf.Bytes(), []byte("\n")) - -// return -// } - -// func execDefraCmdWithArgs(cfg *config.Config, args []string) error { -// defraCmd := cli.NewDefraCommand(cfg) -// defraCmd.RootCmd.SetArgs(args) -// return defraCmd.Execute(context.Background()) -// } - -// type testConfig struct { -// RootDir string -// APIPort int -// RPCPort int -// P2PPort int -// TCPPort int -// ID string -// } - -// var testConfigs = make(map[string]testConfig) - -// func newTestConfig(t *testing.T) testConfig { -// t.Helper() -// tc := testConfig{ -// RootDir: t.TempDir(), -// APIPort: newPort(), -// RPCPort: newPort(), -// P2PPort: newPort(), -// TCPPort: newPort(), -// } -// tc.ID = tc.selfhash() -// testConfigs[tc.ID] = tc -// return tc -// } - -// func (tc testConfig) selfhash() string { -// return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%v", tc)))) -// } - -// // serialize testConfig -// func (tc testConfig) serialize() string { -// return testConfigSerialized{ -// RootDir: tc.RootDir, -// APIAddress: fmt.Sprintf("localhost:%v", tc.APIPort), -// RPCAddress: fmt.Sprintf("localhost:%v", tc.RPCPort), -// P2PAddress: fmt.Sprintf("/ip4/127.0.0.1/tcp/%v", tc.P2PPort), -// TCPAddress: fmt.Sprintf("/ip4/127.0.0.1/tcp/%v", tc.TCPPort), -// } -// } - -// func releaseTestConfig(tc testConfig) { -// } - -// var ports = make(map[int]bool) - -// func newPort() int { -// min := 9999 -// max := 65535 -// for { -// port := rand.Intn(max-min) + min -// if _, ok := ports[port]; !ok { -// ports[port] = true -// return port -// } -// } -// } - -// // this works with the PATH setup ... -// func testFrameworkWithBackgroundNodeExec(t *testing.T, testCfg testConfig, action func()) []string { -// t.Helper() -// c := testCfg.serialize() -// // potential problem with this -// args := []string{"start"} -// args = append(args, "--rootdir", c.RootDir) -// args = append(args, "--url", c.APIAddress) -// args = append(args, "--p2paddr", c.P2PAddress) -// args = append(args, "--tcpaddr", c.TCPAddress) -// go func() { -// cmd := exec.Command("defradb", args...) -// // stdout, _ := cmd.StdoutPipe() -// // stderr, _ := cmd.StderrPipe() - -// err := cmd.Start() -// if err != nil { -// panic(err) -// } - -// // time.Sleep(2 * time.Second) - -// // scanner := bufio.NewScanner(stdout) -// // for scanner.Scan() { -// // fmt.Println(scanner.Text()) -// // } - -// // scanner = bufio.NewScanner(stderr) -// // for scanner.Scan() { -// // fmt.Println("bg node stderr", scanner.Text()) -// // } -// }() -// // sufficient time for the background node to start -// time.Sleep(2 * time.Second) - -// // WIP or sstdout ?? -// // clarify the thing with piping ... -// lines := captureStderr(t, action) -// return lines -// } - -// Execute an action that writes to Stderr and captures the log output. Returns the error of the action. -// func captureStderr(t *testing.T, action func() error) ([]string, error) { -// t.Helper() - -// stderr := os.Stderr -// r, w, err := os.Pipe() -// assert.NoError(t, err) -// os.Stderr = w - -// err = action() - -// _ = w.Close() -// scanner := bufio.NewScanner(r) -// scanner.Split(bufio.ScanLines) -// var lines []string -// for scanner.Scan() { -// text := scanner.Text() -// // t.Log(text) -// lines = append(lines, text) -// } -// _ = r.Close() -// os.Stderr = stderr -// return lines, err -// } - -// goal: capture output of the command independetly from other goroutines. -// we can do so using files -// func captureOutput(t *testing.T, action func() error) ([]string, error) { - -// // parse log line as quasi-csv into a string array -// func parseLogLine(line string) []string { -// return strings.Split(line, ", ") -// } - -// func newTestConfig(t *testing.T) *config.Config { -// t.Helper() -// tc := config.NewConfig() -// tc.RootDir = t.TempDir() -// return tc -// } - -// func newDefraCmd(t *testing.T, cfg *config.Config, args []string) cli.DefraCommand { -// defraCmd := cli.NewDefraCommand(cfg) -// defraCmd.RootCmd.SetArgs(args) -// return defraCmd -// } - -// func executeWithArgs(t *testing.T, defraCmd cli.DefraCommand, args []string) error { -// ctx := context.Background() -// defraCmd.RootCmd.SetArgs(args) -// return defraCmd.Execute(ctx) -// } +func assertContainsSubstring(t *testing.T, haystack []string, substring string) { + t.Helper() + if !containsSubstring(haystack, substring) { + t.Fatalf("expected %q to contain %q", haystack, substring) + } +} -// func defraStart(t *testing.T, cfg *config.Config) error { -// t.Helper() -// args := []string{"start"} -// return executeWithArgs(t, newDefraCmdt(t, cfg, args), args) -// } +func assertNotContainsSubstring(t *testing.T, haystack []string, substring string) { + t.Helper() + if containsSubstring(haystack, substring) { + t.Fatalf("expected %q to not contain %q", haystack, substring) + } +} -// capture the output of the command +func containsSubstring(haystack []string, substring string) bool { + for _, s := range haystack { + if strings.Contains(s, substring) { + return true + } + } + return false +} diff --git a/tests/integration/cli/utils_test.go b/tests/integration/cli/utils_test.go deleted file mode 100644 index 8344c7e728..0000000000 --- a/tests/integration/cli/utils_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2022 Democratized Data Foundation -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package clitest - -import ( - "strings" - "testing" -) - -/* -func TestCaptureStderr(t *testing.T) { - lines := captureStderr(t, func() { - fmt.Fprintln(os.Stderr, "Test message 1") - log.Println("Test message 2") - fmt.Fprintln(os.Stderr, "Test message 3") - log.Println("Test message 4") - }) - - expectedLines := []string{ - "Test message 1", - "Test message 3", - } - - if !reflect.DeepEqual(lines, expectedLines) { - t.Errorf("expected %q, got %q", expectedLines, lines) - } -} - -func TestBasicStderrCapture(t *testing.T) { - lines := captureStderr(t, func() { - defraCmd := cli.NewDefraCommand(config.NewWithDefaults()) - defraCmd.RootCmd.SetArgs([]string{"129873918269387162"}) - assert.Error(t, defraCmd.Execute(context.Background())) - }) - - t.Log(lines) - - expectedLines := []string{ - "Usage:", - "defradb [command]", - } - - assert.Equal(t, expectedLines, lines) -} - -*/ - -func linesContainSubstring(lines []string, substring string) bool { - for _, line := range lines { - if strings.Contains(line, substring) { - return true - } - } - return false -} - -func TestStartDefraBasic(t *testing.T) { - stopAndOutput := runDefraNode(t) - - out := stopAndOutput() - - if !linesContainSubstring(out, "Starting DefraDB service...") { - t.Error("Expected output to contain 'Starting defradb service...'") - } -} diff --git a/tests/integration/cli/version_test.go b/tests/integration/cli/version_test.go index 91699ea688..1ad1ba8ff7 100644 --- a/tests/integration/cli/version_test.go +++ b/tests/integration/cli/version_test.go @@ -17,8 +17,10 @@ import ( "github.com/stretchr/testify/assert" ) +// note: this assumes the version information *without* build-time info integrated. func TestExecVersion(t *testing.T) { - stdout, stderr, _ := runDefraCommand(t, []string{"version"}) + conf := NewDefraNodeDefaultConfig(t) + stdout, stderr := runDefraCommand(t, conf, []string{"version"}) for _, line := range stderr { assert.NotContains(t, line, "ERROR") }