Files
cleverswarm-python-client/tests/test_cswarm_text_to_kg_client_extended.py
CoreRasurae cd808dc3dd
/ test (push) Successful in 9m10s
feat: Initial version of CleverSwarm Python SDK
Extend CleverSwarm codebase with PDF and Machine and Worker IDs

ISSUES CLOSED: #1
2025-09-29 10:37:49 +01:00

461 lines
20 KiB
Python

"""
Extended unit tests for cswarm_text_to_kg_client module to achieve higher coverage.
Copyright (c) 2016 - present Syncleus, Inc.
Copyright (c) 2016 - present CleverThis, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch
import pytest
from cleverswarm_python_client.cswarm_text_to_kg_client import TextoToKGCLI, main
from cleverswarm_python_client.libs.exceptions import ClientException
from cleverswarm_python_client.libs.job_enums import JobStatus
class TestTextoToKGCLIExtended:
"""Extended test cases for TextoToKGCLI to achieve higher coverage."""
def setup_method(self):
"""Set up test CLI."""
with tempfile.TemporaryDirectory() as temp_dir:
self.cli = TextoToKGCLI(
"http://localhost:8000/api/v0/",
input_prefix=temp_dir,
output_prefix=temp_dir
)
def test_create_job_with_wildcards_and_force_filetype(self):
"""Test job creation with wildcards and force filetype."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create test files
text_file = Path(temp_dir) / "test.txt"
json_file = Path(temp_dir) / "test.json"
owl_file = Path(temp_dir) / "test.owl"
wildcards_file = Path(temp_dir) / "test.wildcards"
text_file.write_text("Test content")
json_file.write_text('{"test": "data"}')
owl_file.write_text('<rdf:RDF>test</rdf:RDF>')
wildcards_file.write_text("wildcard1\nwildcard2")
with patch.object(self.cli, '_client') as mock_client:
mock_client.create_unstructured_to_kg_wildcards_job.return_value = "test_job_123"
result = self.cli.create_job(
str(text_file),
str(json_file),
str(owl_file),
wildcards=str(wildcards_file),
force_filetype="Markdown"
)
assert result == "test_job_123"
mock_client.create_unstructured_to_kg_wildcards_job.assert_called_once()
def test_create_job_with_force_filetype_enum(self):
"""Test job creation with force filetype as enum."""
with tempfile.TemporaryDirectory() as temp_dir:
# Create test files
text_file = Path(temp_dir) / "test.txt"
json_file = Path(temp_dir) / "test.json"
owl_file = Path(temp_dir) / "test.owl"
text_file.write_text("Test content")
json_file.write_text('{"test": "data"}')
owl_file.write_text('<rdf:RDF>test</rdf:RDF>')
with patch.object(self.cli, '_client') as mock_client:
mock_client.create_unstructured_to_kg_job.return_value = "test_job_123"
from cleverswarm_python_client.libs.file_type_enum import FileTypeAPI
result = self.cli.create_job(
str(text_file),
str(json_file),
str(owl_file),
force_filetype=FileTypeAPI.Markdown
)
assert result == "test_job_123"
mock_client.create_unstructured_to_kg_job.assert_called_once()
def test_download_job_result_with_single_file(self):
"""Test downloading job result with single file."""
with patch.object(self.cli, '_client') as mock_client:
mock_job_details = {
"unstructured_sources": ["test.txt"]
}
mock_client.get_job_details.return_value = mock_job_details
mock_client.retrieve_unstructured_to_kg_files.return_value = True
result = self.cli.download_job_result("test_job_123")
assert result is None
mock_client.get_job_details.assert_called_once_with("test_job_123")
mock_client.retrieve_unstructured_to_kg_files.assert_called_once()
def test_download_job_result_with_multiple_files_raises_exception(self):
"""Test downloading job result with multiple files raises exception."""
with patch.object(self.cli, '_client') as mock_client:
mock_job_details = {
"unstructured_sources": ["test1.txt", "test2.txt"]
}
mock_client.get_job_details.return_value = mock_job_details
with pytest.raises(ClientException, match="has more than one input files"):
self.cli.download_job_result("test_job_123")
def test_download_job_result_with_no_files_raises_exception(self):
"""Test downloading job result with no files raises exception."""
with patch.object(self.cli, '_client') as mock_client:
mock_job_details = {
"unstructured_sources": []
}
mock_client.get_job_details.return_value = mock_job_details
with pytest.raises(ClientException, match="has no input files"):
self.cli.download_job_result("test_job_123")
def test_download_job_result_detailed_output(self):
"""Test downloading job result with detailed output."""
with patch.object(self.cli, '_client') as mock_client:
self.cli._detailed = True
mock_job_details = {
"unstructured_sources": ["test.txt"]
}
mock_client.get_job_details.return_value = mock_job_details
mock_client.retrieve_unstructured_to_kg_files.return_value = True
with patch('builtins.print') as mock_print:
result = self.cli.download_job_result("test_job_123")
assert result is None
mock_print.assert_called()
def test_list_server_jobs_success(self):
"""Test listing server jobs successfully."""
with patch.object(self.cli, '_client') as mock_client:
mock_jobs = [
{"id": "job1", "type": "UnstructuredWithOntology"},
{"id": "job2", "type": "UnstructuredWithWildcards"}
]
mock_client.get_jobs_list.return_value = mock_jobs
result = self.cli.list_server_jobs()
assert len(result) == 2
assert result[0]["id"] == "job1"
mock_client.get_jobs_list.assert_called_once_with(is_benchmark=False)
def test_print_server_jobs_with_empty_list(self):
"""Test printing server jobs with empty list."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.get_jobs_list.return_value = []
with patch('builtins.print') as mock_print:
self.cli.print_server_jobs()
# Should not raise any exception
def test_print_server_jobs_with_jobs(self):
"""Test printing server jobs with actual jobs."""
with patch.object(self.cli, '_client') as mock_client:
mock_jobs = [
{
"id": "job1",
"created": "2023-01-01T00:00:00Z",
"type": "UnstructuredWithOntology",
"status": "Completed",
"retries_count": 0,
"file_type": "JSONL",
"machine_id": "machine-001",
"worker_id": "worker-001",
"unstructured_sources": ["test.txt"],
"ontology_sources": ["test.json"],
"ontology_spec_sources": ["test.owl"]
}
]
mock_client.get_jobs_list.return_value = mock_jobs
with patch('builtins.print') as mock_print:
self.cli.print_server_jobs()
assert mock_print.call_count > 0
def test_print_server_jobs_detailed_with_wildcards(self):
"""Test printing server jobs with detailed output including wildcards."""
with patch.object(self.cli, '_client') as mock_client:
mock_jobs = [
{
"id": "job1",
"created": "2023-01-01T00:00:00Z",
"type": "UnstructuredWithWildcards",
"status": "Completed",
"retries_count": 0,
"file_type": "JSONL",
"machine_id": "machine-001",
"worker_id": "worker-001",
"unstructured_sources": ["test.txt"],
"ontology_sources": ["test.json"],
"ontology_spec_sources": ["test.owl"],
"wildcards_sources": ["test.wildcards"]
}
]
mock_client.get_jobs_list.return_value = mock_jobs
with patch('builtins.print') as mock_print:
self.cli.print_server_jobs(is_detailed=True)
assert mock_print.call_count > 0
def test_print_server_jobs_detailed_without_wildcards(self):
"""Test printing server jobs with detailed output without wildcards."""
with patch.object(self.cli, '_client') as mock_client:
mock_jobs = [
{
"id": "job1",
"created": "2023-01-01T00:00:00Z",
"type": "UnstructuredWithOntology",
"status": "Completed",
"retries_count": 0,
"file_type": "JSONL",
"machine_id": "machine-001",
"worker_id": "worker-001",
"unstructured_sources": ["test.txt"],
"ontology_sources": ["test.json"],
"ontology_spec_sources": ["test.owl"]
}
]
mock_client.get_jobs_list.return_value = mock_jobs
with patch('builtins.print') as mock_print:
self.cli.print_server_jobs(is_detailed=True)
assert mock_print.call_count > 0
def test_delete_server_job_success(self):
"""Test successful server job deletion."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.delete_job.return_value = True
with patch('builtins.print') as mock_print:
self.cli.delete_server_job("test_job_123")
mock_print.assert_called_with("Job was successfully deleted")
def test_delete_server_job_failure(self):
"""Test failed server job deletion."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.delete_job.return_value = False
with patch('builtins.print') as mock_print:
self.cli.delete_server_job("test_job_123")
mock_print.assert_called_with("Failed to delete Job")
def test_poll_job_to_completion_with_ready_for_processing(self):
"""Test polling job that is ready for processing."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.get_job_status.return_value = JobStatus.ReadyForProcessing
mock_client.poll_job_ready_or_failed.return_value = JobStatus.Completed
result = self.cli.poll_job_to_completion("test_job_123")
assert result is None # Should not raise exception
mock_client.get_job_status.assert_called_once_with("test_job_123")
mock_client.poll_job_ready_or_failed.assert_called_once_with("test_job_123")
def test_poll_job_to_completion_with_processing(self):
"""Test polling job that is processing."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.get_job_status.return_value = JobStatus.Processing
mock_client.poll_job_ready_or_failed.return_value = JobStatus.Completed
result = self.cli.poll_job_to_completion("test_job_123")
assert result is None # Should not raise exception
mock_client.get_job_status.assert_called_once_with("test_job_123")
mock_client.poll_job_ready_or_failed.assert_called_once_with("test_job_123")
def test_poll_job_to_completion_with_invalid_status(self):
"""Test polling job with invalid status."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.get_job_status.return_value = "InvalidStatus"
with pytest.raises(ClientException, match="is not processing, nor is it ready for processing"):
self.cli.poll_job_to_completion("test_job_123")
def test_poll_job_to_completion_with_poll_failure(self):
"""Test polling job with poll failure."""
with patch.object(self.cli, '_client') as mock_client:
mock_client.get_job_status.return_value = JobStatus.Processing
mock_client.poll_job_ready_or_failed.side_effect = ClientException("Poll failed")
with pytest.raises(ClientException, match="Poll failed"):
self.cli.poll_job_to_completion("test_job_123")
class TestTextoToKGCLIMainFunctionExtended:
"""Extended test cases for TextoToKGCLI main function to achieve higher coverage."""
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_delete_and_exit(self, mock_cli_class):
"""Test main function with delete and exit action."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'delete-and-exit', '--job_id', 'test_job_123']):
main()
mock_cli.delete_server_job.assert_called_once_with("test_job_123")
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_delete_and_exit_missing_job_id(self, mock_cli_class):
"""Test main function with delete and exit action missing job_id."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'delete-and-exit']):
with pytest.raises(SystemExit):
main()
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_download_kg_and_exit(self, mock_cli_class):
"""Test main function with download-kg-and-exit action."""
mock_cli = Mock()
mock_cli.download_job_result.return_value = True
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'download-kg-and-exit', '--job_id', 'test_job_123']):
main()
mock_cli.download_job_result.assert_called_once_with("test_job_123")
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_download_kg_and_exit_missing_job_id(self, mock_cli_class):
"""Test main function with download-kg-and-exit action missing job_id."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'download-kg-and-exit']):
with pytest.raises(SystemExit):
main()
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_list_kg_jobs_and_exit(self, mock_cli_class):
"""Test main function with list-kg-jobs-and-exit action."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'list-kg-jobs-and-exit']):
main()
mock_cli.print_server_jobs.assert_called_once_with(False)
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_create_download_kg(self, mock_cli_class):
"""Test main function with create-download-kg action."""
mock_cli = Mock()
mock_cli.create_job.return_value = "test_job_123"
mock_cli.poll_job_to_completion.return_value = None
mock_cli.download_job_result.return_value = True
mock_cli_class.return_value = mock_cli
with patch('sys.argv', [
'cswarm_text_to_kg_client.py',
'--action', 'create-download-kg',
'--unstructured_text', 'test.txt',
'--ontology_json', 'test.json',
'--ontology_owl', 'test.owl'
]):
main()
mock_cli.create_job.assert_called_once()
mock_cli.poll_job_to_completion.assert_called_once_with("test_job_123")
mock_cli.download_job_result.assert_called_once_with("test_job_123")
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_with_wildcards(self, mock_cli_class):
"""Test main function with wildcards."""
mock_cli = Mock()
mock_cli.create_job.return_value = "test_job_123"
mock_cli_class.return_value = mock_cli
with patch('sys.argv', [
'cswarm_text_to_kg_client.py',
'--action', 'create-and-exit',
'--unstructured_text', 'test.txt',
'--ontology_json', 'test.json',
'--ontology_owl', 'test.owl',
'--wildcards', 'test.wildcards'
]):
main()
mock_cli.create_job.assert_called_once()
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_with_detailed_flag(self, mock_cli_class):
"""Test main function with detailed flag."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'list-kg-jobs-and-exit', '--detailed']):
main()
mock_cli.print_server_jobs.assert_called_once_with(True)
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_with_custom_server_url(self, mock_cli_class):
"""Test main function with custom server URL."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'list-kg-jobs-and-exit', '--server_url', 'http://custom:8000/api/']):
main()
mock_cli_class.assert_called_once()
call_args = mock_cli_class.call_args
assert call_args[1]['base_url'] == 'http://custom:8000/api/'
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_with_username_and_token(self, mock_cli_class):
"""Test main function with username and token."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', ['cswarm_text_to_kg_client.py', '--action', 'list-kg-jobs-and-exit', '--username', 'testuser', '--token', 'testtoken']):
main()
mock_cli_class.assert_called_once()
call_args = mock_cli_class.call_args
assert call_args[1]['username'] == 'testuser'
assert call_args[1]['token'] == 'testtoken'
@patch('cleverswarm_python_client.cswarm_text_to_kg_client.TextoToKGCLI')
def test_main_with_custom_input_output_prefixes(self, mock_cli_class):
"""Test main function with custom input and output prefixes."""
mock_cli = Mock()
mock_cli_class.return_value = mock_cli
with patch('sys.argv', [
'cswarm_text_to_kg_client.py',
'--action', 'list-kg-jobs-and-exit',
'--input_prefix', '/custom/input',
'--output_prefix', '/custom/output'
]):
main()
mock_cli_class.assert_called_once()
call_args = mock_cli_class.call_args
assert call_args[1]['input_prefix'] == '/custom/input'
assert call_args[1]['output_prefix'] == '/custom/output'