mirror of
https://github.com/guggero/chantools.git
synced 2026-08-13 12:33:34 +02:00
itest: add itest framework and basic tests
This commit is contained in:
parent
f794333c0b
commit
d7e03409d5
4 changed files with 233 additions and 7 deletions
149
itest/chantools_harness.go
Normal file
149
itest/chantools_harness.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package itest
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ChantoolsProcess wraps a running chantools process for integration testing.
|
||||
type ChantoolsProcess struct {
|
||||
cmd *exec.Cmd
|
||||
stdin io.WriteCloser
|
||||
stdout *os.File
|
||||
stdoutReader *bufio.Reader
|
||||
stderr *bufio.Reader
|
||||
}
|
||||
|
||||
// StartChantools starts the chantools binary with the given arguments.
|
||||
func StartChantools(t *testing.T, args ...string) *ChantoolsProcess {
|
||||
t.Helper()
|
||||
|
||||
args = append([]string{"--nologfile"}, args...)
|
||||
cmd := exec.Command("chantools", args...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
stderrPipe, err := cmd.StderrPipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
stdoutFile, ok := stdoutPipe.(*os.File)
|
||||
require.True(t, ok)
|
||||
|
||||
require.NoError(t, cmd.Start())
|
||||
|
||||
return &ChantoolsProcess{
|
||||
cmd: cmd,
|
||||
stdin: stdin,
|
||||
stdout: stdoutFile,
|
||||
stdoutReader: bufio.NewReader(stdoutFile),
|
||||
stderr: bufio.NewReader(stderrPipe),
|
||||
}
|
||||
}
|
||||
|
||||
// WriteInput writes input to the process's stdin.
|
||||
func (p *ChantoolsProcess) WriteInput(t *testing.T, input string) {
|
||||
t.Helper()
|
||||
|
||||
_, err := io.WriteString(p.stdin, input)
|
||||
require.NoError(t, err, "failed to write input to chantools")
|
||||
}
|
||||
|
||||
// ReadAllOutput reads all output from the process's stdout until EOF.
|
||||
func (p *ChantoolsProcess) ReadAllOutput(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
resp, err := io.ReadAll(p.stdout)
|
||||
require.NoError(t, err, "failed to read chantools output")
|
||||
|
||||
log.Debugf("[CHANTOOLS]: %s", resp)
|
||||
|
||||
return string(resp)
|
||||
}
|
||||
|
||||
// ReadOutputUntil reads from stdout until the given substring is found or
|
||||
// timeout.
|
||||
func (p *ChantoolsProcess) ReadOutputUntil(t *testing.T, substr string,
|
||||
timeout time.Duration) string {
|
||||
|
||||
t.Helper()
|
||||
|
||||
var out bytes.Buffer
|
||||
deadline := time.Now().Add(timeout)
|
||||
for {
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatal("timeout waiting for chantools output")
|
||||
}
|
||||
line, err := p.stdoutReader.ReadString('\n')
|
||||
out.WriteString(line)
|
||||
|
||||
log.Debugf("[CHANTOOLS]: %s", line)
|
||||
|
||||
if strings.Contains(out.String(), substr) {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadAvailableOutput reads as many bytes as possible from stdout until the
|
||||
// timeout elapses.
|
||||
func (p *ChantoolsProcess) ReadAvailableOutput(t *testing.T,
|
||||
timeout time.Duration) string {
|
||||
|
||||
t.Helper()
|
||||
|
||||
err := p.stdout.SetReadDeadline(time.Now().Add(timeout))
|
||||
require.NoError(t, err, "failed to set read deadline")
|
||||
|
||||
defer func() {
|
||||
_ = p.stdout.SetReadDeadline(time.Time{})
|
||||
}()
|
||||
|
||||
var out bytes.Buffer
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := p.stdoutReader.Read(buf)
|
||||
if n > 0 {
|
||||
chunk := string(buf[:n])
|
||||
out.WriteString(chunk)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) ||
|
||||
errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("[CHANTOOLS]: %s", out.String())
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Wait waits for the process to exit.
|
||||
func (p *ChantoolsProcess) Wait(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
require.NoError(t, p.cmd.Wait())
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
func (p *ChantoolsProcess) Kill(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
require.NoError(t, p.cmd.Process.Kill())
|
||||
}
|
||||
16
itest/log.go
Normal file
16
itest/log.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package itest
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/btcsuite/btclog/v2"
|
||||
)
|
||||
|
||||
var log btclog.Logger
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
logger := btclog.NewSLogger(btclog.NewDefaultHandler(os.Stdout))
|
||||
logger.SetLevel(btclog.LevelTrace)
|
||||
log = logger.SubSystem("ITEST")
|
||||
}
|
||||
57
itest/standalone_test.go
Normal file
57
itest/standalone_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package itest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testXPriv = "xprv9s21ZrQH143K2ZzuN99NjD7oJruBomAVbNDXzPmhHYcwf8WXCsML" +
|
||||
"63azyS1rzzfpUsLifeDkM4Q6U9PF9RP7frSGKkMfDTDiiyQjH2PUj2z"
|
||||
|
||||
testMnemonic = "about wolf boost other battle asthma refuse wedding " +
|
||||
"few purchase track one smooth tunnel immune glass infant " +
|
||||
"tag manual multiply diagram orient wrist agent"
|
||||
)
|
||||
|
||||
var (
|
||||
readTimeout = 100 * time.Millisecond
|
||||
defaultTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
func TestChantoolsShowRootKeyXPriv(t *testing.T) {
|
||||
proc := StartChantools(t, "showrootkey", "--rootkey", testXPriv)
|
||||
defer proc.Kill(t)
|
||||
|
||||
output := proc.ReadOutputUntil(
|
||||
t, "Your BIP32 HD root key is:", defaultTimeout,
|
||||
)
|
||||
require.Contains(t, output, "Your BIP32 HD root key is: "+testXPriv)
|
||||
}
|
||||
|
||||
func TestChantoolsShowRootKeyMnemonic(t *testing.T) {
|
||||
proc := StartChantools(t, "showrootkey")
|
||||
defer proc.Kill(t)
|
||||
|
||||
go func() {
|
||||
errString, err := proc.stderr.ReadString('\n')
|
||||
log.Errorf("chantools stderr: %v, error: %v", errString, err)
|
||||
}()
|
||||
|
||||
mnemonicPrompt := proc.ReadAvailableOutput(t, readTimeout)
|
||||
require.Contains(t, mnemonicPrompt, "Input your 24-word mnemonic")
|
||||
proc.WriteInput(t, testMnemonic+"\n")
|
||||
|
||||
passphrasePrompt := proc.ReadAvailableOutput(t, readTimeout)
|
||||
require.Contains(
|
||||
t, passphrasePrompt, "Input your cipher seed passphrase",
|
||||
)
|
||||
proc.WriteInput(t, "\n")
|
||||
|
||||
output := proc.ReadOutputUntil(
|
||||
t, "Your BIP32 HD root key is:", defaultTimeout,
|
||||
)
|
||||
require.Contains(t, output, "Your BIP32 HD root key is: "+testXPriv)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package lnd
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -139,16 +140,15 @@ func ReadPassphrase(verb string) ([]byte, error) {
|
|||
// The environment variable didn't contain anything, we'll read the
|
||||
// passphrase from the terminal.
|
||||
case passphrase == "":
|
||||
fmt.Printf("Input your cipher seed passphrase (press enter "+
|
||||
"if your seed %s a passphrase): ", verb)
|
||||
var err error
|
||||
passphraseBytes, err = terminal.ReadPassword(
|
||||
int(syscall.Stdin), //nolint
|
||||
passphraseBytes, err = PasswordFromConsole(
|
||||
fmt.Sprintf("Input your cipher seed passphrase "+
|
||||
"(press enter if your seed %s a passphrase): ",
|
||||
verb),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// There was a password in the environment, just convert it to bytes.
|
||||
default:
|
||||
|
|
@ -160,13 +160,15 @@ func ReadPassphrase(verb string) ([]byte, error) {
|
|||
|
||||
// PasswordFromConsole reads a password from the console or stdin.
|
||||
func PasswordFromConsole(userQuery string) ([]byte, error) {
|
||||
fmt.Print(userQuery)
|
||||
|
||||
// Read from terminal (if there is one).
|
||||
if terminal.IsTerminal(int(syscall.Stdin)) { //nolint
|
||||
fmt.Print(userQuery)
|
||||
pw, err := terminal.ReadPassword(int(syscall.Stdin)) //nolint
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
return pw, nil
|
||||
}
|
||||
|
|
@ -177,7 +179,9 @@ func PasswordFromConsole(userQuery string) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pw, nil
|
||||
|
||||
fmt.Println()
|
||||
return bytes.TrimSpace(pw), nil
|
||||
}
|
||||
|
||||
// OpenWallet opens a lnd compatible wallet and returns it, along with the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue