diff --git a/tests/integration/codexclient.nim b/tests/integration/codexclient.nim index e3409b82..68235237 100644 --- a/tests/integration/codexclient.nim +++ b/tests/integration/codexclient.nim @@ -1,6 +1,5 @@ import std/httpclient import std/strutils -import std/sequtils from pkg/libp2p import Cid, `$`, init import pkg/chronicles diff --git a/tests/integration/codexprocess.nim b/tests/integration/codexprocess.nim index 91a79c99..05fda4c3 100644 --- a/tests/integration/codexprocess.nim +++ b/tests/integration/codexprocess.nim @@ -33,6 +33,9 @@ method startedOutput(node: CodexProcess): string = method processOptions(node: CodexProcess): set[AsyncProcessOption] = return {AsyncProcessOption.StdErrToStdOut} +method outputLineEndings(node: CodexProcess): string = + return "\n" + method onOutputLineCaptured(node: CodexProcess, line: string) = discard diff --git a/tests/integration/hardhatprocess.nim b/tests/integration/hardhatprocess.nim index 67219902..e99276cd 100644 --- a/tests/integration/hardhatprocess.nim +++ b/tests/integration/hardhatprocess.nim @@ -4,12 +4,10 @@ import pkg/confutils import pkg/chronicles import pkg/chronos import pkg/stew/io2 -import std/osproc import std/os import std/sets -import std/streams +import std/sequtils import std/strutils -import std/sugar import pkg/codex/conf import pkg/codex/utils/trackedfutures import ./codexclient @@ -30,13 +28,16 @@ method workingDir(node: HardhatProcess): string = return currentSourcePath() / ".." / ".." / ".." / "vendor" / "codex-contracts-eth" method executable(node: HardhatProcess): string = - return "npm start" + return "node_modules" / ".bin" / "hardhat" method startedOutput(node: HardhatProcess): string = return "Started HTTP and WebSocket JSON-RPC server at" method processOptions(node: HardhatProcess): set[AsyncProcessOption] = - return {AsyncProcessOption.EvalCommand, AsyncProcessOption.StdErrToStdOut} + return {} + +method outputLineEndings(node: HardhatProcess): string = + return "\n" proc openLogFile(node: HardhatProcess, logFilePath: string): IoHandle = let logFileHandle = openFile( @@ -53,11 +54,31 @@ proc openLogFile(node: HardhatProcess, logFilePath: string): IoHandle = return fileHandle +method start*(node: HardhatProcess) {.async.} = + + let poptions = node.processOptions + {AsyncProcessOption.StdErrToStdOut} + trace "starting node", + args = node.arguments, + executable = node.executable, + workingDir = node.workingDir, + processOptions = poptions + + try: + node.process = await startProcess( + node.executable, + node.workingDir, + @["node", "--export", "deployment-localhost.json"].concat(node.arguments), + options = poptions, + stdoutHandle = AsyncProcess.Pipe + ) + except CatchableError as e: + error "failed to start node process", error = e.msg + proc startNode*( _: type HardhatProcess, - args: seq[string] = @[], + args: seq[string], debug: string | bool = false, - name: string = "hardhat" + name: string ): Future[HardhatProcess] {.async.} = var logFilePath = "" @@ -70,14 +91,20 @@ proc startNode*( arguments.add arg trace "starting hardhat node", arguments - echo ">>> starting hardhat node with args: ", arguments - let node = await NodeProcess.startNode(arguments, debug, "hardhat") - let hardhat = HardhatProcess(node) + ## Starts a Hardhat Node with the specified arguments. + ## Set debug to 'true' to see output of the node. + let hardhat = HardhatProcess( + arguments: arguments, + debug: ($debug != "false"), + trackedFutures: TrackedFutures.new(), + name: "hardhat" + ) + + await hardhat.start() if logFilePath != "": hardhat.logFile = some hardhat.openLogFile(logFilePath) - # let hardhat = HardhatProcess() return hardhat method onOutputLineCaptured(node: HardhatProcess, line: string) = @@ -91,9 +118,10 @@ method onOutputLineCaptured(node: HardhatProcess, line: string) = method stop*(node: HardhatProcess) {.async.} = # terminate the process - procCall NodeProcess(node).stop() + await procCall NodeProcess(node).stop() if logFile =? node.logFile: + trace "closing hardhat log file" discard logFile.closeFile() method removeDataDir*(node: HardhatProcess) = diff --git a/tests/integration/marketplacesuite.nim b/tests/integration/marketplacesuite.nim index d7cff91e..9d1d44b9 100644 --- a/tests/integration/marketplacesuite.nim +++ b/tests/integration/marketplacesuite.nim @@ -90,7 +90,6 @@ template marketplacesuite*(name: string, body: untyped) = discard setup: - echo "[marketplacesuite.setup] setup start" marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner()) let tokenAddress = await marketplace.token() token = Erc20Token.new(tokenAddress, ethProvider.getSigner()) diff --git a/tests/integration/multinodes.nim b/tests/integration/multinodes.nim index 8ca02a53..9f1e5117 100644 --- a/tests/integration/multinodes.nim +++ b/tests/integration/multinodes.nim @@ -4,13 +4,15 @@ import std/strutils import std/sugar import std/times import pkg/chronicles -import ../ethertest +import pkg/ethers +import pkg/asynctest import ./hardhatprocess import ./codexprocess import ./hardhatconfig import ./codexconfig -export ethertest +export asynctest +export ethers except `%` export hardhatprocess export codexprocess export hardhatconfig @@ -44,25 +46,28 @@ proc nextFreePort(startPort: int): Future[int] {.async.} = "lsof -ti:" var port = startPort while true: + trace "checking if port is free", port let portInUse = await execCommandEx(cmd & $port) if portInUse.stdOutput == "": - echo "port ", port, " is free" + trace "port is free", port return port else: inc port template multinodesuite*(name: string, body: untyped) = - ethersuite name: + asyncchecksuite name: var running: seq[RunningNode] var bootstrap: string let starttime = now().format("yyyy-MM-dd'_'HH:mm:ss") var currentTestName = "" var nodeConfigs: NodeConfigs + var ethProvider {.inject, used.}: JsonRpcProvider + var accounts {.inject, used.}: seq[Address] + var snapshot: JsonNode template test(tname, startNodeConfigs, tbody) = - echo "[multinodes] inside test template, tname: ", tname, ", startNodeConfigs: ", startNodeConfigs currentTestName = tname nodeConfigs = startNodeConfigs test tname: @@ -101,11 +106,11 @@ template multinodesuite*(name: string, body: untyped) = if config.logFile: let updatedLogFile = getLogFile(role, none int) args.add "--log-file=" & updatedLogFile - echo ">>> [multinodes] starting hardhat node with args: ", args + let node = await HardhatProcess.startNode(args, config.debugEnabled, "hardhat") await node.waitUntilStarted() - debug "started new hardhat node" + trace "hardhat node started" return node proc newCodexProcess(roleIdx: int, @@ -148,25 +153,30 @@ template multinodesuite*(name: string, body: untyped) = "--eth-account=" & $accounts[nodeIdx]]) let node = await CodexProcess.startNode(args, conf.debugEnabled, $role & $roleIdx) - echo "[multinodes.newCodexProcess] waiting until ", role, " node started" await node.waitUntilStarted() - echo "[multinodes.newCodexProcess] ", role, " NODE STARTED" + trace "node started", nodeName = $role & $roleIdx return node - proc clients(): seq[CodexProcess] {.used.} = + proc hardhat: HardhatProcess = + for r in running: + if r.role == Role.Hardhat: + return HardhatProcess(r.node) + return nil + + proc clients: seq[CodexProcess] {.used.} = return collect: for r in running: if r.role == Role.Client: CodexProcess(r.node) - proc providers(): seq[CodexProcess] {.used.} = + proc providers: seq[CodexProcess] {.used.} = return collect: for r in running: if r.role == Role.Provider: CodexProcess(r.node) - proc validators(): seq[CodexProcess] {.used.} = + proc validators: seq[CodexProcess] {.used.} = return collect: for r in running: if r.role == Role.Validator: @@ -204,35 +214,38 @@ template multinodesuite*(name: string, body: untyped) = return await newCodexProcess(validatorIdx, config, Role.Validator) setup: - echo "[multinodes.setup] setup start" if not nodeConfigs.hardhat.isNil: - echo "[multinodes.setup] starting hardhat node " let node = await startHardhatNode() running.add RunningNode(role: Role.Hardhat, node: node) + try: + ethProvider = JsonRpcProvider.new("ws://localhost:8545") + # if hardhat was NOT started by the test, take a snapshot so it can be + # reverted in the test teardown + if nodeConfigs.hardhat.isNil: + snapshot = await send(ethProvider, "evm_snapshot") + accounts = await ethProvider.listAccounts() + except CatchableError as e: + fatal "failed to connect to hardhat", error = e.msg + raiseAssert "Hardhat not running. Run hardhat manually before executing tests, or include a HardhatConfig in the test setup." + if not nodeConfigs.clients.isNil: for i in 0.. 0: + error "failed to exit process, check for zombies", exitCode + + trace "closing node process' streams" await node.process.closeWait() - except AsyncTimeoutError as e: - error "waiting for process exit timed out", error = e.msgDetail except CatchableError as e: error "error stopping node process", error = e.msg + finally: node.process = nil + trace "node stopped" proc waitUntilStarted*(node: NodeProcess) {.async.} = diff --git a/tests/integration/testIntegration.nim b/tests/integration/testIntegration.nim index 497c9dd1..2287fd59 100644 --- a/tests/integration/testIntegration.nim +++ b/tests/integration/testIntegration.nim @@ -20,7 +20,7 @@ import ./marketplacesuite # You can also pass a string in same format like for the `--log-level` parameter # to enable custom logging levels for specific topics like: debug2 = "INFO; TRACE: marketplace" -twonodessuite "Integration tests", debug1 = true, debug2 = true: +twonodessuite "Integration tests", debug1 = false, debug2 = false: setup: # Our Hardhat configuration does use automine, which means that time tracked by `ethProvider.currentTime()` is not # advanced until blocks are mined and that happens only when transaction is submitted. @@ -232,20 +232,20 @@ marketplacesuite "Marketplace payouts": test "expired request partially pays out for stored time", NodeConfigs( - # Uncomment to start Hardhat automatically, mainly so logs can be inspected locally + # Uncomment to start Hardhat automatically, typically so logs can be inspected locally # hardhat: HardhatConfig().withLogFile() clients: CodexConfig() .nodes(1) - .debug() # uncomment to enable console log output.debug() + # .debug() # uncomment to enable console log output.debug() .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("node", "erasure"), providers: CodexConfig() .nodes(1) - .debug() # uncomment to enable console log output + # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("marketplace", "sales", "reservations", "node", "proving", "clock"), ): diff --git a/tests/integration/testproofs.nim b/tests/integration/testproofs.nim index 0f1c88f5..db447c1d 100644 --- a/tests/integration/testproofs.nim +++ b/tests/integration/testproofs.nim @@ -16,20 +16,20 @@ logScope: marketplacesuite "Hosts submit regular proofs": test "hosts submit periodic proofs for slots they fill", NodeConfigs( - # Uncomment to start Hardhat automatically, mainly so logs can be inspected locally - # hardhat: HardhatConfig().debug().withLogFile(), + # Uncomment to start Hardhat automatically, typically so logs can be inspected locally + # hardhat: HardhatConfig().withLogFile(), clients: CodexConfig() .nodes(1) - .debug() # uncomment to enable console log output + # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("node"), providers: CodexConfig() .nodes(1) - .debug() # uncomment to enable console log output + # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("marketplace", "sales", "reservations", "node"), ): @@ -56,7 +56,6 @@ marketplacesuite "Hosts submit regular proofs": await subscription.unsubscribe() - marketplacesuite "Simulate invalid proofs": # TODO: these are very loose tests in that they are not testing EXACTLY how @@ -65,8 +64,8 @@ marketplacesuite "Simulate invalid proofs": # proofs are being marked as missed by the validator. test "slot is freed after too many invalid proofs submitted", NodeConfigs( - # Uncomment to start Hardhat automatically, mainly so logs can be inspected locally - # hardhat: HardhatConfig().debug().withLogFile(), + # Uncomment to start Hardhat automatically, typically so logs can be inspected locally + # hardhat: HardhatConfig().withLogFile(), clients: CodexConfig() @@ -117,8 +116,8 @@ marketplacesuite "Simulate invalid proofs": await subscription.unsubscribe() test "slot is not freed when not enough invalid proofs submitted", NodeConfigs( - # Uncomment to start Hardhat automatically, mainly so logs can be inspected locally - # hardhat: HardhatConfig().debug().withLogFile(), + # Uncomment to start Hardhat automatically, typically so logs can be inspected locally + # hardhat: HardhatConfig().withLogFile(), clients: CodexConfig() @@ -170,8 +169,8 @@ marketplacesuite "Simulate invalid proofs": await subscription.unsubscribe() test "host that submits invalid proofs is paid out less", NodeConfigs( - # Uncomment to start Hardhat automatically, mainly so logs can be inspected locally - # hardhat: HardhatConfig().debug().withLogFile(), + # Uncomment to start Hardhat automatically, typically so logs can be inspected locally + # hardhat: HardhatConfig().withLogFile(), clients: CodexConfig()