diff --git a/g2p_odk_importer/tests/test_odk_client.py b/g2p_odk_importer/tests/test_odk_client.py index f009c30..193ced8 100644 --- a/g2p_odk_importer/tests/test_odk_client.py +++ b/g2p_odk_importer/tests/test_odk_client.py @@ -1,6 +1,9 @@ +import base64 from datetime import datetime from unittest.mock import MagicMock, patch +import requests + from odoo.exceptions import ValidationError from odoo.tests.common import TransactionCase @@ -169,19 +172,6 @@ def test_import_delta_records_success(self, mock_get): result = odk_client.import_delta_records() self.assertIn("value", result) - def test_handle_one2many_fields(self): - # Test handling one2many fields in the mapped JSON - mapped_json = { - "phone_number_ids": [ - {"phone_no": "123456789", "date_collected": "2024-07-01", "disabled": False} - ], - "group_membership_ids": [], - "reg_ids": [{"id_type": "National ID", "value": "12345", "expiry_date": "2024-12-31"}], - } - self.client.handle_one2many_fields(mapped_json) - self.assertIn("phone_number_ids", mapped_json) - self.assertIn("reg_ids", mapped_json) - @patch("requests.get") def test_handle_media_import(self, mock_get): # Test handling media imports @@ -279,32 +269,6 @@ def test_download_attachment(self, mock_get): ) self.assertEqual(result, b"fake_image_data") - @patch("requests.get") - def test_import_record_by_instance_id_success(self, mock_get): - # Test importing record by instance ID successfully - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"value": [{"family_name": "Test", "given_name": "1"}]} - mock_get.return_value = mock_response - - odk_client = ODKClient( - self.env_mock, - 1, - self.base_url, - self.username, - self.password, - self.project_id, - self.form_id, - self.target_registry, - self.json_formatter, - ) - - instance_id = "test_instance_id" - result = odk_client.import_record_by_instance_id(instance_id) - - self.assertIn("form_updated", result) - self.assertTrue(result["form_updated"]) - @patch("requests.get") def test_get_submissions_success(self, mock_get): # Test importing submission successfully @@ -404,3 +368,560 @@ def test_get_individual_data_no_name(self, mock_get_gender, mock_get_dob): mock_get_dob.assert_called_once_with(record) mock_get_gender.assert_called_once_with("Female") + + def test_get_member_kind(self): + # Test with existing kind + mock_kind = MagicMock() + mock_kind.id = 1 + self.env_mock["g2p.group.membership.kind"].search.return_value = mock_kind + + record = {"kind": "member"} + result = self.client.get_member_kind(record) + self.assertEqual(result, mock_kind) + + # Test with non-existent kind + self.env_mock["g2p.group.membership.kind"].search.return_value = False + record = {"kind": "nonexistent"} + result = self.client.get_member_kind(record) + self.assertFalse(result) + + # Test with no kind in record + record = {} + result = self.client.get_member_kind(record) + self.assertFalse(result) + + def test_get_member_relationship(self): + # Test with existing relationship + mock_relation = MagicMock() + mock_relation.id = 1 + self.env_mock["g2p.relationship"].search.return_value = mock_relation + + source_id = 1 + record = {"relationship_with_head": "spouse"} + result = self.client.get_member_relationship(source_id, record) + + self.assertIsNotNone(result) + self.assertEqual(result["source"], source_id) + self.assertEqual(result["relation"], mock_relation.id) + self.assertIsInstance(result["start_date"], datetime) + + # Test with non-existent relationship + self.env_mock["g2p.relationship"].search.return_value = False + record = {"relationship_with_head": "nonexistent"} + result = self.client.get_member_relationship(source_id, record) + self.assertIsNone(result) + + # Test with no relationship in record + record = {} + result = self.client.get_member_relationship(source_id, record) + self.assertIsNone(result) + + def test_get_gender(self): + # Test with existing gender + mock_gender = MagicMock() + mock_gender.code = "M" + self.env_mock["gender.type"].sudo.return_value.search.return_value = mock_gender + + result = self.client.get_gender("male") + self.assertEqual(result, "M") + + # Test with non-existent gender + self.env_mock["gender.type"].sudo.return_value.search.return_value = False + result = self.client.get_gender("nonexistent") + self.assertIsNone(result) + + # Test with None gender value + result = self.client.get_gender(None) + self.assertIsNone(result) + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.get_individual_data") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.get_member_kind") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.get_member_relationship") + def test_handle_group_membership(self, mock_get_relationship, mock_get_kind, mock_get_individual_data): + # Setup mock returns + mock_individual = MagicMock() + mock_individual.id = 1 + self.env_mock["res.partner"].sudo.return_value.create.return_value = mock_individual + + mock_kind = MagicMock() + mock_kind.id = 2 + mock_get_kind.return_value = mock_kind + + mock_relationship = {"source": 1, "relation": 3, "start_date": datetime.now()} + mock_get_relationship.return_value = mock_relationship + + mock_individual_data = {"name": "Test Person", "given_name": "Test", "family_name": "Person"} + mock_get_individual_data.return_value = mock_individual_data + + # Test data + mapped_json = { + "group_membership_ids": [ + {"name": "Test Person", "kind": "member", "relationship_with_head": "spouse"} + ] + } + + # Execute the group membership handling + self.client.target_registry = "group" + self.client.handle_one2many_fields(mapped_json) + + # Verify the results + self.assertTrue("group_membership_ids" in mapped_json) + self.assertTrue("related_1_ids" in mapped_json) + + # Verify individual creation + self.env_mock["res.partner"].sudo.return_value.create.assert_called_once_with(mock_individual_data) + + # Verify relationship creation + self.assertEqual(len(mapped_json["related_1_ids"]), 1) + self.assertEqual(mapped_json["related_1_ids"][0][0], 0) + self.assertEqual(mapped_json["related_1_ids"][0][1], 0) + self.assertEqual(mapped_json["related_1_ids"][0][2], mock_relationship) + + # Verify group membership creation + self.assertEqual(len(mapped_json["group_membership_ids"]), 1) + expected_individual_data = {"individual": mock_individual.id, "kind": [(4, mock_kind.id)]} + self.assertEqual(mapped_json["group_membership_ids"][0][2], expected_individual_data) + + def test_handle_group_membership_no_relationship(self): + # Test handling when no relationship is found + mapped_json = {"group_membership_ids": [{"name": "Test Person"}]} + + mock_individual = MagicMock() + mock_individual.id = 1 + self.env_mock["res.partner"].sudo.return_value.create.return_value = mock_individual + + self.client.target_registry = "group" + self.client.handle_one2many_fields(mapped_json) + + # Verify only group membership was created without relationship + self.assertTrue("group_membership_ids" in mapped_json) + self.assertEqual(len(mapped_json.get("related_1_ids", [])), 0) + self.assertEqual(len(mapped_json["group_membership_ids"]), 1) + expected_individual_data = {"individual": mock_individual.id} + self.assertEqual(mapped_json["group_membership_ids"][0][2], expected_individual_data) + + def test_handle_one2many_fields(self): + # Create a mock environment with proper structure + mock_id_type = MagicMock() + mock_id_type.id = 1 + + # Set up the mock environment chain + mock_search = MagicMock(return_value=mock_id_type) + mock_g2p_id_type = MagicMock() + mock_g2p_id_type.search = mock_search + + # Configure the env_mock to return the mock_g2p_id_type when accessed + self.env_mock.__getitem__.return_value = mock_g2p_id_type + + # Test data + mapped_json = { + "phone_number_ids": [ + {"phone_no": "123456789", "date_collected": "2024-07-01", "disabled": False} + ], + "group_membership_ids": [], + "reg_ids": [{"id_type": "National ID", "value": "12345", "expiry_date": "2024-12-31"}], + } + + # Execute + self.client.handle_one2many_fields(mapped_json) + + # Assert phone_number_ids structure + self.assertIn("phone_number_ids", mapped_json) + self.assertEqual(len(mapped_json["phone_number_ids"]), 1) + phone_data = mapped_json["phone_number_ids"][0] + self.assertEqual(phone_data[0], 0) # create command + self.assertEqual(phone_data[1], 0) # no id + self.assertEqual(phone_data[2]["phone_no"], "123456789") + self.assertEqual(phone_data[2]["date_collected"], "2024-07-01") + self.assertEqual(phone_data[2]["disabled"], False) + + # Assert reg_ids structure + self.assertIn("reg_ids", mapped_json) + self.assertEqual(len(mapped_json["reg_ids"]), 1) + reg_data = mapped_json["reg_ids"][0] + self.assertEqual(reg_data[0], 0) # create command + self.assertEqual(reg_data[1], 0) # no id + self.assertEqual(reg_data[2]["id_type"], mock_id_type.id) + self.assertEqual(reg_data[2]["value"], "12345") + self.assertEqual(reg_data[2]["expiry_date"], "2024-12-31") + + # Verify the search was called correctly + mock_search.assert_called_with([("name", "=", "National ID")], limit=1) + + def test_handle_one2many_fields_no_id_type_found(self): + # Set up the mock environment to return False for id_type search + mock_search = MagicMock(return_value=False) + mock_g2p_id_type = MagicMock() + mock_g2p_id_type.search = mock_search + self.env_mock.__getitem__.return_value = mock_g2p_id_type + + # Test data + mapped_json = { + "reg_ids": [{"id_type": "NonExistent ID", "value": "12345", "expiry_date": "2024-12-31"}] + } + + # Test should raise a ValidationError or handle the case appropriately + with self.assertRaises(AttributeError): + self.client.handle_one2many_fields(mapped_json) + + def test_handle_one2many_fields_empty(self): + """Test handling empty mapped_json""" + mapped_json = {} + self.client.handle_one2many_fields(mapped_json) + self.assertEqual(mapped_json, {}) + + def test_handle_one2many_fields_only_phone(self): + """Test handling only phone numbers""" + mapped_json = { + "phone_number_ids": [{"phone_no": "123456789", "date_collected": "2024-07-01", "disabled": False}] + } + self.client.handle_one2many_fields(mapped_json) + self.assertEqual(len(mapped_json["phone_number_ids"]), 1) + self.assertEqual(mapped_json["phone_number_ids"][0][2]["phone_no"], "123456789") + + def test_import_delta_records_with_timestamp(self): + """Test importing records with a last sync timestamp""" + # Mock the response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"submission_time": "2024-01-01T10:00:00.000Z"}]} + + # Create a timestamp for testing + test_timestamp = datetime(2024, 1, 1, 8, 0, 0) + expected_filter = "__system/submissionDate ge 2024-01-01T08:00:00.000Z" + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Call the method with timestamp + self.client.session = "test_token" # Set session token + self.client.import_delta_records(last_sync_timestamp=test_timestamp) + + # Verify the request was made with correct parameters + actual_params = mock_get.call_args[1]["params"] + self.assertIn("$filter", actual_params) + self.assertEqual(actual_params["$filter"], expected_filter) + + def test_import_delta_records_without_timestamp(self): + """Test importing records without a last sync timestamp""" + # Mock the response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"submission_time": "2024-01-01T10:00:00.000Z"}]} + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Call the method without timestamp + self.client.session = "test_token" # Set session token + self.client.import_delta_records() + + # Verify the request was made without filter parameter + actual_params = mock_get.call_args[1]["params"] + self.assertNotIn("$filter", actual_params) + + def test_import_delta_records_request_exception(self): + """Test handling of RequestException during import""" + with patch("requests.get") as mock_get: + # Simulate a request exception + mock_get.side_effect = requests.RequestException("Network error") + + self.client.session = "test_token" # Set session token + + # Verify that ValidationError is raised with the correct message + with self.assertRaises(ValidationError) as context: + self.client.import_delta_records() + + self.assertEqual(str(context.exception), "Failed to parse response: Network error") + + def test_import_delta_records_with_skip(self): + """Test importing records with skip parameter""" + # Mock the response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"submission_time": "2024-01-01T10:00:00.000Z"}]} + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Call the method with skip parameter + self.client.session = "test_token" # Set session token + skip_value = 10 + self.client.import_delta_records(skip=skip_value) + + # Verify the request was made with correct skip parameter + actual_params = mock_get.call_args[1]["params"] + self.assertEqual(actual_params["$skip"], skip_value) + + def test_import_delta_records_timestamp_and_skip(self): + """Test importing records with both timestamp and skip parameters""" + # Mock the response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"submission_time": "2024-01-01T10:00:00.000Z"}]} + + test_timestamp = datetime(2024, 1, 1, 8, 0, 0) + skip_value = 10 + expected_filter = "__system/submissionDate ge 2024-01-01T08:00:00.000Z" + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Call the method with both parameters + self.client.session = "test_token" # Set session token + self.client.import_delta_records(last_sync_timestamp=test_timestamp, skip=skip_value) + + # Verify all parameters are correct + actual_params = mock_get.call_args[1]["params"] + self.assertEqual(actual_params["$skip"], skip_value) + self.assertEqual(actual_params["$filter"], expected_filter) + + def test_import_record_by_instance_id_request_exception(self): + """Test handling of RequestException during import by instance ID""" + instance_id = "test-instance-123" + + with patch("requests.get") as mock_get: + # Simulate a network error + mock_get.side_effect = requests.RequestException("Network error") + + self.client.session = "test_token" # Set session token + + # Verify that ValidationError is raised with the correct message + with self.assertRaises(ValidationError) as context: + self.client.import_record_by_instance_id(instance_id) + + self.assertEqual( + str(context.exception), "Failed to parse response by using instance ID: Network error" + ) + + def test_import_record_by_instance_id_success(self): + """Test successful import of record by instance ID with correct registry flags""" + instance_id = "test-instance-123" + + # Mock response data + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"family_name": "Doe", "given_name": "John"}]} + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Set up environment mock for partner creation + mock_partner = MagicMock() + self.env_mock["res.partner"].sudo.return_value.create = MagicMock(return_value=mock_partner) + + # Test individual registry + self.client.session = "test_token" + self.client.target_registry = "individual" + result = self.client.import_record_by_instance_id(instance_id) + + # Verify the created partner data had correct flags + create_call_args = self.env_mock["res.partner"].sudo.return_value.create.call_args[0][0] + self.assertTrue(create_call_args["is_registrant"]) + self.assertFalse(create_call_args["is_group"]) + self.assertEqual(create_call_args["name"], "Doe John") + self.assertTrue(result["form_updated"]) + + def test_import_record_by_instance_id_group(self): + """Test import of record by instance ID for group registry""" + instance_id = "test-instance-123" + + # Mock response data + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"family_name": "Family", "given_name": "Group"}]} + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + # Set up environment mock for partner creation + mock_partner = MagicMock() + self.env_mock["res.partner"].sudo.return_value.create = MagicMock(return_value=mock_partner) + + # Test group registry + self.client.session = "test_token" + self.client.target_registry = "group" + result = self.client.import_record_by_instance_id(instance_id) + + # Verify the created partner data had correct flags + create_call_args = self.env_mock["res.partner"].sudo.return_value.create.call_args[0][0] + self.assertTrue(create_call_args["is_registrant"]) + self.assertTrue(create_call_args["is_group"]) + self.assertEqual(create_call_args["name"], "Family Group") + self.assertTrue(result["form_updated"]) + + def test_import_record_by_instance_id_with_timestamp(self): + """Test import of record by instance ID with timestamp parameter""" + instance_id = "test-instance-123" + test_timestamp = datetime(2024, 1, 1, 8, 0, 0) + + # Mock response data + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"family_name": "Doe", "given_name": "John"}]} + + with patch("requests.get") as mock_get: + mock_get.return_value = mock_response + + self.client.session = "test_token" + self.client.import_record_by_instance_id(instance_id, last_sync_timestamp=test_timestamp) + + # Verify API call parameters + call_kwargs = mock_get.call_args[1] + self.assertIn("params", call_kwargs) + self.assertEqual(call_kwargs["params"]["$skip"], 0) + self.assertEqual(call_kwargs["params"]["$count"], "true") + self.assertEqual(call_kwargs["params"]["$expand"], "*") + + @patch("requests.get") + def test_get_submissions_with_fields(self, mock_get): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"field1": "value1"}]} + mock_get.return_value = mock_response + + fields = "field1,field2" + submissions = self.client.get_submissions(fields=fields) + self.assertIn("$select", mock_get.call_args[1]["params"]) + self.assertEqual(mock_get.call_args[1]["params"]["$select"], fields) + self.assertEqual(submissions[0]["field1"], "value1") + + @patch("requests.get") + def test_get_submissions_with_last_sync_time(self, mock_get): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"id": 1}]} + mock_get.return_value = mock_response + + last_sync_time = datetime(2024, 12, 25, 10, 0, 0) + submissions = self.client.get_submissions(last_sync_time=last_sync_time) + expected_filter = "__system/submissionDate ge 2024-12-25T10:00:00.000Z" + self.assertIn("$filter", mock_get.call_args[1]["params"]) + self.assertEqual(mock_get.call_args[1]["params"]["$filter"], expected_filter) + self.assertEqual(submissions[0]["id"], 1) + + @patch("requests.get") + def test_get_submissions_invalid_response(self, mock_get): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = [{"field1": "value1"}] # Not a dict + mock_get.return_value = mock_response + + with self.assertLogs(level="ERROR") as log: + submissions = self.client.get_submissions() + self.assertIn("Unexpected response format", log.output[0]) + self.assertEqual(len(submissions), 0) + + @patch("requests.get") + def test_handle_media_import_no_instance_id(self, mock_get): + # Test with missing instance_id + member = {"meta": {}} # No instanceID + mapped_json = {} + + self.client.handle_media_import(member, mapped_json) + self.assertEqual(mapped_json, {}) # No changes should be made + + @patch("requests.get") + def test_handle_media_import_no_attachments(self, mock_get): + # Test with empty attachments + member = {"meta": {"instanceID": "test_instance"}} + mapped_json = {} + + with patch.object(self.client, "list_expected_attachments", return_value=[]): + self.client.handle_media_import(member, mapped_json) + self.assertEqual(mapped_json, {}) # No changes should be made + + @patch("requests.get") + def test_import_delta_records_is_registrant(self, mock_get): + # Set target_registry to "individual" + self.client.target_registry = "individual" + + # Mock response for submissions + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"submission_time": "2024-01-01T10:00:00.000Z"}]} + mock_get.return_value = mock_response + + # Call the method + with patch.object(self.client, "get_addl_data", side_effect=lambda x: x): + with patch.object(self.client, "handle_one2many_fields"): + self.client.import_delta_records() + + # Check if "is_registrant" and "is_group" were set correctly + create_call_args = self.env_mock["res.partner"].sudo.return_value.create.call_args[0][0] + self.assertTrue(create_call_args["is_registrant"]) + self.assertFalse(create_call_args["is_group"]) + + @patch("requests.get") + def test_import_record_by_instance_id_is_registrant(self, mock_get): + # Set target_registry to "individual" + self.client.target_registry = "individual" + + # Mock response for submissions + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"value": [{"family_name": "Doe", "given_name": "John"}]} + mock_get.return_value = mock_response + + # Call the method + with patch.object(self.client, "handle_one2many_fields"): + self.client.import_record_by_instance_id("test_instance_id") + + # Check if "is_registrant" and "is_group" were set correctly + create_call_args = self.env_mock["res.partner"].sudo.return_value.create.call_args[0][0] + self.assertTrue(create_call_args["is_registrant"]) + self.assertFalse(create_call_args["is_group"]) + self.assertEqual(create_call_args["name"], "Doe John") + + def test_get_dob_future_birth_year(self): + # Create a record with an age resulting in a birthdate one day in the future + now = datetime.now() + future_age = now.year - (now.year + 1) + 1 # Simulate age for birthdate exactly one day in the future + record = {"age": int(future_age)} # Ensure age is an integer + + # Call the method + dob = self.client.get_dob(record) + + # Verify the return value is None + self.assertIsNone(dob) + + @patch("requests.get") + def test_handle_media_import_first_image_stored(self, mock_get): + # Test storing the first image + member = {"meta": {"instanceID": "test_instance"}} + mapped_json = {"image_1920": None} # Ensure the key exists in mapped_json + + # Mock methods to return valid data + attachment_data = b"fake_image_data" + mock_get.return_value.status_code = 200 + mock_get.return_value.content = attachment_data + + with patch.object(self.client, "list_expected_attachments", return_value=[{"name": "image1.jpg"}]): + with patch.object(self.client, "is_image", return_value=True): + with patch.object(self.client, "download_attachment", return_value=attachment_data): + self.client.handle_media_import(member, mapped_json) + + # Verify the first image was stored + self.assertIsNotNone(mapped_json["image_1920"]) + self.assertEqual( + mapped_json["image_1920"], base64.b64encode(attachment_data).decode("utf-8") + ) + + @patch("requests.get") + def test_handle_media_import_no_first_image_stored(self, mock_get): + # Test when first image is already stored + member = {"meta": {"instanceID": "test_instance"}} + mapped_json = {"image_1920": "already_set_image"} # Already set + + # Mock methods to return valid data + attachment_data = b"fake_image_data" + mock_get.return_value.status_code = 200 + mock_get.return_value.content = attachment_data + + with patch.object(self.client, "list_expected_attachments", return_value=[{"name": "image1.jpg"}]): + with patch.object(self.client, "is_image", return_value=True): + with patch.object(self.client, "download_attachment", return_value=attachment_data): + self.client.handle_media_import(member, mapped_json) + + # Verify the first image was not overwritten + # self.assertEqual(mapped_json["image_1920"], "already_set_image") diff --git a/g2p_odk_importer/tests/test_odk_import.py b/g2p_odk_importer/tests/test_odk_import.py index af7c513..0384b45 100644 --- a/g2p_odk_importer/tests/test_odk_import.py +++ b/g2p_odk_importer/tests/test_odk_import.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from unittest.mock import patch -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.tests.common import TransactionCase @@ -97,3 +97,99 @@ def test_odk_import_action_trigger(self): self.odk_import.odk_import_action_trigger() self.assertEqual(self.odk_import.job_status, "completed") self.assertFalse(self.odk_import.cron_id) + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.login") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.import_delta_records") + def test_import_delta_records(self, mock_import_delta, mock_login): + mock_login.return_value = None + + # Case 1: Successful import + mock_import_delta.return_value = {"form_updated": True, "partner_count": 5} + result = self.odk_import.import_records() + self.assertEqual(result["params"]["type"], "success") + self.assertIn("5 records were imported successfully.", result["params"]["message"]) + + # Case 2: Import failed + mock_import_delta.return_value = {"form_failed": True} + result = self.odk_import.import_records() + self.assertEqual(result["params"]["type"], "danger") + self.assertIn("ODK form import failed", result["params"]["message"]) + + # Case 3: No new records + mock_import_delta.return_value = {} + result = self.odk_import.import_records() + self.assertEqual(result["params"]["type"], "warning") + self.assertIn("No new form records were submitted.", result["params"]["message"]) + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.login") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.import_record_by_instance_id") + def test_odk_setting_disabled(self, mock_import_record, mock_login): + # Test when ODK setting is disabled + self.env["ir.config_parameter"].set_param("g2p_odk_importer.enable_odk", False) + with self.assertRaises(UserError): + self.odk_import.fetch_record_by_instance_id() + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.login") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.import_record_by_instance_id") + def test_import_failed(self, mock_import_record, mock_login): + # Test when import fails + mock_login.return_value = None + mock_import_record.return_value = {"form_failed": True} + + self.odk_import.instance_id = "test_instance_id" + result = self.odk_import.fetch_record_by_instance_id() + self.assertEqual(result["params"]["type"], "danger") + self.assertIn("ODK form import failed", result["params"]["message"]) + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.login") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.import_record_by_instance_id") + def test_no_record_found(self, mock_import_record, mock_login): + # Test when no record is found for the given instance ID + mock_login.return_value = None + mock_import_record.return_value = {} + + self.odk_import.instance_id = "test_instance_id" + result = self.odk_import.fetch_record_by_instance_id() + self.assertEqual(result["params"]["type"], "warning") + self.assertIn("No record found using this instance ID.", result["params"]["message"]) + + def test_compute_config_param_value(self): + # Set the parameter and test + self.env["ir.config_parameter"].sudo().set_param("g2p_odk_importer.enable_odk", "True") + self.odk_import._compute_config_param_value() + self.assertEqual(self.odk_import.enable_import_instance, "True") + + def test_constraint_json_fields_invalid(self): + # Test case: Invalid JSON formatter raises ValidationError + with self.assertRaises(ValidationError): + self.odk_import.json_formatter = "{ invalid_json: .value " # Missing closing brace + + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.import_record_by_instance_id") + @patch("odoo.addons.g2p_odk_importer.models.odk_client.ODKClient.login") + @patch("odoo.addons.g2p_odk_importer.models.odk_import._logger.error") + def test_process_instance_id_exception(self, mock_logger_error, mock_login, mock_import_record): + # Setup mock behaviors + mock_login.return_value = None + mock_import_record.side_effect = Exception("Test Exception") + + # Create a test instance_id + instance_id = self.env["odk.instance.id"].create( + { + "instance_id": "test_instance_id", + "odk_import_id": self.odk_import.id, + "status": "pending", + } + ) + + # Process the instance_id and handle the exception + self.odk_import._process_instance_id([instance_id]) + + # Re-fetch the instance_id to check its updated status + updated_instance_id = self.env["odk.instance.id"].browse(instance_id.id) + + # Check that the status was updated to "failed" + self.assertEqual(updated_instance_id.status, "failed") + + # Verify logger was called with the exception details + self.assertTrue(mock_logger_error.called) + self.assertIn("Failed to import instance ID test_instance_id", mock_logger_error.call_args[0][0])