1 // Copyright (C) 2021 Simon Ruderich
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
27 "ruderich.org/simon/safcm"
28 ft "ruderich.org/simon/safcm/cmd/safcm-remote/sync/filetest"
29 "ruderich.org/simon/safcm/testutil"
32 var randFilesRegexp = regexp.MustCompile(`\d+"$`)
34 func TestSyncFiles(t *testing.T) {
35 cwd, err := os.Getwd()
41 err = os.RemoveAll("testdata")
45 err = os.Mkdir("testdata", 0700)
52 Mode: fs.ModeDir | 0700,
54 user, uid, group, gid := ft.CurrentUserAndGroup()
56 skipUnlessCiRun := len(os.Getenv("SAFCM_CI_RUN")) == 0
58 tmpTestFilePath := "/tmp/safcm-sync-files-test-file"
67 expResp safcm.MsgSyncResp
72 // NOTE: Also update MsgSyncResp in safcm test cases when
73 // changing anything here!
75 // See TestSyncFile() for most file related tests. This
76 // function only tests the overall results and triggers.
82 Files: map[string]*safcm.File{
85 Mode: fs.ModeDir | 0700,
92 Mode: fs.ModeDir | 0755,
102 Data: []byte("content\n"),
113 Mode: fs.ModeDir | 0755,
118 Data: []byte("content\n"),
122 FileChanges: []safcm.FileChange{
126 New: safcm.FileChangeInfo{
127 Mode: fs.ModeDir | 0755,
137 New: safcm.FileChangeInfo{
148 `4: sync remote: files: "." (group): unchanged`,
149 `4: sync remote: files: "dir" (group): will create`,
150 `3: sync remote: files: "dir" (group): creating`,
151 `4: sync remote: files: "dir" (group): creating directory`,
152 `4: sync remote: files: "dir" (group): chmodding drwxr-xr-x`,
153 fmt.Sprintf(`4: sync remote: files: "dir" (group): chowning %d/%d`, uid, gid),
154 `4: sync remote: files: "dir/file" (group): will create`,
155 `3: sync remote: files: "dir/file" (group): creating`,
156 `4: sync remote: files: "dir/file" (group): creating temporary file "dir/.file*"`,
157 `4: sync remote: files: "dir/file" (group): renaming "dir/.fileRND"`,
166 Files: map[string]*safcm.File{
169 Mode: fs.ModeDir | 0700,
176 Mode: fs.ModeDir | 0755,
186 Data: []byte("content\n"),
192 ft.CreateDirectory("dir", 0755)
193 ft.CreateFile("dir/file", "content\n", 0644)
200 Mode: fs.ModeDir | 0755,
205 Data: []byte("content\n"),
210 `4: sync remote: files: "." (group): unchanged`,
211 `4: sync remote: files: "dir" (group): unchanged`,
212 `4: sync remote: files: "dir/file" (group): unchanged`,
218 "invalid File: user",
221 Files: map[string]*safcm.File{
224 Mode: fs.ModeDir | 0700,
239 fmt.Errorf("\".\": cannot set both User (\"user\") and Uid (1)"),
242 "invalid File: group",
245 Files: map[string]*safcm.File{
248 Mode: fs.ModeDir | 0700,
263 fmt.Errorf("\".\": cannot set both Group (\"group\") and Gid (1)"),
267 // We use relative paths for most tests because we
268 // don't want to modify the running system. Use this
269 // test (and the one below for triggers) as a basic
270 // check that absolute paths work.
272 // Use numeric IDs as not all systems use root/root;
273 // for example BSDs use root/wheel.
274 "absolute paths: no change",
277 Files: map[string]*safcm.File{
280 Mode: fs.ModeDir | 0755,
287 Mode: fs.ModeDir | 0755,
294 Mode: fs.ModeDir | 0777 | fs.ModeSticky,
308 `4: sync remote: files: "/" (group): unchanged`,
309 `4: sync remote: files: "/etc" (group): unchanged`,
310 `4: sync remote: files: "/tmp" (group): unchanged`,
316 "triggers: no change",
319 Files: map[string]*safcm.File{
322 Mode: fs.ModeDir | 0700,
326 TriggerCommands: []string{
332 Mode: fs.ModeDir | 0755,
336 TriggerCommands: []string{
345 Data: []byte("content\n"),
347 TriggerCommands: []string{
348 "echo trigger dir/file",
354 ft.CreateDirectory("dir", 0755)
355 ft.CreateFile("dir/file", "content\n", 0644)
362 Mode: fs.ModeDir | 0755,
367 Data: []byte("content\n"),
372 `4: sync remote: files: "." (group): unchanged`,
373 `4: sync remote: files: "dir" (group): unchanged`,
374 `4: sync remote: files: "dir/file" (group): unchanged`,
380 "triggers: change root",
383 Files: map[string]*safcm.File{
386 Mode: fs.ModeDir | 0700,
390 TriggerCommands: []string{
396 Mode: fs.ModeDir | 0755,
400 TriggerCommands: []string{
409 Data: []byte("content\n"),
411 TriggerCommands: []string{
412 "echo trigger dir/file",
418 err = os.Chmod(".", 0750)
422 ft.CreateDirectory("dir", 0755)
423 ft.CreateFile("dir/file", "content\n", 0644)
432 Mode: fs.ModeDir | 0755,
437 Data: []byte("content\n"),
441 FileChanges: []safcm.FileChange{
444 Old: safcm.FileChangeInfo{
445 Mode: fs.ModeDir | 0750,
451 New: safcm.FileChangeInfo{
452 Mode: fs.ModeDir | 0700,
462 `4: sync remote: files: "." (group): permission differs drwxr-x--- -> drwx------`,
463 `3: sync remote: files: "." (group): updating`,
464 `4: sync remote: files: "." (group): chmodding drwx------`,
465 `3: sync remote: files: ".": queuing trigger on "."`,
466 `4: sync remote: files: "dir" (group): unchanged`,
467 `4: sync remote: files: "dir/file" (group): unchanged`,
473 "triggers: change middle",
476 Files: map[string]*safcm.File{
479 Mode: fs.ModeDir | 0700,
483 TriggerCommands: []string{
489 Mode: fs.ModeDir | 0755,
493 TriggerCommands: []string{
502 Data: []byte("content\n"),
504 TriggerCommands: []string{
505 "echo trigger dir/file",
511 ft.CreateDirectory("dir", 0750)
512 ft.CreateFile("dir/file", "content\n", 0644)
522 Mode: fs.ModeDir | 0755,
527 Data: []byte("content\n"),
531 FileChanges: []safcm.FileChange{
534 Old: safcm.FileChangeInfo{
535 Mode: fs.ModeDir | 0750,
541 New: safcm.FileChangeInfo{
542 Mode: fs.ModeDir | 0755,
552 `4: sync remote: files: "." (group): unchanged`,
553 `4: sync remote: files: "dir" (group): permission differs drwxr-x--- -> drwxr-xr-x`,
554 `3: sync remote: files: "dir" (group): updating`,
555 `4: sync remote: files: "dir" (group): chmodding drwxr-xr-x`,
556 `3: sync remote: files: "dir": queuing trigger on "."`,
557 `3: sync remote: files: "dir": queuing trigger on "dir"`,
558 `4: sync remote: files: "dir/file" (group): unchanged`,
564 "triggers: change leaf",
567 Files: map[string]*safcm.File{
570 Mode: fs.ModeDir | 0700,
574 TriggerCommands: []string{
580 Mode: fs.ModeDir | 0755,
584 TriggerCommands: []string{
593 Data: []byte("content\n"),
595 TriggerCommands: []string{
596 "echo trigger dir/file",
602 ft.CreateDirectory("dir", 0755)
613 Mode: fs.ModeDir | 0755,
618 Data: []byte("content\n"),
622 FileChanges: []safcm.FileChange{
626 New: safcm.FileChangeInfo{
637 `4: sync remote: files: "." (group): unchanged`,
638 `4: sync remote: files: "dir" (group): unchanged`,
639 `4: sync remote: files: "dir/file" (group): will create`,
640 `3: sync remote: files: "dir/file" (group): creating`,
641 `4: sync remote: files: "dir/file" (group): creating temporary file "dir/.file*"`,
642 `4: sync remote: files: "dir/file" (group): renaming "dir/.fileRND"`,
643 `3: sync remote: files: "dir/file": queuing trigger on "."`,
644 `3: sync remote: files: "dir/file": queuing trigger on "dir"`,
645 `3: sync remote: files: "dir/file": queuing trigger on "dir/file"`,
651 "triggers: multiple changes",
654 Files: map[string]*safcm.File{
657 Mode: fs.ModeDir | 0700,
661 TriggerCommands: []string{
667 Mode: fs.ModeDir | 0755,
671 TriggerCommands: []string{
680 Data: []byte("content\n"),
682 TriggerCommands: []string{
683 "echo trigger dir/file",
698 Mode: fs.ModeDir | 0755,
703 Data: []byte("content\n"),
707 FileChanges: []safcm.FileChange{
711 New: safcm.FileChangeInfo{
712 Mode: fs.ModeDir | 0755,
722 New: safcm.FileChangeInfo{
733 `4: sync remote: files: "." (group): unchanged`,
734 `4: sync remote: files: "dir" (group): will create`,
735 `3: sync remote: files: "dir" (group): creating`,
736 `4: sync remote: files: "dir" (group): creating directory`,
737 `4: sync remote: files: "dir" (group): chmodding drwxr-xr-x`,
738 fmt.Sprintf(`4: sync remote: files: "dir" (group): chowning %d/%d`, uid, gid),
739 `3: sync remote: files: "dir": queuing trigger on "."`,
740 `3: sync remote: files: "dir": queuing trigger on "dir"`,
741 `4: sync remote: files: "dir/file" (group): will create`,
742 `3: sync remote: files: "dir/file" (group): creating`,
743 `4: sync remote: files: "dir/file" (group): creating temporary file "dir/.file*"`,
744 `4: sync remote: files: "dir/file" (group): renaming "dir/.fileRND"`,
745 `4: sync remote: files: "dir/file": skipping trigger on ".", already active`,
746 `4: sync remote: files: "dir/file": skipping trigger on "dir", already active`,
747 `3: sync remote: files: "dir/file": queuing trigger on "dir/file"`,
753 "triggers: absolute paths",
756 Files: map[string]*safcm.File{
759 Mode: fs.ModeDir | 0755,
763 TriggerCommands: []string{
769 Mode: fs.ModeDir | 0777 | fs.ModeSticky,
773 TriggerCommands: []string{
778 Path: tmpTestFilePath,
783 TriggerCommands: []string{
784 "echo trigger /tmp/file",
793 // Don't use variable for more robust test
794 "/tmp/safcm-sync-files-test-file",
800 FileChanges: []safcm.FileChange{
802 Path: "/tmp/safcm-sync-files-test-file",
804 New: safcm.FileChangeInfo{
815 `4: sync remote: files: "/" (group): unchanged`,
816 `4: sync remote: files: "/tmp" (group): unchanged`,
817 `4: sync remote: files: "/tmp/safcm-sync-files-test-file" (group): will create`,
818 `3: sync remote: files: "/tmp/safcm-sync-files-test-file" (group): creating`,
819 `4: sync remote: files: "/tmp/safcm-sync-files-test-file" (group): creating temporary file "/tmp/.safcm-sync-files-test-file*"`,
820 `4: sync remote: files: "/tmp/safcm-sync-files-test-file" (group): renaming "/tmp/.safcm-sync-files-test-fileRND"`,
821 `3: sync remote: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/"`,
822 `3: sync remote: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/tmp"`,
823 `3: sync remote: files: "/tmp/safcm-sync-files-test-file": queuing trigger on "/tmp/safcm-sync-files-test-file"`,
829 for _, tc := range tests {
830 t.Run(tc.name, func(t *testing.T) {
835 // Create separate test directory for each test case
836 path := filepath.Join(cwd, "testdata", "files-"+tc.name)
837 err := os.Mkdir(path, 0700)
846 if tc.prepare != nil {
850 s, res := prepareSync(tc.req, &testRunner{
853 err = s.setDefaults()
859 testutil.AssertErrorEqual(t, "err", err, tc.expErr)
861 // Remove random file names from result
862 for i, x := range dbg {
863 dbg[i] = randFilesRegexp.ReplaceAllString(x, `RND"`)
865 testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
867 files, err := ft.WalkDir(path)
871 testutil.AssertEqual(t, "files", files, tc.expFiles)
873 testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
874 testutil.AssertEqual(t, "triggers",
875 s.triggers, tc.expTriggers)
879 os.Remove(tmpTestFilePath)
881 err = os.RemoveAll(filepath.Join(cwd, "testdata"))
888 func TestSyncFile(t *testing.T) {
889 cwd, err := os.Getwd()
895 err = os.RemoveAll("testdata")
899 err = os.Mkdir("testdata", 0700)
906 Mode: fs.ModeDir | 0700,
908 user, uid, group, gid := ft.CurrentUserAndGroup()
917 expResp safcm.MsgSyncResp
922 // NOTE: Also update MsgSyncResp in safcm test cases when
923 // changing anything here!
925 // TODO: Add tests for chown and run them only as root
937 Data: []byte("content\n"),
947 Data: []byte("content\n"),
951 FileChanges: []safcm.FileChange{
955 New: safcm.FileChangeInfo{
966 `4: sync remote: files: "file" (group): will create`,
967 `3: sync remote: files: "file" (group): creating`,
968 `4: sync remote: files: "file" (group): creating temporary file ".file*"`,
969 `4: sync remote: files: "file" (group): renaming "./.fileRND"`,
974 "file: create (dry-run)",
983 Data: []byte("content\n"),
990 FileChanges: []safcm.FileChange{
994 New: safcm.FileChangeInfo{
1005 `4: sync remote: files: "file" (group): will create`,
1006 `3: sync remote: files: "file" (group): creating`,
1007 `4: sync remote: files: "file" (group): dry-run, skipping changes`,
1020 Data: []byte("content\n"),
1024 ft.CreateFile("file", "content\n", 0644)
1032 Data: []byte("content\n"),
1035 safcm.MsgSyncResp{},
1037 `4: sync remote: files: "file" (group): unchanged`,
1043 "file: unchanged (non-default user-group)",
1052 Data: []byte("content\n"),
1056 ft.CreateFile("file", "content\n", 0644)
1064 Data: []byte("content\n"),
1067 safcm.MsgSyncResp{},
1069 `4: sync remote: files: "file" (group): unchanged`,
1079 Mode: 0755 | fs.ModeSetuid,
1082 Data: []byte("content\n"),
1086 ft.CreateFile("file", "content\n", 0755)
1093 Mode: 0755 | fs.ModeSetuid,
1094 Data: []byte("content\n"),
1098 FileChanges: []safcm.FileChange{
1101 Old: safcm.FileChangeInfo{
1108 New: safcm.FileChangeInfo{
1109 Mode: 0755 | fs.ModeSetuid,
1119 `4: sync remote: files: "file" (group): permission differs -rwxr-xr-x -> urwxr-xr-x`,
1120 `3: sync remote: files: "file" (group): updating`,
1121 `4: sync remote: files: "file" (group): creating temporary file ".file*"`,
1122 `4: sync remote: files: "file" (group): renaming "./.fileRND"`,
1135 Data: []byte("content\n"),
1139 ft.CreateFile("file", "old content\n", 0644)
1147 Data: []byte("content\n"),
1151 FileChanges: []safcm.FileChange{
1154 Old: safcm.FileChangeInfo{
1161 New: safcm.FileChangeInfo{
1168 DataDiff: `@@ -1,2 +1,2 @@
1177 `4: sync remote: files: "file" (group): content differs`,
1178 `3: sync remote: files: "file" (group): updating`,
1179 `4: sync remote: files: "file" (group): creating temporary file ".file*"`,
1180 `4: sync remote: files: "file" (group): renaming "./.fileRND"`,
1192 Mode: fs.ModeSymlink | 0777,
1195 Data: []byte("target"),
1204 Mode: fs.ModeSymlink | 0777,
1205 Data: []byte("target"),
1209 FileChanges: []safcm.FileChange{
1213 New: safcm.FileChangeInfo{
1214 Mode: fs.ModeSymlink | 0777,
1224 `4: sync remote: files: "link" (group): will create`,
1225 `3: sync remote: files: "link" (group): creating`,
1226 `4: sync remote: files: "link" (group): creating temporary symlink ".linkRND"`,
1227 `4: sync remote: files: "link" (group): renaming ".linkRND"`,
1232 "symlink: create (conflict)",
1236 Mode: fs.ModeSymlink | 0777,
1239 Data: []byte("target"),
1243 ft.CreateFile(".link8717895732742165505", "", 0600)
1249 Path: ".link8717895732742165505",
1255 Mode: fs.ModeSymlink | 0777,
1256 Data: []byte("target"),
1260 FileChanges: []safcm.FileChange{
1264 New: safcm.FileChangeInfo{
1265 Mode: fs.ModeSymlink | 0777,
1275 `4: sync remote: files: "link" (group): will create`,
1276 `3: sync remote: files: "link" (group): creating`,
1277 `4: sync remote: files: "link" (group): creating temporary symlink ".linkRND"`,
1278 `4: sync remote: files: "link" (group): creating temporary symlink ".linkRND"`,
1279 `4: sync remote: files: "link" (group): renaming ".linkRND"`,
1284 "symlink: create (dry-run)",
1290 Mode: fs.ModeSymlink | 0777,
1293 Data: []byte("target"),
1300 FileChanges: []safcm.FileChange{
1304 New: safcm.FileChangeInfo{
1305 Mode: fs.ModeSymlink | 0777,
1315 `4: sync remote: files: "link" (group): will create`,
1316 `3: sync remote: files: "link" (group): creating`,
1317 `4: sync remote: files: "link" (group): dry-run, skipping changes`,
1323 "symlink: unchanged",
1327 Mode: fs.ModeSymlink | 0777,
1330 Data: []byte("target"),
1334 ft.CreateSymlink("link", "target")
1341 Mode: fs.ModeSymlink | 0777,
1342 Data: []byte("target"),
1345 safcm.MsgSyncResp{},
1347 `4: sync remote: files: "link" (group): unchanged`,
1357 Mode: fs.ModeSymlink | 0777,
1360 Data: []byte("target"),
1364 ft.CreateSymlink("link", "old-target")
1371 Mode: fs.ModeSymlink | 0777,
1372 Data: []byte("target"),
1376 FileChanges: []safcm.FileChange{
1379 Old: safcm.FileChangeInfo{
1380 Mode: fs.ModeSymlink | 0777,
1386 New: safcm.FileChangeInfo{
1387 Mode: fs.ModeSymlink | 0777,
1393 DataDiff: `@@ -1 +1 @@
1401 `4: sync remote: files: "link" (group): content differs`,
1402 `3: sync remote: files: "link" (group): updating`,
1403 `4: sync remote: files: "link" (group): creating temporary symlink ".linkRND"`,
1404 `4: sync remote: files: "link" (group): renaming ".linkRND"`,
1412 "directory: create",
1416 Mode: fs.ModeDir | 0705,
1427 Mode: fs.ModeDir | 0705,
1431 FileChanges: []safcm.FileChange{
1435 New: safcm.FileChangeInfo{
1436 Mode: fs.ModeDir | 0705,
1446 `4: sync remote: files: "dir" (group): will create`,
1447 `3: sync remote: files: "dir" (group): creating`,
1448 `4: sync remote: files: "dir" (group): creating directory`,
1449 `4: sync remote: files: "dir" (group): chmodding drwx---r-x`,
1450 fmt.Sprintf(`4: sync remote: files: "dir" (group): chowning %d/%d`, uid, gid),
1455 "directory: create (dry-run)",
1461 Mode: fs.ModeDir | 0644,
1470 FileChanges: []safcm.FileChange{
1474 New: safcm.FileChangeInfo{
1475 Mode: fs.ModeDir | 0644,
1485 `4: sync remote: files: "dir" (group): will create`,
1486 `3: sync remote: files: "dir" (group): creating`,
1487 `4: sync remote: files: "dir" (group): dry-run, skipping changes`,
1493 "directory: unchanged",
1497 Mode: fs.ModeDir | 0755,
1503 ft.CreateDirectory("dir", 0755)
1510 Mode: fs.ModeDir | 0755,
1513 safcm.MsgSyncResp{},
1515 `4: sync remote: files: "dir" (group): unchanged`,
1521 "directory: permission",
1525 Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
1531 ft.CreateDirectory("dir", 0500|fs.ModeSticky)
1538 Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
1542 FileChanges: []safcm.FileChange{
1545 Old: safcm.FileChangeInfo{
1546 Mode: fs.ModeDir | 0500 | fs.ModeSticky,
1552 New: safcm.FileChangeInfo{
1553 Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
1563 `4: sync remote: files: "dir" (group): permission differs dtr-x------ -> dgrwxr-xr-x`,
1564 `3: sync remote: files: "dir" (group): updating`,
1565 `4: sync remote: files: "dir" (group): chmodding dgrwxr-xr-x`,
1573 "change: file to directory",
1577 Mode: fs.ModeDir | 0751,
1583 ft.CreateFile("path", "content\n", 0644)
1590 Mode: fs.ModeDir | 0751,
1594 FileChanges: []safcm.FileChange{
1597 Old: safcm.FileChangeInfo{
1604 New: safcm.FileChangeInfo{
1605 Mode: fs.ModeDir | 0751,
1611 DataDiff: `@@ -1,2 +1 @@
1619 `4: sync remote: files: "path" (group): type differs ---------- -> d---------`,
1620 `3: sync remote: files: "path" (group): updating`,
1621 `4: sync remote: files: "path" (group): removing (due to type change)`,
1622 `4: sync remote: files: "path" (group): creating directory`,
1623 `4: sync remote: files: "path" (group): chmodding drwxr-x--x`,
1624 fmt.Sprintf(`4: sync remote: files: "path" (group): chowning %d/%d`, uid, gid),
1630 "change: file to symlink",
1634 Mode: fs.ModeSymlink | 0777,
1638 Data: []byte("target"),
1641 ft.CreateFile("path", "content\n", 0644)
1648 Mode: fs.ModeSymlink | 0777,
1649 Data: []byte("target"),
1653 FileChanges: []safcm.FileChange{
1656 Old: safcm.FileChangeInfo{
1663 New: safcm.FileChangeInfo{
1664 Mode: fs.ModeSymlink | 0777,
1670 DataDiff: `@@ -1,2 +1 @@
1679 `4: sync remote: files: "path" (group): type differs ---------- -> L---------`,
1680 `3: sync remote: files: "path" (group): updating`,
1681 `4: sync remote: files: "path" (group): creating temporary symlink ".pathRND"`,
1682 `4: sync remote: files: "path" (group): renaming ".pathRND"`,
1688 "change: symlink to file",
1696 Data: []byte("content\n"),
1699 ft.CreateSymlink("path", "target")
1707 Data: []byte("content\n"),
1711 FileChanges: []safcm.FileChange{
1714 Old: safcm.FileChangeInfo{
1715 Mode: fs.ModeSymlink | 0777,
1721 New: safcm.FileChangeInfo{
1728 DataDiff: `@@ -1 +1,2 @@
1737 `4: sync remote: files: "path" (group): type differs L--------- -> ----------`,
1738 `3: sync remote: files: "path" (group): updating`,
1739 `4: sync remote: files: "path" (group): creating temporary file ".path*"`,
1740 `4: sync remote: files: "path" (group): renaming "./.pathRND"`,
1746 "change: symlink to directory",
1750 Mode: fs.ModeDir | 0751,
1756 ft.CreateSymlink("path", "target")
1763 Mode: fs.ModeDir | 0751,
1767 FileChanges: []safcm.FileChange{
1770 Old: safcm.FileChangeInfo{
1771 Mode: fs.ModeSymlink | 0777,
1777 New: safcm.FileChangeInfo{
1778 Mode: fs.ModeDir | 0751,
1784 DataDiff: `@@ -1 +1 @@
1792 `4: sync remote: files: "path" (group): type differs L--------- -> d---------`,
1793 `3: sync remote: files: "path" (group): updating`,
1794 `4: sync remote: files: "path" (group): removing (due to type change)`,
1795 `4: sync remote: files: "path" (group): creating directory`,
1796 `4: sync remote: files: "path" (group): chmodding drwxr-x--x`,
1797 fmt.Sprintf(`4: sync remote: files: "path" (group): chowning %d/%d`, uid, gid),
1803 "change: directory to file",
1811 Data: []byte("content\n"),
1814 ft.CreateDirectory("path", 0777)
1822 Data: []byte("content\n"),
1826 FileChanges: []safcm.FileChange{
1829 Old: safcm.FileChangeInfo{
1830 Mode: fs.ModeDir | 0777,
1836 New: safcm.FileChangeInfo{
1847 `4: sync remote: files: "path" (group): type differs d--------- -> ----------`,
1848 `3: sync remote: files: "path" (group): updating`,
1849 `4: sync remote: files: "path" (group): removing (due to type change)`,
1850 `4: sync remote: files: "path" (group): creating temporary file ".path*"`,
1851 `4: sync remote: files: "path" (group): renaming "./.pathRND"`,
1857 "change: directory to symlink",
1861 Mode: fs.ModeSymlink | 0777,
1865 Data: []byte("target"),
1868 ft.CreateDirectory("path", 0777)
1875 Mode: fs.ModeSymlink | 0777,
1876 Data: []byte("target"),
1880 FileChanges: []safcm.FileChange{
1883 Old: safcm.FileChangeInfo{
1884 Mode: fs.ModeDir | 0777,
1890 New: safcm.FileChangeInfo{
1891 Mode: fs.ModeSymlink | 0777,
1901 `4: sync remote: files: "path" (group): type differs d--------- -> L---------`,
1902 `3: sync remote: files: "path" (group): updating`,
1903 `4: sync remote: files: "path" (group): removing (due to type change)`,
1904 `4: sync remote: files: "path" (group): creating temporary symlink ".pathRND"`,
1905 `4: sync remote: files: "path" (group): renaming ".pathRND"`,
1911 "change: other to file",
1919 Data: []byte("content\n"),
1922 ft.CreateFifo("path", 0666)
1930 Data: []byte("content\n"),
1934 FileChanges: []safcm.FileChange{
1937 Old: safcm.FileChangeInfo{
1938 Mode: fs.ModeNamedPipe | 0666,
1944 New: safcm.FileChangeInfo{
1955 `4: sync remote: files: "path" (group): type differs p--------- -> ----------`,
1956 `3: sync remote: files: "path" (group): updating`,
1957 `4: sync remote: files: "path" (group): creating temporary file ".path*"`,
1958 `4: sync remote: files: "path" (group): renaming "./.pathRND"`,
1964 "change: other to symlink",
1968 Mode: fs.ModeSymlink | 0777,
1972 Data: []byte("target"),
1975 ft.CreateFifo("path", 0666)
1982 Mode: fs.ModeSymlink | 0777,
1983 Data: []byte("target"),
1987 FileChanges: []safcm.FileChange{
1990 Old: safcm.FileChangeInfo{
1991 Mode: fs.ModeNamedPipe | 0666,
1997 New: safcm.FileChangeInfo{
1998 Mode: fs.ModeSymlink | 0777,
2008 `4: sync remote: files: "path" (group): type differs p--------- -> L---------`,
2009 `3: sync remote: files: "path" (group): updating`,
2010 `4: sync remote: files: "path" (group): creating temporary symlink ".pathRND"`,
2011 `4: sync remote: files: "path" (group): renaming ".pathRND"`,
2017 "change: other to directory",
2021 Mode: fs.ModeDir | 0751,
2027 ft.CreateFifo("path", 0666)
2034 Mode: fs.ModeDir | 0751,
2038 FileChanges: []safcm.FileChange{
2041 Old: safcm.FileChangeInfo{
2042 Mode: fs.ModeNamedPipe | 0666,
2048 New: safcm.FileChangeInfo{
2049 Mode: fs.ModeDir | 0751,
2059 `4: sync remote: files: "path" (group): type differs p--------- -> d---------`,
2060 `3: sync remote: files: "path" (group): updating`,
2061 `4: sync remote: files: "path" (group): removing (due to type change)`,
2062 `4: sync remote: files: "path" (group): creating directory`,
2063 `4: sync remote: files: "path" (group): chmodding drwxr-x--x`,
2064 fmt.Sprintf(`4: sync remote: files: "path" (group): chowning %d/%d`, uid, gid),
2070 "change: file to symlink (same content)",
2074 Mode: fs.ModeSymlink | 0777,
2078 Data: []byte("target"),
2081 ft.CreateFile("path", "target", 0644)
2088 Mode: fs.ModeSymlink | 0777,
2089 Data: []byte("target"),
2093 FileChanges: []safcm.FileChange{
2096 Old: safcm.FileChangeInfo{
2103 New: safcm.FileChangeInfo{
2104 Mode: fs.ModeSymlink | 0777,
2114 `4: sync remote: files: "path" (group): type differs ---------- -> L---------`,
2115 `3: sync remote: files: "path" (group): updating`,
2116 `4: sync remote: files: "path" (group): creating temporary symlink ".pathRND"`,
2117 `4: sync remote: files: "path" (group): renaming ".pathRND"`,
2144 ft.CreateFile("file", `this
2164 FileChanges: []safcm.FileChange{
2167 Old: safcm.FileChangeInfo{
2174 New: safcm.FileChangeInfo{
2181 DataDiff: `@@ -1,5 +1,7 @@
2195 `4: sync remote: files: "file" (group): content differs`,
2196 `3: sync remote: files: "file" (group): updating`,
2197 `4: sync remote: files: "file" (group): dry-run, skipping changes`,
2203 "diff: binary both",
2212 Data: []byte("\x00\x01\x02\x03"),
2216 ft.CreateFile("file", "\x00\x01\x02", 0644)
2224 Data: []byte("\x00\x01\x02"),
2228 FileChanges: []safcm.FileChange{
2231 Old: safcm.FileChangeInfo{
2238 New: safcm.FileChangeInfo{
2245 DataDiff: "Binary files differ, cannot show diff",
2250 `4: sync remote: files: "file" (group): content differs`,
2251 `3: sync remote: files: "file" (group): updating`,
2252 `4: sync remote: files: "file" (group): dry-run, skipping changes`,
2267 Data: []byte("content\n"),
2271 ft.CreateFile("file", "\x00\x01\x02", 0644)
2279 Data: []byte("\x00\x01\x02"),
2283 FileChanges: []safcm.FileChange{
2286 Old: safcm.FileChangeInfo{
2293 New: safcm.FileChangeInfo{
2300 DataDiff: `@@ -1,2 +1,2 @@
2309 `4: sync remote: files: "file" (group): content differs`,
2310 `3: sync remote: files: "file" (group): updating`,
2311 `4: sync remote: files: "file" (group): dry-run, skipping changes`,
2326 Data: []byte("\x00\x01\x02\x03"),
2330 ft.CreateFile("file", "content\n", 0644)
2338 Data: []byte("content\n"),
2342 FileChanges: []safcm.FileChange{
2345 Old: safcm.FileChangeInfo{
2352 New: safcm.FileChangeInfo{
2359 DataDiff: `@@ -1,2 +1,2 @@
2368 `4: sync remote: files: "file" (group): content differs`,
2369 `3: sync remote: files: "file" (group): updating`,
2370 `4: sync remote: files: "file" (group): dry-run, skipping changes`,
2376 for _, tc := range tests {
2377 t.Run(tc.name, func(t *testing.T) {
2378 // Create separate test directory for each test case
2379 path := filepath.Join(cwd, "testdata", "file-"+tc.name)
2380 err := os.Mkdir(path, 0700)
2384 err = os.Chdir(path)
2389 if tc.prepare != nil {
2393 s, res := prepareSync(tc.req, &testRunner{
2396 err = s.setDefaults()
2401 // Deterministic temporary symlink names
2405 err = s.syncFile(tc.file, &changed)
2406 testutil.AssertErrorEqual(t, "err", err, tc.expErr)
2408 // Remove random file names from result
2409 for i, x := range dbg {
2410 dbg[i] = randFilesRegexp.ReplaceAllString(x, `RND"`)
2412 testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
2414 files, err := ft.WalkDir(path)
2418 testutil.AssertEqual(t, "files", files, tc.expFiles)
2420 testutil.AssertEqual(t, "changed", changed, tc.expChanged)
2421 testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
2426 err = os.RemoveAll(filepath.Join(cwd, "testdata"))