Skip to content

Commit af28c7d

Browse files
committed
test: add integration test scaffold
Signed-off-by: WANG Xuerui <[email protected]>
1 parent 10fd728 commit af28c7d

File tree

2 files changed

+188
-3
lines changed

2 files changed

+188
-3
lines changed

tests/fixtures/__init__.py

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
from contextlib import contextmanager
1+
from contextlib import contextmanager, redirect_stderr, redirect_stdout
22
from importlib import resources
3+
from dataclasses import dataclass
4+
import io
5+
import os
36
import pathlib
47
import sys
5-
from typing import Generator
8+
from typing import Generator, cast
69

10+
from pygit2 import Repository
711
import pytest
812

13+
from ruyi.cli.main import main as ruyi_main
14+
from ruyi.config import GlobalConfig
915
from ruyi.log import RuyiConsoleLogger, RuyiLogger
10-
from ruyi.utils.global_mode import GlobalModeProvider
16+
from ruyi.ruyipkg.repo import MetadataRepo
17+
from ruyi.utils.global_mode import EnvGlobalModeProvider, GlobalModeProvider
1118

1219

1320
class RuyiFileFixtureFactory:
@@ -158,3 +165,140 @@ def mock_gm() -> MockGlobalModeProvider:
158165
def ruyi_logger(mock_gm: GlobalModeProvider) -> RuyiLogger:
159166
"""Fixture for creating a RuyiLogger instance."""
160167
return RuyiConsoleLogger(mock_gm)
168+
169+
170+
@dataclass
171+
class CLIRunResult:
172+
exit_code: int
173+
stdout: str
174+
stderr: str
175+
176+
177+
class MockRepository:
178+
def __init__(self, root: pathlib.Path) -> None:
179+
self.workdir = root
180+
self.path = root
181+
182+
183+
class IntegrationTestHarness:
184+
def __init__(
185+
self,
186+
env: dict[str, str],
187+
repo_root: pathlib.Path,
188+
repo_url: str,
189+
repo_branch: str,
190+
) -> None:
191+
self._env = env
192+
self.repo_root = repo_root
193+
self.repo_url = repo_url
194+
self.repo_branch = repo_branch
195+
196+
def __call__(self, *args: str) -> CLIRunResult:
197+
return self.run(*args)
198+
199+
def run(self, *args: str) -> CLIRunResult:
200+
argv = ["ruyi", *args]
201+
stdout_io = io.StringIO()
202+
stderr_io = io.StringIO()
203+
with redirect_stdout(stdout_io), redirect_stderr(stderr_io):
204+
gm = EnvGlobalModeProvider(self._env, argv)
205+
gm.record_self_exe(argv[0], __file__, argv[0])
206+
logger = RuyiConsoleLogger(gm, stdout=stdout_io, stderr=stderr_io)
207+
gc = GlobalConfig.load_from_config(gm, logger)
208+
gc.override_repo_dir = str(self.repo_root)
209+
gc.override_repo_url = self.repo_url
210+
gc.override_repo_branch = self.repo_branch
211+
exit_code = ruyi_main(gm, gc, argv)
212+
return CLIRunResult(exit_code, stdout_io.getvalue(), stderr_io.getvalue())
213+
214+
def add_package(
215+
self,
216+
category: str,
217+
name: str,
218+
version: str,
219+
manifest_toml: str,
220+
) -> pathlib.Path:
221+
pkg_dir = self.repo_root / "manifests" / category / name
222+
pkg_dir.mkdir(parents=True, exist_ok=True)
223+
manifest_path = pkg_dir / f"{version}.toml"
224+
manifest_path.write_text(manifest_toml, encoding="utf-8")
225+
return manifest_path
226+
227+
228+
def _populate_default_packages_index(repo_root: pathlib.Path) -> None:
229+
repo_root.mkdir(parents=True, exist_ok=True)
230+
231+
config_text = """\
232+
ruyi-repo = "v1"
233+
234+
[[mirrors]]
235+
id = "ruyi-dist"
236+
urls = ["https://example.invalid/dist/"]
237+
"""
238+
239+
(repo_root / "config.toml").write_text(config_text + "\n", encoding="utf-8")
240+
241+
sha_stub = "0" * 64
242+
manifest_text = f"""\
243+
format = "v1"
244+
kind = ["source"]
245+
246+
[metadata]
247+
desc = "Sample integration package"
248+
vendor = {{ name = "Ruyi Integration Tests", eula = "" }}
249+
250+
[[distfiles]]
251+
name = "sample-src.tar.zst"
252+
size = 0
253+
254+
[distfiles.checksums]
255+
sha256 = "{sha_stub}"
256+
"""
257+
258+
manifest_dir = repo_root / "manifests" / "dev-tools" / "sample-cli"
259+
manifest_dir.mkdir(parents=True, exist_ok=True)
260+
(manifest_dir / "1.0.0.toml").write_text(manifest_text + "\n", encoding="utf-8")
261+
262+
263+
@pytest.fixture
264+
def ruyi_cli_runner(
265+
tmp_path: pathlib.Path,
266+
monkeypatch: pytest.MonkeyPatch,
267+
) -> IntegrationTestHarness:
268+
base_dir = tmp_path / "integration-env"
269+
home_dir = base_dir / "home"
270+
cache_dir = base_dir / "cache"
271+
config_dir = base_dir / "config"
272+
data_dir = base_dir / "data"
273+
state_dir = base_dir / "state"
274+
275+
for p in (home_dir, cache_dir, config_dir, data_dir, state_dir):
276+
p.mkdir(parents=True, exist_ok=True)
277+
278+
monkeypatch.setenv("HOME", str(home_dir))
279+
monkeypatch.setenv("XDG_CACHE_HOME", str(cache_dir))
280+
monkeypatch.setenv("XDG_CONFIG_HOME", str(config_dir))
281+
monkeypatch.setenv("XDG_DATA_HOME", str(data_dir))
282+
monkeypatch.setenv("XDG_STATE_HOME", str(state_dir))
283+
monkeypatch.setenv("RUYI_TELEMETRY_OPTOUT", "1")
284+
285+
repo_root = cache_dir / "ruyi" / "packages-index"
286+
_populate_default_packages_index(repo_root)
287+
288+
def _ensure_git_repo_stub(self: MetadataRepo) -> Repository:
289+
if self.repo is None:
290+
repo_path = pathlib.Path(self.root)
291+
repo_path.mkdir(parents=True, exist_ok=True)
292+
self.repo = cast(Repository, MockRepository(repo_path))
293+
return self.repo
294+
295+
monkeypatch.setattr(MetadataRepo, "ensure_git_repo", _ensure_git_repo_stub)
296+
297+
env = dict(os.environ)
298+
299+
return IntegrationTestHarness(
300+
env=env,
301+
repo_root=repo_root,
302+
repo_url="https://example.invalid/packages-index.git",
303+
repo_branch="main",
304+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from tests.fixtures import IntegrationTestHarness
2+
3+
4+
def test_cli_version(ruyi_cli_runner: IntegrationTestHarness) -> None:
5+
for argv in [
6+
["--version"],
7+
["version"],
8+
]:
9+
result = ruyi_cli_runner(*argv)
10+
assert result.exit_code == 0
11+
assert "Ruyi" in result.stdout
12+
assert "fatal error" not in result.stderr.lower()
13+
14+
15+
def test_cli_list_with_mock_repo(ruyi_cli_runner: IntegrationTestHarness) -> None:
16+
result = ruyi_cli_runner("list", "--name-contains", "sample-cli")
17+
18+
assert result.exit_code == 0
19+
assert "dev-tools/sample-cli" in result.stdout
20+
21+
22+
def test_cli_list_with_custom_package(ruyi_cli_runner: IntegrationTestHarness) -> None:
23+
sha_stub = "1" * 64
24+
manifest = (
25+
'format = "v1"\n'
26+
'kind = ["source"]\n\n'
27+
"[metadata]\n"
28+
'desc = "Custom integration package"\n'
29+
'vendor = { name = "Integration Tests", eula = "" }\n\n'
30+
"[[distfiles]]\n"
31+
'name = "custom-src.tar.zst"\n'
32+
"size = 0\n\n"
33+
"[distfiles.checksums]\n"
34+
f'sha256 = "{sha_stub}"\n'
35+
)
36+
ruyi_cli_runner.add_package("examples", "custom-cli", "0.1.0", manifest)
37+
38+
result = ruyi_cli_runner("list", "--category-is", "examples")
39+
40+
assert result.exit_code == 0
41+
assert "examples/custom-cli" in result.stdout

0 commit comments

Comments
 (0)