cognee 0.2.3.dev1__py3-none-any.whl → 0.3.0.dev0__py3-none-any.whl
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.
- cognee/__init__.py +1 -0
- cognee/__main__.py +4 -0
- cognee/api/client.py +28 -3
- cognee/api/health.py +10 -13
- cognee/api/v1/add/add.py +20 -6
- cognee/api/v1/add/routers/get_add_router.py +12 -37
- cognee/api/v1/cloud/routers/__init__.py +1 -0
- cognee/api/v1/cloud/routers/get_checks_router.py +23 -0
- cognee/api/v1/cognify/code_graph_pipeline.py +14 -3
- cognee/api/v1/cognify/cognify.py +67 -105
- cognee/api/v1/cognify/routers/get_cognify_router.py +11 -3
- cognee/api/v1/datasets/routers/get_datasets_router.py +16 -5
- cognee/api/v1/memify/routers/__init__.py +1 -0
- cognee/api/v1/memify/routers/get_memify_router.py +100 -0
- cognee/api/v1/notebooks/routers/__init__.py +1 -0
- cognee/api/v1/notebooks/routers/get_notebooks_router.py +96 -0
- cognee/api/v1/responses/default_tools.py +4 -0
- cognee/api/v1/responses/dispatch_function.py +6 -1
- cognee/api/v1/responses/models.py +1 -1
- cognee/api/v1/search/routers/get_search_router.py +20 -1
- cognee/api/v1/search/search.py +17 -4
- cognee/api/v1/sync/__init__.py +17 -0
- cognee/api/v1/sync/routers/__init__.py +3 -0
- cognee/api/v1/sync/routers/get_sync_router.py +241 -0
- cognee/api/v1/sync/sync.py +877 -0
- cognee/api/v1/users/routers/get_auth_router.py +13 -1
- cognee/base_config.py +10 -1
- cognee/cli/__init__.py +10 -0
- cognee/cli/_cognee.py +180 -0
- cognee/cli/commands/__init__.py +1 -0
- cognee/cli/commands/add_command.py +80 -0
- cognee/cli/commands/cognify_command.py +128 -0
- cognee/cli/commands/config_command.py +225 -0
- cognee/cli/commands/delete_command.py +80 -0
- cognee/cli/commands/search_command.py +149 -0
- cognee/cli/config.py +33 -0
- cognee/cli/debug.py +21 -0
- cognee/cli/echo.py +45 -0
- cognee/cli/exceptions.py +23 -0
- cognee/cli/minimal_cli.py +97 -0
- cognee/cli/reference.py +26 -0
- cognee/cli/suppress_logging.py +12 -0
- cognee/eval_framework/corpus_builder/corpus_builder_executor.py +2 -2
- cognee/eval_framework/eval_config.py +1 -1
- cognee/infrastructure/databases/graph/config.py +10 -4
- cognee/infrastructure/databases/graph/get_graph_engine.py +4 -9
- cognee/infrastructure/databases/graph/kuzu/adapter.py +199 -2
- cognee/infrastructure/databases/graph/neo4j_driver/adapter.py +138 -0
- cognee/infrastructure/databases/relational/__init__.py +2 -0
- cognee/infrastructure/databases/relational/get_async_session.py +15 -0
- cognee/infrastructure/databases/relational/sqlalchemy/SqlAlchemyAdapter.py +6 -1
- cognee/infrastructure/databases/relational/with_async_session.py +25 -0
- cognee/infrastructure/databases/vector/chromadb/ChromaDBAdapter.py +1 -1
- cognee/infrastructure/databases/vector/config.py +13 -6
- cognee/infrastructure/databases/vector/embeddings/FastembedEmbeddingEngine.py +6 -4
- cognee/infrastructure/databases/vector/embeddings/LiteLLMEmbeddingEngine.py +16 -7
- cognee/infrastructure/databases/vector/embeddings/OllamaEmbeddingEngine.py +5 -5
- cognee/infrastructure/databases/vector/embeddings/config.py +2 -2
- cognee/infrastructure/databases/vector/embeddings/embedding_rate_limiter.py +2 -6
- cognee/infrastructure/databases/vector/embeddings/get_embedding_engine.py +10 -7
- cognee/infrastructure/files/storage/LocalFileStorage.py +9 -0
- cognee/infrastructure/files/storage/S3FileStorage.py +5 -0
- cognee/infrastructure/files/storage/StorageManager.py +7 -1
- cognee/infrastructure/files/storage/storage.py +16 -0
- cognee/infrastructure/files/utils/get_data_file_path.py +14 -9
- cognee/infrastructure/files/utils/get_file_metadata.py +2 -1
- cognee/infrastructure/llm/LLMGateway.py +32 -5
- cognee/infrastructure/llm/config.py +6 -4
- cognee/infrastructure/llm/prompts/extract_query_time.txt +15 -0
- cognee/infrastructure/llm/prompts/generate_event_entity_prompt.txt +25 -0
- cognee/infrastructure/llm/prompts/generate_event_graph_prompt.txt +30 -0
- cognee/infrastructure/llm/structured_output_framework/baml/baml_src/extraction/knowledge_graph/extract_content_graph.py +16 -5
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/__init__.py +2 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/extract_event_entities.py +44 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/knowledge_graph/__init__.py +1 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/knowledge_graph/extract_content_graph.py +19 -15
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/extraction/knowledge_graph/extract_event_graph.py +46 -0
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/anthropic/adapter.py +3 -3
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/gemini/adapter.py +3 -3
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/generic_llm_api/adapter.py +2 -2
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/get_llm_client.py +14 -8
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/ollama/adapter.py +6 -4
- cognee/infrastructure/llm/structured_output_framework/litellm_instructor/llm/openai/adapter.py +28 -4
- cognee/infrastructure/llm/tokenizer/Gemini/adapter.py +2 -2
- cognee/infrastructure/llm/tokenizer/HuggingFace/adapter.py +3 -3
- cognee/infrastructure/llm/tokenizer/Mistral/adapter.py +3 -3
- cognee/infrastructure/llm/tokenizer/TikToken/adapter.py +6 -6
- cognee/infrastructure/llm/utils.py +7 -7
- cognee/infrastructure/utils/run_sync.py +8 -1
- cognee/modules/chunking/models/DocumentChunk.py +4 -3
- cognee/modules/cloud/exceptions/CloudApiKeyMissingError.py +15 -0
- cognee/modules/cloud/exceptions/CloudConnectionError.py +15 -0
- cognee/modules/cloud/exceptions/__init__.py +2 -0
- cognee/modules/cloud/operations/__init__.py +1 -0
- cognee/modules/cloud/operations/check_api_key.py +25 -0
- cognee/modules/data/deletion/prune_system.py +1 -1
- cognee/modules/data/methods/__init__.py +2 -0
- cognee/modules/data/methods/check_dataset_name.py +1 -1
- cognee/modules/data/methods/create_authorized_dataset.py +19 -0
- cognee/modules/data/methods/get_authorized_dataset.py +11 -5
- cognee/modules/data/methods/get_authorized_dataset_by_name.py +16 -0
- cognee/modules/data/methods/get_dataset_data.py +1 -1
- cognee/modules/data/methods/load_or_create_datasets.py +2 -20
- cognee/modules/engine/models/Event.py +16 -0
- cognee/modules/engine/models/Interval.py +8 -0
- cognee/modules/engine/models/Timestamp.py +13 -0
- cognee/modules/engine/models/__init__.py +3 -0
- cognee/modules/engine/utils/__init__.py +2 -0
- cognee/modules/engine/utils/generate_event_datapoint.py +46 -0
- cognee/modules/engine/utils/generate_timestamp_datapoint.py +51 -0
- cognee/modules/graph/cognee_graph/CogneeGraph.py +2 -2
- cognee/modules/graph/methods/get_formatted_graph_data.py +3 -2
- cognee/modules/graph/utils/__init__.py +1 -0
- cognee/modules/graph/utils/resolve_edges_to_text.py +71 -0
- cognee/modules/memify/__init__.py +1 -0
- cognee/modules/memify/memify.py +118 -0
- cognee/modules/notebooks/methods/__init__.py +5 -0
- cognee/modules/notebooks/methods/create_notebook.py +26 -0
- cognee/modules/notebooks/methods/delete_notebook.py +13 -0
- cognee/modules/notebooks/methods/get_notebook.py +21 -0
- cognee/modules/notebooks/methods/get_notebooks.py +18 -0
- cognee/modules/notebooks/methods/update_notebook.py +17 -0
- cognee/modules/notebooks/models/Notebook.py +53 -0
- cognee/modules/notebooks/models/__init__.py +1 -0
- cognee/modules/notebooks/operations/__init__.py +1 -0
- cognee/modules/notebooks/operations/run_in_local_sandbox.py +55 -0
- cognee/modules/pipelines/__init__.py +1 -1
- cognee/modules/pipelines/exceptions/tasks.py +18 -0
- cognee/modules/pipelines/layers/__init__.py +1 -0
- cognee/modules/pipelines/layers/check_pipeline_run_qualification.py +59 -0
- cognee/modules/pipelines/layers/pipeline_execution_mode.py +127 -0
- cognee/modules/pipelines/layers/reset_dataset_pipeline_run_status.py +28 -0
- cognee/modules/pipelines/layers/resolve_authorized_user_dataset.py +34 -0
- cognee/modules/pipelines/layers/resolve_authorized_user_datasets.py +55 -0
- cognee/modules/pipelines/layers/setup_and_check_environment.py +41 -0
- cognee/modules/pipelines/layers/validate_pipeline_tasks.py +20 -0
- cognee/modules/pipelines/methods/__init__.py +2 -0
- cognee/modules/pipelines/methods/get_pipeline_runs_by_dataset.py +34 -0
- cognee/modules/pipelines/methods/reset_pipeline_run_status.py +16 -0
- cognee/modules/pipelines/operations/__init__.py +0 -1
- cognee/modules/pipelines/operations/log_pipeline_run_initiated.py +1 -1
- cognee/modules/pipelines/operations/pipeline.py +24 -138
- cognee/modules/pipelines/operations/run_tasks.py +17 -41
- cognee/modules/retrieval/base_feedback.py +11 -0
- cognee/modules/retrieval/base_graph_retriever.py +18 -0
- cognee/modules/retrieval/base_retriever.py +1 -1
- cognee/modules/retrieval/code_retriever.py +8 -0
- cognee/modules/retrieval/coding_rules_retriever.py +31 -0
- cognee/modules/retrieval/completion_retriever.py +9 -3
- cognee/modules/retrieval/context_providers/TripletSearchContextProvider.py +1 -0
- cognee/modules/retrieval/cypher_search_retriever.py +1 -9
- cognee/modules/retrieval/graph_completion_context_extension_retriever.py +29 -13
- cognee/modules/retrieval/graph_completion_cot_retriever.py +30 -13
- cognee/modules/retrieval/graph_completion_retriever.py +107 -56
- cognee/modules/retrieval/graph_summary_completion_retriever.py +5 -1
- cognee/modules/retrieval/insights_retriever.py +14 -3
- cognee/modules/retrieval/natural_language_retriever.py +0 -4
- cognee/modules/retrieval/summaries_retriever.py +1 -1
- cognee/modules/retrieval/temporal_retriever.py +152 -0
- cognee/modules/retrieval/user_qa_feedback.py +83 -0
- cognee/modules/retrieval/utils/brute_force_triplet_search.py +7 -32
- cognee/modules/retrieval/utils/completion.py +10 -3
- cognee/modules/retrieval/utils/extract_uuid_from_node.py +18 -0
- cognee/modules/retrieval/utils/models.py +40 -0
- cognee/modules/search/methods/get_search_type_tools.py +168 -0
- cognee/modules/search/methods/no_access_control_search.py +47 -0
- cognee/modules/search/methods/search.py +239 -118
- cognee/modules/search/types/SearchResult.py +21 -0
- cognee/modules/search/types/SearchType.py +3 -0
- cognee/modules/search/types/__init__.py +1 -0
- cognee/modules/search/utils/__init__.py +2 -0
- cognee/modules/search/utils/prepare_search_result.py +41 -0
- cognee/modules/search/utils/transform_context_to_graph.py +38 -0
- cognee/modules/settings/get_settings.py +2 -2
- cognee/modules/sync/__init__.py +1 -0
- cognee/modules/sync/methods/__init__.py +23 -0
- cognee/modules/sync/methods/create_sync_operation.py +53 -0
- cognee/modules/sync/methods/get_sync_operation.py +107 -0
- cognee/modules/sync/methods/update_sync_operation.py +248 -0
- cognee/modules/sync/models/SyncOperation.py +142 -0
- cognee/modules/sync/models/__init__.py +3 -0
- cognee/modules/users/__init__.py +0 -1
- cognee/modules/users/methods/__init__.py +4 -1
- cognee/modules/users/methods/create_user.py +26 -1
- cognee/modules/users/methods/get_authenticated_user.py +36 -42
- cognee/modules/users/methods/get_default_user.py +3 -1
- cognee/modules/users/permissions/methods/get_specific_user_permission_datasets.py +2 -1
- cognee/root_dir.py +19 -0
- cognee/shared/CodeGraphEntities.py +1 -0
- cognee/shared/logging_utils.py +143 -32
- cognee/shared/utils.py +0 -1
- cognee/tasks/codingagents/coding_rule_associations.py +127 -0
- cognee/tasks/graph/extract_graph_from_data.py +6 -2
- cognee/tasks/ingestion/save_data_item_to_storage.py +23 -0
- cognee/tasks/memify/__init__.py +2 -0
- cognee/tasks/memify/extract_subgraph.py +7 -0
- cognee/tasks/memify/extract_subgraph_chunks.py +11 -0
- cognee/tasks/repo_processor/get_local_dependencies.py +2 -0
- cognee/tasks/repo_processor/get_repo_file_dependencies.py +144 -47
- cognee/tasks/storage/add_data_points.py +33 -3
- cognee/tasks/temporal_graph/__init__.py +1 -0
- cognee/tasks/temporal_graph/add_entities_to_event.py +85 -0
- cognee/tasks/temporal_graph/enrich_events.py +34 -0
- cognee/tasks/temporal_graph/extract_events_and_entities.py +32 -0
- cognee/tasks/temporal_graph/extract_knowledge_graph_from_events.py +41 -0
- cognee/tasks/temporal_graph/models.py +49 -0
- cognee/tests/integration/cli/__init__.py +3 -0
- cognee/tests/integration/cli/test_cli_integration.py +331 -0
- cognee/tests/integration/documents/PdfDocument_test.py +2 -2
- cognee/tests/integration/documents/TextDocument_test.py +2 -4
- cognee/tests/integration/documents/UnstructuredDocument_test.py +5 -8
- cognee/tests/{test_deletion.py → test_delete_hard.py} +0 -37
- cognee/tests/test_delete_soft.py +85 -0
- cognee/tests/test_kuzu.py +2 -2
- cognee/tests/test_neo4j.py +2 -2
- cognee/tests/test_permissions.py +3 -3
- cognee/tests/test_relational_db_migration.py +7 -5
- cognee/tests/test_search_db.py +136 -23
- cognee/tests/test_temporal_graph.py +167 -0
- cognee/tests/unit/api/__init__.py +1 -0
- cognee/tests/unit/api/test_conditional_authentication_endpoints.py +246 -0
- cognee/tests/unit/cli/__init__.py +3 -0
- cognee/tests/unit/cli/test_cli_commands.py +483 -0
- cognee/tests/unit/cli/test_cli_edge_cases.py +625 -0
- cognee/tests/unit/cli/test_cli_main.py +173 -0
- cognee/tests/unit/cli/test_cli_runner.py +62 -0
- cognee/tests/unit/cli/test_cli_utils.py +127 -0
- cognee/tests/unit/modules/retrieval/chunks_retriever_test.py +18 -2
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_context_extension_test.py +12 -15
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py +10 -15
- cognee/tests/unit/modules/retrieval/graph_completion_retriever_test.py +4 -3
- cognee/tests/unit/modules/retrieval/insights_retriever_test.py +4 -2
- cognee/tests/unit/modules/retrieval/rag_completion_retriever_test.py +18 -2
- cognee/tests/unit/modules/retrieval/temporal_retriever_test.py +225 -0
- cognee/tests/unit/modules/users/__init__.py +1 -0
- cognee/tests/unit/modules/users/test_conditional_authentication.py +277 -0
- cognee/tests/unit/processing/utils/utils_test.py +20 -1
- {cognee-0.2.3.dev1.dist-info → cognee-0.3.0.dev0.dist-info}/METADATA +13 -9
- {cognee-0.2.3.dev1.dist-info → cognee-0.3.0.dev0.dist-info}/RECORD +245 -135
- cognee-0.3.0.dev0.dist-info/entry_points.txt +2 -0
- cognee/infrastructure/databases/graph/networkx/adapter.py +0 -1017
- cognee/infrastructure/pipeline/models/Operation.py +0 -60
- cognee/notebooks/github_analysis_step_by_step.ipynb +0 -37
- cognee/tests/tasks/descriptive_metrics/networkx_metrics_test.py +0 -7
- cognee/tests/unit/modules/search/search_methods_test.py +0 -223
- /cognee/{infrastructure/databases/graph/networkx → api/v1/memify}/__init__.py +0 -0
- /cognee/{infrastructure/pipeline/models → tasks/codingagents}/__init__.py +0 -0
- {cognee-0.2.3.dev1.dist-info → cognee-0.3.0.dev0.dist-info}/WHEEL +0 -0
- {cognee-0.2.3.dev1.dist-info → cognee-0.3.0.dev0.dist-info}/licenses/LICENSE +0 -0
- {cognee-0.2.3.dev1.dist-info → cognee-0.3.0.dev0.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for CLI edge cases and error scenarios with proper mocking.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
import sys
|
|
7
|
+
import asyncio
|
|
8
|
+
import argparse
|
|
9
|
+
from unittest.mock import patch, MagicMock, AsyncMock, ANY, call
|
|
10
|
+
from cognee.cli.commands.add_command import AddCommand
|
|
11
|
+
from cognee.cli.commands.search_command import SearchCommand
|
|
12
|
+
from cognee.cli.commands.cognify_command import CognifyCommand
|
|
13
|
+
from cognee.cli.commands.delete_command import DeleteCommand
|
|
14
|
+
from cognee.cli.commands.config_command import ConfigCommand
|
|
15
|
+
from cognee.cli.exceptions import CliCommandException, CliCommandInnerException
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Mock asyncio.run to properly handle coroutines
|
|
19
|
+
def _mock_run(coro):
|
|
20
|
+
# Create an event loop and run the coroutine
|
|
21
|
+
loop = asyncio.new_event_loop()
|
|
22
|
+
try:
|
|
23
|
+
return loop.run_until_complete(coro)
|
|
24
|
+
finally:
|
|
25
|
+
loop.close()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestAddCommandEdgeCases:
|
|
29
|
+
"""Test edge cases for AddCommand"""
|
|
30
|
+
|
|
31
|
+
@patch("cognee.cli.commands.add_command.asyncio.run", side_effect=_mock_run)
|
|
32
|
+
def test_add_empty_data_list(self, mock_asyncio_run):
|
|
33
|
+
mock_cognee = MagicMock()
|
|
34
|
+
mock_cognee.add = AsyncMock()
|
|
35
|
+
|
|
36
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
37
|
+
command = AddCommand()
|
|
38
|
+
args = argparse.Namespace(data=[], dataset_name="test_dataset")
|
|
39
|
+
command.execute(args)
|
|
40
|
+
|
|
41
|
+
mock_asyncio_run.assert_called_once()
|
|
42
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
43
|
+
mock_cognee.add.assert_awaited_once_with(data=[], dataset_name="test_dataset")
|
|
44
|
+
|
|
45
|
+
@patch("cognee.cli.commands.add_command.asyncio.run")
|
|
46
|
+
def test_add_asyncio_run_exception(self, mock_asyncio_run):
|
|
47
|
+
"""Test add command when asyncio.run itself fails"""
|
|
48
|
+
command = AddCommand()
|
|
49
|
+
args = argparse.Namespace(data=["test.txt"], dataset_name="test_dataset")
|
|
50
|
+
|
|
51
|
+
mock_asyncio_run.side_effect = RuntimeError("Event loop error")
|
|
52
|
+
|
|
53
|
+
with pytest.raises(CliCommandException):
|
|
54
|
+
command.execute(args)
|
|
55
|
+
|
|
56
|
+
def test_add_special_characters_in_data(self):
|
|
57
|
+
"""Test add command with special characters in file paths"""
|
|
58
|
+
command = AddCommand()
|
|
59
|
+
|
|
60
|
+
# Create parser to test argument parsing with special characters
|
|
61
|
+
parser = argparse.ArgumentParser()
|
|
62
|
+
command.configure_parser(parser)
|
|
63
|
+
|
|
64
|
+
# Test parsing with special characters
|
|
65
|
+
special_paths = [
|
|
66
|
+
"file with spaces.txt",
|
|
67
|
+
"file-with-dashes.txt",
|
|
68
|
+
"file_with_underscores.txt",
|
|
69
|
+
"file.with.dots.txt",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
args = parser.parse_args(special_paths + ["--dataset-name", "test"])
|
|
73
|
+
assert args.data == special_paths
|
|
74
|
+
assert args.dataset_name == "test"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TestSearchCommandEdgeCases:
|
|
78
|
+
"""Test edge cases for SearchCommand"""
|
|
79
|
+
|
|
80
|
+
@patch("cognee.cli.commands.search_command.asyncio.run", side_effect=_mock_run)
|
|
81
|
+
def test_search_empty_results(self, mock_asyncio_run):
|
|
82
|
+
"""Test search command with empty results"""
|
|
83
|
+
# Mock the cognee module and SearchType
|
|
84
|
+
mock_cognee = MagicMock()
|
|
85
|
+
mock_cognee.search = AsyncMock(return_value=[])
|
|
86
|
+
mock_search_type = MagicMock()
|
|
87
|
+
mock_search_type.__getitem__.return_value = "GRAPH_COMPLETION"
|
|
88
|
+
|
|
89
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
90
|
+
command = SearchCommand()
|
|
91
|
+
args = argparse.Namespace(
|
|
92
|
+
query_text="nonexistent query",
|
|
93
|
+
query_type="GRAPH_COMPLETION",
|
|
94
|
+
datasets=None,
|
|
95
|
+
top_k=10,
|
|
96
|
+
system_prompt=None,
|
|
97
|
+
output_format="pretty",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Should handle empty results gracefully
|
|
101
|
+
command.execute(args)
|
|
102
|
+
|
|
103
|
+
mock_asyncio_run.return_value = []
|
|
104
|
+
mock_asyncio_run.assert_called_once()
|
|
105
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
106
|
+
mock_cognee.search.assert_awaited_once_with(
|
|
107
|
+
query_text="nonexistent query",
|
|
108
|
+
query_type=ANY,
|
|
109
|
+
datasets=None,
|
|
110
|
+
top_k=10,
|
|
111
|
+
system_prompt_path="answer_simple_question.txt",
|
|
112
|
+
)
|
|
113
|
+
# verify the enum’s name separately
|
|
114
|
+
called_enum = mock_cognee.search.await_args.kwargs["query_type"]
|
|
115
|
+
assert called_enum.name == "GRAPH_COMPLETION"
|
|
116
|
+
|
|
117
|
+
@patch("cognee.cli.commands.search_command.asyncio.run", side_effect=_mock_run)
|
|
118
|
+
def test_search_very_large_top_k(self, mock_asyncio_run):
|
|
119
|
+
"""Test search command with very large top-k value"""
|
|
120
|
+
# Mock the cognee module and SearchType
|
|
121
|
+
mock_cognee = MagicMock()
|
|
122
|
+
mock_cognee.search = AsyncMock(return_value=["result1"])
|
|
123
|
+
mock_search_type = MagicMock()
|
|
124
|
+
mock_search_type.__getitem__.return_value = "CHUNKS"
|
|
125
|
+
|
|
126
|
+
mock_asyncio_run.return_value = ["result1"]
|
|
127
|
+
|
|
128
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
129
|
+
command = SearchCommand()
|
|
130
|
+
args = argparse.Namespace(
|
|
131
|
+
query_text="test query",
|
|
132
|
+
query_type="CHUNKS",
|
|
133
|
+
datasets=None,
|
|
134
|
+
top_k=999999, # Very large value
|
|
135
|
+
system_prompt=None,
|
|
136
|
+
output_format="json",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
command.execute(args)
|
|
140
|
+
|
|
141
|
+
mock_asyncio_run.assert_called_once()
|
|
142
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
143
|
+
mock_cognee.search.assert_awaited_once_with(
|
|
144
|
+
query_text="test query",
|
|
145
|
+
query_type=ANY,
|
|
146
|
+
datasets=None,
|
|
147
|
+
top_k=999999,
|
|
148
|
+
system_prompt_path="answer_simple_question.txt",
|
|
149
|
+
)
|
|
150
|
+
# verify the enum’s name separately
|
|
151
|
+
called_enum = mock_cognee.search.await_args.kwargs["query_type"]
|
|
152
|
+
assert called_enum.name == "CHUNKS"
|
|
153
|
+
|
|
154
|
+
@patch("builtins.__import__")
|
|
155
|
+
def test_search_invalid_search_type_enum(self, mock_import):
|
|
156
|
+
"""Test search command with invalid SearchType enum conversion"""
|
|
157
|
+
# Mock SearchType to raise KeyError
|
|
158
|
+
mock_search_type = MagicMock()
|
|
159
|
+
mock_search_type.__getitem__.side_effect = KeyError("INVALID_TYPE")
|
|
160
|
+
|
|
161
|
+
def mock_import_func(name, fromlist=None, *args, **kwargs):
|
|
162
|
+
if name == "cognee.modules.search.types":
|
|
163
|
+
module = MagicMock()
|
|
164
|
+
module.SearchType = mock_search_type
|
|
165
|
+
return module
|
|
166
|
+
return MagicMock()
|
|
167
|
+
|
|
168
|
+
mock_import.side_effect = mock_import_func
|
|
169
|
+
|
|
170
|
+
command = SearchCommand()
|
|
171
|
+
args = argparse.Namespace(
|
|
172
|
+
query_text="test query",
|
|
173
|
+
query_type="INVALID_TYPE", # This would fail enum conversion
|
|
174
|
+
datasets=None,
|
|
175
|
+
top_k=10,
|
|
176
|
+
system_prompt=None,
|
|
177
|
+
output_format="pretty",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
with pytest.raises(CliCommandException):
|
|
181
|
+
command.execute(args)
|
|
182
|
+
|
|
183
|
+
def test_search_unicode_query(self):
|
|
184
|
+
"""Test search command with unicode characters in query"""
|
|
185
|
+
command = SearchCommand()
|
|
186
|
+
parser = argparse.ArgumentParser()
|
|
187
|
+
command.configure_parser(parser)
|
|
188
|
+
|
|
189
|
+
unicode_query = "测试查询 🔍 émojis and spéciál chars"
|
|
190
|
+
args = parser.parse_args([unicode_query])
|
|
191
|
+
assert args.query_text == unicode_query
|
|
192
|
+
|
|
193
|
+
@patch("cognee.cli.commands.search_command.asyncio.run", side_effect=_mock_run)
|
|
194
|
+
def test_search_results_with_none_values(self, mock_asyncio_run):
|
|
195
|
+
"""Test search command when results contain None values"""
|
|
196
|
+
# Mock the cognee module and SearchType
|
|
197
|
+
mock_cognee = MagicMock()
|
|
198
|
+
mock_cognee.search = AsyncMock(return_value=[None, "valid result", None])
|
|
199
|
+
mock_search_type = MagicMock()
|
|
200
|
+
mock_search_type.__getitem__.return_value = "CHUNKS"
|
|
201
|
+
|
|
202
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
203
|
+
command = SearchCommand()
|
|
204
|
+
args = argparse.Namespace(
|
|
205
|
+
query_text="test query",
|
|
206
|
+
query_type="CHUNKS",
|
|
207
|
+
datasets=None,
|
|
208
|
+
top_k=10,
|
|
209
|
+
system_prompt=None,
|
|
210
|
+
output_format="pretty",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Should handle None values gracefully
|
|
214
|
+
command.execute(args)
|
|
215
|
+
|
|
216
|
+
mock_asyncio_run.assert_called_once()
|
|
217
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
218
|
+
mock_cognee.search.assert_awaited_once_with(
|
|
219
|
+
query_text="test query",
|
|
220
|
+
query_type=ANY,
|
|
221
|
+
datasets=None,
|
|
222
|
+
top_k=10,
|
|
223
|
+
system_prompt_path="answer_simple_question.txt",
|
|
224
|
+
)
|
|
225
|
+
# verify the enum’s name separately
|
|
226
|
+
called_enum = mock_cognee.search.await_args.kwargs["query_type"]
|
|
227
|
+
assert called_enum.name == "CHUNKS"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TestCognifyCommandEdgeCases:
|
|
231
|
+
"""Test edge cases for CognifyCommand"""
|
|
232
|
+
|
|
233
|
+
@patch("cognee.cli.commands.cognify_command.asyncio.run", side_effect=_mock_run)
|
|
234
|
+
def test_cognify_invalid_chunk_size(self, mock_asyncio_run):
|
|
235
|
+
"""Test cognify command with invalid chunk size"""
|
|
236
|
+
# Mock the cognee module
|
|
237
|
+
mock_cognee = MagicMock()
|
|
238
|
+
mock_cognee.cognify = AsyncMock()
|
|
239
|
+
|
|
240
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
241
|
+
command = CognifyCommand()
|
|
242
|
+
args = argparse.Namespace(
|
|
243
|
+
datasets=None,
|
|
244
|
+
chunk_size=-100, # Invalid negative chunk size
|
|
245
|
+
ontology_file=None,
|
|
246
|
+
chunker="TextChunker",
|
|
247
|
+
background=False,
|
|
248
|
+
verbose=False,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Should pass the invalid value to cognify and let it handle the validation
|
|
252
|
+
command.execute(args)
|
|
253
|
+
|
|
254
|
+
mock_asyncio_run.assert_called_once()
|
|
255
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
256
|
+
from cognee.modules.chunking.TextChunker import TextChunker
|
|
257
|
+
|
|
258
|
+
mock_cognee.cognify.assert_awaited_once_with(
|
|
259
|
+
datasets=None,
|
|
260
|
+
chunk_size=-100,
|
|
261
|
+
ontology_file_path=None,
|
|
262
|
+
chunker=TextChunker,
|
|
263
|
+
run_in_background=False,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
@patch("cognee.cli.commands.cognify_command.asyncio.run", side_effect=_mock_run)
|
|
267
|
+
def test_cognify_nonexistent_ontology_file(self, mock_asyncio_run):
|
|
268
|
+
"""Test cognify command with nonexistent ontology file"""
|
|
269
|
+
# Mock the cognee module
|
|
270
|
+
mock_cognee = MagicMock()
|
|
271
|
+
mock_cognee.cognify = AsyncMock()
|
|
272
|
+
|
|
273
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
274
|
+
command = CognifyCommand()
|
|
275
|
+
args = argparse.Namespace(
|
|
276
|
+
datasets=None,
|
|
277
|
+
chunk_size=None,
|
|
278
|
+
ontology_file="/nonexistent/path/ontology.owl",
|
|
279
|
+
chunker="TextChunker",
|
|
280
|
+
background=False,
|
|
281
|
+
verbose=False,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Should pass the path to cognify and let it handle file validation
|
|
285
|
+
command.execute(args)
|
|
286
|
+
|
|
287
|
+
mock_asyncio_run.assert_called_once()
|
|
288
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
289
|
+
from cognee.modules.chunking.TextChunker import TextChunker
|
|
290
|
+
|
|
291
|
+
mock_cognee.cognify.assert_awaited_once_with(
|
|
292
|
+
datasets=None,
|
|
293
|
+
chunk_size=None,
|
|
294
|
+
ontology_file_path="/nonexistent/path/ontology.owl",
|
|
295
|
+
chunker=TextChunker,
|
|
296
|
+
run_in_background=False,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
@patch("cognee.cli.commands.cognify_command.asyncio.run")
|
|
300
|
+
def test_cognify_langchain_chunker_import_error(self, mock_asyncio_run):
|
|
301
|
+
"""Test cognify command when LangchainChunker import fails"""
|
|
302
|
+
# Mock the cognee module
|
|
303
|
+
mock_cognee = MagicMock()
|
|
304
|
+
mock_cognee.cognify = AsyncMock()
|
|
305
|
+
|
|
306
|
+
def mock_import_func(name, fromlist=None, *args, **kwargs):
|
|
307
|
+
if name == "cognee":
|
|
308
|
+
return mock_cognee
|
|
309
|
+
elif (
|
|
310
|
+
name == "cognee.modules.chunking.LangchainChunker"
|
|
311
|
+
and fromlist
|
|
312
|
+
and "LangchainChunker" in fromlist
|
|
313
|
+
):
|
|
314
|
+
raise ImportError("LangchainChunker not available")
|
|
315
|
+
elif (
|
|
316
|
+
name == "cognee.modules.chunking.TextChunker"
|
|
317
|
+
and fromlist
|
|
318
|
+
and "TextChunker" in fromlist
|
|
319
|
+
):
|
|
320
|
+
module = MagicMock()
|
|
321
|
+
module.TextChunker = MagicMock()
|
|
322
|
+
return module
|
|
323
|
+
return MagicMock()
|
|
324
|
+
|
|
325
|
+
with (
|
|
326
|
+
patch("builtins.__import__", side_effect=mock_import_func),
|
|
327
|
+
patch.dict(sys.modules, {"cognee": mock_cognee}),
|
|
328
|
+
):
|
|
329
|
+
command = CognifyCommand()
|
|
330
|
+
args = argparse.Namespace(
|
|
331
|
+
datasets=None,
|
|
332
|
+
chunk_size=None,
|
|
333
|
+
ontology_file=None,
|
|
334
|
+
chunker="LangchainChunker",
|
|
335
|
+
background=False,
|
|
336
|
+
verbose=True,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# Should fall back to TextChunker and show warning
|
|
340
|
+
command.execute(args)
|
|
341
|
+
|
|
342
|
+
mock_asyncio_run.assert_called_once()
|
|
343
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
344
|
+
|
|
345
|
+
@patch("cognee.cli.commands.cognify_command.asyncio.run", side_effect=_mock_run)
|
|
346
|
+
def test_cognify_empty_datasets_list(self, mock_asyncio_run):
|
|
347
|
+
"""Test cognify command with nonexistent ontology file"""
|
|
348
|
+
# Mock the cognee module
|
|
349
|
+
mock_cognee = MagicMock()
|
|
350
|
+
mock_cognee.cognify = AsyncMock()
|
|
351
|
+
|
|
352
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
353
|
+
command = CognifyCommand()
|
|
354
|
+
args = argparse.Namespace(
|
|
355
|
+
datasets=[],
|
|
356
|
+
chunk_size=None,
|
|
357
|
+
ontology_file=None,
|
|
358
|
+
chunker="TextChunker",
|
|
359
|
+
background=False,
|
|
360
|
+
verbose=False,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
command.execute(args)
|
|
364
|
+
|
|
365
|
+
mock_asyncio_run.assert_called_once()
|
|
366
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
367
|
+
from cognee.modules.chunking.TextChunker import TextChunker
|
|
368
|
+
|
|
369
|
+
mock_cognee.cognify.assert_awaited_once_with(
|
|
370
|
+
datasets=None,
|
|
371
|
+
chunk_size=None,
|
|
372
|
+
ontology_file_path=None,
|
|
373
|
+
chunker=TextChunker,
|
|
374
|
+
run_in_background=False,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class TestDeleteCommandEdgeCases:
|
|
379
|
+
"""Test edge cases for DeleteCommand"""
|
|
380
|
+
|
|
381
|
+
@patch("cognee.cli.commands.delete_command.fmt.confirm")
|
|
382
|
+
@patch("cognee.cli.commands.delete_command.asyncio.run", side_effect=_mock_run)
|
|
383
|
+
def test_delete_all_with_user_id(self, mock_asyncio_run, mock_confirm):
|
|
384
|
+
"""Test delete command with both --all and --user-id"""
|
|
385
|
+
# Mock the cognee module
|
|
386
|
+
mock_cognee = MagicMock()
|
|
387
|
+
mock_cognee.delete = AsyncMock()
|
|
388
|
+
|
|
389
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
390
|
+
command = DeleteCommand()
|
|
391
|
+
args = argparse.Namespace(dataset_name=None, user_id="test_user", all=True, force=False)
|
|
392
|
+
|
|
393
|
+
mock_confirm.return_value = True
|
|
394
|
+
|
|
395
|
+
# Should handle both flags being set
|
|
396
|
+
command.execute(args)
|
|
397
|
+
|
|
398
|
+
mock_confirm.assert_called_once_with("Delete ALL data from cognee?")
|
|
399
|
+
mock_asyncio_run.assert_called_once()
|
|
400
|
+
assert asyncio.iscoroutine(mock_asyncio_run.call_args[0][0])
|
|
401
|
+
mock_cognee.delete.assert_awaited_once_with(dataset_name=None, user_id="test_user")
|
|
402
|
+
|
|
403
|
+
@patch("cognee.cli.commands.delete_command.fmt.confirm")
|
|
404
|
+
def test_delete_confirmation_keyboard_interrupt(self, mock_confirm):
|
|
405
|
+
"""Test delete command when user interrupts confirmation"""
|
|
406
|
+
command = DeleteCommand()
|
|
407
|
+
args = argparse.Namespace(dataset_name="test_dataset", user_id=None, all=False, force=False)
|
|
408
|
+
|
|
409
|
+
mock_confirm.side_effect = KeyboardInterrupt()
|
|
410
|
+
|
|
411
|
+
# Should handle KeyboardInterrupt gracefully
|
|
412
|
+
with pytest.raises(KeyboardInterrupt):
|
|
413
|
+
command.execute(args)
|
|
414
|
+
|
|
415
|
+
@patch("cognee.cli.commands.delete_command.asyncio.run")
|
|
416
|
+
def test_delete_async_exception_handling(self, mock_asyncio_run):
|
|
417
|
+
"""Test delete command async exception handling"""
|
|
418
|
+
command = DeleteCommand()
|
|
419
|
+
args = argparse.Namespace(dataset_name="test_dataset", user_id=None, all=False, force=True)
|
|
420
|
+
|
|
421
|
+
# Mock asyncio.run to raise exception directly
|
|
422
|
+
mock_asyncio_run.side_effect = ValueError("Database connection failed")
|
|
423
|
+
|
|
424
|
+
with pytest.raises(CliCommandException):
|
|
425
|
+
command.execute(args)
|
|
426
|
+
|
|
427
|
+
def test_delete_special_characters_in_dataset_name(self):
|
|
428
|
+
"""Test delete command with special characters in dataset name"""
|
|
429
|
+
command = DeleteCommand()
|
|
430
|
+
parser = argparse.ArgumentParser()
|
|
431
|
+
command.configure_parser(parser)
|
|
432
|
+
|
|
433
|
+
special_names = [
|
|
434
|
+
"dataset with spaces",
|
|
435
|
+
"dataset-with-dashes",
|
|
436
|
+
"dataset_with_underscores",
|
|
437
|
+
"dataset.with.dots",
|
|
438
|
+
"dataset/with/slashes",
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
for name in special_names:
|
|
442
|
+
args = parser.parse_args(["--dataset-name", name])
|
|
443
|
+
assert args.dataset_name == name
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class TestConfigCommandEdgeCases:
|
|
447
|
+
"""Test edge cases for ConfigCommand"""
|
|
448
|
+
|
|
449
|
+
def test_config_no_subcommand_specified(self):
|
|
450
|
+
"""Test config command when no subcommand is specified"""
|
|
451
|
+
command = ConfigCommand()
|
|
452
|
+
parser = argparse.ArgumentParser()
|
|
453
|
+
command.configure_parser(parser)
|
|
454
|
+
|
|
455
|
+
# Parse with no subcommand - should set config_action to None
|
|
456
|
+
args = parser.parse_args([])
|
|
457
|
+
assert not hasattr(args, "config_action") or args.config_action is None
|
|
458
|
+
|
|
459
|
+
@patch("builtins.__import__")
|
|
460
|
+
def test_config_get_nonexistent_key(self, mock_import):
|
|
461
|
+
"""Test config get with nonexistent key"""
|
|
462
|
+
# Mock config.get to raise exception for nonexistent key
|
|
463
|
+
mock_cognee = MagicMock()
|
|
464
|
+
mock_cognee.config.get = MagicMock(side_effect=KeyError("Key not found"))
|
|
465
|
+
mock_import.return_value = mock_cognee
|
|
466
|
+
|
|
467
|
+
command = ConfigCommand()
|
|
468
|
+
args = argparse.Namespace(config_action="get", key="nonexistent_key")
|
|
469
|
+
|
|
470
|
+
# Should handle the exception gracefully
|
|
471
|
+
command.execute(args)
|
|
472
|
+
mock_cognee.config.get.assert_called_once_with("nonexistent_key")
|
|
473
|
+
|
|
474
|
+
@patch("builtins.__import__")
|
|
475
|
+
def test_config_set_complex_json_value(self, mock_import):
|
|
476
|
+
"""Test config set with complex JSON value"""
|
|
477
|
+
# Mock the cognee module
|
|
478
|
+
mock_cognee = MagicMock()
|
|
479
|
+
mock_cognee.config.set = MagicMock()
|
|
480
|
+
mock_import.return_value = mock_cognee
|
|
481
|
+
|
|
482
|
+
command = ConfigCommand()
|
|
483
|
+
complex_json = '{"nested": {"key": "value"}, "array": [1, 2, 3]}'
|
|
484
|
+
complex_json_expected_value = {"nested": {"key": "value"}, "array": [1, 2, 3]}
|
|
485
|
+
args = argparse.Namespace(config_action="set", key="complex_config", value=complex_json)
|
|
486
|
+
|
|
487
|
+
command.execute(args)
|
|
488
|
+
mock_cognee.config.set.assert_called_once_with(
|
|
489
|
+
"complex_config", complex_json_expected_value
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
@patch("builtins.__import__")
|
|
493
|
+
def test_config_set_invalid_json_value(self, mock_import):
|
|
494
|
+
"""Test config set with invalid JSON value"""
|
|
495
|
+
# Mock the cognee module
|
|
496
|
+
mock_cognee = MagicMock()
|
|
497
|
+
mock_cognee.config.set = MagicMock()
|
|
498
|
+
mock_import.return_value = mock_cognee
|
|
499
|
+
|
|
500
|
+
command = ConfigCommand()
|
|
501
|
+
invalid_json = '{"invalid": json}'
|
|
502
|
+
args = argparse.Namespace(config_action="set", key="test_key", value=invalid_json)
|
|
503
|
+
|
|
504
|
+
command.execute(args)
|
|
505
|
+
mock_cognee.config.set.assert_called_once_with("test_key", invalid_json)
|
|
506
|
+
|
|
507
|
+
@patch("cognee.cli.commands.config_command.fmt.confirm")
|
|
508
|
+
def test_config_unset_unknown_key(self, mock_confirm):
|
|
509
|
+
"""Test config unset with unknown key"""
|
|
510
|
+
# Mock the cognee module
|
|
511
|
+
mock_cognee = MagicMock()
|
|
512
|
+
|
|
513
|
+
with patch.dict(sys.modules, {"cognee": mock_cognee}):
|
|
514
|
+
command = ConfigCommand()
|
|
515
|
+
args = argparse.Namespace(config_action="unset", key="unknown_key", force=False)
|
|
516
|
+
|
|
517
|
+
mock_confirm.return_value = True
|
|
518
|
+
|
|
519
|
+
# Should show error for unknown key
|
|
520
|
+
command.execute(args)
|
|
521
|
+
|
|
522
|
+
mock_confirm.assert_called_once()
|
|
523
|
+
|
|
524
|
+
@patch("builtins.__import__")
|
|
525
|
+
def test_config_unset_method_not_found(self, mock_import):
|
|
526
|
+
"""Test config unset when method doesn't exist on config object"""
|
|
527
|
+
# Mock config object without the expected method
|
|
528
|
+
mock_cognee = MagicMock()
|
|
529
|
+
mock_cognee.config = MagicMock()
|
|
530
|
+
# Don't set the set_llm_provider method
|
|
531
|
+
mock_import.return_value = mock_cognee
|
|
532
|
+
|
|
533
|
+
command = ConfigCommand()
|
|
534
|
+
args = argparse.Namespace(config_action="unset", key="llm_provider", force=True)
|
|
535
|
+
|
|
536
|
+
# Should handle AttributeError gracefully
|
|
537
|
+
command.execute(args)
|
|
538
|
+
mock_cognee.config.unset.assert_not_called()
|
|
539
|
+
|
|
540
|
+
def test_config_invalid_subcommand(self):
|
|
541
|
+
"""Test config command with invalid subcommand"""
|
|
542
|
+
command = ConfigCommand()
|
|
543
|
+
args = argparse.Namespace(config_action="invalid_action")
|
|
544
|
+
|
|
545
|
+
# Should handle unknown subcommand gracefully
|
|
546
|
+
command.execute(args)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class TestGeneralEdgeCases:
|
|
550
|
+
"""Test general edge cases that apply to multiple commands"""
|
|
551
|
+
|
|
552
|
+
def test_command_with_none_args(self):
|
|
553
|
+
"""Test command execution with None args"""
|
|
554
|
+
commands = [
|
|
555
|
+
AddCommand(),
|
|
556
|
+
SearchCommand(),
|
|
557
|
+
CognifyCommand(),
|
|
558
|
+
DeleteCommand(),
|
|
559
|
+
ConfigCommand(),
|
|
560
|
+
]
|
|
561
|
+
|
|
562
|
+
for command in commands:
|
|
563
|
+
# Should not crash with None args, though it might raise exceptions
|
|
564
|
+
try:
|
|
565
|
+
command.execute(None)
|
|
566
|
+
except (AttributeError, CliCommandException):
|
|
567
|
+
# Expected behavior for None args
|
|
568
|
+
pass
|
|
569
|
+
|
|
570
|
+
def test_parser_configuration_with_none_parser(self):
|
|
571
|
+
"""Test parser configuration with None parser"""
|
|
572
|
+
commands = [
|
|
573
|
+
AddCommand(),
|
|
574
|
+
SearchCommand(),
|
|
575
|
+
CognifyCommand(),
|
|
576
|
+
DeleteCommand(),
|
|
577
|
+
ConfigCommand(),
|
|
578
|
+
]
|
|
579
|
+
|
|
580
|
+
for command in commands:
|
|
581
|
+
# Should not crash, though it might raise AttributeError
|
|
582
|
+
try:
|
|
583
|
+
command.configure_parser(None)
|
|
584
|
+
except AttributeError:
|
|
585
|
+
# Expected behavior for None parser
|
|
586
|
+
pass
|
|
587
|
+
|
|
588
|
+
def test_command_properties_are_strings(self):
|
|
589
|
+
"""Test that all command properties are proper strings"""
|
|
590
|
+
commands = [
|
|
591
|
+
AddCommand(),
|
|
592
|
+
SearchCommand(),
|
|
593
|
+
CognifyCommand(),
|
|
594
|
+
DeleteCommand(),
|
|
595
|
+
ConfigCommand(),
|
|
596
|
+
]
|
|
597
|
+
|
|
598
|
+
for command in commands:
|
|
599
|
+
assert isinstance(command.command_string, str)
|
|
600
|
+
assert len(command.command_string) > 0
|
|
601
|
+
|
|
602
|
+
assert isinstance(command.help_string, str)
|
|
603
|
+
assert len(command.help_string) > 0
|
|
604
|
+
|
|
605
|
+
if hasattr(command, "description") and command.description:
|
|
606
|
+
assert isinstance(command.description, str)
|
|
607
|
+
|
|
608
|
+
if hasattr(command, "docs_url") and command.docs_url:
|
|
609
|
+
assert isinstance(command.docs_url, str)
|
|
610
|
+
|
|
611
|
+
@patch("tempfile.NamedTemporaryFile")
|
|
612
|
+
def test_commands_with_temp_files(self, mock_temp_file):
|
|
613
|
+
"""Test commands that might work with temporary files"""
|
|
614
|
+
# Mock a temporary file
|
|
615
|
+
mock_file = MagicMock()
|
|
616
|
+
mock_file.name = "/tmp/test_file.txt"
|
|
617
|
+
mock_temp_file.return_value.__enter__.return_value = mock_file
|
|
618
|
+
|
|
619
|
+
# Test AddCommand with temp file
|
|
620
|
+
command = AddCommand()
|
|
621
|
+
parser = argparse.ArgumentParser()
|
|
622
|
+
command.configure_parser(parser)
|
|
623
|
+
|
|
624
|
+
args = parser.parse_args([mock_file.name])
|
|
625
|
+
assert args.data == [mock_file.name]
|