claude-dev-env 2.35.0 → 2.36.0
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.
- package/.agents/skills/autoconverge/SKILL.md +19 -15
- package/.agents/skills/autoconverge/reference/gotchas.md +3 -3
- package/.agents/skills/autoconverge/reference/multi-pr.md +6 -2
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +11 -0
- package/.agents/skills/autoconverge/workflow/converge.mjs +26 -33
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +26 -2
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +12 -28
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +16 -0
- package/bin/cde.mjs +175 -0
- package/bin/cde.test.mjs +222 -0
- package/bin/install-constants.mjs +4 -0
- package/bin/install.cursor-rules.test.mjs +233 -2
- package/bin/install.mjs +49 -3
- package/bin/pstack-model-preferences.codex.json +23 -0
- package/bin/select-pstack-models.test.mjs +378 -0
- package/hooks/hooks_constants/tool_names.py +20 -0
- package/hooks/validators/fast_save_validators.py +63 -89
- package/hooks/validators/run_all_validators.py +16 -8
- package/hooks/validators/test_fast_save_validators_policy_lint.py +25 -0
- package/hooks/validators/test_run_all_validators_policy_lint.py +52 -0
- package/package.json +3 -2
- package/rules/pstack-models.md +119 -0
- package/scripts/cde_lint.py +258 -0
- package/scripts/policy_lint/__init__.py +41 -0
- package/scripts/policy_lint/adapter_configuration.py +91 -0
- package/scripts/policy_lint/adapter_detectors.py +328 -0
- package/scripts/policy_lint/adapter_pairing.py +145 -0
- package/scripts/policy_lint/adapter_support.py +97 -0
- package/scripts/policy_lint/adapters.py +352 -0
- package/scripts/policy_lint/config/__init__.py +1 -0
- package/scripts/policy_lint/config/constants.py +66 -0
- package/scripts/policy_lint/engine.py +224 -0
- package/scripts/policy_lint/model.py +387 -0
- package/scripts/policy_lint/registry.py +162 -0
- package/scripts/policy_lint/render.py +91 -0
- package/scripts/policy_lint/selection.py +372 -0
- package/scripts/policy_lint/selection_git.py +147 -0
- package/scripts/select_pstack_models.mjs +304 -0
- package/scripts/tests/test_cde_lint.py +374 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +381 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +252 -0
- package/scripts/tests/test_policy_lint_selection.py +373 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Document-selection boundary tests for files, staged, base, and repository sources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path, PurePosixPath
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from policy_lint import selection as selection_module
|
|
11
|
+
from policy_lint import selection_git
|
|
12
|
+
from policy_lint.config import constants
|
|
13
|
+
from policy_lint.model import (
|
|
14
|
+
ContentOrigin,
|
|
15
|
+
Document,
|
|
16
|
+
DocumentSet,
|
|
17
|
+
LintRequest,
|
|
18
|
+
SelectionKind,
|
|
19
|
+
)
|
|
20
|
+
from policy_lint.selection import SelectionRunFatal, select_documents
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _git_environment() -> dict[str, str]:
|
|
24
|
+
return {
|
|
25
|
+
each_name: each_environment_text
|
|
26
|
+
for each_name, each_environment_text in os.environ.items()
|
|
27
|
+
if not each_name.upper().startswith("GIT_")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _run_git(repository_root: Path, *all_arguments: str) -> None:
|
|
32
|
+
completed = subprocess.run(
|
|
33
|
+
[constants.GIT_EXECUTABLE, *all_arguments],
|
|
34
|
+
cwd=repository_root,
|
|
35
|
+
check=False,
|
|
36
|
+
capture_output=True,
|
|
37
|
+
text=True,
|
|
38
|
+
env=_git_environment(),
|
|
39
|
+
)
|
|
40
|
+
assert completed.returncode == 0, completed.stderr
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _initialize_repository(repository_root: Path) -> Path:
|
|
44
|
+
_run_git(repository_root, "init", "--quiet", "-b", "main")
|
|
45
|
+
_run_git(repository_root, "config", "user.name", "Selection Tests")
|
|
46
|
+
_run_git(repository_root, "config", "user.email", "selection@example.invalid")
|
|
47
|
+
_run_git(repository_root, "config", "commit.gpgsign", "false")
|
|
48
|
+
_run_git(repository_root, "config", "core.autocrlf", "false")
|
|
49
|
+
return repository_root
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _write_text(repository_root: Path, relative_path: str, text: str) -> None:
|
|
53
|
+
file_path = repository_root / relative_path
|
|
54
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
file_path.write_bytes(text.encode(constants.UTF8_ENCODING))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _commit_text(repository_root: Path, relative_path: str, text: str) -> None:
|
|
59
|
+
_write_text(repository_root, relative_path, text)
|
|
60
|
+
_run_git(repository_root, "add", "--", relative_path)
|
|
61
|
+
_run_git(repository_root, "commit", "--quiet", "-m", "selection fixture")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _document_for(document_set: DocumentSet, relative_path: str) -> Document:
|
|
65
|
+
for each_document in document_set.documents:
|
|
66
|
+
if each_document.path.as_posix() == relative_path:
|
|
67
|
+
return each_document
|
|
68
|
+
raise AssertionError(f"missing document: {relative_path}")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_files_source_should_read_current_worktree_bytes(tmp_path: Path) -> None:
|
|
72
|
+
repository_root = _initialize_repository(tmp_path)
|
|
73
|
+
_commit_text(repository_root, "file.py", "committed\n")
|
|
74
|
+
_write_text(repository_root, "file.py", "worktree\n")
|
|
75
|
+
|
|
76
|
+
document_set = select_documents(
|
|
77
|
+
LintRequest.files(repository_root, [Path("file.py")])
|
|
78
|
+
)
|
|
79
|
+
selected_document = _document_for(document_set, "file.py")
|
|
80
|
+
|
|
81
|
+
assert document_set.selection == SelectionKind.FILES
|
|
82
|
+
assert selected_document.text == "worktree\n"
|
|
83
|
+
assert selected_document.origin == ContentOrigin.WORKTREE
|
|
84
|
+
assert selected_document.prior_text is None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_staged_modified_file_should_read_index_bytes_not_worktree(
|
|
88
|
+
tmp_path: Path,
|
|
89
|
+
) -> None:
|
|
90
|
+
repository_root = _initialize_repository(tmp_path)
|
|
91
|
+
_commit_text(repository_root, "file.py", "committed\n")
|
|
92
|
+
_write_text(repository_root, "file.py", "staged\n")
|
|
93
|
+
_run_git(repository_root, "add", "--", "file.py")
|
|
94
|
+
_write_text(repository_root, "file.py", "worktree\n")
|
|
95
|
+
|
|
96
|
+
document_set = select_documents(LintRequest.staged(repository_root))
|
|
97
|
+
selected_document = _document_for(document_set, "file.py")
|
|
98
|
+
|
|
99
|
+
assert document_set.selection == SelectionKind.STAGED
|
|
100
|
+
assert selected_document.text == "staged\n"
|
|
101
|
+
assert selected_document.prior_text == "committed\n"
|
|
102
|
+
assert selected_document.origin == ContentOrigin.INDEX
|
|
103
|
+
assert selected_document.prior_path is None
|
|
104
|
+
assert document_set.deleted_paths == ()
|
|
105
|
+
assert 1 in (selected_document.changed_lines or frozenset())
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_staged_added_file_should_have_no_prior_text(tmp_path: Path) -> None:
|
|
109
|
+
repository_root = _initialize_repository(tmp_path)
|
|
110
|
+
_commit_text(repository_root, "keep.py", "keep\n")
|
|
111
|
+
_write_text(repository_root, "new.py", "added\n")
|
|
112
|
+
_run_git(repository_root, "add", "--", "new.py")
|
|
113
|
+
|
|
114
|
+
document_set = select_documents(LintRequest.staged(repository_root))
|
|
115
|
+
selected_document = _document_for(document_set, "new.py")
|
|
116
|
+
|
|
117
|
+
assert selected_document.text == "added\n"
|
|
118
|
+
assert selected_document.prior_text is None
|
|
119
|
+
assert document_set.deleted_paths == ()
|
|
120
|
+
assert selected_document.changed_lines == frozenset({1})
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_staged_deleted_file_should_record_a_true_delete_only(tmp_path: Path) -> None:
|
|
124
|
+
repository_root = _initialize_repository(tmp_path)
|
|
125
|
+
_commit_text(repository_root, "gone.py", "body\n")
|
|
126
|
+
_run_git(repository_root, "rm", "--quiet", "--", "gone.py")
|
|
127
|
+
|
|
128
|
+
document_set = select_documents(LintRequest.staged(repository_root))
|
|
129
|
+
|
|
130
|
+
assert document_set.documents == ()
|
|
131
|
+
assert document_set.deleted_paths == (PurePosixPath("gone.py"),)
|
|
132
|
+
assert document_set.renamed_paths == ()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_staged_renamed_file_should_keep_prior_path_metadata(tmp_path: Path) -> None:
|
|
136
|
+
repository_root = _initialize_repository(tmp_path)
|
|
137
|
+
_commit_text(repository_root, "old.py", "body\n")
|
|
138
|
+
_run_git(repository_root, "mv", "--", "old.py", "new.py")
|
|
139
|
+
|
|
140
|
+
document_set = select_documents(LintRequest.staged(repository_root))
|
|
141
|
+
selected_document = _document_for(document_set, "new.py")
|
|
142
|
+
|
|
143
|
+
assert selected_document.text == "body\n"
|
|
144
|
+
assert selected_document.prior_text == "body\n"
|
|
145
|
+
assert selected_document.prior_path == PurePosixPath("old.py")
|
|
146
|
+
assert document_set.renamed_paths == (
|
|
147
|
+
(PurePosixPath("old.py"), PurePosixPath("new.py")),
|
|
148
|
+
)
|
|
149
|
+
assert document_set.deleted_paths == ()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_staged_copied_file_should_not_record_a_rename(
|
|
153
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
154
|
+
) -> None:
|
|
155
|
+
repository_root = _initialize_repository(tmp_path)
|
|
156
|
+
_commit_text(repository_root, "source.py", "copied-body\n")
|
|
157
|
+
_write_text(repository_root, "copy.py", "copied-body\n")
|
|
158
|
+
_run_git(repository_root, "add", "--", "copy.py")
|
|
159
|
+
original_git_bytes_for = selection_module.git_bytes_for
|
|
160
|
+
|
|
161
|
+
def git_bytes_for_with_copy(
|
|
162
|
+
git_repository_root: Path, all_arguments: tuple[str, ...]
|
|
163
|
+
) -> bytes:
|
|
164
|
+
if constants.GIT_NAME_STATUS_FLAG in all_arguments:
|
|
165
|
+
return b"C100\0source.py\0copy.py\0"
|
|
166
|
+
return original_git_bytes_for(git_repository_root, all_arguments)
|
|
167
|
+
|
|
168
|
+
monkeypatch.setattr(selection_module, "git_bytes_for", git_bytes_for_with_copy)
|
|
169
|
+
document_set = select_documents(LintRequest.staged(repository_root))
|
|
170
|
+
selected_document = _document_for(document_set, "copy.py")
|
|
171
|
+
|
|
172
|
+
assert selected_document.text == "copied-body\n"
|
|
173
|
+
assert selected_document.prior_path is None
|
|
174
|
+
assert selected_document.prior_text is None
|
|
175
|
+
assert document_set.renamed_paths == ()
|
|
176
|
+
assert document_set.deleted_paths == ()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_staged_missing_non_delete_blob_should_raise(
|
|
180
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
181
|
+
) -> None:
|
|
182
|
+
repository_root = _initialize_repository(tmp_path)
|
|
183
|
+
_commit_text(repository_root, "file.py", "committed\n")
|
|
184
|
+
_write_text(repository_root, "file.py", "staged\n")
|
|
185
|
+
_run_git(repository_root, "add", "--", "file.py")
|
|
186
|
+
original_read_blob = selection_module.read_blob
|
|
187
|
+
|
|
188
|
+
def missing_index_blob(
|
|
189
|
+
git_repository_root: Path, revision: str, relative_path: str
|
|
190
|
+
) -> str | None:
|
|
191
|
+
if revision == constants.GIT_INDEX_REFERENCE_PREFIX:
|
|
192
|
+
return None
|
|
193
|
+
return original_read_blob(git_repository_root, revision, relative_path)
|
|
194
|
+
|
|
195
|
+
monkeypatch.setattr(selection_module, "read_blob", missing_index_blob)
|
|
196
|
+
with pytest.raises(SelectionRunFatal, match="unavailable"):
|
|
197
|
+
select_documents(LintRequest.staged(repository_root))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_git_blob_unexpected_failure_should_raise(
|
|
201
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
202
|
+
) -> None:
|
|
203
|
+
def failed_git(*_all_arguments: object, **_all_keywords: object) -> subprocess.CompletedProcess[bytes]:
|
|
204
|
+
return subprocess.CompletedProcess(
|
|
205
|
+
args=[constants.GIT_EXECUTABLE],
|
|
206
|
+
returncode=1,
|
|
207
|
+
stdout=b"",
|
|
208
|
+
stderr=b"fatal: Not a valid object name HEAD:file.py",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
monkeypatch.setattr(selection_git.subprocess, "run", failed_git)
|
|
212
|
+
with pytest.raises(selection_git.GitSelectionError, match="Not a valid object"):
|
|
213
|
+
selection_git.read_blob(tmp_path, "HEAD", "file.py")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_unborn_repository_head_should_return_none(tmp_path: Path) -> None:
|
|
217
|
+
repository_root = _initialize_repository(tmp_path)
|
|
218
|
+
assert selection_git.head_revision(repository_root) is None
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_unexpected_head_failure_should_raise(
|
|
222
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
223
|
+
) -> None:
|
|
224
|
+
def failed_git(
|
|
225
|
+
*_all_arguments: object, **_all_keywords: object
|
|
226
|
+
) -> subprocess.CompletedProcess[bytes]:
|
|
227
|
+
return subprocess.CompletedProcess(
|
|
228
|
+
args=[constants.GIT_EXECUTABLE],
|
|
229
|
+
returncode=1,
|
|
230
|
+
stdout=b"",
|
|
231
|
+
stderr=b"fatal: broken HEAD state",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
monkeypatch.setattr(selection_git.subprocess, "run", failed_git)
|
|
235
|
+
with pytest.raises(selection_git.GitSelectionError, match="broken HEAD"):
|
|
236
|
+
selection_git.head_revision(tmp_path)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_base_source_should_compare_merge_base_and_read_worktree_bytes(
|
|
240
|
+
tmp_path: Path,
|
|
241
|
+
) -> None:
|
|
242
|
+
repository_root = _initialize_repository(tmp_path)
|
|
243
|
+
_commit_text(repository_root, "file.py", "base-text\n")
|
|
244
|
+
_run_git(repository_root, "checkout", "--quiet", "-b", "feature")
|
|
245
|
+
_commit_text(repository_root, "file.py", "feature-committed\n")
|
|
246
|
+
_write_text(repository_root, "file.py", "feature-worktree\n")
|
|
247
|
+
|
|
248
|
+
document_set = select_documents(LintRequest.base(repository_root, "main"))
|
|
249
|
+
selected_document = _document_for(document_set, "file.py")
|
|
250
|
+
|
|
251
|
+
assert document_set.selection == SelectionKind.BASE
|
|
252
|
+
assert selected_document.text == "feature-worktree\n"
|
|
253
|
+
assert selected_document.prior_text == "base-text\n"
|
|
254
|
+
assert selected_document.origin == ContentOrigin.REVISION_DIFF
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_repository_source_should_read_tracked_worktree_files_only(
|
|
258
|
+
tmp_path: Path,
|
|
259
|
+
) -> None:
|
|
260
|
+
repository_root = _initialize_repository(tmp_path)
|
|
261
|
+
_commit_text(repository_root, "tracked.py", "tracked\n")
|
|
262
|
+
_write_text(repository_root, "untracked.py", "nope\n")
|
|
263
|
+
|
|
264
|
+
document_set = select_documents(LintRequest.repository(repository_root))
|
|
265
|
+
all_paths = {
|
|
266
|
+
each_document.path.as_posix() for each_document in document_set.documents
|
|
267
|
+
}
|
|
268
|
+
selected_document = _document_for(document_set, "tracked.py")
|
|
269
|
+
|
|
270
|
+
assert document_set.selection == SelectionKind.REPOSITORY
|
|
271
|
+
assert all_paths == {"tracked.py"}
|
|
272
|
+
assert selected_document.text == "tracked\n"
|
|
273
|
+
assert selected_document.origin == ContentOrigin.WORKTREE
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_prebuilt_document_set_should_keep_repository_relative_paths(
|
|
277
|
+
tmp_path: Path,
|
|
278
|
+
) -> None:
|
|
279
|
+
repository_root = _initialize_repository(tmp_path)
|
|
280
|
+
document_set = DocumentSet(
|
|
281
|
+
(
|
|
282
|
+
Document(
|
|
283
|
+
PurePosixPath("ok.py"),
|
|
284
|
+
"text\n",
|
|
285
|
+
None,
|
|
286
|
+
None,
|
|
287
|
+
ContentOrigin.EDITOR,
|
|
288
|
+
),
|
|
289
|
+
),
|
|
290
|
+
SelectionKind.TEXT,
|
|
291
|
+
repository_root,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
selected_document_set = select_documents(LintRequest(repository_root, document_set))
|
|
295
|
+
|
|
296
|
+
assert selected_document_set.documents[0].path == PurePosixPath("ok.py")
|
|
297
|
+
assert selected_document_set.selection == SelectionKind.TEXT
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_files_path_outside_repository_should_raise(tmp_path: Path) -> None:
|
|
301
|
+
repository_root = _initialize_repository(tmp_path)
|
|
302
|
+
outside_path = repository_root.parent / "escape.py"
|
|
303
|
+
with pytest.raises(SelectionRunFatal, match="outside"):
|
|
304
|
+
select_documents(LintRequest.files(repository_root, [outside_path]))
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_missing_git_should_raise_a_selection_error(
|
|
308
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
309
|
+
) -> None:
|
|
310
|
+
def missing_git(_repository_root: Path, _all_arguments: tuple[str, ...]) -> bytes:
|
|
311
|
+
raise FileNotFoundError("git")
|
|
312
|
+
|
|
313
|
+
monkeypatch.setattr(selection_module, "git_bytes_for", missing_git)
|
|
314
|
+
with pytest.raises(SelectionRunFatal, match="git"):
|
|
315
|
+
select_documents(LintRequest.files(tmp_path, [Path("file.py")]))
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def test_prebuilt_prior_path_outside_repository_should_raise(tmp_path: Path) -> None:
|
|
319
|
+
repository_root = _initialize_repository(tmp_path)
|
|
320
|
+
document_set = DocumentSet(
|
|
321
|
+
(
|
|
322
|
+
Document(
|
|
323
|
+
PurePosixPath("ok.py"),
|
|
324
|
+
"text\n",
|
|
325
|
+
None,
|
|
326
|
+
None,
|
|
327
|
+
ContentOrigin.EDITOR,
|
|
328
|
+
PurePosixPath("../escape"),
|
|
329
|
+
),
|
|
330
|
+
),
|
|
331
|
+
SelectionKind.TEXT,
|
|
332
|
+
repository_root,
|
|
333
|
+
)
|
|
334
|
+
with pytest.raises(SelectionRunFatal, match="outside"):
|
|
335
|
+
select_documents(LintRequest(repository_root, document_set))
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_prebuilt_deleted_path_outside_repository_should_raise(tmp_path: Path) -> None:
|
|
339
|
+
repository_root = _initialize_repository(tmp_path)
|
|
340
|
+
document_set = DocumentSet(
|
|
341
|
+
(),
|
|
342
|
+
SelectionKind.STAGED,
|
|
343
|
+
repository_root,
|
|
344
|
+
deleted_paths=(PurePosixPath("../escape"),),
|
|
345
|
+
)
|
|
346
|
+
with pytest.raises(SelectionRunFatal, match="outside"):
|
|
347
|
+
select_documents(LintRequest(repository_root, document_set))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def test_prebuilt_renamed_path_outside_repository_should_raise(tmp_path: Path) -> None:
|
|
351
|
+
repository_root = _initialize_repository(tmp_path)
|
|
352
|
+
document_set = DocumentSet(
|
|
353
|
+
(),
|
|
354
|
+
SelectionKind.STAGED,
|
|
355
|
+
repository_root,
|
|
356
|
+
renamed_paths=((PurePosixPath("../escape"), PurePosixPath("ok.py")),),
|
|
357
|
+
)
|
|
358
|
+
with pytest.raises(SelectionRunFatal, match="outside"):
|
|
359
|
+
select_documents(LintRequest(repository_root, document_set))
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def test_tracked_worktree_document_preserves_git_path_spelling(
|
|
363
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
364
|
+
) -> None:
|
|
365
|
+
monkeypatch.setattr(
|
|
366
|
+
selection_module,
|
|
367
|
+
"_read_worktree_text",
|
|
368
|
+
lambda _file_path: "body\n",
|
|
369
|
+
)
|
|
370
|
+
selected_document = selection_module._tracked_worktree_document(
|
|
371
|
+
tmp_path, "MiXeD.py"
|
|
372
|
+
)
|
|
373
|
+
assert selected_document.path == PurePosixPath("MiXeD.py")
|