Option C Tools
Loading...
Searching...
No Matches
test_set_level_safety.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_diag/test_set_level_safety.py
4
5"""
6Purpose
7-------
8OI-528 regression coverage — :func:`oct.diag.oct_diag.set_level` and the
9paired :func:`restore_config` helper must provide safe in-place edits of
10``debug_config.json``. A bad level write can brick diagnostics, so the
11operation is backed by a ``<path>.bk`` sidecar and a ``--dry-run`` flag
12that short-circuits both writes.
13
14Responsibilities
15----------------
16- ``--dry-run`` never writes to the config or creates ``.bk``.
17- A real write creates ``<path>.bk`` containing the original text.
18- :func:`restore_config` round-trips the backup back onto the config.
19- Calling :func:`restore_config` with no backup raises cleanly.
20- Re-running ``set_level`` overwrites ``.bk`` with the pre-edit text.
21
22Diagnostics
23-----------
24Domain: DIAG-TESTS
25Levels:
26 L2 — test lifecycle
27 L3 — assertion details
28 L4 — deep tracing
29
30Contracts
31---------
32- All config writes and backups use ``tmp_path``; no real
33 ``debug_config.json`` is modified.
34"""
35
36from __future__ import annotations
37
38import json
39from pathlib import Path
40
41import pytest
42from click.testing import CliRunner
43
44from oct.cli import cli
45from oct.diag.oct_diag import restore_config, set_level
46
47
48_V2_CONFIG = {
49 "_schema_version": "2.0",
50 "DEBUG_LEVELS": {
51 "GENERAL": {"level": 1, "color": "default"},
52 "NET": {"level": 2, "color": "blue"},
53 },
54 "global_settings": {"mode": "dev"},
55}
56
57
58def _write_config(root: Path, data: dict) -> Path:
59 diag = root / "oc_diagnostics"
60 diag.mkdir()
61 # find_project_root requires docs/ + tests/ siblings alongside
62 # oc_diagnostics/ to recognise the directory as an OC project.
63 (root / "docs").mkdir()
64 (root / "tests").mkdir()
65 cfg = diag / "debug_config.json"
66 cfg.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
67 return cfg
68
69
71 cfg = _write_config(tmp_path, _V2_CONFIG)
72 original_text = cfg.read_text(encoding="utf-8")
73
74 result = set_level(tmp_path, "GENERAL", 4, dry_run=True)
75
76 assert cfg.read_text(encoding="utf-8") == original_text
77 assert not cfg.with_suffix(".json.bk").exists()
78 assert result["DEBUG_LEVELS"]["GENERAL"]["level"] == 4
79
80
82 cfg = _write_config(tmp_path, _V2_CONFIG)
83 original_text = cfg.read_text(encoding="utf-8")
84
85 set_level(tmp_path, "GENERAL", 3)
86
87 bk = cfg.with_suffix(".json.bk")
88 assert bk.is_file()
89 assert bk.read_text(encoding="utf-8") == original_text
90 reloaded = json.loads(cfg.read_text(encoding="utf-8"))
91 assert reloaded["DEBUG_LEVELS"]["GENERAL"]["level"] == 3
92
93
95 cfg = _write_config(tmp_path, _V2_CONFIG)
96 original_text = cfg.read_text(encoding="utf-8")
97 set_level(tmp_path, "GENERAL", 4)
98 assert json.loads(cfg.read_text(encoding="utf-8"))["DEBUG_LEVELS"]["GENERAL"]["level"] == 4
99
100 restored = restore_config(tmp_path)
101
102 assert restored == cfg
103 assert cfg.read_text(encoding="utf-8") == original_text
104
105
107 _write_config(tmp_path, _V2_CONFIG)
108
109 with pytest.raises(FileNotFoundError):
110 restore_config(tmp_path)
111
112
114 cfg = _write_config(tmp_path, _V2_CONFIG)
115 set_level(tmp_path, "GENERAL", 3) # first edit: .bk holds original
116 set_level(tmp_path, "NET", 0) # second edit: .bk holds post-first-edit
117
118 bk = cfg.with_suffix(".json.bk")
119 backup_state = json.loads(bk.read_text(encoding="utf-8"))
120 assert backup_state["DEBUG_LEVELS"]["GENERAL"]["level"] == 3
121 assert backup_state["DEBUG_LEVELS"]["NET"]["level"] == 2
122
123
125 cfg = _write_config(tmp_path, _V2_CONFIG)
126 original_text = cfg.read_text(encoding="utf-8")
127 monkeypatch.chdir(tmp_path)
128
129 runner = CliRunner()
130 result = runner.invoke(cli, ["diag", "set-level", "GENERAL", "4", "--dry-run"])
131
132 assert result.exit_code == 0, result.output
133 assert "[DRY RUN]" in result.output
134 assert cfg.read_text(encoding="utf-8") == original_text
135 assert not cfg.with_suffix(".json.bk").exists()
136
137
138def test_cli_restore_command_round_trips(tmp_path, monkeypatch):
139 cfg = _write_config(tmp_path, _V2_CONFIG)
140 original_text = cfg.read_text(encoding="utf-8")
141 monkeypatch.chdir(tmp_path)
142
143 runner = CliRunner()
144 runner.invoke(cli, ["diag", "set-level", "GENERAL", "4"])
145 result = runner.invoke(cli, ["diag", "restore"])
146
147 assert result.exit_code == 0, result.output
148 assert cfg.read_text(encoding="utf-8") == original_text
Definition cli.py:1
Path _write_config(Path root, dict data)
test_cli_restore_command_round_trips(tmp_path, monkeypatch)
test_backup_is_overwritten_with_pre_edit_text_on_second_write(tmp_path)
test_cli_set_level_dry_run_does_not_touch_files(tmp_path, monkeypatch)