aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-10-12 01:34:59 +0700
committerShulhan <ms@kilabit.info>2023-10-12 22:14:51 +0700
commita0c9e5e87e951df4d1bc86c6ccc2ed73dbe7f942 (patch)
treed0650c477fff66fe87afb51233ce9c88e8c9fd79
parent207e7f016bf96ab037f510837940c037111cd42f (diff)
downloadawwan-a0c9e5e87e951df4d1bc86c6ccc2ed73dbe7f942.tar.xz
all: add integration tests for magic command "#get!"
-rw-r--r--awwan_sudo_test.go100
-rw-r--r--request.go13
-rw-r--r--session.go22
-rw-r--r--testdata/local/get.aww2
-rw-r--r--testdata/local/get.data15
5 files changed, 142 insertions, 10 deletions
diff --git a/awwan_sudo_test.go b/awwan_sudo_test.go
new file mode 100644
index 0000000..720726c
--- /dev/null
+++ b/awwan_sudo_test.go
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: 2023 M. Shulhan <ms@kilabit.info>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+//go:build integration
+
+package awwan
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/shuLhan/share/lib/test"
+ "github.com/shuLhan/share/lib/test/mock"
+)
+
+func TestAwwan_Local_SudoGet(t *testing.T) {
+ type testCase struct {
+ desc string
+ lineRange string
+ fileDest string
+ sudoPass string
+ expContent string
+ expError string
+ }
+
+ // Load the test data.
+ var (
+ baseDir = filepath.Join(`testdata`, `local`)
+ testdataFile = filepath.Join(baseDir, `get.data`)
+
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(testdataFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ mockrw = mock.ReadWriter{}
+
+ aww *Awwan
+ )
+
+ aww, err = New(baseDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Mock terminal to read passphrase for private key.
+ aww.cryptoc.termrw = &mockrw
+
+ var cases = []testCase{{
+ desc: `WithPlainFile`,
+ lineRange: `3`,
+ sudoPass: "awwan\r\n",
+ fileDest: filepath.Join(baseDir, `tmp`, `os-release`),
+ expContent: string(tdata.Output[`tmp/os-release`]),
+ }, {
+ desc: `WithInvalidPassword`,
+ lineRange: `3`,
+ sudoPass: "invalid\r\n",
+ expError: `Local: SudoCopy: ExecLocal: exit status 1`,
+ }}
+
+ var (
+ script = filepath.Join(baseDir, `get.aww`)
+ mockStdin = bytes.Buffer{}
+
+ c testCase
+ gotContent []byte
+ )
+
+ for _, c = range cases {
+ t.Log(c.desc)
+
+ var req = NewRequest(CommandModeLocal, script, c.lineRange)
+
+ // Mock the request stdin to read password from buffer.
+ mockStdin.Reset()
+ mockStdin.WriteString(c.sudoPass)
+ req.stdin = &mockStdin
+
+ err = aww.Local(req)
+ if err != nil {
+ test.Assert(t, `Local: error`, c.expError, err.Error())
+ continue
+ }
+
+ gotContent, err = os.ReadFile(c.fileDest)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, `content`, c.expContent, string(gotContent))
+ }
+}
diff --git a/request.go b/request.go
index 52cda4e..2f24729 100644
--- a/request.go
+++ b/request.go
@@ -12,11 +12,14 @@ import (
// Each request define the Mode of execution, Script file to be executed,
// and the lineRange -- list of line numbers to be executed.
type Request struct {
- // Each request may set the Writer where the command output and
- // error will be written.
- // If its nil, it will default to os.Stdout and os.Stderr.
- stdout io.Writer
- stderr io.Writer
+ // Each request may set the Writer where the command read input from
+ // or output and error will be written.
+ // If its nil, it will default to os.DevNull (default os
+ // [exec/Cmd]), os.Stdout, and os.Stderr, respectively.
+ stdin io.Reader
+ stdout io.Writer
+ stderr io.Writer
+
scriptPath string // The actual or cleaned up path of the Script.
script *Script
diff --git a/session.go b/session.go
index 86d4fae..760b362 100644
--- a/session.go
+++ b/session.go
@@ -214,9 +214,16 @@ func (ses *Session) SudoCopy(req *Request, stmt *Statement) (err error) {
sudoCp = &Statement{
kind: statementKindDefault,
- cmd: "sudo",
- args: []string{"cp", src, dst},
- raw: []byte(`sudo cp "` + src + `" "` + dst + `"`),
+ }
+
+ // Detect which stdin we use.
+ // If its non-nil, use "sudo -S" to read password from stdin instead
+ // of from terminal.
+ // This will allow us to test sudo behaviour.
+ if req.stdin == nil {
+ sudoCp.raw = []byte(`sudo cp "` + src + `" "` + dst + `"`)
+ } else {
+ sudoCp.raw = []byte(`sudo -S cp "` + src + `" "` + dst + `"`)
}
err = ses.ExecLocal(req, sudoCp)
@@ -280,9 +287,16 @@ func (ses *Session) ExecLocal(req *Request, stmt *Statement) (err error) {
args = string(stmt.raw)
cmd = exec.Command(`/bin/sh`, `-c`, args)
)
+
+ cmd.Stdin = req.stdin
cmd.Stdout = req.stdout
cmd.Stderr = req.stderr
- return cmd.Run()
+
+ err = cmd.Run()
+ if err != nil {
+ return fmt.Errorf(`ExecLocal: %w`, err)
+ }
+ return nil
}
// executeRequires run the "#require:" statements from line 0 until
diff --git a/testdata/local/get.aww b/testdata/local/get.aww
index 15878c6..d889f22 100644
--- a/testdata/local/get.aww
+++ b/testdata/local/get.aww
@@ -1 +1,3 @@
#get: {{.ScriptDir}}/plain.txt {{.ScriptDir}}/tmp/get_plain.txt
+
+#get! /etc/os-release {{.ScriptDir}}/tmp/os-release
diff --git a/testdata/local/get.data b/testdata/local/get.data
index 0e7ee57..c0e24ec 100644
--- a/testdata/local/get.data
+++ b/testdata/local/get.data
@@ -1,5 +1,18 @@
-Test on magic command "#get:".
+Test output from magic command "#get:" and "#get!"
<<< tmp/get_plain.txt
The host name is {{.Val "host::name"}}.
+
+<<< tmp/os-release
+NAME="Arch Linux"
+PRETTY_NAME="Arch Linux"
+ID=arch
+BUILD_ID=rolling
+ANSI_COLOR="38;2;23;147;209"
+HOME_URL="https://archlinux.org/"
+DOCUMENTATION_URL="https://wiki.archlinux.org/"
+SUPPORT_URL="https://bbs.archlinux.org/"
+BUG_REPORT_URL="https://bugs.archlinux.org/"
+PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/"
+LOGO=archlinux-logo