test(dry-run): strengthen no-mutation coverage and refresh docs
Extend _snapshot_tree to record mode, inode, xattrs, directories and special nodes, and add coverage proving a server-contacting --dry-run leaves the destination structurally identical for --delay-updates, --backup, symlinks, hardlinks, FIFOs, and daemon modules (including a read-only module). Add a regression test for the --read-batch --dry-run refusal and for a missing/non-directory receive root failing a dry-run exactly like a real run. Fix stale version comments (2.20.0/633 -> 2.21.0/637) and RSYNC_COMPAT's current --protocol value, and add a unit assertion that --server-port/--port (and --server-host) set the dry-run routing bit.
This commit is contained in:
@@ -43,6 +43,9 @@ from common import (
|
||||
_find_free_port,
|
||||
_wait_for_port,
|
||||
)
|
||||
# The dry-run no-mutation contract is asserted with the same structural snapshot
|
||||
# (mode/inode/mtime/xattr/content) the feature suite uses.
|
||||
from test_features import _snapshot_tree
|
||||
|
||||
SOURCE_DIR = os.path.join(TEST_DATA_DIR, "daemon_source")
|
||||
MODULE_ROOT = os.path.join(TEST_DATA_DIR, "daemon_modules")
|
||||
@@ -355,6 +358,33 @@ class TestDaemonRejection:
|
||||
assert result.returncode != 0
|
||||
assert self._tree_files() == before, "read-only rejection wrote under the module root"
|
||||
|
||||
@pytest.mark.ci
|
||||
def test_read_only_module_allows_dry_run(self, daemon):
|
||||
"""A server-contacting --dry-run IS a read-only wire operation, so a
|
||||
`read only = yes` module is the safest dry-run target and must accept it
|
||||
while writing nothing."""
|
||||
result, _ = run_client(SOURCE_DIR, "127.0.0.1::readonly", flags=["--dry-run"],
|
||||
port=daemon.port)
|
||||
assert result.returncode == 0, (result.stderr or result.stdout)[:300]
|
||||
assert "Dry run:" in result.stdout, result.stdout[:200]
|
||||
assert _tree_file_count(READONLY_MODULE) == 0, "read-only dry-run wrote a file"
|
||||
|
||||
@pytest.mark.ci
|
||||
def test_module_dry_run_mutates_nothing(self, daemon):
|
||||
"""A daemon-module dry-run reports would-transfer entries but leaves the
|
||||
module tree structurally identical (mode/inode/mtime/xattr/content)."""
|
||||
result = _push("127.0.0.1::files", daemon.port)
|
||||
assert result.returncode == 0, result.stderr or result.stdout
|
||||
before = _snapshot_tree(FILES_MODULE)
|
||||
# --ignore-times forces every regular file to be reported as
|
||||
# would-transfer, so the dry-run exercises the receiver decision rather
|
||||
# than an all-skip shortcut -- while still mutating nothing.
|
||||
result, _ = run_client(SOURCE_DIR, "127.0.0.1::files",
|
||||
flags=["--dry-run", "--ignore-times"], port=daemon.port)
|
||||
assert result.returncode == 0, (result.stderr or result.stdout)[:300]
|
||||
assert "Dry run:" in result.stdout, result.stdout[:200]
|
||||
assert _snapshot_tree(FILES_MODULE) == before, "daemon dry-run mutated the module root"
|
||||
|
||||
def test_unknown_module_rejected(self, daemon):
|
||||
result = _push("127.0.0.1::no-such-module", daemon.port)
|
||||
assert result.returncode != 0
|
||||
|
||||
Reference in New Issue
Block a user