From 169898e352786988a88b450e33ac3227baf60784 Mon Sep 17 00:00:00 2001 From: juk0de Date: Sun, 20 Aug 2023 19:59:38 +0200 Subject: [PATCH] tests: added testcases for Message.from_file() and Message.to_file() --- tests/test_message.py | 521 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 520 insertions(+), 1 deletion(-) diff --git a/tests/test_message.py b/tests/test_message.py index 220fef2..5066068 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -1,5 +1,9 @@ +import pathlib +import tempfile +from typing import cast from .test_main import CmmTestCase -from chatmastermind.message import source_code, MessageError, Question, Answer +from chatmastermind.message import source_code, Message, MessageError, Question, Answer, AILine, ModelLine, MessageFilter +from chatmastermind.tags import Tag, TagLine class SourceCodeTestCase(CmmTestCase): @@ -76,3 +80,518 @@ class AnswerTestCase(CmmTestCase): answer = Answer("No") self.assertIsInstance(answer, Answer) self.assertEqual(answer, "No") + + +class MessageToFileTxtTestCase(CmmTestCase): + def setUp(self) -> None: + self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt') + self.file_path = pathlib.Path(self.file.name) + self.message_complete = Message(Question('This is a question.'), + Answer('This is an answer.'), + {Tag('tag1'), Tag('tag2')}, + ai='ChatGPT', + model='gpt-3.5-turbo', + file_path=self.file_path) + self.message_min = Message(Question('This is a question.'), + file_path=self.file_path) + + def tearDown(self) -> None: + self.file.close() + self.file_path.unlink() + + def test_to_file_txt_complete(self) -> None: + self.message_complete.to_file(self.file_path) + + with open(self.file_path, "r") as fd: + content = fd.read() + expected_content = f"""{TagLine.prefix} tag1 tag2 +{AILine.prefix} ChatGPT +{ModelLine.prefix} gpt-3.5-turbo +{Question.txt_header} +This is a question. +{Answer.txt_header} +This is an answer. +""" + self.assertEqual(content, expected_content) + + def test_to_file_txt_min(self) -> None: + self.message_min.to_file(self.file_path) + + with open(self.file_path, "r") as fd: + content = fd.read() + expected_content = f"""{Question.txt_header} +This is a question. +""" + self.assertEqual(content, expected_content) + + def test_to_file_unsupported_file_type(self) -> None: + unsupported_file_path = pathlib.Path("example.doc") + with self.assertRaises(MessageError) as cm: + self.message_complete.to_file(unsupported_file_path) + self.assertEqual(str(cm.exception), "File type '.doc' is not supported") + + def test_to_file_no_file_path(self) -> None: + """ + Provoke an exception using an empty path. + """ + with self.assertRaises(MessageError) as cm: + # clear the internal file_path + self.message_complete.file_path = None + self.message_complete.to_file(None) + self.assertEqual(str(cm.exception), "Got no valid path to write message") + # reset the internal file_path + self.message_complete.file_path = self.file_path + + +class MessageToFileYamlTestCase(CmmTestCase): + def setUp(self) -> None: + self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml') + self.file_path = pathlib.Path(self.file.name) + self.message_complete = Message(Question('This is a question.'), + Answer('This is an answer.'), + {Tag('tag1'), Tag('tag2')}, + ai='ChatGPT', + model='gpt-3.5-turbo', + file_path=self.file_path) + self.message_multiline = Message(Question('This is a\nmultiline question.'), + Answer('This is a\nmultiline answer.'), + {Tag('tag1'), Tag('tag2')}, + ai='ChatGPT', + model='gpt-3.5-turbo', + file_path=self.file_path) + self.message_min = Message(Question('This is a question.'), + file_path=self.file_path) + + def tearDown(self) -> None: + self.file.close() + self.file_path.unlink() + + def test_to_file_yaml_complete(self) -> None: + self.message_complete.to_file(self.file_path) + + with open(self.file_path, "r") as fd: + content = fd.read() + expected_content = f"""{Question.yaml_key}: This is a question. +{Answer.yaml_key}: This is an answer. +{Message.ai_yaml_key}: ChatGPT +{Message.model_yaml_key}: gpt-3.5-turbo +{Message.tags_yaml_key}: +- tag1 +- tag2 +""" + self.assertEqual(content, expected_content) + + def test_to_file_yaml_multiline(self) -> None: + self.message_multiline.to_file(self.file_path) + + with open(self.file_path, "r") as fd: + content = fd.read() + expected_content = f"""{Question.yaml_key}: |- + This is a + multiline question. +{Answer.yaml_key}: |- + This is a + multiline answer. +{Message.ai_yaml_key}: ChatGPT +{Message.model_yaml_key}: gpt-3.5-turbo +{Message.tags_yaml_key}: +- tag1 +- tag2 +""" + self.assertEqual(content, expected_content) + + def test_to_file_yaml_min(self) -> None: + self.message_min.to_file(self.file_path) + + with open(self.file_path, "r") as fd: + content = fd.read() + expected_content = f"{Question.yaml_key}: This is a question.\n" + self.assertEqual(content, expected_content) + + +class MessageFromFileTxtTestCase(CmmTestCase): + def setUp(self) -> None: + self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt') + self.file_path = pathlib.Path(self.file.name) + with open(self.file_path, "w") as fd: + fd.write(f"""{TagLine.prefix} tag1 tag2 +{AILine.prefix} ChatGPT +{ModelLine.prefix} gpt-3.5-turbo +{Question.txt_header} +This is a question. +{Answer.txt_header} +This is an answer. +""") + self.file_min = tempfile.NamedTemporaryFile(delete=False, suffix='.txt') + self.file_path_min = pathlib.Path(self.file_min.name) + with open(self.file_path_min, "w") as fd: + fd.write(f"""{Question.txt_header} +This is a question. +""") + + def tearDown(self) -> None: + self.file.close() + self.file_min.close() + self.file_path.unlink() + self.file_path_min.unlink() + + def test_from_file_txt_complete(self) -> None: + """ + Read a complete message (with all optional values). + """ + message = Message.from_file(self.file_path) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertEqual(message.answer, 'This is an answer.') + self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')}) + self.assertEqual(message.ai, 'ChatGPT') + self.assertEqual(message.model, 'gpt-3.5-turbo') + self.assertEqual(message.file_path, self.file_path) + + def test_from_file_txt_min(self) -> None: + """ + Read a message with only required values. + """ + message = Message.from_file(self.file_path_min) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertEqual(message.file_path, self.file_path_min) + self.assertIsNone(message.answer) + + def test_from_file_txt_tags_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(tags_or={Tag('tag1')})) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertEqual(message.answer, 'This is an answer.') + self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')}) + self.assertEqual(message.file_path, self.file_path) + + def test_from_file_txt_tags_dont_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(tags_or={Tag('tag3')})) + self.assertIsNone(message) + + def test_from_file_txt_no_tags_dont_match(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(tags_or={Tag('tag1')})) + self.assertIsNone(message) + + def test_from_file_txt_no_tags_match_tags_not(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(tags_not={Tag('tag1')})) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertSetEqual(cast(set[Tag], message.tags), set()) + self.assertEqual(message.file_path, self.file_path_min) + + def test_from_file_not_exists(self) -> None: + file_not_exists = pathlib.Path("example.txt") + with self.assertRaises(MessageError) as cm: + Message.from_file(file_not_exists) + self.assertEqual(str(cm.exception), f"Message file '{file_not_exists}' does not exist") + + def test_from_file_txt_question_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(question_contains='question')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_answer_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_contains='answer')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_answer_available(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_state='available')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_answer_missing(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_state='missing')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_question_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(question_contains='answer')) + self.assertIsNone(message) + + def test_from_file_txt_answer_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_contains='question')) + self.assertIsNone(message) + + def test_from_file_txt_answer_not_exists(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_contains='answer')) + self.assertIsNone(message) + + def test_from_file_txt_answer_not_available(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_state='available')) + self.assertIsNone(message) + + def test_from_file_txt_answer_not_missing(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_state='missing')) + self.assertIsNone(message) + + def test_from_file_txt_ai_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(ai='ChatGPT')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_ai_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(ai='Foo')) + self.assertIsNone(message) + + def test_from_file_txt_model_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(model='gpt-3.5-turbo')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_txt_model_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(model='Bar')) + self.assertIsNone(message) + + +class MessageFromFileYamlTestCase(CmmTestCase): + def setUp(self) -> None: + self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml') + self.file_path = pathlib.Path(self.file.name) + with open(self.file_path, "w") as fd: + fd.write(f""" +{Question.yaml_key}: |- + This is a question. +{Answer.yaml_key}: |- + This is an answer. +{Message.ai_yaml_key}: ChatGPT +{Message.model_yaml_key}: gpt-3.5-turbo +{Message.tags_yaml_key}: + - tag1 + - tag2 +""") + self.file_min = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml') + self.file_path_min = pathlib.Path(self.file_min.name) + with open(self.file_path_min, "w") as fd: + fd.write(f""" +{Question.yaml_key}: |- + This is a question. +""") + + def tearDown(self) -> None: + self.file.close() + self.file_path.unlink() + self.file_min.close() + self.file_path_min.unlink() + + def test_from_file_yaml_complete(self) -> None: + """ + Read a complete message (with all optional values). + """ + message = Message.from_file(self.file_path) + self.assertIsInstance(message, Message) + self.assertIsNotNone(message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertEqual(message.answer, 'This is an answer.') + self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')}) + self.assertEqual(message.ai, 'ChatGPT') + self.assertEqual(message.model, 'gpt-3.5-turbo') + self.assertEqual(message.file_path, self.file_path) + + def test_from_file_yaml_min(self) -> None: + """ + Read a message with only the required values. + """ + message = Message.from_file(self.file_path_min) + self.assertIsInstance(message, Message) + self.assertIsNotNone(message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertSetEqual(cast(set[Tag], message.tags), set()) + self.assertEqual(message.file_path, self.file_path_min) + self.assertIsNone(message.answer) + + def test_from_file_not_exists(self) -> None: + file_not_exists = pathlib.Path("example.yaml") + with self.assertRaises(MessageError) as cm: + Message.from_file(file_not_exists) + self.assertEqual(str(cm.exception), f"Message file '{file_not_exists}' does not exist") + + def test_from_file_yaml_tags_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(tags_or={Tag('tag1')})) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertEqual(message.answer, 'This is an answer.') + self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')}) + self.assertEqual(message.file_path, self.file_path) + + def test_from_file_yaml_tags_dont_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(tags_or={Tag('tag3')})) + self.assertIsNone(message) + + def test_from_file_yaml_no_tags_dont_match(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(tags_or={Tag('tag1')})) + self.assertIsNone(message) + + def test_from_file_yaml_no_tags_match_tags_not(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(tags_not={Tag('tag1')})) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + if message: # mypy bug + self.assertEqual(message.question, 'This is a question.') + self.assertSetEqual(cast(set[Tag], message.tags), set()) + self.assertEqual(message.file_path, self.file_path_min) + + def test_from_file_yaml_question_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(question_contains='question')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_answer_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_contains='answer')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_answer_available(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_state='available')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_answer_missing(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_state='missing')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_question_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(question_contains='answer')) + self.assertIsNone(message) + + def test_from_file_yaml_answer_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_contains='question')) + self.assertIsNone(message) + + def test_from_file_yaml_answer_not_exists(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_contains='answer')) + self.assertIsNone(message) + + def test_from_file_yaml_answer_not_available(self) -> None: + message = Message.from_file(self.file_path_min, + MessageFilter(answer_state='available')) + self.assertIsNone(message) + + def test_from_file_yaml_answer_not_missing(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(answer_state='missing')) + self.assertIsNone(message) + + def test_from_file_yaml_ai_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(ai='ChatGPT')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_ai_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(ai='Foo')) + self.assertIsNone(message) + + def test_from_file_yaml_model_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(model='gpt-3.5-turbo')) + self.assertIsNotNone(message) + self.assertIsInstance(message, Message) + + def test_from_file_yaml_model_doesnt_match(self) -> None: + message = Message.from_file(self.file_path, + MessageFilter(model='Bar')) + self.assertIsNone(message) + + +class TagsFromFileTestCase(CmmTestCase): + def setUp(self) -> None: + self.file_txt = tempfile.NamedTemporaryFile(delete=False, suffix='.txt') + self.file_path_txt = pathlib.Path(self.file_txt.name) + with open(self.file_path_txt, "w") as fd: + fd.write(f"""{TagLine.prefix} tag1 tag2 +{Question.txt_header} +This is a question. +{Answer.txt_header} +This is an answer. +""") + self.file_yaml = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml') + self.file_path_yaml = pathlib.Path(self.file_yaml.name) + with open(self.file_path_yaml, "w") as fd: + fd.write(f""" +{Question.yaml_key}: |- + This is a question. +{Answer.yaml_key}: |- + This is an answer. +{Message.tags_yaml_key}: + - tag1 + - tag2 +""") + + def tearDown(self) -> None: + self.file_txt.close() + self.file_path_txt.unlink() + self.file_yaml.close() + self.file_path_yaml.unlink() + + def test_tags_from_file_txt(self) -> None: + tags = Message.tags_from_file(self.file_path_txt) + self.assertSetEqual(tags, {Tag('tag1'), Tag('tag2')}) + + def test_tags_from_file_yaml(self) -> None: + tags = Message.tags_from_file(self.file_path_yaml) + self.assertSetEqual(tags, {Tag('tag1'), Tag('tag2')}) + + +class MessageIDTestCase(CmmTestCase): + def setUp(self) -> None: + self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt') + self.file_path = pathlib.Path(self.file.name) + self.message = Message(Question('This is a question.'), + file_path=self.file_path) + self.message_no_file_path = Message(Question('This is a question.')) + + def tearDown(self) -> None: + self.file.close() + self.file_path.unlink() + + def test_msg_id_txt(self) -> None: + self.assertEqual(self.message.msg_id(), self.file_path.name) + + def test_msg_id_txt_exception(self) -> None: + with self.assertRaises(MessageError): + self.message_no_file_path.msg_id()