claude-dev-env 1.29.2 → 1.29.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -208,7 +208,7 @@ def test_extract_body_reassembles_split_quoted_equals_value() -> None:
|
|
|
208
208
|
assert extract_body_from_command(command) == "this body has multiple words"
|
|
209
209
|
|
|
210
210
|
|
|
211
|
-
def test_read_body_file_rejects_relative_path_traversal(tmp_path) -> None:
|
|
211
|
+
def test_read_body_file_rejects_relative_path_traversal(tmp_path, monkeypatch) -> None:
|
|
212
212
|
import importlib.util, pathlib, sys
|
|
213
213
|
_HOOK_DIR = pathlib.Path(__file__).parent
|
|
214
214
|
if str(_HOOK_DIR) not in sys.path:
|
|
@@ -217,14 +217,15 @@ def test_read_body_file_rejects_relative_path_traversal(tmp_path) -> None:
|
|
|
217
217
|
m = importlib.util.module_from_spec(spec)
|
|
218
218
|
spec.loader.exec_module(m)
|
|
219
219
|
import os, pytest
|
|
220
|
-
|
|
220
|
+
sentinel_directory = tmp_path / 'sentinel'
|
|
221
|
+
sentinel_directory.mkdir()
|
|
222
|
+
working_directory = tmp_path / 'workdir'
|
|
223
|
+
working_directory.mkdir()
|
|
224
|
+
sentinel_file = sentinel_directory / 'secret.txt'
|
|
221
225
|
sentinel_file.write_text('secret')
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
pytest.skip('tmp_path on different drive than cwd; relpath undefined on Windows')
|
|
226
|
-
if '..' not in rel_path:
|
|
227
|
-
pytest.skip('file is under cwd, not a traversal case')
|
|
226
|
+
monkeypatch.chdir(working_directory)
|
|
227
|
+
rel_path = os.path.relpath(str(sentinel_file))
|
|
228
|
+
assert '..' in rel_path, 'chdir to a sibling of the sentinel must produce a traversal relpath'
|
|
228
229
|
with pytest.raises(m.PathTraversalError):
|
|
229
230
|
m._read_body_file_contents(rel_path)
|
|
230
231
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Exclude validator fixture files from pytest collection.
|
|
2
|
+
|
|
3
|
+
Files in this directory are inputs for a skip-detection validator (see
|
|
4
|
+
packages/claude-dev-env/hooks/validators/), not real tests. Each file
|
|
5
|
+
demonstrates a skip/xfail pattern the validator must detect. Collecting
|
|
6
|
+
them as real tests produces spurious skipped/xfailed counts in the suite.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
collect_ignore_glob = ["test_*.py"]
|
package/package.json
CHANGED
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
import os
|
|
7
|
-
import subprocess
|
|
8
6
|
import sys
|
|
9
7
|
from pathlib import Path
|
|
10
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
11
|
_SCRIPTS_DIR = Path(__file__).resolve().parent.parent
|
|
12
12
|
if str(_SCRIPTS_DIR) not in sys.path:
|
|
13
13
|
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
14
14
|
|
|
15
15
|
import sync_to_cursor as mod
|
|
16
|
+
from sync_to_cursor.engine import run as run_sync_to_cursor
|
|
16
17
|
from sync_to_cursor.rules import _read_paths_glob
|
|
17
18
|
|
|
18
|
-
_SYNC_SCRIPT = _SCRIPTS_DIR / "sync_to_cursor.py"
|
|
19
|
-
|
|
20
19
|
|
|
21
20
|
def _minimal_rule_files(claude_rules: Path) -> None:
|
|
22
21
|
claude_rules.mkdir(parents=True, exist_ok=True)
|
|
@@ -72,66 +71,45 @@ def test_sync_canonical_docs_skips_missing_with_no_dst(tmp_path: Path) -> None:
|
|
|
72
71
|
assert not (cursor / "docs" / "TEST_QUALITY.md").is_file()
|
|
73
72
|
|
|
74
73
|
|
|
75
|
-
def test_check_fails_when_doc_source_changes_without_resync(
|
|
74
|
+
def test_check_fails_when_doc_source_changes_without_resync(
|
|
75
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
76
|
+
) -> None:
|
|
76
77
|
claude = tmp_path / ".claude"
|
|
77
|
-
cursor = tmp_path / ".cursor"
|
|
78
78
|
_minimal_rule_files(claude / "rules")
|
|
79
79
|
_minimal_code_rules_and_test_quality(claude / "docs")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
[sys.executable, str(_SYNC_SCRIPT), "--force"],
|
|
83
|
-
env=env,
|
|
84
|
-
check=True,
|
|
85
|
-
cwd=str(_SCRIPTS_DIR),
|
|
86
|
-
)
|
|
80
|
+
monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
|
|
81
|
+
assert run_sync_to_cursor(["--force"]) == 0
|
|
87
82
|
(claude / "docs" / "CODE_RULES.md").write_bytes(b"changed\n")
|
|
88
|
-
|
|
89
|
-
[sys.executable, str(_SYNC_SCRIPT), "--check"],
|
|
90
|
-
env=env,
|
|
91
|
-
cwd=str(_SCRIPTS_DIR),
|
|
92
|
-
)
|
|
93
|
-
assert subprocess_result.returncode != 0
|
|
83
|
+
assert run_sync_to_cursor(["--check"]) != 0
|
|
94
84
|
|
|
95
85
|
|
|
96
|
-
def test_check_passes_after_resync(
|
|
86
|
+
def test_check_passes_after_resync(
|
|
87
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
88
|
+
) -> None:
|
|
97
89
|
claude = tmp_path / ".claude"
|
|
98
|
-
cursor = tmp_path / ".cursor"
|
|
99
90
|
_minimal_rule_files(claude / "rules")
|
|
100
91
|
_minimal_code_rules_and_test_quality(claude / "docs")
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
env=env,
|
|
105
|
-
check=True,
|
|
106
|
-
cwd=str(_SCRIPTS_DIR),
|
|
107
|
-
)
|
|
108
|
-
subprocess_result = subprocess.run(
|
|
109
|
-
[sys.executable, str(_SYNC_SCRIPT), "--check"],
|
|
110
|
-
env=env,
|
|
111
|
-
cwd=str(_SCRIPTS_DIR),
|
|
112
|
-
)
|
|
113
|
-
assert subprocess_result.returncode == 0
|
|
92
|
+
monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
|
|
93
|
+
assert run_sync_to_cursor(["--force"]) == 0
|
|
94
|
+
assert run_sync_to_cursor(["--check"]) == 0
|
|
114
95
|
|
|
115
96
|
|
|
116
|
-
def test_manifest_includes_docs_entries(
|
|
97
|
+
def test_manifest_includes_docs_entries(
|
|
98
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
99
|
+
) -> None:
|
|
117
100
|
claude = tmp_path / ".claude"
|
|
118
101
|
cursor = tmp_path / ".cursor"
|
|
119
102
|
_minimal_rule_files(claude / "rules")
|
|
120
103
|
_minimal_code_rules_and_test_quality(claude / "docs")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
[sys.executable, str(_SYNC_SCRIPT), "--force"],
|
|
124
|
-
env=env,
|
|
125
|
-
check=True,
|
|
126
|
-
cwd=str(_SCRIPTS_DIR),
|
|
127
|
-
)
|
|
104
|
+
monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
|
|
105
|
+
assert run_sync_to_cursor(["--force"]) == 0
|
|
128
106
|
manifest = json.loads((cursor / ".sync-manifest.json").read_text(encoding="utf-8"))
|
|
129
107
|
assert "docs_entries" in manifest
|
|
130
|
-
|
|
131
|
-
assert "docs/CODE_RULES.md" in
|
|
132
|
-
assert "docs/TEST_QUALITY.md" in
|
|
133
|
-
assert "sources_hash" in
|
|
134
|
-
assert "output_hash" in
|
|
108
|
+
docs_entries = manifest["docs_entries"]
|
|
109
|
+
assert "docs/CODE_RULES.md" in docs_entries
|
|
110
|
+
assert "docs/TEST_QUALITY.md" in docs_entries
|
|
111
|
+
assert "sources_hash" in docs_entries["docs/CODE_RULES.md"]
|
|
112
|
+
assert "output_hash" in docs_entries["docs/CODE_RULES.md"]
|
|
135
113
|
|
|
136
114
|
|
|
137
115
|
def test_merge_code_standards_with_pointer_style_code_rules(tmp_path: Path) -> None:
|
|
@@ -166,40 +144,28 @@ def test_sync_canonical_docs_deletes_stale_copy_when_source_removed(tmp_path: Pa
|
|
|
166
144
|
assert not (cursor / "docs" / "CODE_RULES.md").is_file()
|
|
167
145
|
|
|
168
146
|
|
|
169
|
-
def test_dry_run_does_not_create_output_directory(
|
|
147
|
+
def test_dry_run_does_not_create_output_directory(
|
|
148
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
149
|
+
) -> None:
|
|
170
150
|
claude = tmp_path / ".claude"
|
|
171
151
|
cursor = tmp_path / ".cursor"
|
|
172
152
|
_minimal_rule_files(claude / "rules")
|
|
173
153
|
_minimal_code_rules_and_test_quality(claude / "docs")
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
[sys.executable, str(_SYNC_SCRIPT), "--dry-run", "--quiet"],
|
|
177
|
-
env=env,
|
|
178
|
-
check=True,
|
|
179
|
-
cwd=str(_SCRIPTS_DIR),
|
|
180
|
-
)
|
|
154
|
+
monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
|
|
155
|
+
assert run_sync_to_cursor(["--dry-run", "--quiet"]) == 0
|
|
181
156
|
assert not (cursor / "rules").exists(), "--dry-run must not create the output directory"
|
|
182
157
|
|
|
183
158
|
|
|
184
|
-
def test_check_skips_optional_mapping_when_source_missing(
|
|
159
|
+
def test_check_skips_optional_mapping_when_source_missing(
|
|
160
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
161
|
+
) -> None:
|
|
185
162
|
claude = tmp_path / ".claude"
|
|
186
|
-
cursor = tmp_path / ".cursor"
|
|
187
163
|
_minimal_rule_files(claude / "rules")
|
|
188
164
|
_minimal_code_rules_and_test_quality(claude / "docs")
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
[sys.executable, str(_SYNC_SCRIPT), "--force"],
|
|
192
|
-
env=env,
|
|
193
|
-
check=True,
|
|
194
|
-
cwd=str(_SCRIPTS_DIR),
|
|
195
|
-
)
|
|
165
|
+
monkeypatch.setenv("LLM_SETTINGS_ROOT", str(tmp_path))
|
|
166
|
+
assert run_sync_to_cursor(["--force"]) == 0
|
|
196
167
|
(claude / "rules" / "tasklings-preferences.md").unlink()
|
|
197
|
-
|
|
198
|
-
[sys.executable, str(_SYNC_SCRIPT), "--check"],
|
|
199
|
-
env=env,
|
|
200
|
-
cwd=str(_SCRIPTS_DIR),
|
|
201
|
-
)
|
|
202
|
-
assert subprocess_result.returncode == 0, "--check must pass when only optional sources are missing"
|
|
168
|
+
assert run_sync_to_cursor(["--check"]) == 0, "--check must pass when only optional sources are missing"
|
|
203
169
|
|
|
204
170
|
|
|
205
171
|
def test_tasklings_glob_derived_from_frontmatter(tmp_path: Path) -> None:
|