-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtest_cmd_clone.py
More file actions
154 lines (118 loc) · 4.72 KB
/
test_cmd_clone.py
File metadata and controls
154 lines (118 loc) · 4.72 KB
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
"""Tests for the 'docbuild repo clone' command."""
import logging
import re
from unittest.mock import AsyncMock
import pytest
from docbuild.cli.cmd_repo.cmd_clone import clone
import docbuild.cli.cmd_repo.process as mod_process
from docbuild.cli.context import DocBuildContext
from docbuild.utils import shell as shell_module
log = logging.getLogger(__name__)
class _DummyPaths:
"""Lightweight stand-in for EnvPathsConfig used in tests.
Only the attributes accessed by cmd_repo.process are provided.
"""
def __init__(self, *, config_dir: str, repo_dir: str) -> None:
self.config_dir = config_dir
self.repo_dir = repo_dir
class _DummyEnv:
"""Minimal envconfig replacement exposing ``paths`` for tests.
This avoids constructing a full EnvConfig instance while still matching
the runtime behaviour expected by the cloning logic.
"""
def __init__(self, *, config_dir: str, repo_dir: str) -> None:
self.paths = _DummyPaths(config_dir=config_dir, repo_dir=repo_dir)
#
# @pytest.fixture
# def process_mock() -> AsyncMock:
# """Mock the process object returned by create_subprocess_exec."""
# mock = AsyncMock()
# mock.communicate.return_value = (b'stdout', b'stderr')
# mock.returncode = 0
# return mock
@pytest.fixture
def mock_subprocess(monkeypatch) -> AsyncMock:
"""Fixture to mock asyncio.create_subprocess_exec."""
process_mock = AsyncMock()
process_mock.communicate.return_value = (b"stdout", b"stderr")
process_mock.returncode = 0
mock_create_subprocess = AsyncMock(return_value=process_mock)
# Git commands go through shell.run_command → asyncio.create_subprocess_exec
# in the shell utility module.
monkeypatch.setattr(
shell_module.asyncio, "create_subprocess_exec", mock_create_subprocess
)
return mock_create_subprocess
def test_clone_from_xml_config(runner, tmp_path, mock_subprocess, caplog):
"""Test cloning repositories defined in an XML configuration file."""
caplog.set_level(logging.INFO, logger="docbuild.git")
config_dir = tmp_path / "config"
config_dir.mkdir()
repo_dir = tmp_path / "repos"
repo_dir.mkdir()
# Create a dummy XML config
xml_content = """
<docservconfig>
<product productid="sles">
<docset setid="15-SP1">
<builddocs>
<git remote="https://github.com/test/one.git" />
</builddocs>
</docset>
<docset setid="15-SP2">
<builddocs>
<git remote="https://github.com/test/two.git" />
</builddocs>
</docset>
</product>
</docservconfig>
"""
(config_dir / "sles.xml").write_text(xml_content)
context = DocBuildContext(
envconfig=_DummyEnv(
config_dir=str(config_dir),
repo_dir=str(repo_dir),
),
)
runner.invoke(clone, [], obj=context)
assert mock_subprocess.call_count == 2
calls = mock_subprocess.call_args_list
cloned_repos = {call[0][6] for call in calls} # The 7th arg is the repo URL
assert "https://github.com/test/one.git" in cloned_repos
assert "https://github.com/test/two.git" in cloned_repos
# @pytest.mark.asyncio
async def test_process_stitchnode_none(monkeypatch, tmp_path):
"""Test that process raises ValueError if create_stitchfile returns None."""
# Patch create_stitchfile to return None
monkeypatch.setattr(mod_process, "create_stitchfile", AsyncMock(return_value=None))
context = DocBuildContext(
envconfig=_DummyEnv(
config_dir=str(tmp_path / "config"),
repo_dir=str(tmp_path / "repos"),
)
)
# The config_dir must exist, even if empty
(tmp_path / "config").mkdir()
(tmp_path / "repos").mkdir()
with pytest.raises(ValueError,
match=re.escape("Stitch node could not be created.")):
await mod_process.process(context, repos=())
async def test_process_configdir_none():
"""This scenario is no longer reachable with validated EnvConfig.
The higher-level CLI now ensures ``envconfig`` is a fully validated
environment configuration. Retain this test as a smoke check that
calling ``process`` with a dummy, but structurally valid, envconfig
does not raise and returns an integer exit code.
"""
context = DocBuildContext(
envconfig=_DummyEnv(config_dir="/non/existent/config", repo_dir="/tmp/repos"),
)
result = await mod_process.process(context, repos=())
assert isinstance(result, int)
async def test_process_repodir_none():
"""See docstring of test_process_configdir_none for rationale."""
context = DocBuildContext(
envconfig=_DummyEnv(config_dir="/tmp/config", repo_dir="/non/existent/repos"),
)
result = await mod_process.process(context, repos=())
assert isinstance(result, int)