Skip to content

Commit ca8a42e

Browse files
authored
Fix directory tests (#458)
1 parent 1e1e51d commit ca8a42e

File tree

8 files changed

+51
-30
lines changed

8 files changed

+51
-30
lines changed

.github/actions/run-ats/action.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ runs:
4848
echo "No tests to run, skipping..."
4949
exit 0
5050
fi
51-
echo $TESTS_TO_RUN | xargs uv run pytest --cov \
51+
echo $TESTS_TO_RUN | xargs uv run --frozen pytest --cov \
5252
-o junit_suite_name="${{ github.job }}" \
5353
-n auto \
5454
-vv \
55-
--cov \
5655
--cov-append \
5756
${{ inputs.collect_args }}
5857

src/codegen/extensions/lsp/io.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
@dataclass
1818
class File:
19-
doc: TextDocument
19+
doc: TextDocument | None
2020
path: Path
2121
change: TextEdit | None = None
2222
other_change: CreateFile | RenameFile | DeleteFile | None = None
@@ -65,6 +65,8 @@ def read_text(self, path: Path) -> str:
6565
return file.change.new_text
6666
if file.created:
6767
return ""
68+
if file.doc is None:
69+
return self.base_io.read_text(path)
6870
return file.doc.source
6971

7072
def read_bytes(self, path: Path) -> bytes:
@@ -76,6 +78,8 @@ def read_bytes(self, path: Path) -> bytes:
7678
return file.change.new_text.encode("utf-8")
7779
if file.created:
7880
return b""
81+
if file.doc is None:
82+
return self.base_io.read_bytes(path)
7983
return file.doc.source.encode("utf-8")
8084

8185
def write_bytes(self, path: Path, content: bytes) -> None:
@@ -112,6 +116,8 @@ def file_exists(self, path: Path) -> bool:
112116
return True
113117
if file.created:
114118
return True
119+
if file.doc is None:
120+
return self.base_io.file_exists(path)
115121
try:
116122
file.doc.source
117123
return True
@@ -134,3 +140,13 @@ def get_workspace_edit(self) -> types.WorkspaceEdit:
134140
file.change = None
135141
logger.info(f"Workspace edit: {pprint.pformat(list(map(asdict, document_changes)))}")
136142
return types.WorkspaceEdit(document_changes=document_changes)
143+
144+
def update_file(self, path: Path, version: int | None = None) -> None:
145+
file = self._get_file(path)
146+
file.doc = self.workspace.get_text_document(path.as_uri())
147+
if version is not None:
148+
file.version = version
149+
150+
def close_file(self, path: Path) -> None:
151+
file = self._get_file(path)
152+
file.doc = None

src/codegen/extensions/lsp/lsp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def did_open(server: CodegenLanguageServer, params: types.DidOpenTextDocumentPar
2424
# The document is automatically added to the workspace by pygls
2525
# We can perform any additional processing here if needed
2626
path = get_path(params.text_document.uri)
27+
server.io.update_file(path, params.text_document.version)
2728
file = server.codebase.get_file(str(path), optional=True)
2829
if not isinstance(file, SourceFile) and path.suffix in server.codebase.ctx.extensions:
2930
sync = DiffLite(change_type=ChangeType.Added, path=path)
@@ -37,6 +38,7 @@ def did_change(server: CodegenLanguageServer, params: types.DidChangeTextDocumen
3738
# The document is automatically updated in the workspace by pygls
3839
# We can perform any additional processing here if needed
3940
path = get_path(params.text_document.uri)
41+
server.io.update_file(path, params.text_document.version)
4042
sync = DiffLite(change_type=ChangeType.Modified, path=path)
4143
server.codebase.ctx.apply_diffs([sync])
4244

@@ -63,6 +65,8 @@ def did_close(server: CodegenLanguageServer, params: types.DidCloseTextDocumentP
6365
logger.info(f"Document closed: {params.text_document.uri}")
6466
# The document is automatically removed from the workspace by pygls
6567
# We can perform any additional cleanup here if needed
68+
path = get_path(params.text_document.uri)
69+
server.io.close_file(path)
6670

6771

6872
@server.feature(

src/codegen/sdk/core/codebase.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ def get_file_from_path(path: Path) -> File | None:
510510
if file is not None:
511511
return file
512512
absolute_path = self.ctx.to_absolute(filepath)
513-
if absolute_path.suffix in self.ctx.extensions:
513+
if absolute_path.suffix in self.ctx.extensions and not self.ctx.io.file_exists(absolute_path):
514514
return None
515515
if self.ctx.io.file_exists(absolute_path):
516516
return get_file_from_path(absolute_path)

tests/unit/codegen/extensions/lsp/test_workspace_sync.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Callable
2+
13
import pytest
24
from lsprotocol.types import (
35
DidChangeTextDocumentParams,
@@ -16,6 +18,7 @@
1618
from pytest_lsp import LanguageClient
1719

1820
from codegen.sdk.core.codebase import Codebase
21+
from tests.unit.codegen.extensions.lsp.utils import apply_edit
1922

2023

2124
@pytest.fixture()
@@ -88,6 +91,7 @@ def example_function():
8891
""".strip(),
8992
),
9093
],
94+
ids=["example_function"],
9195
indirect=["document_uri", "original"],
9296
)
9397
async def test_did_change(
@@ -137,7 +141,7 @@ def example_function():
137141
pass
138142
""".strip(),
139143
},
140-
"file://{worskpaceFolder}test.py",
144+
"file://{worskpaceFolder}/test.py",
141145
),
142146
],
143147
)
@@ -165,11 +169,11 @@ async def test_did_close(
165169

166170
# Verify the document is removed from the workspace
167171
document = await client.workspace_text_document_content_async(TextDocumentContentParams(uri=document_uri))
168-
assert document.text == ""
172+
assert document.text == original["test.py"]
169173

170174

171175
@pytest.mark.parametrize(
172-
"original, document_uri, position, new_name, expected_text",
176+
"original, document_uri, position, new_name, expected",
173177
[
174178
(
175179
{
@@ -182,15 +186,17 @@ def main():
182186
""".strip(),
183187
},
184188
"file://{workspaceFolder}/test.py",
185-
Position(line=0, character=0), # Position of 'example_function'
189+
Position(line=0, character=5), # Position of 'example_function'
186190
"renamed_function",
187-
"""
191+
{
192+
"test.py": """
188193
def renamed_function():
189194
pass # modified
190195
191196
def main():
192197
renamed_function()
193198
""".strip(),
199+
},
194200
),
195201
],
196202
indirect=["document_uri", "original"],
@@ -202,7 +208,7 @@ async def test_rename_after_sync(
202208
document_uri: str,
203209
position: Position,
204210
new_name: str,
205-
expected_text: str,
211+
assert_expected: Callable,
206212
):
207213
# First open the document
208214
client.text_document_did_open(
@@ -243,8 +249,6 @@ async def test_rename_after_sync(
243249
new_name=new_name,
244250
)
245251
)
246-
247-
# Verify the rename was successful
248-
document = await client.workspace_text_document_content_async(TextDocumentContentParams(uri=document_uri))
249-
assert document is not None
250-
assert document.text == expected_text
252+
if result:
253+
apply_edit(codebase, result)
254+
assert_expected(codebase)

tests/unit/codegen/extensions/lsp/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ def apply_edit(codebase: Codebase, edit: WorkspaceEdit):
1010
path = get_path(change.text_document.uri)
1111
file = codebase.get_file(str(path.relative_to(codebase.repo_path)))
1212
for edit in change.edits:
13-
print("BRUH")
1413
file.edit(edit.new_text)
1514
if isinstance(change, CreateFile):
1615
path = get_path(change.uri)

tests/unit/codegen/sdk/core/test_directory.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313

1414
@pytest.fixture
15-
def mock_codebase_graph(tmp_path):
15+
def mock_codebase_context(tmp_path):
1616
mock = MagicMock(spec=CodebaseContext)
1717
mock.transaction_manager = MagicMock()
1818
mock.config = CodebaseConfig()
@@ -39,8 +39,8 @@ def sub_dir(subdir_path, tmp_path):
3939

4040

4141
@pytest.fixture
42-
def mock_file(dir_path, mock_codebase_graph):
43-
return File(filepath=dir_path / "example.py", ctx=mock_codebase_graph)
42+
def mock_file(dir_path, mock_codebase_context):
43+
return File(filepath=dir_path / "example.py", ctx=mock_codebase_context)
4444

4545

4646
@pytest.fixture
@@ -66,9 +66,9 @@ def test_name_property(mock_directory):
6666
assert mock_directory.name == "mock_dir"
6767

6868

69-
def test_add_and_file(mock_directory, mock_codebase_graph):
69+
def test_add_and_file(mock_directory, mock_codebase_context):
7070
"""Test adding a file to the directory."""
71-
mock_file = File(filepath=Path("mock_dir/example_2.py"), ctx=mock_codebase_graph)
71+
mock_file = File(filepath=Path("mock_dir/example_2.py"), ctx=mock_codebase_context)
7272
mock_directory.add_file(mock_file)
7373
rel_path = os.path.relpath(mock_file.file_path, mock_directory.dirpath)
7474
assert rel_path in mock_directory.items
@@ -139,12 +139,12 @@ def test_get_subdirectory(mock_directory, sub_dir):
139139
assert retrieved_subdir is sub_dir
140140

141141

142-
def test_files_property(mock_directory, sub_dir, mock_codebase_graph):
142+
def test_files_property(mock_directory, sub_dir, mock_codebase_context):
143143
"""Test the 'files' property returns all files recursively."""
144144
all_files = mock_directory.files
145145
assert len(all_files) == 1
146146

147-
new_file = File(filepath=Path("mock_dir/example_2.py"), ctx=mock_codebase_graph)
147+
new_file = File(filepath=Path("mock_dir/example_2.py"), ctx=mock_codebase_context)
148148
sub_dir.add_file(new_file)
149149

150150
all_files = mock_directory.files
@@ -171,26 +171,26 @@ def test_subdirectories_property(mock_directory, sub_dir):
171171
assert new_sub_dir in all_subdirs
172172

173173

174-
def test_update_filepath(mock_directory, mock_codebase_graph, mock_file):
174+
def test_update_filepath(mock_directory, mock_codebase_context, mock_file):
175175
"""Test updating file paths when the directory path changes."""
176176
mock_directory.update_filepath("/absolute/new_mock_dir")
177177

178178
# Verify the files have updated file paths
179-
mock_codebase_graph.transaction_manager.add_file_rename_transaction.assert_called_once_with(mock_file, "/absolute/new_mock_dir/example.py")
179+
mock_codebase_context.transaction_manager.add_file_rename_transaction.assert_called_once_with(mock_file, "/absolute/new_mock_dir/example.py")
180180

181181

182-
def test_remove(mock_directory, sub_dir, mock_codebase_graph, mock_file):
182+
def test_remove(mock_directory, sub_dir, mock_codebase_context, mock_file):
183183
mock_directory.remove()
184184

185-
mock_codebase_graph.transaction_manager.add_file_remove_transaction.assert_called_once_with(mock_file)
185+
mock_codebase_context.transaction_manager.add_file_remove_transaction.assert_called_once_with(mock_file)
186186

187187

188-
def test_rename(mock_directory, mock_codebase_graph, mock_file):
188+
def test_rename(mock_directory, mock_codebase_context, mock_file):
189189
"""Test renaming the directory."""
190190
mock_directory.rename("renamed_dir")
191191
# This fails because it is not implemented to rename the directory itself.
192192
# assert mock_directory.dirpath == "/absolute/renamed_dir"
193-
mock_codebase_graph.transaction_manager.add_file_rename_transaction.assert_called_once_with(mock_file, "renamed_dir/example.py")
193+
mock_codebase_context.transaction_manager.add_file_rename_transaction.assert_called_once_with(mock_file, "renamed_dir/example.py")
194194

195195

196196
def test_iteration(mock_directory):

uv.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)