]> ruderich.org/simon Gitweb - safcm/safcm.git/blobdiff - cmd/safcm-remote/sync/files_test.go
Move implementation of cmd/safcm-remote/ to remote/
[safcm/safcm.git] / cmd / safcm-remote / sync / files_test.go
diff --git a/cmd/safcm-remote/sync/files_test.go b/cmd/safcm-remote/sync/files_test.go
deleted file mode 100644 (file)
index 88a5b51..0000000
+++ /dev/null
@@ -1,2428 +0,0 @@
-// Copyright (C) 2021  Simon Ruderich
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-package sync
-
-import (
-       "fmt"
-       "io/fs"
-       "math/rand"
-       "os"
-       "path/filepath"
-       "regexp"
-       "testing"
-
-       "ruderich.org/simon/safcm"
-       ft "ruderich.org/simon/safcm/cmd/safcm-remote/sync/filetest"
-       "ruderich.org/simon/safcm/testutil"
-)
-
-var randFilesRegexp = regexp.MustCompile(`\d+"$`)
-
-func TestSyncFiles(t *testing.T) {
-       cwd, err := os.Getwd()
-       if err != nil {
-               t.Fatal(err)
-       }
-       defer os.Chdir(cwd)
-
-       err = os.RemoveAll("testdata")
-       if err != nil {
-               t.Fatal(err)
-       }
-       err = os.Mkdir("testdata", 0700)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       root := ft.File{
-               Path: ".",
-               Mode: fs.ModeDir | 0700,
-       }
-       user, uid, group, gid := ft.CurrentUserAndGroup()
-
-       skipUnlessCiRun := len(os.Getenv("SAFCM_CI_RUN")) == 0
-
-       tmpTestFilePath := "/tmp/safcm-sync-files-test-file"
-
-       tests := []struct {
-               name        string
-               skip        bool
-               req         safcm.MsgSyncReq
-               prepare     func()
-               expTriggers []string
-               expFiles    []ft.File
-               expResp     safcm.MsgSyncResp
-               expDbg      []string
-               expErr      error
-       }{
-
-               // NOTE: Also update MsgSyncResp in safcm test cases when
-               // changing the MsgSyncResp struct!
-
-               // See TestSyncFile() for most file related tests. This
-               // function only tests the overall results and triggers.
-
-               {
-                       "basic: create",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                       },
-                               },
-                       },
-                       nil,
-                       nil,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "dir",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0755,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                                       {
-                                               Path:    "dir/file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): will create`,
-                               `3: files: "dir" (group): creating`,
-                               `4: files: "dir" (group): creating directory`,
-                               `4: files: "dir" (group): chmodding drwxr-xr-x`,
-                               fmt.Sprintf(`4: files: "dir" (group): chowning %d/%d`, uid, gid),
-                               `4: files: "dir/file" (group): will create`,
-                               `3: files: "dir/file" (group): creating`,
-                               `4: files: "dir/file" (group): creating temporary file "dir/.file*"`,
-                               `4: files: "dir/file" (group): renaming "dir/.fileRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "basic: no change",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                       },
-                               },
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0755)
-                               ft.CreateFile("dir/file", "content\n", 0644)
-                       },
-                       nil,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): unchanged`,
-                               `4: files: "dir/file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "invalid File: user",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               User:      "user",
-                                               Uid:       1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                       },
-                               },
-                       },
-                       nil,
-                       nil,
-                       []ft.File{
-                               root,
-                       },
-                       safcm.MsgSyncResp{},
-                       nil,
-                       fmt.Errorf("\".\": cannot set both User (\"user\") and Uid (1)"),
-               },
-               {
-                       "invalid File: group",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Group:     "group",
-                                               Gid:       1,
-                                               OrigGroup: "group",
-                                       },
-                               },
-                       },
-                       nil,
-                       nil,
-                       []ft.File{
-                               root,
-                       },
-                       safcm.MsgSyncResp{},
-                       nil,
-                       fmt.Errorf("\".\": cannot set both Group (\"group\") and Gid (1)"),
-               },
-
-               {
-                       // We use relative paths for most tests because we
-                       // don't want to modify the running system. Use this
-                       // test (and the one below for triggers) as a basic
-                       // check that absolute paths work.
-                       //
-                       // Use numeric IDs as not all systems use root/root;
-                       // for example BSDs use root/wheel.
-                       "absolute paths: no change",
-                       skipUnlessCiRun,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       "/": {
-                                               Path:      "/",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       0,
-                                               Gid:       0,
-                                               OrigGroup: "group",
-                                       },
-                                       "/etc": {
-                                               Path:      "/etc",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       0,
-                                               Gid:       0,
-                                               OrigGroup: "group",
-                                       },
-                                       "/tmp": {
-                                               Path:      "/tmp",
-                                               Mode:      fs.ModeDir | 0777 | fs.ModeSticky,
-                                               Uid:       0,
-                                               Gid:       0,
-                                               OrigGroup: "group",
-                                       },
-                               },
-                       },
-                       nil,
-                       nil,
-                       []ft.File{
-                               root,
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "/" (group): unchanged`,
-                               `4: files: "/etc" (group): unchanged`,
-                               `4: files: "/tmp" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: no change",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger .",
-                                               },
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir",
-                                               },
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir/file",
-                                               },
-                                       },
-                               },
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0755)
-                               ft.CreateFile("dir/file", "content\n", 0644)
-                       },
-                       nil,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): unchanged`,
-                               `4: files: "dir/file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: change root",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger .",
-                                               },
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir",
-                                               },
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir/file",
-                                               },
-                                       },
-                               },
-                       },
-                       func() {
-                               ft.CreateDirectoryExists(".", 0750)
-                               ft.CreateDirectory("dir", 0755)
-                               ft.CreateFile("dir/file", "content\n", 0644)
-                       },
-                       []string{
-                               ".",
-                       },
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: ".",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0750,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0700,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "." (group): permission differs drwxr-x--- -> drwx------`,
-                               `3: files: "." (group): updating`,
-                               `4: files: "." (group): chmodding drwx------`,
-                               `3: files: ".": queuing trigger on "."`,
-                               `4: files: "dir" (group): unchanged`,
-                               `4: files: "dir/file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: change middle",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger .",
-                                               },
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir",
-                                               },
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir/file",
-                                               },
-                                       },
-                               },
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0750)
-                               ft.CreateFile("dir/file", "content\n", 0644)
-                       },
-                       []string{
-                               ".",
-                               "dir",
-                       },
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "dir",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0750,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0755,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): permission differs drwxr-x--- -> drwxr-xr-x`,
-                               `3: files: "dir" (group): updating`,
-                               `4: files: "dir" (group): chmodding drwxr-xr-x`,
-                               `3: files: "dir": queuing trigger on "."`,
-                               `3: files: "dir": queuing trigger on "dir"`,
-                               `4: files: "dir/file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: change leaf",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger .",
-                                               },
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir",
-                                               },
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir/file",
-                                               },
-                                       },
-                               },
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0755)
-                       },
-                       []string{
-                               ".",
-                               "dir",
-                               "dir/file",
-                       },
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "dir/file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): unchanged`,
-                               `4: files: "dir/file" (group): will create`,
-                               `3: files: "dir/file" (group): creating`,
-                               `4: files: "dir/file" (group): creating temporary file "dir/.file*"`,
-                               `4: files: "dir/file" (group): renaming "dir/.fileRND"`,
-                               `3: files: "dir/file": queuing trigger on "."`,
-                               `3: files: "dir/file": queuing trigger on "dir"`,
-                               `3: files: "dir/file": queuing trigger on "dir/file"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: multiple changes",
-                       false,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       ".": {
-                                               Path:      ".",
-                                               Mode:      fs.ModeDir | 0700,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger .",
-                                               },
-                                       },
-                                       "dir": {
-                                               Path:      "dir",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir",
-                                               },
-                                       },
-                                       "dir/file": {
-                                               Path:      "dir/file",
-                                               Mode:      0644,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               Data:      []byte("content\n"),
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger dir/file",
-                                               },
-                                       },
-                               },
-                       },
-                       nil,
-                       []string{
-                               ".",
-                               "dir",
-                               "dir/file",
-                       },
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                               {
-                                       Path: "dir/file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "dir",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0755,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                                       {
-                                               Path:    "dir/file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "." (group): unchanged`,
-                               `4: files: "dir" (group): will create`,
-                               `3: files: "dir" (group): creating`,
-                               `4: files: "dir" (group): creating directory`,
-                               `4: files: "dir" (group): chmodding drwxr-xr-x`,
-                               fmt.Sprintf(`4: files: "dir" (group): chowning %d/%d`, uid, gid),
-                               `3: files: "dir": queuing trigger on "."`,
-                               `3: files: "dir": queuing trigger on "dir"`,
-                               `4: files: "dir/file" (group): will create`,
-                               `3: files: "dir/file" (group): creating`,
-                               `4: files: "dir/file" (group): creating temporary file "dir/.file*"`,
-                               `4: files: "dir/file" (group): renaming "dir/.fileRND"`,
-                               `4: files: "dir/file": skipping trigger on ".", already active`,
-                               `4: files: "dir/file": skipping trigger on "dir", already active`,
-                               `3: files: "dir/file": queuing trigger on "dir/file"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "triggers: absolute paths",
-                       skipUnlessCiRun,
-                       safcm.MsgSyncReq{
-                               Files: map[string]*safcm.File{
-                                       "/": {
-                                               Path:      "/",
-                                               Mode:      fs.ModeDir | 0755,
-                                               Uid:       0,
-                                               Gid:       0,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger /",
-                                               },
-                                       },
-                                       "/tmp": {
-                                               Path:      "/tmp",
-                                               Mode:      fs.ModeDir | 0777 | fs.ModeSticky,
-                                               Uid:       0,
-                                               Gid:       0,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger /tmp",
-                                               },
-                                       },
-                                       tmpTestFilePath: {
-                                               Path:      tmpTestFilePath,
-                                               Mode:      0600,
-                                               Uid:       -1,
-                                               Gid:       -1,
-                                               OrigGroup: "group",
-                                               TriggerCommands: []string{
-                                                       "echo trigger /tmp/file",
-                                               },
-                                       },
-                               },
-                       },
-                       nil,
-                       []string{
-                               "/",
-                               "/tmp",
-                               // Don't use variable for more robust test
-                               "/tmp/safcm-sync-files-test-file",
-                       },
-                       []ft.File{
-                               root,
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "/tmp/safcm-sync-files-test-file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0600,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "/" (group): unchanged`,
-                               `4: files: "/tmp" (group): unchanged`,
-                               `4: files: "/tmp/safcm-sync-files-test-file" (group): will create`,
-                               `3: files: "/tmp/safcm-sync-files-test-file" (group): creating`,
-                               `4: files: "/tmp/safcm-sync-files-test-file" (group): creating temporary file "/tmp/.safcm-sync-files-test-file*"`,
-                               `4: files: "/tmp/safcm-sync-files-test-file" (group): renaming "/tmp/.safcm-sync-files-test-fileRND"`,
-                               `3: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/"`,
-                               `3: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/tmp"`,
-                               `3: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/tmp/safcm-sync-files-test-file"`,
-                       },
-                       nil,
-               },
-       }
-
-       for _, tc := range tests {
-               t.Run(tc.name, func(t *testing.T) {
-                       if tc.skip {
-                               t.SkipNow()
-                       }
-
-                       // Create separate test directory for each test case
-                       path := filepath.Join(cwd, "testdata", "files-"+tc.name)
-                       err := os.Mkdir(path, 0700)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       err = os.Chdir(path)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-
-                       if tc.prepare != nil {
-                               tc.prepare()
-                       }
-
-                       s, res := prepareSync(tc.req, &testRunner{
-                               t: t,
-                       })
-                       err = s.setDefaults()
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-
-                       err = s.syncFiles()
-                       testutil.AssertErrorEqual(t, "err", err, tc.expErr)
-                       dbg := res.Wait()
-                       // Remove random file names from result
-                       for i, x := range dbg {
-                               dbg[i] = randFilesRegexp.ReplaceAllString(x, `RND"`)
-                       }
-                       testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
-
-                       files, err := ft.WalkDir(path)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       testutil.AssertEqual(t, "files", files, tc.expFiles)
-
-                       testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
-                       testutil.AssertEqual(t, "triggers",
-                               s.triggers, tc.expTriggers)
-               })
-       }
-
-       os.Remove(tmpTestFilePath)
-       if !t.Failed() {
-               err = os.RemoveAll(filepath.Join(cwd, "testdata"))
-               if err != nil {
-                       t.Fatal(err)
-               }
-       }
-}
-
-func TestSyncFile(t *testing.T) {
-       cwd, err := os.Getwd()
-       if err != nil {
-               t.Fatal(err)
-       }
-       defer os.Chdir(cwd)
-
-       err = os.RemoveAll("testdata")
-       if err != nil {
-               t.Fatal(err)
-       }
-       err = os.Mkdir("testdata", 0700)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       root := ft.File{
-               Path: ".",
-               Mode: fs.ModeDir | 0700,
-       }
-       user, uid, group, gid := ft.CurrentUserAndGroup()
-
-       tests := []struct {
-               name       string
-               req        safcm.MsgSyncReq
-               file       *safcm.File
-               prepare    func()
-               expChanged bool
-               expFiles   []ft.File
-               expResp    safcm.MsgSyncResp
-               expDbg     []string
-               expErr     error
-       }{
-
-               // NOTE: Also update MsgSyncResp in safcm test cases when
-               // changing the MsgSyncResp struct!
-
-               // TODO: Add tests for chown and run them only as root
-
-               // Regular file
-
-               {
-                       "file: create",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): will create`,
-                               `3: files: "file" (group): creating`,
-                               `4: files: "file" (group): creating temporary file ".file*"`,
-                               `4: files: "file" (group): renaming "./.fileRND"`,
-                       },
-                       nil,
-               },
-               {
-                       "file: create (dry-run)",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{root},
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "file",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): will create`,
-                               `3: files: "file" (group): creating`,
-                               `4: files: "file" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "file: unchanged",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "content\n", 0644)
-                       },
-                       false,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "file: unchanged (non-default user-group)",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               User:      user,
-                               Uid:       -1,
-                               Group:     group,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "content\n", 0644)
-                       },
-                       false,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "file" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "file: permission",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0755 | fs.ModeSetuid,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "content\n", 0755)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0755 | fs.ModeSetuid,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0755,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0755 | fs.ModeSetuid,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): permission differs -rwxr-xr-x -> urwxr-xr-x`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): creating temporary file ".file*"`,
-                               `4: files: "file" (group): renaming "./.fileRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "file: content",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "old content\n", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,2 +1,2 @@
--old content
-+content
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): content differs`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): creating temporary file ".file*"`,
-                               `4: files: "file" (group): renaming "./.fileRND"`,
-                       },
-                       nil,
-               },
-
-               // Symbolic link
-
-               {
-                       "symlink: create",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "link",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("target"),
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "link",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "link",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "link" (group): will create`,
-                               `3: files: "link" (group): creating`,
-                               `4: files: "link" (group): creating temporary symlink ".linkRND"`,
-                               `4: files: "link" (group): renaming ".linkRND"`,
-                       },
-                       nil,
-               },
-               {
-                       "symlink: create (conflict)",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "link",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("target"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile(".link8717895732742165505", "", 0600)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: ".link8717895732742165505",
-                                       Mode: 0600,
-                                       Data: []byte(""),
-                               },
-                               {
-                                       Path: "link",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "link",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "link" (group): will create`,
-                               `3: files: "link" (group): creating`,
-                               `4: files: "link" (group): creating temporary symlink ".linkRND"`,
-                               `4: files: "link" (group): creating temporary symlink ".linkRND"`,
-                               `4: files: "link" (group): renaming ".linkRND"`,
-                       },
-                       nil,
-               },
-               {
-                       "symlink: create (dry-run)",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "link",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("target"),
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{root},
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "link",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "link" (group): will create`,
-                               `3: files: "link" (group): creating`,
-                               `4: files: "link" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "symlink: unchanged",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "link",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("target"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateSymlink("link", "target")
-                       },
-                       false,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "link",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "link" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "symlink: content",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "link",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("target"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateSymlink("link", "old-target")
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "link",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "link",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1 +1 @@
--old-target
-+target
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "link" (group): content differs`,
-                               `3: files: "link" (group): updating`,
-                               `4: files: "link" (group): creating temporary symlink ".linkRND"`,
-                               `4: files: "link" (group): renaming ".linkRND"`,
-                       },
-                       nil,
-               },
-
-               // Directory
-
-               {
-                       "directory: create",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "dir",
-                               Mode:      fs.ModeDir | 0705,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0705,
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "dir",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0705,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "dir" (group): will create`,
-                               `3: files: "dir" (group): creating`,
-                               `4: files: "dir" (group): creating directory`,
-                               `4: files: "dir" (group): chmodding drwx---r-x`,
-                               fmt.Sprintf(`4: files: "dir" (group): chowning %d/%d`, uid, gid),
-                       },
-                       nil,
-               },
-               {
-                       "directory: create (dry-run)",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "dir",
-                               Mode:      fs.ModeDir | 0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       nil,
-                       true,
-                       []ft.File{root},
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path:    "dir",
-                                               Created: true,
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "dir" (group): will create`,
-                               `3: files: "dir" (group): creating`,
-                               `4: files: "dir" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "directory: unchanged",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "dir",
-                               Mode:      fs.ModeDir | 0755,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0755)
-                       },
-                       false,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755,
-                               },
-                       },
-                       safcm.MsgSyncResp{},
-                       []string{
-                               `4: files: "dir" (group): unchanged`,
-                       },
-                       nil,
-               },
-
-               {
-                       "directory: permission",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "dir",
-                               Mode:      fs.ModeDir | 0755 | fs.ModeSetgid,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateDirectory("dir", 0500|fs.ModeSticky)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "dir",
-                                       Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "dir",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0500 | fs.ModeSticky,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0755 | fs.ModeSetgid,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "dir" (group): permission differs dtr-x------ -> dgrwxr-xr-x`,
-                               `3: files: "dir" (group): updating`,
-                               `4: files: "dir" (group): chmodding dgrwxr-xr-x`,
-                       },
-                       nil,
-               },
-
-               // Type changes
-
-               {
-                       "change: file to directory",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeDir | 0751,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("path", "content\n", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeDir | 0751,
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0751,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,2 +1 @@
--content
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs ---------- -> d---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): removing (due to type change)`,
-                               `4: files: "path" (group): creating directory`,
-                               `4: files: "path" (group): chmodding drwxr-x--x`,
-                               fmt.Sprintf(`4: files: "path" (group): chowning %d/%d`, uid, gid),
-                       },
-                       nil,
-               },
-
-               {
-                       "change: file to symlink",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("target"),
-                       },
-                       func() {
-                               ft.CreateFile("path", "content\n", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,2 +1 @@
--content
--
-+target
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs ---------- -> L---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): creating temporary symlink ".pathRND"`,
-                               `4: files: "path" (group): renaming ".pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: symlink to file",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      0640,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("content\n"),
-                       },
-                       func() {
-                               ft.CreateSymlink("path", "target")
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: 0640,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0640,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1 +1,2 @@
--target
-+content
-+
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs L--------- -> ----------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): creating temporary file ".path*"`,
-                               `4: files: "path" (group): renaming "./.pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: symlink to directory",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeDir | 0751,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateSymlink("path", "target")
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeDir | 0751,
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0751,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1 +1 @@
--target
-+
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs L--------- -> d---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): removing (due to type change)`,
-                               `4: files: "path" (group): creating directory`,
-                               `4: files: "path" (group): chmodding drwxr-x--x`,
-                               fmt.Sprintf(`4: files: "path" (group): chowning %d/%d`, uid, gid),
-                       },
-                       nil,
-               },
-
-               {
-                       "change: directory to file",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      0666,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("content\n"),
-                       },
-                       func() {
-                               ft.CreateDirectory("path", 0777)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: 0666,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0666,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs d--------- -> ----------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): removing (due to type change)`,
-                               `4: files: "path" (group): creating temporary file ".path*"`,
-                               `4: files: "path" (group): renaming "./.pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: directory to symlink",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("target"),
-                       },
-                       func() {
-                               ft.CreateDirectory("path", 0777)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs d--------- -> L---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): removing (due to type change)`,
-                               `4: files: "path" (group): creating temporary symlink ".pathRND"`,
-                               `4: files: "path" (group): renaming ".pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: other to file",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      0640,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("content\n"),
-                       },
-                       func() {
-                               ft.CreateFifo("path", 0666)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: 0640,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeNamedPipe | 0666,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0640,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs p--------- -> ----------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): creating temporary file ".path*"`,
-                               `4: files: "path" (group): renaming "./.pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: other to symlink",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("target"),
-                       },
-                       func() {
-                               ft.CreateFifo("path", 0666)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeNamedPipe | 0666,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs p--------- -> L---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): creating temporary symlink ".pathRND"`,
-                               `4: files: "path" (group): renaming ".pathRND"`,
-                       },
-                       nil,
-               },
-
-               {
-                       "change: other to directory",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeDir | 0751,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFifo("path", 0666)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeDir | 0751,
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeNamedPipe | 0666,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeDir | 0751,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs p--------- -> d---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): removing (due to type change)`,
-                               `4: files: "path" (group): creating directory`,
-                               `4: files: "path" (group): chmodding drwxr-x--x`,
-                               fmt.Sprintf(`4: files: "path" (group): chowning %d/%d`, uid, gid),
-                       },
-                       nil,
-               },
-
-               {
-                       "change: file to symlink (same content)",
-                       safcm.MsgSyncReq{},
-                       &safcm.File{
-                               Path:      "path",
-                               Mode:      fs.ModeSymlink | 0777,
-                               Uid:       -1,
-                               Gid:       -1,
-                               OrigGroup: "group",
-                               Data:      []byte("target"),
-                       },
-                       func() {
-                               ft.CreateFile("path", "target", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "path",
-                                       Mode: fs.ModeSymlink | 0777,
-                                       Data: []byte("target"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "path",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  fs.ModeSymlink | 0777,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "path" (group): type differs ---------- -> L---------`,
-                               `3: files: "path" (group): updating`,
-                               `4: files: "path" (group): creating temporary symlink ".pathRND"`,
-                               `4: files: "path" (group): renaming ".pathRND"`,
-                       },
-                       nil,
-               },
-
-               // Diffs
-
-               {
-                       "diff: textual",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path: "file",
-                               Mode: 0644,
-                               Uid:  -1,
-                               Gid:  -1,
-                               Data: []byte(`
-this
-is
-a
-simple
-file
-`),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", `this
-is
-file
-!
-`, 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte(`this
-is
-file
-!
-`),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,5 +1,7 @@
-+
- this
- is
-+a
-+simple
- file
--!
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): content differs`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "diff: binary both",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("\x00\x01\x02\x03"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "\x00\x01\x02", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("\x00\x01\x02"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: "Binary files differ (3 -> 4 bytes), cannot show diff",
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): content differs`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "diff: binary old",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("content\n"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "\x00\x01\x02", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("\x00\x01\x02"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,2 +1,2 @@
--<binary content, 3 bytes>
-+content
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): content differs`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-
-               {
-                       "diff: binary new",
-                       safcm.MsgSyncReq{
-                               DryRun: true,
-                       },
-                       &safcm.File{
-                               Path:      "file",
-                               Mode:      0644,
-                               Uid:       -1,
-                               Gid:       -1,
-                               Data:      []byte("\x00\x01\x02\x03"),
-                               OrigGroup: "group",
-                       },
-                       func() {
-                               ft.CreateFile("file", "content\n", 0644)
-                       },
-                       true,
-                       []ft.File{
-                               root,
-                               {
-                                       Path: "file",
-                                       Mode: 0644,
-                                       Data: []byte("content\n"),
-                               },
-                       },
-                       safcm.MsgSyncResp{
-                               FileChanges: []safcm.FileChange{
-                                       {
-                                               Path: "file",
-                                               Old: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               New: safcm.FileChangeInfo{
-                                                       Mode:  0644,
-                                                       User:  user,
-                                                       Uid:   uid,
-                                                       Group: group,
-                                                       Gid:   gid,
-                                               },
-                                               DataDiff: `@@ -1,2 +1,2 @@
--content
-+<binary content, 4 bytes>
-`,
-                                       },
-                               },
-                       },
-                       []string{
-                               `4: files: "file" (group): content differs`,
-                               `3: files: "file" (group): updating`,
-                               `4: files: "file" (group): dry-run, skipping changes`,
-                       },
-                       nil,
-               },
-       }
-
-       for _, tc := range tests {
-               t.Run(tc.name, func(t *testing.T) {
-                       // Create separate test directory for each test case
-                       path := filepath.Join(cwd, "testdata", "file-"+tc.name)
-                       err := os.Mkdir(path, 0700)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       err = os.Chdir(path)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-
-                       if tc.prepare != nil {
-                               tc.prepare()
-                       }
-
-                       s, res := prepareSync(tc.req, &testRunner{
-                               t: t,
-                       })
-                       err = s.setDefaults()
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-
-                       // Deterministic temporary symlink names
-                       rand.Seed(0)
-
-                       var changed bool
-                       err = s.syncFile(tc.file, &changed)
-                       testutil.AssertErrorEqual(t, "err", err, tc.expErr)
-                       dbg := res.Wait()
-                       // Remove random file names from result
-                       for i, x := range dbg {
-                               dbg[i] = randFilesRegexp.ReplaceAllString(x, `RND"`)
-                       }
-                       testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
-
-                       files, err := ft.WalkDir(path)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
-                       testutil.AssertEqual(t, "files", files, tc.expFiles)
-
-                       testutil.AssertEqual(t, "changed", changed, tc.expChanged)
-                       testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
-               })
-       }
-
-       if !t.Failed() {
-               err = os.RemoveAll(filepath.Join(cwd, "testdata"))
-               if err != nil {
-                       t.Fatal(err)
-               }
-       }
-}