From cca390fbd022af59d98332885f22367d422efea5 Mon Sep 17 00:00:00 2001 From: Antonov Ilia Date: Wed, 28 May 2025 12:16:22 +0300 Subject: [PATCH 1/5] fix(web): `_get_file_with_content` content encoding set to UTF-8 --- web/server/api/endpoints/files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/server/api/endpoints/files.py b/web/server/api/endpoints/files.py index 7e4d365227..d454418912 100644 --- a/web/server/api/endpoints/files.py +++ b/web/server/api/endpoints/files.py @@ -168,7 +168,7 @@ def walk_path( def _get_file_with_content(file_path: Path, relative_path: str) -> models.File: """Get a file, including its contents.""" try: - content = file_path.read_text() + content = file_path.read_text(encoding="utf-8") except FileNotFoundError as e: raise e except Exception: From e9144ed9bbd645b4c20e000c8475461b03fd4b73 Mon Sep 17 00:00:00 2001 From: Antonov Ilia Date: Wed, 28 May 2025 13:23:15 +0300 Subject: [PATCH 2/5] upd(web): added test covering UTF-8 for `_get_file_with_content` --- tests/web/test_main.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/web/test_main.py b/tests/web/test_main.py index 99b268cf0d..a996c203fa 100644 --- a/tests/web/test_main.py +++ b/tests/web/test_main.py @@ -106,6 +106,19 @@ def test_write_file(client: TestClient, project_tmp_path: Path) -> None: } +def test_write_file_non_ascii(client: TestClient, project_tmp_path: Path) -> None: + response = client.post("/api/files/foo.txt", json={"content": "何か良いこと"}) + file = _get_file_with_content(project_tmp_path / "foo.txt", "foo.txt") + assert response.status_code == 204 + assert file.dict() == { + "name": "foo.txt", + "path": "foo.txt", + "extension": ".txt", + "content": "何か良いこと", + } + + + def test_update_file(client: TestClient, project_tmp_path: Path) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.write_text("bar") From 17257998c331a052c7794ec55cba1f4fa06304d5 Mon Sep 17 00:00:00 2001 From: Antonov Ilia Date: Wed, 28 May 2025 14:39:15 +0300 Subject: [PATCH 3/5] chore: ruff file format --- tests/web/test_main.py | 52 +++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/tests/web/test_main.py b/tests/web/test_main.py index a996c203fa..25306f03d0 100644 --- a/tests/web/test_main.py +++ b/tests/web/test_main.py @@ -118,7 +118,6 @@ def test_write_file_non_ascii(client: TestClient, project_tmp_path: Path) -> Non } - def test_update_file(client: TestClient, project_tmp_path: Path) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.write_text("bar") @@ -150,7 +149,9 @@ def test_rename_file(client: TestClient, project_tmp_path: Path) -> None: assert not txt_file.exists() -def test_rename_file_and_keep_content(client: TestClient, project_tmp_path: Path) -> None: +def test_rename_file_and_keep_content( + client: TestClient, project_tmp_path: Path +) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.write_text("bar") @@ -191,7 +192,9 @@ def test_rename_file_already_exists(client: TestClient, project_tmp_path: Path) assert not foo_file.exists() -def test_rename_file_to_existing_directory(client: TestClient, project_tmp_path: Path) -> None: +def test_rename_file_to_existing_directory( + client: TestClient, project_tmp_path: Path +) -> None: foo_file = project_tmp_path / "foo.txt" foo_file.touch() existing_dir = project_tmp_path / "existing_dir" @@ -225,10 +228,17 @@ def test_create_directory(client: TestClient, project_tmp_path: Path) -> None: response = client.post("/api/directories/new_dir") assert response.status_code == 200 assert (project_tmp_path / "new_dir").exists() - assert response.json() == {"directories": [], "files": [], "name": "new_dir", "path": "new_dir"} + assert response.json() == { + "directories": [], + "files": [], + "name": "new_dir", + "path": "new_dir", + } -def test_create_directory_already_exists(client: TestClient, project_tmp_path: Path) -> None: +def test_create_directory_already_exists( + client: TestClient, project_tmp_path: Path +) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() @@ -253,7 +263,9 @@ def test_rename_directory(client: TestClient, project_tmp_path: Path) -> None: } -def test_rename_directory_already_exists_empty(client: TestClient, project_tmp_path: Path) -> None: +def test_rename_directory_already_exists_empty( + client: TestClient, project_tmp_path: Path +) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() existing_dir = project_tmp_path / "renamed_dir" @@ -287,7 +299,9 @@ def test_rename_directory_already_exists_not_empty( assert new_dir.exists() -def test_rename_directory_to_existing_file(client: TestClient, project_tmp_path: Path) -> None: +def test_rename_directory_to_existing_file( + client: TestClient, project_tmp_path: Path +) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() existing_file = project_tmp_path / "foo.txt" @@ -313,7 +327,9 @@ def test_delete_directory_not_found(client: TestClient, project_tmp_path: Path) assert response.status_code == 404 -def test_delete_directory_not_a_directory(client: TestClient, project_tmp_path: Path) -> None: +def test_delete_directory_not_a_directory( + client: TestClient, project_tmp_path: Path +) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.touch() @@ -377,7 +393,9 @@ def test_plan_test_failures( async def test_cancel(client: TestClient) -> None: client.app.state.circuit_breaker = threading.Event() # type: ignore transport = ASGITransport(client.app) # type: ignore - async with AsyncClient(transport=transport, base_url="http://testserver") as _client: + async with AsyncClient( + transport=transport, base_url="http://testserver" + ) as _client: await _client.post("/api/plan", json={"environment": "dev"}) response = await _client.post("/api/plan/cancel") assert response.status_code == 204 @@ -422,7 +440,9 @@ def test_modules(client: TestClient) -> None: def test_fetchdf(client: TestClient, web_sushi_context: Context) -> None: - response = client.post("/api/commands/fetchdf", json={"sql": "SELECT * from sushi.top_waiters"}) + response = client.post( + "/api/commands/fetchdf", json={"sql": "SELECT * from sushi.top_waiters"} + ) assert response.status_code == 200 with pa.ipc.open_stream(response.content) as reader: df = reader.read_pandas() @@ -490,7 +510,9 @@ def test_get_environments(client: TestClient, project_context: Context) -> None: plan_id="", suffix_target="schema", ) - assert response_json["pinned_environments"] == list(project_context.config.pinned_environments) + assert response_json["pinned_environments"] == list( + project_context.config.pinned_environments + ) assert ( response_json["default_target_environment"] == project_context.config.default_target_environment @@ -507,7 +529,9 @@ def test_delete_environment_failure( client: TestClient, web_sushi_context: Context, mocker: MockerFixture ): mocker.patch.object( - web_sushi_context.state_sync, "invalidate_environment", side_effect=Exception("Some error") + web_sushi_context.state_sync, + "invalidate_environment", + side_effect=Exception("Some error"), ) response = client.delete("/api/environments/test") @@ -544,7 +568,9 @@ def test_test(client: TestClient, web_sushi_context: Context) -> None: assert response_json["failures"] == [] # Single test - response = client.get("/api/commands/test", params={"test": "tests/test_order_items.yaml"}) + response = client.get( + "/api/commands/test", params={"test": "tests/test_order_items.yaml"} + ) assert response.status_code == 200 response_json = response.json() assert response_json["tests_run"] == 1 From 5f83b92791b061535a4dfbb57f6f2a4c188ec8f7 Mon Sep 17 00:00:00 2001 From: Antonov Ilia Date: Thu, 29 May 2025 09:09:23 +0300 Subject: [PATCH 4/5] chore: make style code format --- tests/web/test_main.py | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/tests/web/test_main.py b/tests/web/test_main.py index 25306f03d0..cf2220ad6d 100644 --- a/tests/web/test_main.py +++ b/tests/web/test_main.py @@ -149,9 +149,7 @@ def test_rename_file(client: TestClient, project_tmp_path: Path) -> None: assert not txt_file.exists() -def test_rename_file_and_keep_content( - client: TestClient, project_tmp_path: Path -) -> None: +def test_rename_file_and_keep_content(client: TestClient, project_tmp_path: Path) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.write_text("bar") @@ -192,9 +190,7 @@ def test_rename_file_already_exists(client: TestClient, project_tmp_path: Path) assert not foo_file.exists() -def test_rename_file_to_existing_directory( - client: TestClient, project_tmp_path: Path -) -> None: +def test_rename_file_to_existing_directory(client: TestClient, project_tmp_path: Path) -> None: foo_file = project_tmp_path / "foo.txt" foo_file.touch() existing_dir = project_tmp_path / "existing_dir" @@ -236,9 +232,7 @@ def test_create_directory(client: TestClient, project_tmp_path: Path) -> None: } -def test_create_directory_already_exists( - client: TestClient, project_tmp_path: Path -) -> None: +def test_create_directory_already_exists(client: TestClient, project_tmp_path: Path) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() @@ -263,9 +257,7 @@ def test_rename_directory(client: TestClient, project_tmp_path: Path) -> None: } -def test_rename_directory_already_exists_empty( - client: TestClient, project_tmp_path: Path -) -> None: +def test_rename_directory_already_exists_empty(client: TestClient, project_tmp_path: Path) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() existing_dir = project_tmp_path / "renamed_dir" @@ -299,9 +291,7 @@ def test_rename_directory_already_exists_not_empty( assert new_dir.exists() -def test_rename_directory_to_existing_file( - client: TestClient, project_tmp_path: Path -) -> None: +def test_rename_directory_to_existing_file(client: TestClient, project_tmp_path: Path) -> None: new_dir = project_tmp_path / "new_dir" new_dir.mkdir() existing_file = project_tmp_path / "foo.txt" @@ -327,9 +317,7 @@ def test_delete_directory_not_found(client: TestClient, project_tmp_path: Path) assert response.status_code == 404 -def test_delete_directory_not_a_directory( - client: TestClient, project_tmp_path: Path -) -> None: +def test_delete_directory_not_a_directory(client: TestClient, project_tmp_path: Path) -> None: txt_file = project_tmp_path / "foo.txt" txt_file.touch() @@ -393,9 +381,7 @@ def test_plan_test_failures( async def test_cancel(client: TestClient) -> None: client.app.state.circuit_breaker = threading.Event() # type: ignore transport = ASGITransport(client.app) # type: ignore - async with AsyncClient( - transport=transport, base_url="http://testserver" - ) as _client: + async with AsyncClient(transport=transport, base_url="http://testserver") as _client: await _client.post("/api/plan", json={"environment": "dev"}) response = await _client.post("/api/plan/cancel") assert response.status_code == 204 @@ -440,9 +426,7 @@ def test_modules(client: TestClient) -> None: def test_fetchdf(client: TestClient, web_sushi_context: Context) -> None: - response = client.post( - "/api/commands/fetchdf", json={"sql": "SELECT * from sushi.top_waiters"} - ) + response = client.post("/api/commands/fetchdf", json={"sql": "SELECT * from sushi.top_waiters"}) assert response.status_code == 200 with pa.ipc.open_stream(response.content) as reader: df = reader.read_pandas() @@ -510,9 +494,7 @@ def test_get_environments(client: TestClient, project_context: Context) -> None: plan_id="", suffix_target="schema", ) - assert response_json["pinned_environments"] == list( - project_context.config.pinned_environments - ) + assert response_json["pinned_environments"] == list(project_context.config.pinned_environments) assert ( response_json["default_target_environment"] == project_context.config.default_target_environment @@ -568,9 +550,7 @@ def test_test(client: TestClient, web_sushi_context: Context) -> None: assert response_json["failures"] == [] # Single test - response = client.get( - "/api/commands/test", params={"test": "tests/test_order_items.yaml"} - ) + response = client.get("/api/commands/test", params={"test": "tests/test_order_items.yaml"}) assert response.status_code == 200 response_json = response.json() assert response_json["tests_run"] == 1 From e7af0c051fb8ab266b458adbf6338668399beaee Mon Sep 17 00:00:00 2001 From: Antonov Ilia Date: Tue, 17 Jun 2025 12:58:22 +0300 Subject: [PATCH 5/5] fix(web): non-posix relative path for Windows OS causes "File not found" in UI "Source Code" tab --- web/server/api/endpoints/files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/server/api/endpoints/files.py b/web/server/api/endpoints/files.py index db58fce55e..880171855f 100644 --- a/web/server/api/endpoints/files.py +++ b/web/server/api/endpoints/files.py @@ -181,6 +181,6 @@ def _get_file_with_content(file_path: Path, relative_path: str) -> models.File: return models.File( name=file_path.name, - path=relative_path, + path=str(Path(relative_path).as_posix()), content=content, )