From dcbd1bfe024ace4f0f85e16d30f32b63e4040394 Mon Sep 17 00:00:00 2001 From: alvintran Date: Sat, 14 Jun 2025 11:10:55 -0700 Subject: [PATCH 1/5] Add dont-delete-pdfs argument --- docker-compose.dev.yml | 1 + printer/server.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 5fbfe5f8..08e505bd 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -16,6 +16,7 @@ services: command: - --development - --port=14000 + # - --dont-delete-pdfs snmp-collector: build: context: . diff --git a/printer/server.py b/printer/server.py index 4741a386..044f1467 100644 --- a/printer/server.py +++ b/printer/server.py @@ -56,6 +56,13 @@ def get_args() -> argparse.Namespace: default=False, help="specify if server should run in development. this means requests won't get sent to a printer but logger instead", ) + parser.add_argument( + "--dont-delete-pdfs", + action="store_true", + default=False, + help="specify if server should delete pdfs after printing" + ) + return parser.parse_args() @@ -138,7 +145,8 @@ async def read_item(file: UploadFile = File(...), copies: str = Form(...), sides copies, sides=sides, ) - pathlib.Path(file_path).unlink() + if not args.dont_delete_pdfs: + pathlib.Path(file_path).unlink() return "worked!" except Exception: logging.exception("printing failed!") From 8309fd3b175d3279131ab65a95c75e92fe593961 Mon Sep 17 00:00:00 2001 From: altran03 <143759122+altran03@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:31:00 -0700 Subject: [PATCH 2/5] Update argument Co-authored-by: Evan Ugarte <36345325+evanugarte@users.noreply.github.com> --- docker-compose.dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 08e505bd..99eda4a4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -16,7 +16,7 @@ services: command: - --development - --port=14000 - # - --dont-delete-pdfs + - --dont-delete-pdfs snmp-collector: build: context: . From a44210330b762ee56bbd70fb190f8e7b939db184 Mon Sep 17 00:00:00 2001 From: altran03 <143759122+altran03@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:32:46 -0700 Subject: [PATCH 3/5] Update logging Co-authored-by: Evan Ugarte <36345325+evanugarte@users.noreply.github.com> --- printer/server.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/printer/server.py b/printer/server.py index 044f1467..86be48b4 100644 --- a/printer/server.py +++ b/printer/server.py @@ -145,8 +145,10 @@ async def read_item(file: UploadFile = File(...), copies: str = Form(...), sides copies, sides=sides, ) - if not args.dont_delete_pdfs: - pathlib.Path(file_path).unlink() + if args.dont_delete_pdfs: + logger.info(f'--dont-delete-pdfs is set, skipping deletion of file {file_path}') + return "worked!" + pathlib.Path(file_path).unlink() return "worked!" except Exception: logging.exception("printing failed!") From d95838bda89ab9d59d4645fb69035c8847fe5eff Mon Sep 17 00:00:00 2001 From: evan Date: Sat, 14 Jun 2025 11:49:43 -0700 Subject: [PATCH 4/5] logger -> logging --- printer/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printer/server.py b/printer/server.py index 86be48b4..3f07bd12 100644 --- a/printer/server.py +++ b/printer/server.py @@ -146,7 +146,7 @@ async def read_item(file: UploadFile = File(...), copies: str = Form(...), sides sides=sides, ) if args.dont_delete_pdfs: - logger.info(f'--dont-delete-pdfs is set, skipping deletion of file {file_path}') + logging.info(f'--dont-delete-pdfs is set, skipping deletion of file {file_path}') return "worked!" pathlib.Path(file_path).unlink() return "worked!" From ad729300c28e4b8f4809c5f388a59ffc836ea7b8 Mon Sep 17 00:00:00 2001 From: evan Date: Sat, 14 Jun 2025 12:09:40 -0700 Subject: [PATCH 5/5] add unit test for --dont-delete-pdfs --- printer/test_server.py | 63 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/printer/test_server.py b/printer/test_server.py index cc99527e..33d5e16c 100644 --- a/printer/test_server.py +++ b/printer/test_server.py @@ -1,19 +1,29 @@ +import importlib import io import os import unittest from unittest import mock from fastapi.testclient import TestClient -from server import app + +import server class TestFastAPI(unittest.TestCase): - def setUp(self): + def load_server_with_args(self, argv=[]): + argv_to_use = ["server.py"] + argv_to_use.extend(argv) + os.environ["RIGHT_PRINTER_NAME"] = "HP_P2015_DN" - self.client = TestClient(app) + + with mock.patch("sys.argv", argv_to_use): + importlib.reload(server) + + return TestClient(server.app) def test_health_check(self): - response = self.client.get("/healthcheck/printer") + client = self.load_server_with_args() + response = client.get("/healthcheck/printer") self.assertEqual(response.status_code, 200) self.assertEqual(response.text, '"printer is up!"') @@ -22,8 +32,9 @@ def test_health_check(self): @mock.patch("builtins.open", new_callable=mock.mock_open) @mock.patch("pathlib.Path.unlink") def test_print_endpoint(self, mock_pathlib_unlink, mock_open_func, mock_popen, _): + client = self.load_server_with_args() test_file = io.BytesIO(b"dummy file content") - response = self.client.post( + response = client.post( "/print", files={"file": ("test.txt", test_file, "text/plain")}, data={"copies": "1", "sides": "one-sided"}, @@ -51,12 +62,50 @@ def test_print_endpoint(self, mock_pathlib_unlink, mock_open_func, mock_popen, _ mock_pathlib_unlink.assert_called_once() + @mock.patch("server.uuid.uuid4", return_value="test-id") + @mock.patch("server.subprocess.Popen") + @mock.patch("builtins.open", new_callable=mock.mock_open) + @mock.patch("pathlib.Path.unlink") + def test_print_endpoint_dont_delete_pdf( + self, mock_pathlib_unlink, mock_open_func, mock_popen, _ + ): + client = self.load_server_with_args(["--dont-delete-pdfs"]) + test_file = io.BytesIO(b"dummy file content") + response = client.post( + "/print", + files={"file": ("test.txt", test_file, "text/plain")}, + data={"copies": "1", "sides": "one-sided"}, + ) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.text, '"worked!"') + + mock_open_func.assert_called_once_with("/tmp/test-id", "wb") + + mock_open_func().write.assert_called_once() + self.assertEqual( + mock_open_func().write.call_args_list[0], mock.call(b"dummy file content") + ) + + mock_popen.assert_called_once() + + self.assertEqual( + mock_popen.call_args_list[0], + mock.call( + "lp -n 1 -o sides=one-sided -o media=na_letter_8.5x11in -d HP_P2015_DN /tmp/test-id", + shell=True, + ), + ) + + mock_pathlib_unlink.assert_not_called() + @mock.patch("server.subprocess.Popen") @mock.patch("builtins.open", side_effect=FileNotFoundError("sorry!")) @mock.patch("pathlib.Path.unlink") - def test_print_endpoint(self, mock_pathlib_unlink, _, mock_popen): + def test_print_endpoint_error(self, mock_pathlib_unlink, _, mock_popen): + client = self.load_server_with_args() test_file = io.BytesIO(b"dummy file content") - response = self.client.post( + response = client.post( "/print", files={"file": ("test.txt", test_file, "text/plain")}, data={"copies": "1", "sides": "one-sided"},