Option C Tools
Loading...
Searching...
No Matches
test_option_c_migration.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_migrate/test_option_c_migration.py
4
5"""
6Purpose
7-------
8FS-539 regression coverage — :func:`oct.migrate.oct_migrate.run_migrate_option_c`
9relocates legacy artefacts into ``.option_c/`` and writes ``oc_status.json``.
10
11Responsibilities
12----------------
13- dry-run is side-effect-free.
14- Each legacy artefact is copied (not moved) to ``.option_c/``.
15- A ``.bk`` backup is created when the destination exists and differs.
16- ``oc_status.json`` records ``migrated_from`` and pillar enablement.
17- An idempotent second run skips already-migrated artefacts.
18- An unmigrated project with no legacy artefacts produces "nothing to
19 migrate".
20
21Diagnostics
22-----------
23Domain: MIGRATE-TESTS
24 Levels:
25 L2 — test lifecycle
26 L3 — assertion details
27 L4 — deep tracing
28
29Contracts
30---------
31- All migration operations use ``tmp_path`` as the project root; no
32 real project files are touched.
33"""
34
35from __future__ import annotations
36
37import json
38from pathlib import Path
39
40import pytest
41
42from oct.migrate.oct_migrate import run_migrate_option_c
43
44
45def _seed_legacy_project(root: Path) -> None:
46 """Create a minimal legacy Option C project layout."""
47 (root / ".octrc.json").write_text(
48 json.dumps({"linter": {"profile": "compact"}}), encoding="utf-8",
49 )
50 (root / "oc_diagnostics").mkdir()
51 (root / "oc_diagnostics" / "debug_config.json").write_text(
52 json.dumps({"_schema_version": "2.0", "DEBUG_LEVELS": {}}),
53 encoding="utf-8",
54 )
55 (root / "logs").mkdir()
56 (root / "logs" / "audit.jsonl").write_text("line1\n", encoding="utf-8")
57 (root / "docs").mkdir()
58 (root / "tests").mkdir()
59
60
62 def test_dry_run_creates_nothing(self, tmp_path: Path):
63 _seed_legacy_project(tmp_path)
64 result = run_migrate_option_c(tmp_path, dry_run=True)
65
66 assert not (tmp_path / ".option_c").exists()
67 assert any("[DRY RUN]" in m for m in result.messages)
68
69 def test_dry_run_lists_all_artefacts(self, tmp_path: Path):
70 _seed_legacy_project(tmp_path)
71 result = run_migrate_option_c(tmp_path, dry_run=True)
72
73 msgs = "\n".join(result.messages)
74 assert "octrc" in msgs
75 assert "debug_config" in msgs
76 assert "logs" in msgs
77
78
80 def test_copies_all_artefacts(self, tmp_path: Path):
81 _seed_legacy_project(tmp_path)
82 result = run_migrate_option_c(tmp_path)
83
84 oc = tmp_path / ".option_c"
85 assert (oc / "octrc.json").is_file()
86 assert (oc / "debug_config.json").is_file()
87 assert (oc / "logs").is_dir()
88 assert (oc / "logs" / "audit.jsonl").is_file()
89 assert len(result.copied) == 3
90
91 def test_legacy_files_preserved(self, tmp_path: Path):
92 _seed_legacy_project(tmp_path)
93 run_migrate_option_c(tmp_path)
94
95 assert (tmp_path / ".octrc.json").is_file()
96 assert (tmp_path / "oc_diagnostics" / "debug_config.json").is_file()
97 assert (tmp_path / "logs" / "audit.jsonl").is_file()
98
99 def test_cache_dir_created(self, tmp_path: Path):
100 _seed_legacy_project(tmp_path)
101 run_migrate_option_c(tmp_path)
102
103 assert (tmp_path / ".option_c" / "cache").is_dir()
104
105
107 def test_backup_on_conflict(self, tmp_path: Path):
108 _seed_legacy_project(tmp_path)
109 oc = tmp_path / ".option_c"
110 oc.mkdir()
111 (oc / "octrc.json").write_text('{"old": true}', encoding="utf-8")
112
113 result = run_migrate_option_c(tmp_path)
114
115 assert (oc / "octrc.json.bk").is_file()
116 bk_data = json.loads(
117 (oc / "octrc.json.bk").read_text(encoding="utf-8")
118 )
119 assert bk_data == {"old": True}
120 assert any(oc / "octrc.json.bk" == p for p in result.backed_up)
121
122 def test_no_backup_flag_skips_bk(self, tmp_path: Path):
123 _seed_legacy_project(tmp_path)
124 oc = tmp_path / ".option_c"
125 oc.mkdir()
126 (oc / "octrc.json").write_text('{"old": true}', encoding="utf-8")
127
128 result = run_migrate_option_c(tmp_path, no_backup=True)
129
130 assert not (oc / "octrc.json.bk").exists()
131 assert result.backed_up == []
132
133
135 def test_oc_status_written(self, tmp_path: Path):
136 _seed_legacy_project(tmp_path)
137 run_migrate_option_c(tmp_path)
138
139 path = tmp_path / ".option_c" / "oc_status.json"
140 assert path.is_file()
141 raw = json.loads(path.read_text(encoding="utf-8"))
142 assert raw["_schema_version"] == "1.0"
143 assert raw["stage"] in ("bootstrap", "migrating", "compliant")
144
145 def test_migrated_from_records_sources(self, tmp_path: Path):
146 _seed_legacy_project(tmp_path)
147 run_migrate_option_c(tmp_path)
148
149 raw = json.loads(
150 (tmp_path / ".option_c" / "oc_status.json").read_text(
151 encoding="utf-8",
152 )
153 )
154 mf = raw["migrated_from"]
155 assert mf["octrc"] == ".octrc.json"
156 assert "debug_config" in mf
157
158
160 def test_second_run_skips_identical(self, tmp_path: Path):
161 _seed_legacy_project(tmp_path)
162 run_migrate_option_c(tmp_path)
163 second = run_migrate_option_c(tmp_path)
164
165 assert second.copied == []
166 assert any(
167 reason == "already migrated" for _, reason in second.skipped
168 )
169
170
172 def test_no_artefacts_found(self, tmp_path: Path):
173 result = run_migrate_option_c(tmp_path)
174
175 assert result.copied == []
176 assert all(
177 reason == "not found" for _, reason in result.skipped
178 )