196 lines
5.7 KiB
Go
196 lines
5.7 KiB
Go
package agent
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestParseStatLine(t *testing.T) {
|
|
// statCommand prints: type|size|mtime|mode|name
|
|
e, ok := parseStatLine("directory|4096|1760000000|drwxr-xr-x|my dir")
|
|
if !ok || !e.IsDir || e.Name != "my dir" || e.MTime != 1760000000 || e.Mode != "drwxr-xr-x" {
|
|
t.Fatalf("dir: %+v %v", e, ok)
|
|
}
|
|
e, ok = parseStatLine("regular file|12|1760000001|-rw-r--r--|a|b.txt")
|
|
if !ok || e.IsDir || e.Size != 12 || e.Name != "a|b.txt" {
|
|
t.Fatalf("file with | in name: %+v %v", e, ok)
|
|
}
|
|
if _, ok := parseStatLine("garbage"); ok {
|
|
t.Fatal("garbage parsed")
|
|
}
|
|
}
|
|
|
|
func TestStatCommandQuotes(t *testing.T) {
|
|
for _, fl := range []string{"gnu", "bsd"} {
|
|
c := statCommand(fl, "/srv/it's here")
|
|
if !strings.Contains(c, `'/srv/it'\''s here/'`) {
|
|
t.Errorf("%s: dir not quoted: %s", fl, c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseLsLine(t *testing.T) {
|
|
e, ok := parseLsLine("-rw-r--r-- 1 root root 1234 Oct 15 10:00 my file.txt")
|
|
if !ok || e.Name != "my file.txt" || e.Size != 1234 || e.IsDir {
|
|
t.Fatalf("file: %+v %v", e, ok)
|
|
}
|
|
e, ok = parseLsLine("lrwxrwxrwx 1 root root 7 Oct 15 10:00 link -> target")
|
|
if !ok || e.Name != "link" {
|
|
t.Fatalf("link: %+v %v", e, ok)
|
|
}
|
|
if _, ok := parseLsLine("drwxr-xr-x 2 root root 4096 Oct 15 10:00 .."); ok {
|
|
t.Fatal(".. listed")
|
|
}
|
|
if _, ok := parseLsLine("total 8"); ok {
|
|
t.Fatal("total parsed")
|
|
}
|
|
}
|
|
|
|
func TestRenameTarEntries(t *testing.T) {
|
|
var src bytes.Buffer
|
|
tw := tar.NewWriter(&src)
|
|
for _, n := range []string{"old/", "old/a.txt", "old/sub/b"} {
|
|
typ := byte(tar.TypeReg)
|
|
if strings.HasSuffix(n, "/") {
|
|
typ = tar.TypeDir
|
|
}
|
|
_ = tw.WriteHeader(&tar.Header{Name: n, Mode: 0o644, Typeflag: typ})
|
|
}
|
|
_ = tw.Close()
|
|
var out bytes.Buffer
|
|
if err := renameTar(&out, &src, map[string]string{"old": "new"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tr := tar.NewReader(&out)
|
|
var names []string
|
|
for {
|
|
h, err := tr.Next()
|
|
if err != nil {
|
|
break
|
|
}
|
|
names = append(names, h.Name)
|
|
}
|
|
if strings.Join(names, ",") != "new/,new/a.txt,new/sub/b" {
|
|
t.Fatalf("names: %v", names)
|
|
}
|
|
}
|
|
|
|
// A macOS host's tar packs "._name" companions with the file's attributes;
|
|
// they are not the file and must not land under its name.
|
|
func TestRenameTarDropsOtherEntries(t *testing.T) {
|
|
var src bytes.Buffer
|
|
tw := tar.NewWriter(&src)
|
|
for _, e := range []struct{ n, body string }{{"./._r.txt", "attrs"}, {"./r.txt", "data"}} {
|
|
_ = tw.WriteHeader(&tar.Header{Name: e.n, Mode: 0o644, Typeflag: tar.TypeReg, Size: int64(len(e.body))})
|
|
_, _ = tw.Write([]byte(e.body))
|
|
}
|
|
_ = tw.Close()
|
|
var out bytes.Buffer
|
|
if err := renameTar(&out, &src, map[string]string{"r.txt": "new.txt"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tr := tar.NewReader(&out)
|
|
var got []string
|
|
for {
|
|
h, err := tr.Next()
|
|
if err != nil {
|
|
break
|
|
}
|
|
b, _ := io.ReadAll(tr)
|
|
got = append(got, h.Name+"="+string(b))
|
|
}
|
|
if strings.Join(got, ",") != "new.txt=data" {
|
|
t.Fatalf("entries: %v", got)
|
|
}
|
|
}
|
|
|
|
// zip on an SSH host: from the items' folder, by their own names, quoted.
|
|
func TestZipCommand(t *testing.T) {
|
|
cmd, err := zipCommand([]string{"/srv/a b", "/srv/it's"}, "/tmp/x.zip")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := `cd -- '/srv' && if command -v zip >/dev/null 2>&1; then zip -r -q -y '/tmp/x.zip' './a b' './it'\''s'; ` +
|
|
`else python3 -m zipfile -c '/tmp/x.zip' './a b' './it'\''s'; fi`
|
|
if cmd != want {
|
|
t.Fatalf("got %s\nwant %s", cmd, want)
|
|
}
|
|
if _, err := zipCommand([]string{"/a/x", "/b/y"}, "/t.zip"); err == nil {
|
|
t.Fatal("items of two folders: want an error")
|
|
}
|
|
if _, err := zipCommand(nil, "/t.zip"); err == nil {
|
|
t.Fatal("no items: want an error")
|
|
}
|
|
if got := unzipCommand("/srv/a.zip", ""); got != `if command -v unzip >/dev/null 2>&1; then unzip -o -q '/srv/a.zip' -d '/srv'; else python3 -m zipfile -e '/srv/a.zip' '/srv'; fi` {
|
|
t.Fatalf("unzip: %s", got)
|
|
}
|
|
}
|
|
|
|
// A connection busy with a long transfer is not idle, however long ago it
|
|
// was opened; once the transfer ends the idle clock starts again.
|
|
func TestSweepSparesBusyConn(t *testing.T) {
|
|
c := &sshfsConn{last: time.Now().Add(-2 * sshfsIdle)}
|
|
release := c.hold()
|
|
if c.idle(time.Now()) {
|
|
t.Fatal("a busy connection was taken for idle")
|
|
}
|
|
release()
|
|
if c.idle(time.Now()) {
|
|
t.Fatal("just released: not idle yet")
|
|
}
|
|
if !c.idle(time.Now().Add(2 * sshfsIdle)) {
|
|
t.Fatal("unused for long: idle")
|
|
}
|
|
}
|
|
|
|
// A file's size is its target's when it is a link: what cat will read.
|
|
func TestSizeCommandFollowsLinks(t *testing.T) {
|
|
for _, f := range []string{"gnu", "bsd"} {
|
|
if cmd := sizeCommand(f, "/x"); !strings.Contains(cmd, "stat -L ") {
|
|
t.Fatalf("%s: %s", f, cmd)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nothing of a tar stream lands outside name: other entries and ".." paths
|
|
// are dropped.
|
|
func TestRenameTarKeepsInsideName(t *testing.T) {
|
|
var src bytes.Buffer
|
|
tw := tar.NewWriter(&src)
|
|
for _, n := range []string{"d/a", "d/../../etc/x", ".ssh/authorized_keys", "d/b"} {
|
|
_ = tw.WriteHeader(&tar.Header{Name: n, Mode: 0o644, Typeflag: tar.TypeReg})
|
|
}
|
|
_ = tw.Close()
|
|
var out bytes.Buffer
|
|
if err := renameTar(&out, &src, map[string]string{"d": "d"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tr := tar.NewReader(&out)
|
|
var got []string
|
|
for {
|
|
h, err := tr.Next()
|
|
if err != nil {
|
|
break
|
|
}
|
|
got = append(got, h.Name)
|
|
}
|
|
if strings.Join(got, ",") != "d/a,d/b" {
|
|
t.Fatalf("entries: %v", got)
|
|
}
|
|
}
|
|
|
|
// Into an SSH host, "replace" replaces the thing whole, as on a machine.
|
|
func TestUnpackCommandReplaces(t *testing.T) {
|
|
cmd := unpackCommand("/srv", []string{"d"}, "replace")
|
|
if !strings.HasPrefix(cmd, "rm -rf -- '/srv/d' && ") || strings.Contains(cmd, "--overwrite") {
|
|
t.Fatalf("replace: %s", cmd)
|
|
}
|
|
if cmd := unpackCommand("/srv", []string{"d"}, "error"); cmd != "mkdir -p -- '/srv' && cd -- '/srv' && tar -xf -" {
|
|
t.Fatalf("error: %s", cmd)
|
|
}
|
|
}
|