|
| 1 | +import os |
1 | 2 | from contextlib import asynccontextmanager |
2 | 3 | from unittest.mock import AsyncMock, MagicMock, patch |
3 | 4 |
|
|
6 | 7 | from pygls.server import LanguageServer |
7 | 8 |
|
8 | 9 | from vectorcode import __version__ |
9 | | -from vectorcode.cli_utils import CliAction, Config, QueryInclude |
| 10 | +from vectorcode.cli_utils import CliAction, Config, FilesAction, QueryInclude |
10 | 11 | from vectorcode.lsp_main import ( |
11 | 12 | execute_command, |
12 | 13 | lsp_start, |
@@ -374,3 +375,124 @@ async def test_execute_command_no_default_project_root( |
374 | 375 | with pytest.raises((AssertionError, JsonRpcInternalError)): |
375 | 376 | await execute_command(mock_language_server, ["query", "test"]) |
376 | 377 | DEFAULT_PROJECT_ROOT = None # Reset the global variable |
| 378 | + |
| 379 | + |
| 380 | +@pytest.mark.asyncio |
| 381 | +async def test_execute_command_files_ls(mock_language_server, mock_config: Config): |
| 382 | + mock_config.action = CliAction.files |
| 383 | + mock_config.files_action = FilesAction.ls |
| 384 | + mock_config.project_root = "/test/project" |
| 385 | + |
| 386 | + dummy_files = ["/test/project/file1.py", "/test/project/file2.txt"] |
| 387 | + mock_client = AsyncMock() |
| 388 | + mock_collection = AsyncMock() |
| 389 | + |
| 390 | + with ( |
| 391 | + patch( |
| 392 | + "vectorcode.lsp_main.parse_cli_args", new_callable=AsyncMock |
| 393 | + ) as mock_parse_cli_args, |
| 394 | + patch( |
| 395 | + "vectorcode.lsp_main.ClientManager._create_client", return_value=mock_client |
| 396 | + ), |
| 397 | + patch("vectorcode.lsp_main.get_collection", return_value=mock_collection), |
| 398 | + patch( |
| 399 | + "vectorcode.lsp_main.list_collection_files", return_value=dummy_files |
| 400 | + ) as mock_list_collection_files, |
| 401 | + ): |
| 402 | + mock_parse_cli_args.return_value = mock_config |
| 403 | + |
| 404 | + mock_config.merge_from = AsyncMock(return_value=mock_config) |
| 405 | + |
| 406 | + result = await execute_command(mock_language_server, ["files", "ls"]) |
| 407 | + |
| 408 | + assert result == dummy_files |
| 409 | + mock_language_server.progress.create_async.assert_called_once() |
| 410 | + |
| 411 | + mock_list_collection_files.assert_called_once_with(mock_collection) |
| 412 | + # For 'ls' action, progress.begin/end are not explicitly called in the lsp_main, |
| 413 | + # but create_async is called before the match statement. |
| 414 | + mock_language_server.progress.begin.assert_not_called() |
| 415 | + mock_language_server.progress.end.assert_not_called() |
| 416 | + |
| 417 | + |
| 418 | +@pytest.mark.asyncio |
| 419 | +async def test_execute_command_files_rm(mock_language_server, mock_config: Config): |
| 420 | + mock_config.action = CliAction.files |
| 421 | + mock_config.files_action = FilesAction.rm |
| 422 | + mock_config.project_root = "/test/project" |
| 423 | + mock_config.rm_paths = ["file_to_remove.py", "another_file.txt"] |
| 424 | + |
| 425 | + expanded_paths = [ |
| 426 | + "/test/project/file_to_remove.py", |
| 427 | + "/test/project/another_file.txt", |
| 428 | + ] |
| 429 | + mock_client = AsyncMock() |
| 430 | + mock_collection = AsyncMock() |
| 431 | + |
| 432 | + with ( |
| 433 | + patch( |
| 434 | + "vectorcode.lsp_main.parse_cli_args", new_callable=AsyncMock |
| 435 | + ) as mock_parse_cli_args, |
| 436 | + patch( |
| 437 | + "vectorcode.lsp_main.ClientManager._create_client", return_value=mock_client |
| 438 | + ), |
| 439 | + patch("vectorcode.lsp_main.get_collection", return_value=mock_collection), |
| 440 | + patch( |
| 441 | + "os.path.isfile", |
| 442 | + side_effect=lambda x: x in expanded_paths or x in mock_config.rm_paths, |
| 443 | + ), |
| 444 | + patch( |
| 445 | + "vectorcode.lsp_main.expand_path", |
| 446 | + side_effect=lambda p, *args: os.path.join(mock_config.project_root, p), |
| 447 | + ), |
| 448 | + ): |
| 449 | + mock_parse_cli_args.return_value = mock_config |
| 450 | + |
| 451 | + mock_config.merge_from = AsyncMock(return_value=mock_config) |
| 452 | + |
| 453 | + await execute_command( |
| 454 | + mock_language_server, |
| 455 | + ["files", "rm", "file_to_remove.py", "another_file.txt"], |
| 456 | + ) |
| 457 | + |
| 458 | + mock_collection.delete.assert_called_once_with( |
| 459 | + where={"path": {"$in": expanded_paths}} |
| 460 | + ) |
| 461 | + |
| 462 | + |
| 463 | +@pytest.mark.asyncio |
| 464 | +async def test_execute_command_files_rm_no_files_to_remove( |
| 465 | + mock_language_server, mock_config: Config |
| 466 | +): |
| 467 | + mock_config.action = CliAction.files |
| 468 | + mock_config.files_action = FilesAction.rm |
| 469 | + mock_config.project_root = "/test/project" |
| 470 | + mock_config.rm_paths = ["non_existent_file.py"] |
| 471 | + |
| 472 | + mock_client = AsyncMock() |
| 473 | + mock_collection = AsyncMock() |
| 474 | + |
| 475 | + with ( |
| 476 | + patch( |
| 477 | + "vectorcode.lsp_main.parse_cli_args", new_callable=AsyncMock |
| 478 | + ) as mock_parse_cli_args, |
| 479 | + patch( |
| 480 | + "vectorcode.lsp_main.ClientManager._create_client", return_value=mock_client |
| 481 | + ), |
| 482 | + patch("vectorcode.lsp_main.get_collection", return_value=mock_collection), |
| 483 | + patch("os.path.isfile", return_value=False), |
| 484 | + patch( |
| 485 | + "vectorcode.lsp_main.expand_path", |
| 486 | + side_effect=lambda p, *args: os.path.join(mock_config.project_root, p), |
| 487 | + ), |
| 488 | + ): |
| 489 | + mock_parse_cli_args.return_value = mock_config |
| 490 | + |
| 491 | + mock_config.merge_from = AsyncMock(return_value=mock_config) |
| 492 | + |
| 493 | + result = await execute_command( |
| 494 | + mock_language_server, ["files", "rm", "non_existent_file.py"] |
| 495 | + ) |
| 496 | + |
| 497 | + assert result is None |
| 498 | + mock_collection.delete.assert_not_called() |
0 commit comments