392 lines
15 KiB
Python
392 lines
15 KiB
Python
import pathlib
|
|
import tempfile
|
|
from typing import cast
|
|
from .test_main import CmmTestCase
|
|
from chatmastermind.message import source_code, Message, MessageError, Question, Answer, AILine, ModelLine
|
|
from chatmastermind.tags import Tag, TagLine
|
|
|
|
|
|
class SourceCodeTestCase(CmmTestCase):
|
|
def test_source_code_with_include_delims(self) -> None:
|
|
text = """
|
|
Some text before the code block
|
|
```python
|
|
print("Hello, World!")
|
|
```
|
|
Some text after the code block
|
|
```python
|
|
x = 10
|
|
y = 20
|
|
print(x + y)
|
|
```
|
|
"""
|
|
expected_result = [
|
|
" ```python\n print(\"Hello, World!\")\n ```\n",
|
|
" ```python\n x = 10\n y = 20\n print(x + y)\n ```\n"
|
|
]
|
|
result = source_code(text, include_delims=True)
|
|
self.assertEqual(result, expected_result)
|
|
|
|
def test_source_code_without_include_delims(self) -> None:
|
|
text = """
|
|
Some text before the code block
|
|
```python
|
|
print("Hello, World!")
|
|
```
|
|
Some text after the code block
|
|
```python
|
|
x = 10
|
|
y = 20
|
|
print(x + y)
|
|
```
|
|
"""
|
|
expected_result = [
|
|
" print(\"Hello, World!\")\n",
|
|
" x = 10\n y = 20\n print(x + y)\n"
|
|
]
|
|
result = source_code(text, include_delims=False)
|
|
self.assertEqual(result, expected_result)
|
|
|
|
def test_source_code_with_single_code_block(self) -> None:
|
|
text = "```python\nprint(\"Hello, World!\")\n```"
|
|
expected_result = ["```python\nprint(\"Hello, World!\")\n```\n"]
|
|
result = source_code(text, include_delims=True)
|
|
self.assertEqual(result, expected_result)
|
|
|
|
def test_source_code_with_no_code_blocks(self) -> None:
|
|
text = "Some text without any code blocks"
|
|
expected_result: list[str] = []
|
|
result = source_code(text, include_delims=True)
|
|
self.assertEqual(result, expected_result)
|
|
|
|
|
|
class QuestionTestCase(CmmTestCase):
|
|
def test_question_with_prefix(self) -> None:
|
|
with self.assertRaises(MessageError):
|
|
Question("=== QUESTION === What is your name?")
|
|
|
|
def test_question_without_prefix(self) -> None:
|
|
question = Question("What is your favorite color?")
|
|
self.assertIsInstance(question, Question)
|
|
self.assertEqual(question, "What is your favorite color?")
|
|
|
|
|
|
class AnswerTestCase(CmmTestCase):
|
|
def test_answer_with_prefix(self) -> None:
|
|
with self.assertRaises(MessageError):
|
|
Answer("=== ANSWER === Yes")
|
|
|
|
def test_answer_without_prefix(self) -> None:
|
|
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 = 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)
|
|
|
|
def tearDown(self) -> None:
|
|
self.file.close()
|
|
self.file_path.unlink()
|
|
|
|
def test_to_file_txt(self) -> None:
|
|
self.message.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_unsupported_file_type(self) -> None:
|
|
unsupported_file_path = pathlib.Path("example.doc")
|
|
with self.assertRaises(MessageError) as cm:
|
|
self.message.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.file_path = None
|
|
self.message.to_file(None)
|
|
self.assertEqual(str(cm.exception), "Got no valid path to write message")
|
|
# reset the internal file_path
|
|
self.message.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 = 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)
|
|
|
|
def tearDown(self) -> None:
|
|
self.file.close()
|
|
self.file_path.unlink()
|
|
|
|
def test_to_file_yaml(self) -> None:
|
|
self.message.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)
|
|
|
|
|
|
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
|
|
{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.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, 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, 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, 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, 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")
|
|
|
|
|
|
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.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.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, 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, 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, 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, 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)
|
|
|
|
|
|
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 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')})
|