@@ -643,3 +643,296 @@ def test_serialize_for_json_pil_image(self):
643643 assert "data" in result
644644 assert result ["format" ] == "PNG"
645645
646+
647+ class TestDocumentActionToolsFileValidation :
648+ """Test file validation methods."""
649+
650+ @pytest .mark .asyncio
651+ async def test_validate_document_file_valid (self , tmp_path ):
652+ """Test _validate_document_file with valid file."""
653+ tools = DocumentActionTools ()
654+
655+ # Create a test PDF file
656+ test_file = tmp_path / "test.pdf"
657+ test_file .write_bytes (b"fake pdf content" )
658+
659+ result = await tools ._validate_document_file (str (test_file ))
660+
661+ assert result ["valid" ] is True
662+ assert result ["file_type" ] == "pdf"
663+ assert result ["file_size" ] > 0
664+
665+ @pytest .mark .asyncio
666+ async def test_validate_document_file_not_exists (self ):
667+ """Test _validate_document_file with non-existent file."""
668+ tools = DocumentActionTools ()
669+
670+ result = await tools ._validate_document_file ("/nonexistent/file.pdf" )
671+
672+ assert result ["valid" ] is False
673+ assert "does not exist" in result ["error" ]
674+
675+ @pytest .mark .asyncio
676+ async def test_validate_document_file_too_large (self , tmp_path ):
677+ """Test _validate_document_file with file exceeding size limit."""
678+ tools = DocumentActionTools ()
679+ tools .max_file_size = 10 # Very small limit for testing
680+
681+ # Create a large file
682+ test_file = tmp_path / "large.pdf"
683+ test_file .write_bytes (b"x" * 100 )
684+
685+ result = await tools ._validate_document_file (str (test_file ))
686+
687+ assert result ["valid" ] is False
688+ assert "exceeds" in result ["error" ]
689+
690+ @pytest .mark .asyncio
691+ async def test_validate_document_file_unsupported_type (self , tmp_path ):
692+ """Test _validate_document_file with unsupported file type."""
693+ tools = DocumentActionTools ()
694+
695+ test_file = tmp_path / "test.xyz"
696+ test_file .write_bytes (b"content" )
697+
698+ result = await tools ._validate_document_file (str (test_file ))
699+
700+ assert result ["valid" ] is False
701+ assert "Unsupported file type" in result ["error" ]
702+
703+
704+ class TestDocumentActionToolsQualityScore :
705+ """Test quality score creation methods."""
706+
707+ def test_create_quality_score_from_validation_dict (self ):
708+ """Test _create_quality_score_from_validation with dictionary."""
709+ tools = DocumentActionTools ()
710+
711+ validation_data = {
712+ "overall_score" : 0.85 ,
713+ "completeness_score" : 0.90 ,
714+ "accuracy_score" : 0.80 ,
715+ "compliance_score" : 0.75 ,
716+ "quality_score" : 0.85 ,
717+ "decision" : "APPROVED" ,
718+ "reasoning" : "Good quality document" ,
719+ "issues_found" : [],
720+ "confidence" : 0.95 ,
721+ }
722+
723+ result = tools ._create_quality_score_from_validation (validation_data )
724+
725+ assert result .overall_score == 0.85
726+ assert result .completeness_score == 0.90
727+ assert result .accuracy_score == 0.80
728+ assert result .compliance_score == 0.75
729+ assert result .quality_score == 0.85
730+ assert result .decision .value == "APPROVED"
731+ assert isinstance (result .reasoning , dict )
732+ assert result .confidence == 0.95
733+ assert result .judge_model == tools .MODEL_LARGE_JUDGE
734+
735+ def test_create_quality_score_from_validation_dict_string_reasoning (self ):
736+ """Test _create_quality_score_from_validation with string reasoning."""
737+ tools = DocumentActionTools ()
738+
739+ validation_data = {
740+ "overall_score" : 0.75 ,
741+ "reasoning" : "String reasoning text" ,
742+ }
743+
744+ result = tools ._create_quality_score_from_validation (validation_data )
745+
746+ assert isinstance (result .reasoning , dict )
747+ assert result .reasoning ["summary" ] == "String reasoning text"
748+ assert result .reasoning ["details" ] == "String reasoning text"
749+
750+ def test_create_quality_score_from_validation_object (self ):
751+ """Test _create_quality_score_from_validation with object."""
752+ tools = DocumentActionTools ()
753+
754+ class ValidationObj :
755+ def __init__ (self ):
756+ self .overall_score = 0.85
757+ self .completeness_score = 0.90
758+ self .accuracy_score = 0.80
759+ self .compliance_score = 0.75
760+ self .quality_score = 0.85
761+ self .decision = "APPROVED"
762+ self .reasoning = "Object reasoning"
763+ self .issues_found = []
764+ self .confidence = 0.95
765+
766+ obj = ValidationObj ()
767+ result = tools ._create_quality_score_from_validation (obj )
768+
769+ assert result .overall_score == 0.85
770+ assert result .decision .value == "APPROVED"
771+ assert isinstance (result .reasoning , dict )
772+ assert result .reasoning ["summary" ] == "Object reasoning"
773+
774+ @pytest .mark .asyncio
775+ async def test_extract_quality_from_extraction_data_success (self ):
776+ """Test _extract_quality_from_extraction_data with valid data."""
777+ tools = DocumentActionTools ()
778+
779+ with patch .object (tools , "_get_extraction_data" , return_value = {"quality_score" : 0.85 }):
780+ result = await tools ._extract_quality_from_extraction_data ("doc-1" )
781+ assert result == 0.85
782+
783+ @pytest .mark .asyncio
784+ async def test_extract_quality_from_extraction_data_no_score (self ):
785+ """Test _extract_quality_from_extraction_data with no quality score."""
786+ tools = DocumentActionTools ()
787+
788+ with patch .object (tools , "_get_extraction_data" , return_value = {}):
789+ result = await tools ._extract_quality_from_extraction_data ("doc-1" )
790+ assert result == 0.0
791+
792+ @pytest .mark .asyncio
793+ async def test_extract_quality_from_extraction_data_error (self ):
794+ """Test _extract_quality_from_extraction_data handles errors gracefully."""
795+ tools = DocumentActionTools ()
796+
797+ with patch .object (tools , "_get_extraction_data" , side_effect = Exception ("Test error" )):
798+ result = await tools ._extract_quality_from_extraction_data ("doc-1" )
799+ assert result == 0.0
800+
801+
802+ class TestDocumentActionToolsMockData :
803+ """Test mock data generation."""
804+
805+ def test_get_mock_extraction_data (self ):
806+ """Test _get_mock_extraction_data generates valid structure."""
807+ tools = DocumentActionTools ()
808+
809+ result = tools ._get_mock_extraction_data ()
810+
811+ assert isinstance (result , dict )
812+ assert "extraction_results" in result
813+ assert "confidence_scores" in result
814+ assert "stages" in result
815+ assert "quality_score" in result
816+ assert "routing_decision" in result
817+ assert len (result ["extraction_results" ]) > 0
818+
819+
820+ class TestDocumentActionToolsDocumentOperations :
821+ """Test main document operation methods."""
822+
823+ @pytest .mark .asyncio
824+ async def test_upload_document_validation_failure (self , tmp_path ):
825+ """Test upload_document with validation failure."""
826+ tools = DocumentActionTools ()
827+
828+ # File doesn't exist
829+ result = await tools .upload_document (
830+ file_path = "/nonexistent/file.pdf" ,
831+ document_type = "invoice"
832+ )
833+
834+ assert result ["success" ] is False
835+ assert "validation failed" in result ["message" ].lower ()
836+
837+ @pytest .mark .asyncio
838+ async def test_upload_document_success (self , tmp_path ):
839+ """Test upload_document with valid file."""
840+ tools = DocumentActionTools ()
841+
842+ # Create test file
843+ test_file = tmp_path / "test.pdf"
844+ test_file .write_bytes (b"fake pdf content" )
845+
846+ with patch .object (tools , "_start_document_processing" , return_value = {"processing_started" : True }), \
847+ patch .object (tools , "_save_status_data" ):
848+ result = await tools .upload_document (
849+ file_path = str (test_file ),
850+ document_type = "invoice"
851+ )
852+
853+ assert result ["success" ] is True
854+ assert "document_id" in result
855+ assert result ["status" ] == "processing_started"
856+
857+ @pytest .mark .asyncio
858+ async def test_upload_document_with_custom_id (self , tmp_path ):
859+ """Test upload_document with custom document ID."""
860+ tools = DocumentActionTools ()
861+
862+ test_file = tmp_path / "test.pdf"
863+ test_file .write_bytes (b"fake pdf content" )
864+ custom_id = "custom-doc-123"
865+
866+ with patch .object (tools , "_start_document_processing" , return_value = {"processing_started" : True }), \
867+ patch .object (tools , "_save_status_data" ):
868+ result = await tools .upload_document (
869+ file_path = str (test_file ),
870+ document_type = "invoice" ,
871+ document_id = custom_id
872+ )
873+
874+ assert result ["success" ] is True
875+ assert result ["document_id" ] == custom_id
876+
877+ @pytest .mark .asyncio
878+ async def test_get_document_status_not_found (self ):
879+ """Test get_document_status with non-existent document."""
880+ tools = DocumentActionTools ()
881+
882+ with patch .object (tools , "_get_processing_status" , return_value = None ):
883+ result = await tools .get_document_status ("nonexistent-doc" )
884+
885+ assert result ["success" ] is True # Returns success with unknown status
886+ assert result ["status" ] == "unknown"
887+
888+ @pytest .mark .asyncio
889+ async def test_get_document_status_found (self ):
890+ """Test get_document_status with existing document."""
891+ tools = DocumentActionTools ()
892+
893+ doc_id = "test-doc-123"
894+ tools .document_statuses [doc_id ] = {
895+ "status" : "processing" ,
896+ "current_stage" : "OCR Extraction" ,
897+ "progress" : 50 ,
898+ "stages" : [{"name" : "preprocessing" , "status" : "completed" }],
899+ "estimated_completion" : datetime .now ().timestamp () + 60 ,
900+ }
901+
902+ with patch .object (tools , "_get_processing_status" ) as mock_get :
903+ mock_get .return_value = {
904+ "status" : "processing" ,
905+ "current_stage" : "OCR Extraction" ,
906+ "progress" : 50 ,
907+ "stages" : [{"name" : "preprocessing" , "status" : "completed" }],
908+ "estimated_completion" : datetime .now ().timestamp () + 60 ,
909+ }
910+
911+ result = await tools .get_document_status (doc_id )
912+
913+ assert result ["success" ] is True
914+ assert result ["status" ] == "processing"
915+ assert result ["progress" ] == 50
916+
917+ @pytest .mark .asyncio
918+ async def test_extract_document_data_not_found (self ):
919+ """Test extract_document_data with non-existent document."""
920+ tools = DocumentActionTools ()
921+
922+ with patch .object (tools , "_get_extraction_data" , return_value = None ):
923+ result = await tools .extract_document_data ("nonexistent-doc" )
924+
925+ assert result ["success" ] is False
926+ assert "not found" in result ["message" ].lower ()
927+
928+ @pytest .mark .asyncio
929+ async def test_start_document_processing (self ):
930+ """Test _start_document_processing returns processing info."""
931+ tools = DocumentActionTools ()
932+
933+ result = await tools ._start_document_processing ()
934+
935+ assert result ["processing_started" ] is True
936+ assert "pipeline_id" in result
937+ assert "estimated_completion" in result
938+
0 commit comments