diff --git a/kairon/api/app/routers/bot/data.py b/kairon/api/app/routers/bot/data.py index a30e3dc0f..eec45924e 100644 --- a/kairon/api/app/routers/bot/data.py +++ b/kairon/api/app/routers/bot/data.py @@ -1,6 +1,7 @@ import os from fastapi import UploadFile, File, Security, APIRouter +from starlette.requests import Request from starlette.responses import FileResponse from kairon.api.models import Response, TextData, CognitiveDataRequest @@ -50,10 +51,11 @@ async def download_faq_files( return response -@router.post("/text/faq", response_model=Response) -def save_bot_text( +@router.post("/text/faq/{collection}", response_model=Response) +async def save_bot_text( text: TextData, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), + collection: str = None ): """ Saves text content into the bot @@ -65,16 +67,18 @@ def save_bot_text( text.data, current_user.get_user(), current_user.get_bot(), + collection ) } } -@router.put("/text/faq/{text_id}", response_model=Response) -def update_bot_text( +@router.put("/text/faq/{text_id}/{collection}", response_model=Response) +async def update_bot_text( text_id: str, text: TextData, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), + collection: str = None, ): """ Updates text content into the bot @@ -87,13 +91,14 @@ def update_bot_text( text.data, current_user.get_user(), current_user.get_bot(), + collection ) } } @router.delete("/text/faq/{text_id}", response_model=Response) -def delete_bot_text( +async def delete_bot_text( text_id: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), ): @@ -107,17 +112,29 @@ def delete_bot_text( @router.get("/text/faq", response_model=Response) -def get_text( +async def get_text( + request: Request, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), ): """ Fetches text content of the bot """ - return {"data": list(processor.get_content(current_user.get_bot()))} + kwargs = request.query_params._dict.copy() + return {"data": list(processor.get_content(current_user.get_bot(), **kwargs))} + + +@router.get("/text/faq/collection", response_model=Response) +async def list_collection( + current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), +): + """ + Fetches text content of the bot + """ + return {"data": processor.list_collection(current_user.get_bot())} @router.post("/cognition", response_model=Response) -def save_cognition_data( +async def save_cognition_data( cognition: CognitiveDataRequest, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), ): @@ -137,7 +154,7 @@ def save_cognition_data( @router.put("/cognition/{cognition_id}", response_model=Response) -def update_cognition_data( +async def update_cognition_data( cognition_id: str, cognition: CognitiveDataRequest, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), @@ -159,7 +176,7 @@ def update_cognition_data( @router.delete("/cognition/{cognition_id}", response_model=Response) -def delete_cognition_data( +async def delete_cognition_data( cognition_id: str, current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), ): @@ -173,7 +190,7 @@ def delete_cognition_data( @router.get("/cognition", response_model=Response) -def list_cognition_data( +async def list_cognition_data( current_user: User = Security(Authentication.get_current_user_and_bot, scopes=DESIGNER_ACCESS), ): """ diff --git a/kairon/api/models.py b/kairon/api/models.py index 69b0073d7..caefbe28e 100644 --- a/kairon/api/models.py +++ b/kairon/api/models.py @@ -883,6 +883,7 @@ class PromptActionConfigRequest(BaseModel): hyperparameters: dict = None llm_prompts: List[LlmPromptRequest] instructions: List[str] = [] + collection: str = None set_slots: List[SetSlotsUsingActionResponse] = [] dispatch_response: bool = True diff --git a/kairon/chat/converters/channels/telegram.py b/kairon/chat/converters/channels/telegram.py index 1271bf4c5..9b5b83d72 100644 --- a/kairon/chat/converters/channels/telegram.py +++ b/kairon/chat/converters/channels/telegram.py @@ -34,6 +34,27 @@ def link_transformer(self, message): except Exception as ex: raise Exception(f" Error in TelegramResponseConverter::link_transformer {str(ex)}") + def button_transformer(self, message): + try: + jsoniterator = ElementTransformerOps.json_generator(message) + reply_markup = {} + inline_keyboard = [] + reply_markup.update({"inline_keyboard": inline_keyboard}) + inline_keyboard_array = [] + for item in jsoniterator: + if item.get("type") == ElementTypes.BUTTON.value: + title = ElementTransformerOps.json_generator(item.get("children")) + for titletext in title: + button_text = titletext.get("text") + btn_body = {} + btn_body.update({"text": button_text}) + btn_body.update({"callback_data": item.get("value")}) + inline_keyboard_array.append(btn_body) + inline_keyboard.append(inline_keyboard_array) + return reply_markup + except Exception as ex: + raise Exception(f"Exception in TelegramResponseConverter::button_transfomer: {str(ex)}") + async def messageConverter(self, message): try: if self.message_type == ElementTypes.IMAGE.value: @@ -42,5 +63,7 @@ async def messageConverter(self, message): return self.link_transformer(message) elif self.message_type == ElementTypes.VIDEO.value: return super().video_transformer(message) + elif self.message_type == ElementTypes.BUTTON.value: + return self.button_transformer(message) except Exception as ex: raise Exception(f"Error in TelegramResponseConverter::messageConverter {str(ex)}") diff --git a/kairon/chat/handlers/channels/telegram.py b/kairon/chat/handlers/channels/telegram.py index 0669f7ee8..15615e78f 100644 --- a/kairon/chat/handlers/channels/telegram.py +++ b/kairon/chat/handlers/channels/telegram.py @@ -25,6 +25,8 @@ from kairon.chat.agent_processor import AgentProcessor from kairon import Utility from kairon.chat.converters.channels.response_factory import ConverterFactory +from kairon.chat.converters.channels.responseconverter import ElementTransformerOps +import json logger = logging.getLogger(__name__) @@ -159,6 +161,9 @@ async def send_custom_json( del response["text"] api_call = getattr(self, send_functions[("text",)]) api_call(recipient_id, *response_list, **response) + elif ops_type in ["button"]: + body_default = ElementTransformerOps.getChannelConfig(ChannelTypes.TELEGRAM.value, "body_message") + self.send_message(recipient_id, text=body_default, reply_markup=json.dumps(response)) else: self.send_message(recipient_id, str(json_message)) except Exception as ap: diff --git a/kairon/shared/data/data_objects.py b/kairon/shared/data/data_objects.py index c55fa58bb..d74b965b1 100644 --- a/kairon/shared/data/data_objects.py +++ b/kairon/shared/data/data_objects.py @@ -30,10 +30,10 @@ from validators import url, ValidationFailure from kairon.exceptions import AppException +from kairon.shared.data.audit.data_objects import Auditlog from kairon.shared.data.signals import push_notification, auditlogger from kairon.shared.models import TemplateType, StoryStepType, StoryType, CognitionDataType, CognitionMetadataType from kairon.shared.utils import Utility -from kairon.shared.data.audit.data_objects import Auditlog from .constant import EVENT_STATUS, SLOT_MAPPING_TYPE, TrainingDataSourceType from ..constants import WhatsappBSPTypes, LLMResourceProvider @@ -717,7 +717,7 @@ def clean(self): @auditlogger.log @push_notification.apply -class CognitionData(Auditlog): +class CognitionData(Document): vector_id = SequenceField(required=True) data = DynamicField(required=True) content_type = StringField(default=CognitionDataType.text.value, choices=[CognitionDataType.text.value, @@ -728,7 +728,7 @@ class CognitionData(Auditlog): bot = StringField(required=True) timestamp = DateTimeField(default=datetime.utcnow) - meta = {"indexes": [{"fields": ["bot"]}]} + meta = {"indexes": [{"fields": ["$data", "bot"]}]} def validate(self, clean=True): if clean: diff --git a/kairon/shared/data/processor.py b/kairon/shared/data/processor.py index 9c93ac442..479cf243f 100644 --- a/kairon/shared/data/processor.py +++ b/kairon/shared/data/processor.py @@ -5438,6 +5438,7 @@ def edit_prompt_action(self, prompt_action_id: str, request_data: dict, bot: Tex action.hyperparameters = request_data.get('hyperparameters', Utility.get_llm_hyperparameters()) action.llm_prompts = [LlmPrompt(**prompt) for prompt in request_data.get('llm_prompts', [])] action.instructions = request_data.get('instructions', []) + action.collection = request_data.get('collection') action.set_slots = request_data.get('set_slots', []) action.dispatch_response = request_data.get('dispatch_response', True) action.timestamp = datetime.utcnow() @@ -5754,7 +5755,7 @@ def get_razorpay_action_config(self, bot: Text, with_doc_id: bool = True): yield action - def save_content(self, content: Text, user: Text, bot: Text): + def save_content(self, content: Text, user: Text, bot: Text, collection: Text = None): bot_settings = self.get_bot_settings(bot=bot, user=user) if not bot_settings["llm_settings"]['enable_faq']: raise AppException('Faq feature is disabled for the bot! Please contact support.') @@ -5763,6 +5764,7 @@ def save_content(self, content: Text, user: Text, bot: Text): content_obj = CognitionData() content_obj.data = content + content_obj.collection = collection content_obj.user = user content_obj.bot = bot id = ( @@ -5770,7 +5772,7 @@ def save_content(self, content: Text, user: Text, bot: Text): ) return id - def update_content(self, content_id: str, content: Text, user: Text, bot: Text): + def update_content(self, content_id: str, content: Text, user: Text, bot: Text, collection: Text = None): if len(content.split()) < 10: raise AppException("Content should contain atleast 10 words.") @@ -5780,6 +5782,7 @@ def update_content(self, content_id: str, content: Text, user: Text, bot: Text): try: content_obj = CognitionData.objects(bot=bot, id=content_id).get() content_obj.data = content + content_obj.collection = collection content_obj.user = user content_obj.timestamp = datetime.utcnow() content_obj.save() @@ -5793,20 +5796,35 @@ def delete_content(self, content_id: str, user: Text, bot: Text): except DoesNotExist: raise AppException("Text does not exists!") - def get_content(self, bot: Text): + def get_content(self, bot: Text, **kwargs): """ fetches content :param bot: bot id + :param collection: name of the collection :return: yield dict """ - for value in CognitionData.objects(bot=bot): - final_data = {} + kwargs["bot"] = bot + search = kwargs.pop('data', None) + start_idx = kwargs.pop('start_idx', None) + page_size = kwargs.pop('page_size', None) + cognition_data = CognitionData.objects(**kwargs) + if search: + cognition_data = cognition_data.search_text(search) + for value in cognition_data.skip(start_idx).limit(page_size): item = value.to_mongo().to_dict() - data = item.pop("data") - final_data["_id"] = item["_id"].__str__() - final_data['content'] = data - yield final_data + item.pop('timestamp') + item["_id"] = item["_id"].__str__() + yield item + + def list_collection(self, bot: Text): + """ + Retrieve cognition data. + + :param bot: bot id + """ + collections = list(CognitionData.objects(bot=bot).distinct(field='collection')) + return collections def save_cognition_data(self, payload: Dict, user: Text, bot: Text): bot_settings = self.get_bot_settings(bot=bot, user=user) diff --git a/metadata/message_template.yml b/metadata/message_template.yml index 4b97be8f5..335e81bc4 100644 --- a/metadata/message_template.yml +++ b/metadata/message_template.yml @@ -122,6 +122,7 @@ telegram: "disable_notification":false, "reply_to_message_id":0}' video: '{"text":""}' + body_message: 'Please select from quick buttons:' msteams: body_message: "Please select from quick buttons:" diff --git a/tests/integration_test/services_test.py b/tests/integration_test/services_test.py index 6f8da5848..6ef2d0ac8 100644 --- a/tests/integration_test/services_test.py +++ b/tests/integration_test/services_test.py @@ -983,7 +983,7 @@ def test_list_bots(): def test_content_upload_api_with_gpt_feature_disabled(): response = client.post( - url=f"/api/bot/{pytest.bot}/data/text/faq", + url=f"/api/bot/{pytest.bot}/data/text/faq/data_details", json={ "data": "Data refers to any collection of facts, statistics, or information that can be analyzed or " "used to inform decision-making. Data can take many forms, including text, numbers, images, " @@ -1272,8 +1272,9 @@ def _mock_get_bot_settings(*args, **kwargs): return BotSettings(bot=pytest.bot, user="integration@demo.ai", llm_settings=LLMSettings(enable_faq=True)) monkeypatch.setattr(MongoProcessor, 'get_bot_settings', _mock_get_bot_settings) + collection = "data_details" response = client.post( - url=f"/api/bot/{pytest.bot}/data/text/faq", + url=f"/api/bot/{pytest.bot}/data/text/faq/{collection}", json={ "data": "Data refers to any collection of facts, statistics, or information that can be analyzed or " "used to inform decision-making. Data can take many forms, including text, numbers, images, " @@ -1293,8 +1294,9 @@ def _mock_get_bot_settings(*args, **kwargs): return BotSettings(bot=pytest.bot, user="integration@demo.ai", llm_settings=LLMSettings(enable_faq=True)) monkeypatch.setattr(MongoProcessor, 'get_bot_settings', _mock_get_bot_settings) + collection = "data" response = client.post( - url=f"/api/bot/{pytest.bot}/data/text/faq", + url=f"/api/bot/{pytest.bot}/data/text/faq/{collection}", json={ "data": "Data" }, @@ -1309,13 +1311,15 @@ def _mock_get_bot_settings(*args, **kwargs): def test_content_updated_api(): + collection = "aws" response = client.put( - url=f"/api/bot/{pytest.bot}/data/text/faq/{pytest.content_id}", + url=f"/api/bot/{pytest.bot}/data/text/faq/{pytest.content_id}/{collection}", json={ "text_id": pytest.content_id, "data": "AWS Fargate is a serverless compute engine for containers that allows you to run " "Docker containers without having to manage the underlying EC2 instances. With Fargate, " - "you can focus on developing and deploying your applications rather than managing the infrastructure." + "you can focus on developing and deploying your applications rather than managing the infrastructure.", + "collection": "aws" }, headers={"Authorization": pytest.token_type + " " + pytest.access_token} @@ -1328,7 +1332,7 @@ def test_content_updated_api(): def test_content_update_api_invalid(): response = client.put( - url=f"/api/bot/{pytest.bot}/data/text/faq/{pytest.content_id}", + url=f"/api/bot/{pytest.bot}/data/text/faq/{pytest.content_id}/{None}", json={ "text_id": pytest.content_id, "data": "Data" @@ -1346,7 +1350,7 @@ def test_content_update_api_invalid(): def test_content_update_api_already_exist(): content_id = '6009cb85e65f6dce28fb3e51' response = client.put( - url=f"/api/bot/{pytest.bot}/data/text/faq/{content_id}", + url=f"/api/bot/{pytest.bot}/data/text/faq/{content_id}/{None}", json={ "text_id": content_id, "data": "AWS Fargate is a serverless compute engine for containers that allows you to run " @@ -1366,7 +1370,7 @@ def test_content_update_api_already_exist(): def test_content_update_api_id_not_found(): content_id = '594ced02ed345b2b049222c5' response = client.put( - url=f"/api/bot/{pytest.bot}/data/text/faq/{content_id}", + url=f"/api/bot/{pytest.bot}/data/text/faq/{content_id}/{None}", json={ "text_id": content_id, "data": "Artificial intelligence (AI) involves using computers to do things that traditionally require human " @@ -1383,12 +1387,38 @@ def test_content_update_api_id_not_found(): assert actual["error_code"] == 422 -def test_get_content(): +@mock.patch('kairon.shared.data.processor.MongoProcessor.get_content', autospec=True) +def test_get_content(mock_get_content): + def _get_content(*args, **kwargs): + return [{'vector_id': 1, + '_id': '65266ff16f0190ca4fd09898', + 'data': 'AWS Fargate is a serverless compute engine for containers that allows you to run Docker containers without having to manage the underlying EC2 instances. With Fargate, you can focus on developing and deploying your applications rather than managing the infrastructure.', + 'user': '"integration@demo.ai"', 'bot': pytest.bot, + 'content_type': 'text', + 'metadata': [], + 'collection': 'aws'}] + + mock_get_content.return_value = _get_content() + filter_query = 'without having to manage' + response = client.get( + url=f"/api/bot/{pytest.bot}/data/text/faq?data={filter_query}&start_idx=0&page_size=10", + headers={"Authorization": pytest.token_type + " " + pytest.access_token} + ) + actual = response.json() + print(actual) + assert actual["success"] + assert actual["error_code"] == 0 + assert actual["data"] + assert actual["data"][0]['collection'] + + +def test_list_collection(): response = client.get( - url=f"/api/bot/{pytest.bot}/data/text/faq", + url=f"/api/bot/{pytest.bot}/data/text/faq/collection", headers={"Authorization": pytest.token_type + " " + pytest.access_token} ) actual = response.json() + print(actual) assert actual["success"] assert actual["error_code"] == 0 assert actual["data"] @@ -1425,9 +1455,14 @@ def test_delete_content_does_not_exist(): assert actual["error_code"] == 422 -def test_get_content_not_exists(): +@mock.patch('kairon.shared.data.processor.MongoProcessor.get_content', autospec=True) +def test_get_content_not_exists(mock_get_content): + def _get_content(*args, **kwargs): + return [] + + mock_get_content.return_value = _get_content() response = client.get( - url=f"/api/bot/{pytest.bot}/data/text/faq", + url=f"/api/bot/{pytest.bot}/data/text/faq?start_idx=0&page_size=10", headers={"Authorization": pytest.token_type + " " + pytest.access_token} ) actual = response.json() @@ -1977,6 +2012,7 @@ def _mock_get_bot_settings(*args, **kwargs): 'data': 'If there is no specific query, assume that user is aking about java programming.', 'instructions': 'Answer according to the context', 'type': 'query', 'source': 'static', 'is_enabled': True}], 'instructions': ['Answer in a short manner.', 'Keep it simple.'], + 'collection': 'Bot_collection', 'num_bot_responses': 5, "failure_message": DEFAULT_NLU_FALLBACK_RESPONSE, "top_results": 10, "similarity_threshold": 0.70} response = client.post( diff --git a/tests/testing_data/channel_data/channel_data.json b/tests/testing_data/channel_data/channel_data.json index 582cef603..1e7cfd240 100644 --- a/tests/testing_data/channel_data/channel_data.json +++ b/tests/testing_data/channel_data/channel_data.json @@ -457,5 +457,12 @@ ], "button": "Submit Option" } - } + }, + "telegram_button_op_one": {"inline_keyboard": [[{"text": "One", "callback_data": "single button is clicked"}]]}, + "telegram_button_op_multi": {"inline_keyboard": [[{"text": "Veg", "callback_data": "Vegetables only"}, + {"text": "Non-veg", "callback_data": "fish food served"}, + {"text": "Desert", "callback_data": "Only Desert food"}]]}, + + "button_one_exception": [{"type":"button","value":"single button is clicked","id":"5"}, + {"type":"paragraph","children":[{"text":""}]}] } \ No newline at end of file diff --git a/tests/unit_test/data_processor/data_processor_test.py b/tests/unit_test/data_processor/data_processor_test.py index 7f4a9c642..43f17e64b 100644 --- a/tests/unit_test/data_processor/data_processor_test.py +++ b/tests/unit_test/data_processor/data_processor_test.py @@ -14237,13 +14237,14 @@ def test_save_content_with_gpt_feature_disabled(self): processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = "Bot" content = 'A bot, short for robot, is a program or software application designed to automate certain tasks or ' \ 'perform specific functions, usually in an automated or semi-automated manner. Bots can be programmed' \ ' to perform a wide range of tasks, from simple tasks like answering basic questions or sending ' \ 'automated messages to complex tasks like performing data analysis, playing games, or even controlling ' \ 'physical machines.' with pytest.raises(AppException, match="Faq feature is disabled for the bot! Please contact support."): - processor.save_content(content, user, bot) + processor.save_content(content, user, bot, collection) settings = BotSettings.objects(bot=bot).get() settings.llm_settings = LLMSettings(enable_faq=True) @@ -14253,41 +14254,45 @@ def test_save_content(self): processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = "Bot" content = 'A bot, short for robot, is a program or software application designed to automate certain tasks or ' \ 'perform specific functions, usually in an automated or semi-automated manner. Bots can be programmed' \ ' to perform a wide range of tasks, from simple tasks like answering basic questions or sending ' \ 'automated messages to complex tasks like performing data analysis, playing games, or even controlling ' \ 'physical machines.' - pytest.content_id = processor.save_content(content, user, bot) + pytest.content_id = processor.save_content(content, user, bot, collection) content_id = '5349b4ddd2791d08c09890f3' with pytest.raises(AppException, match="Text already exists!"): - processor.update_content(content_id, content, user, bot) + processor.update_content(content_id, content, user, bot, None) def test_save_content_invalid(self): processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = 'example' content = 'A bot, short for robot, is a program.' with pytest.raises(AppException, match="Content should contain atleast 10 words."): - processor.save_content(content, user, bot) + processor.save_content(content, user, bot, collection) def test_update_content(self): processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = 'Bot_details' content = 'Bots are commonly used in various industries, such as e-commerce, customer service, gaming, ' \ 'and social media. Some bots are designed to interact with humans in a conversational manner and are ' \ 'called chatbots or virtual assistants.' - processor.update_content(pytest.content_id, content, user, bot) + processor.update_content(pytest.content_id, content, user, bot, collection) def test_update_content_invalid(self): processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = 'example_one' content = 'Bots are commonly used in various industries.' with pytest.raises(AppException, match="Content should contain atleast 10 words."): - content_id = processor.save_content(content, user, bot) - processor.update_content(content_id, content, user, bot) + content_id = processor.save_content(content, user, bot, collection) + processor.update_content(content_id, content, user, bot, collection) def test_update_content_not_found(self): processor = MongoProcessor() @@ -14299,7 +14304,7 @@ def test_update_content_not_found(self): 'MongoDB is developed by MongoDB Inc. and licensed under the Server Side Public License which is ' \ 'deemed non-free by several distributions.' with pytest.raises(AppException, match="Content with given id not found!"): - processor.update_content(content_id, content, user, bot) + processor.update_content(content_id, content, user, bot, None) def test_delete_content(self): processor = MongoProcessor() @@ -14314,23 +14319,51 @@ def test_delete_content_does_not_exists(self): with pytest.raises(AppException, match="Text does not exists!"): processor.delete_content("507f191e810c19729de860ea", user, bot) - def test_get_content_not_exists(self): + @patch("kairon.shared.data.processor.MongoProcessor.get_content", autospec=True) + def test_get_content_not_exists(self, mock_get_content): + def _get_content(*args, **kwargs): + return [] + + mock_get_content.return_value = _get_content() + kwargs = {} processor = MongoProcessor() bot = 'test' - assert list(processor.get_content(bot)) == [] + assert list(processor.get_content(bot, **kwargs)) == [] - def test_get_content(self): + @patch("kairon.shared.data.processor.MongoProcessor.get_content", autospec=True) + def test_get_content(self, mock_get_content): + def _get_content(*args, **kwargs): + return [{'vector_id': 1, + '_id': '65266ff16f0190ca4fd09898', + 'data': 'Unit testing is a software testing technique in which individual units or components of a software application are tested in isolation to ensure that each unit functions as expected. ', + 'user': 'testUser', 'bot': 'test', + 'content_type': 'text', + 'metadata': [], + 'collection': 'testing'}] + + mock_get_content.return_value = _get_content() processor = MongoProcessor() bot = 'test' user = 'testUser' + collection = 'testing' content = 'Unit testing is a software testing technique in which individual units or components of a software ' \ 'application are tested in isolation to ensure that each unit functions as expected. ' - pytest.content_id = processor.save_content(content, user, bot) - data = list(processor.get_content(bot)) + pytest.content_id = processor.save_content(content, user, bot, collection) + kwargs = {'data': 'Unit testing'} + data = list(processor.get_content(bot, **kwargs)) + print(data) assert data[0][ - 'content'] == 'Unit testing is a software testing technique in which individual units or components of a ' \ + 'data'] == 'Unit testing is a software testing technique in which individual units or components of a ' \ 'software application are tested in isolation to ensure that each unit functions as expected. ' assert data[0]['_id'] + assert data[0]['collection'] == 'testing' + + def test_list_content(self): + bot = 'test' + user = 'testUser' + processor = MongoProcessor() + contents = processor.list_collection(bot) + assert contents def test_delete_content_for_action(self): processor = MongoProcessor() diff --git a/tests/unit_test/utility_test.py b/tests/unit_test/utility_test.py index 1f34d7ac5..acb8fcb9b 100644 --- a/tests/unit_test/utility_test.py +++ b/tests/unit_test/utility_test.py @@ -41,6 +41,7 @@ from kairon.shared.models import TemplateType from kairon.shared.utils import Utility, MailUtility from kairon.shared.verification.email import QuickEmailVerification +from kairon.chat.converters.channels.telegram import TelegramResponseConverter class TestUtility: @@ -2747,3 +2748,35 @@ def test_is_picklable_for_mongo_failure(self): assert not Utility.is_picklable_for_mongo({"requests": requests}) assert not Utility.is_picklable_for_mongo({"utility": Utility}) assert not Utility.is_picklable_for_mongo({"is_picklable_for_mongo": Utility.is_picklable_for_mongo}) + + def test_button_transformer_telegram_single_button(self): + json_data = json.load(open("tests/testing_data/channel_data/channel_data.json")) + input_json = json_data.get("button_one") + telegram = TelegramResponseConverter("button", "telegram") + response = telegram.button_transformer(input_json) + expected_output = json_data.get("telegram_button_op_one") + assert expected_output == response + + def test_button_transformer_telegram_multi_buttons(self): + json_data = json.load(open("tests/testing_data/channel_data/channel_data.json")) + input_json = json_data.get("button_three") + telegram = TelegramResponseConverter("button", "telegram") + response = telegram.button_transformer(input_json) + expected_output = json_data.get("telegram_button_op_multi") + assert expected_output == response + + @pytest.mark.asyncio + async def test_button_transformer_telegram_messageConverter(self): + json_data = json.load(open("tests/testing_data/channel_data/channel_data.json")) + input_json = json_data.get("button_three") + telegram = ConverterFactory.getConcreteInstance("button", "telegram") + response = await telegram.messageConverter(input_json) + expected_output = json_data.get("telegram_button_op_multi") + assert expected_output == response + + def test_button_transformer_telegram_exception(self): + json_data = json.load(open("tests/testing_data/channel_data/channel_data.json")) + input_json = json_data.get("button_one_exception") + telegram = TelegramResponseConverter("button", "telegram") + with pytest.raises(Exception): + telegram.button_transformer(input_json)