Option C Tools
Loading...
Searching...
No Matches
test_run_scaffold.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_scaffold/test_run_scaffold.py
4
5"""
6Purpose
7-------
8OI-512 / FS-518 regression coverage — :func:`oct.scaffold.run_scaffold`
9must materialise the full Option C project skeleton (directories, bundled
10files, ``.gitignore`` / ``.octignore`` entries including ``.venv/``) and
11behave idempotently (already-present paths land in ``skipped`` rather
12than being overwritten). ``dry_run=True`` must be strictly side-effect
13free.
14
15The ``include_venv`` flag and its health-warning companion are covered
16in :mod:`test_scaffold_venv`.
17
18Responsibilities
19----------------
20- Verify full skeleton materialisation (directories and bundled files).
21- Verify idempotent re-runs land existing paths in ``skipped``.
22- Verify ``dry_run=True`` produces no filesystem side-effects.
23
24Diagnostics
25-----------
26Domain: SCAFFOLD-TESTS
27Levels:
28 L2 — test lifecycle
29 L3 — assertion details
30 L4 — deep tracing
31
32Contracts
33---------
34- ``dry_run=True`` → no filesystem changes; messages populated.
35- All documented files appear in ``result.created`` on first run.
36- A second ``run_scaffold`` call on the same directory creates nothing
37 new and produces no duplicate-file errors.
38"""
39
40from __future__ import annotations
41
42from pathlib import Path
43
44import pytest
45
46from oct.scaffold import ScaffoldResult, run_scaffold
47
48
49def test_dry_run_creates_nothing(tmp_path: Path):
50 """OI-512: dry_run emits plan messages and never touches the disk."""
51 result = run_scaffold("demo", tmp_path, dry_run=True, include_venv=False)
52 assert isinstance(result, ScaffoldResult)
53 assert result.created == []
54 assert not (tmp_path / "demo").exists()
55 assert any("DRY RUN" in m for m in result.messages)
56
57
59 """FS-539: docs/, tests/, .option_c/, .option_c/logs/ all appear."""
60 result = run_scaffold("demo", tmp_path, include_venv=False)
61 project = tmp_path / "demo"
62 for sub in (
63 "docs",
64 "tests",
65 ".option_c",
66 ".option_c/logs",
67 ".option_c/cache",
68 "docs/_build",
69 ):
70 assert (project / sub).is_dir(), sub
71 assert project in result.created or (project / "docs") in result.created
72
73
75 """FS-539: debug_config + octrc land under .option_c/ with expected shape."""
76 run_scaffold("demo", tmp_path, include_venv=False)
77 project = tmp_path / "demo"
78 debug_cfg = project / ".option_c" / "debug_config.json"
79 octrc = project / ".option_c" / "octrc.json"
80 assert debug_cfg.is_file()
81 assert octrc.is_file()
82 # Debug config must be the v2 schema.
83 assert '"_schema_version": "2.0"' in debug_cfg.read_text(encoding="utf-8")
84 assert '"sphinx_output"' in octrc.read_text(encoding="utf-8")
85
86
88 """FS-503 / FS-504: .gitignore and .octignore both include .venv/."""
89 run_scaffold("demo", tmp_path, include_venv=False)
90 project = tmp_path / "demo"
91 gitignore = (project / ".gitignore").read_text(encoding="utf-8")
92 octignore = (project / ".octignore").read_text(encoding="utf-8")
93 assert ".venv/" in gitignore
94 assert ".venv/" in octignore
95 # Also covers the documented secret-filename entries.
96 assert ".env" in gitignore
97 assert "*.pem" in gitignore
98
99
100def test_scaffold_is_idempotent(tmp_path: Path):
101 """OI-512: a second run must skip everything, not overwrite."""
102 first = run_scaffold("demo", tmp_path, include_venv=False)
103 assert first.created # first run creates many entries.
104 second = run_scaffold("demo", tmp_path, include_venv=False)
105 # Every core file from the first run is in ``skipped`` on the second.
106 project = tmp_path / "demo"
107 assert (project / ".option_c" / "octrc.json") in second.skipped
108 assert (project / ".option_c" / "debug_config.json") in second.skipped
109 assert (project / ".option_c" / "oc_status.json") in second.skipped
110
111
113 """OI-512: the return type must be a ScaffoldResult with the documented fields."""
114 result = run_scaffold("demo", tmp_path, include_venv=False)
115 assert hasattr(result, "created")
116 assert hasattr(result, "skipped")
117 assert hasattr(result, "messages")
118 assert hasattr(result, "venv_path")
119 # include_venv=False → venv_path stays None.
120 assert result.venv_path is None
test_scaffold_writes_debug_config_and_octrc(Path tmp_path)
test_scaffold_creates_core_directories(Path tmp_path)
test_scaffold_writes_gitignore_with_venv(Path tmp_path)