From 434c3e6e5563fda09822aa34c50543e411d2fa7a Mon Sep 17 00:00:00 2001 From: tessier Date: Fri, 21 Mar 2025 18:22:31 +0900 Subject: [PATCH] feat(scripts): add docstring and tests --- diagrams/cli.py | 11 +++++- tests/test_cli.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/test_cli.py diff --git a/diagrams/cli.py b/diagrams/cli.py index 22a73685..c75eb3d9 100644 --- a/diagrams/cli.py +++ b/diagrams/cli.py @@ -3,6 +3,14 @@ import sys def run() -> int: + """ + Run diagrams code files in a diagrams environment. + Args: + paths: A list of paths to Python files containing diagrams code. + + Returns: + The exit code. + """ parser = argparse.ArgumentParser( description="Run diagrams code files in a diagrams environment.", ) @@ -16,8 +24,7 @@ def run() -> int: args = parser.parse_args() for path in args.paths: - print(path) - with open(path) as f: + with open(path, encoding='utf-8') as f: exec(f.read()) return 0 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..3b3e9750 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,94 @@ +import os +import unittest +from unittest.mock import patch, mock_open +from io import StringIO + +from diagrams.cli import run + +class CliTest(unittest.TestCase): + def setUp(self): + self.test_file = "test_diagram.py" + # dummy content for the test file + self.test_content_1 = """ +from diagrams import Diagram +with Diagram(name="Test", show=False): + pass +""" + # content from getting started examples with utf-8 + # only support the installed fonts defined in Dockerfile + self.test_content_2 = """ +from diagrams import Diagram +from diagrams.aws.compute import EC2 +from diagrams.aws.database import RDS +from diagrams.aws.network import ELB + +with Diagram("test_2", show=False, direction="TB"): + ELB("lb") >> [EC2("ワーカー1"), + EC2("작업자 2를"), + EC2("робітник 3"), + EC2("worker4"), + EC2("työntekijä 4")] >> RDS("events") +""" + def tearDown(self): + try: + os.remove("test.png") + except FileNotFoundError: + pass + + def test_run_with_valid_file(self): + # write the test file + with open(self.test_file, "w") as f: + f.write(self.test_content_1) + with patch("sys.argv", ["diagrams", self.test_file]): + exit_code = run() + self.assertEqual(exit_code, 0) + try: + os.remove(self.test_file) + except FileNotFoundError: + pass + + + def test_run_with_multiple_files(self): + + multiple_files = ["file1.py", "file2.py"] + + # write the code files + with open("file1.py", "w") as f: + f.write(self.test_content_1) + with open("file2.py", "w") as f: + f.write(self.test_content_2) + + with patch("sys.argv", ["diagrams"] + multiple_files): + exit_code = run() + self.assertEqual(exit_code, 0) + + # cleanup code file + for one_file in multiple_files: + try: + os.remove(one_file) + except FileNotFoundError: + pass + # cleanup generated image + try: + os.remove("test_2.png") + except FileNotFoundError: + pass + + def test_run_with_no_arguments(self): + with patch("sys.argv", ["diagrams"]): + with patch("sys.stderr", new=StringIO()) as fake_stderr: + with self.assertRaises(SystemExit): + run() + self.assertIn("the following arguments are required: path", fake_stderr.getvalue()) + + def test_run_with_nonexistent_file(self): + with patch("sys.argv", ["diagrams", "nonexistent.py"]): + with self.assertRaises(FileNotFoundError): + run() + + def test_run_with_invalid_python_code(self): + invalid_content = "this is not valid python code" + with patch("builtins.open", mock_open(read_data=invalid_content)): + with patch("sys.argv", ["diagrams", self.test_file]): + with self.assertRaises(SyntaxError): + run()