File: test_status.py

package info (click to toggle)
yarsync 0.3.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 652 kB
  • sloc: python: 2,615; makefile: 22
file content (156 lines) | stat: -rw-r--r-- 5,130 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import os
import pytest

from yarsync import YARsync
from yarsync.yarsync import _Sync
from .settings import (
    TEST_DIR, TEST_DIR_EMPTY, TEST_DIR_CONFIG_DIR, TEST_DIR_WORK_DIR, TEST_DIR_FILTER,
    TEST_DIR_YS_BAD_PERMISSIONS,
)


def test_status_error(mocker, test_dir_read_only):
    ## test directory without .ys configuration
    os.chdir(test_dir_read_only)
    mocker_stdout = mocker.patch("sys.stdout")
    mocker_stderr = mocker.patch("sys.stderr")
    call = mocker.call

    args = ["yarsync", "status"]
    # issues a mocker warning
    # with mocker.patch("sys.stderr") as mocker_stderr:
    with pytest.raises(OSError) as err:
        ys = YARsync(args)
    assert ".ys not found" in repr(err.value)

    # adapted from https://stackoverflow.com/a/59398826/952234
    write_calls = mocker_stderr.write.call_args_list
    # [0] is call args, [1] is kwargs
    written_strs = "".join(call[0][0] for call in write_calls)
    # error message is correct
    assert written_strs.startswith(
        "! fatal: no yarsync configuration directory .ys found\n"
    )
    # no stdout output
    assert mocker_stdout.mock_calls == []

    # don't test for exact messages,
    # because we might improve them in the future.
    # assert mocker_print.mock_calls == [
    #     call.write('!'),
    #     call.write(' '),
    #     call.write("fatal: no yarsync configuration "
    #                ".ys found"),
    #     call.write('\n')
    # ]


def test_status_error_bad_permissions(capfd):
    os.chdir(TEST_DIR_YS_BAD_PERMISSIONS)
    ys = YARsync(["yarsync", "status"])
    returncode = ys()
    # rsync returns 23 in case of permission errors
    assert returncode == 23
    # mock will not work with non-Python stderr,
    # https://github.com/pytest-dev/pytest-mock/issues/295#issuecomment-1155105804
    # so we use capfd
    # https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html#accessing-captured-output-from-a-test-function
    # https://docs.pytest.org/en/stable/reference/reference.html#capfd
    captured = capfd.readouterr()
    assert 'test_dir_ys_bad_permissions/forbidden" failed: Permission denied '\
           in captured.err
    assert "No synchronization information found." in captured.out


def test_status_no_commits(mocker):
    os.chdir(TEST_DIR_EMPTY)
    # io.StringIO uses only utf-8
    mocker_print = mocker.patch("sys.stdout")  #, new_callable=StringIO)

    args = ["yarsync", "status"]
    ys = YARsync(args)
    res = ys()
    call = mocker.call
    assert res == 0
    assert mocker_print.mock_calls == [
        call.write('In repository myhost'), call.write('\n'),
        call.write('No commits found'), call.write('\n')
    ]


@pytest.mark.parametrize(
    "command,test_dir",
    [
        (["yarsync"], TEST_DIR_FILTER),
        (["yarsync", "--config-dir="+TEST_DIR_CONFIG_DIR], TEST_DIR_WORK_DIR),
    ]
)
def test_status_existing_commits(capfd, command, test_dir):
    """Status works same for a normal and a detached configuration."""
    os.chdir(test_dir)
    # mocker_print = mocker.patch("sys.stdout")

    command.append("status")
    ys = YARsync(command)
    res = ys()
    # we don't check for the exact rsync message here, only for results.
    # # filter is needed, because not only .ys can be excluded
    assert res == 0

    ## stdout is correct
    # we don't test for exact lines, because they can change
    # when we e.g. add and remove a file to a subdirectory
    # (thus changing its timestamps)
    captured = capfd.readouterr()
    assert not captured.err
    assert captured.out.endswith(
        'Nothing to commit, working directory clean.\n'
        'No synchronization directory found.\n'
        'No synchronization information found.\n'
    )
    # assert mocker_print.mock_calls == [
    #     # this is written only with -v
    #     # call.write('# '),
    #     # call.write(''),
    #     # call.write(
    #     #     "rsync -aun --delete -i --exclude=/.ys {} --outbuf=L {}/ {}/commits/2"\
    #     #     .format(filter_str, ys.root_dir, ys.config_dir)
    #     # ),
    #     # call.write('\n'),
    #     call.write('Nothing to commit, working directory clean.'),
    #     call.write('\n'),
    #     call.write('No synchronization information found.'),
    #     call.write('\n'),
    # ]


@pytest.mark.parametrize(
    "local_sync",
    [
        (["1_other_repo.txt"]),
        (["2_other_repo.txt", "1_other_repo.txt"]),
    ]
)
def test_status_existing_sync(mocker, capfd, local_sync):
    n_commits_ahead = 1
    other_repo = "other_repo"

    os.chdir(TEST_DIR)
    ys = YARsync(["yarsync", "status"])
    sync = _Sync(local_sync)

    mock = mocker.Mock()
    mock.return_value = sync
    ys._get_local_sync = mock

    res = ys()

    captured = capfd.readouterr()
    assert not captured.err
    if len(local_sync) == 1:
        sync_str = "Local repository is {} commits ahead of {}\n"\
                   .format(n_commits_ahead, other_repo)
        assert sync_str in captured.out
    else:
        sync_str = "Commits are up to date with {}.\n".format(other_repo)
        assert sync_str in captured.out