diff --git a/docs/dev/mockoon.json b/docs/dev/mockoon.json index a73eb5beb..443343a89 100644 --- a/docs/dev/mockoon.json +++ b/docs/dev/mockoon.json @@ -1602,6 +1602,111 @@ } ], "responseMode": null + }, + { + "uuid": "af7fdcb0-721d-4198-a8ef-c6d8c4eba8c8", + "type": "http", + "documentation": "Get a Testrun PDF profile", + "method": "post", + "endpoint": "report/{profile_name}", + "responses": [ + { + "uuid": "9a759f46-4bc4-433a-be86-e456f069c217", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "Profile found - no device selected", + "headers": [], + "bodyType": "FILE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "c9a09ae7-3158-4956-93ac-4c8a90dfced8", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "Profile found - device selected ", + "headers": [], + "bodyType": "FILE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "5f98471e-15b6-47a4-a68d-e98c3a538b40", + "body": "{\n \"error\": \"Profile could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Profile not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "767d9e78-386e-4bf7-bec8-71a005efdce9", + "body": "{\n \"error\": \"A device with that mac address could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Device not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "5d76bea0-39c1-45f2-80f1-de6f770cb999", + "body": "{\n \"error\": \"Error retrieving the profile PDF\"\n}", + "latency": 0, + "statusCode": 500, + "label": "Error occured", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null } ], "rootChildren": [ @@ -1700,6 +1805,10 @@ { "type": "route", "uuid": "26f0f76f-e787-4ebe-a3f8-ea3a6004bc15" + }, + { + "type": "route", + "uuid": "af7fdcb0-721d-4198-a8ef-c6d8c4eba8c8" } ], "proxyMode": false, diff --git a/docs/dev/postman.json b/docs/dev/postman.json index 642090dd4..73617d2f9 100644 --- a/docs/dev/postman.json +++ b/docs/dev/postman.json @@ -1348,7 +1348,7 @@ ] }, { - "name": "Export", + "name": "Export Report", "request": { "method": "POST", "header": [], @@ -2305,7 +2305,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2501,7 +2500,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2578,7 +2576,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2622,7 +2619,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2666,7 +2662,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2741,7 +2736,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2784,7 +2778,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2827,7 +2820,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -2971,6 +2963,218 @@ } ] }, + { + "name": "Export Profile", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:8000/profile/{profile_name}", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "{profile_name}" + ] + }, + "description": "Get the PDF report for a specific device and timestamp" + }, + "response": [ + { + "name": "Get Profile No Device (200)", + "originalRequest": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:8000/profile/Test", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "Test" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "application/pdf", + "description": "", + "type": "text" + } + ], + "cookie": [], + "body": "profile.pdf" + }, + { + "name": "Get Profile with Device (200))", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\"mac_addr\": \"00:1e:42:35:73:c4\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8000/profile/Test", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "Test" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "application/pdf", + "description": "", + "type": "text" + } + ], + "cookie": [], + "body": "profile.pdf" + }, + { + "name": "Profile Not Found (404)", + "originalRequest": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:8000/profile/NonExistingProfile", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "NonExistingProfile" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + } + ], + "cookie": [], + "body": "{\n \"error\": \"Profile could not be found\"\n}" + }, + { + "name": "Device Not Found (404)", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\"mac_addr\": \"non_existing_mac_addr\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8000/profile/Test", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "Test" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + } + ], + "cookie": [], + "body": "{\n \"error\": \"A device with that mac address could not be found\"\n}" + }, + { + "name": "Internal Server Error (500) Copy", + "originalRequest": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:8000/profile/Test", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "Test" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "", + "type": "text" + } + ], + "cookie": [], + "body": "{\n \"error\": \"Error retrieving the profile PDF\"\n}" + } + ] + }, { "name": "Update Profile", "request": { @@ -3006,7 +3210,7 @@ "header": [], "body": { "mode": "raw", - "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]", + "raw": "\n{\n \"name\": \"New Profile\",\n \"rename\": \"Updated New Profile\",\n \"version\": \"2.0.1\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}", "options": { "raw": { "language": "json" @@ -3032,7 +3236,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3047,7 +3250,7 @@ "header": [], "body": { "mode": "raw", - "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]", + "raw": "\n{\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}\n", "options": { "raw": { "language": "json" @@ -3073,7 +3276,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3114,7 +3316,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3225,7 +3426,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3266,7 +3466,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3307,7 +3506,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3348,7 +3546,6 @@ { "key": "Content-Type", "value": "application/json", - "name": "Content-Type", "description": "", "type": "text" } @@ -3357,6 +3554,41 @@ "body": "{\n \"error\": \"Invalid request received\"\n}" } ] + }, + { + "name": "http://localhost:8000/profile/Test", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\"mac_addr\": \"non_existing_mac_addr\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8000/profile/Test", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "profile", + "Test" + ] + } + }, + "response": [] } ] } \ No newline at end of file diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index 5799fd478..8e36120d7 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -43,6 +43,7 @@ DEVICE_ADDITIONAL_INFO_KEY = "additional_info" DEVICES_PATH = "local/devices" +PROFILES_PATH = "local/risk_profiles" RESOURCES_PATH = "resources" DEVICE_FOLDER_PATH = "devices" @@ -133,6 +134,9 @@ def __init__(self, testrun): self._router.add_api_route("/profiles", self.delete_profile, methods=["DELETE"]) + self._router.add_api_route("/profile/{profile_name}", + self.export_profile, + methods=["POST"]) # Allow all origins to access the API origins = ["*"] @@ -928,6 +932,93 @@ async def delete_profile(self, request: Request, response: Response): return self._generate_msg(True, "Successfully deleted that profile") + async def export_profile(self, request: Request, response: Response, + profile_name): + + LOGGER.debug(f"Received get profile request for {profile_name}") + + device = None + + try: + req_raw = (await request.body()).decode("UTF-8") + req_json = json.loads(req_raw) + + # Check if device mac_addr has been specified + if "mac_addr" in req_json and len(req_json.get("mac_addr")) > 0: + device_mac_addr = req_json.get("mac_addr") + device = self.get_session().get_device(device_mac_addr) + + # If device is not found return 404 + if device is None: + response.status_code = status.HTTP_404_NOT_FOUND + return self._generate_msg( + False, "A device with that mac address could not be found") + + except JSONDecodeError: + # Device not specified + pass + + # Retrieve the profile + profile = self._session.get_profile(profile_name) + + # If the profile not found return 404 + if profile is None: + LOGGER.info("Profile not found, returning 404") + response.status_code = 404 + return self._generate_msg(False, "Profile could not be found") + + # If device has been added into the body + if device: + + try: + + # Path where the PDF will be saved + profile_pdf_path = os.path.join(PROFILES_PATH, f"{profile_name}.pdf") + + # Write the PDF content + with open(profile_pdf_path, "wb") as f: + f.write(profile.to_pdf(device).getvalue()) + + # Return the pdf file + if os.path.isfile(profile_pdf_path): + return FileResponse(profile_pdf_path) + else: + LOGGER.info("Profile could not be found, returning 404") + response.status_code = 404 + return self._generate_msg(False, "Profile could not be found") + + # Exceptions if the PDF creation fails + except Exception as e: + LOGGER.error(f"Error creating the profile PDF: {e}") + response.status_code = 500 + return self._generate_msg(False, "Error retrieving the profile PDF") + + # If device not added into the body + else: + + try: + + # Path where the PDF will be saved + profile_pdf_path = os.path.join(PROFILES_PATH, f"{profile_name}.pdf") + + # Write the PDF content + with open(profile_pdf_path, "wb") as f: + f.write(profile.to_pdf_no_device().getvalue()) + + # Return the pdf file + if os.path.isfile(profile_pdf_path): + return FileResponse(profile_pdf_path) + else: + LOGGER.info("Profile could not be found, returning 404") + response.status_code = 404 + return self._generate_msg(False, "Profile could not be found") + + # Exceptions if the PDF creation fails + except Exception as e: + LOGGER.error(f"Error creating the profile PDF: {e}") + response.status_code = 500 + return self._generate_msg(False, "Error retrieving the profile PDF") + # Certificates def get_certs(self): LOGGER.debug("Received certs list request") diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 559117aec..fe613f69a 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -351,7 +351,7 @@ def to_html(self, device): logo_img_b64 = base64.b64encode(f.read()).decode('utf-8') self._device = self._format_device_profile(device) - pages = self._generate_report_pages() + pages = self._generate_report_pages(device) return self._template.render( styles=self._template_styles, manufacturer=self._device.manufacturer, @@ -366,7 +366,33 @@ def to_html(self, device): created_at=self.created.strftime('%d.%m.%Y') ) - def _generate_report_pages(self): + def to_html_no_device(self): + """Returns the risk profile in HTML format without device info""" + + high_risk_message = '''The device has been assessed to be high + risk due to the nature of the answers provided + about the device functionality.''' + limited_risk_message = '''The device has been assessed to be limited risk + due to the nature of the answers provided about + the device functionality.''' + + with open(test_run_img_file, 'rb') as f: + logo_img_b64 = base64.b64encode(f.read()).decode('utf-8') + + pages = self._generate_report_pages() + return self._template.render( + styles=self._template_styles, + logo=logo_img_b64, + risk=self.risk, + high_risk_message=high_risk_message, + limited_risk_message=limited_risk_message, + pages=pages, + total_pages=len(pages), + version=self.version, + created_at=self.created.strftime('%d.%m.%Y') + ) + + def _generate_report_pages(self, device=None): # Text block heght block_height = 18 @@ -391,8 +417,11 @@ def _generate_report_pages(self): current_page = [] index = 1 - questions = deepcopy(self._device.additional_info) - questions.extend(self.questions) + questions = deepcopy(self.questions) + + if device: + questions = deepcopy(self._device.additional_info) + questions.extend(self.questions) for question in questions: @@ -456,6 +485,17 @@ def to_pdf(self, device): HTML(string=html).write_pdf(pdf_bytes) return pdf_bytes + def to_pdf_no_device(self): + """Returns the risk profile in PDF format without device info""" + + # Resolve the data as html first + html = self.to_html_no_device() + + # Convert HTML to PDF in memory using weasyprint + pdf_bytes = BytesIO() + HTML(string=html).write_pdf(pdf_bytes) + return pdf_bytes + # Adding risks to device profile questions def _format_device_profile(self, device): device_copy = deepcopy(device) diff --git a/framework/python/src/core/session.py b/framework/python/src/core/session.py index 144b333f4..8b3d84c7c 100644 --- a/framework/python/src/core/session.py +++ b/framework/python/src/core/session.py @@ -539,6 +539,9 @@ def _load_profiles(self): for risk_profile_file in os.listdir( os.path.join(self._root_dir, PROFILES_DIR)): + if not risk_profile_file.endswith('.json'): + continue + LOGGER.debug(f'Discovered profile {risk_profile_file}') # Open the risk profile file diff --git a/testing/api/test_api.py b/testing/api/test_api.py index f328d7885..c9ef17f86 100644 --- a/testing/api/test_api.py +++ b/testing/api/test_api.py @@ -1285,7 +1285,7 @@ def test_export_report_not_found(empty_devices_dir, add_devices, testrun): # pyl # Send the post request to trigger the zipping process r = requests.post(f"{API}/export/{device_name}/{timestamp}", timeout=10) - # Check if status code is 500 (Internal Server Error) + # Check if status code is 404 (Not Found) assert r.status_code == 404 # Parse the json response @@ -3123,6 +3123,118 @@ def test_delete_profile_server_error(empty_profiles_dir, add_profiles, # pylint: # Check if error in response assert "error" in response +@pytest.mark.parametrize("add_profiles", [ + ["valid_profile.json"] +],indirect=True) +def test_export_profile_success(empty_profiles_dir, add_profiles, testrun): # pylint: disable=W0613 + """Test for successfully export profile as PDF (200)""" + + # Assign the profile from the fixture + profile = load_json("valid_profile.json", directory=PROFILES_PATH) + + # Assign the profile name to profile_name + profile_name = profile["name"] + + # Send the get request + r = requests.post(f"{API}/profile/{profile_name}", timeout=5) + + # Check if status code is 200 (ok) + assert r.status_code == 200 + + # Check if the response is a PDF + assert r.headers["Content-Type"] == "application/pdf" + +def test_export_profile_not_found(testrun): # pylint: disable=W0613 + """Test export profile as PDF when profile doesn't exist (404)""" + + # Assign the profile name + profile_name = "non_existing" + + # Send the post request + r = requests.post(f"{API}/profile/{profile_name}", timeout=5) + + # Check if status code is 404 (not found) + assert r.status_code == 404 + + # Parse the response json + response = r.json() + + # Check if "error" in response + assert "error" in response + + # Check if the correct error message returned + assert "Profile could not be found" in response["error"] + +@pytest.mark.parametrize("add_devices, add_profiles", [ + (["device_1"], ["valid_profile.json"]) +], indirect=True) +def test_export_profile_with_device(empty_devices_dir, add_devices, # pylint: disable=W0613 + empty_profiles_dir, add_profiles, # pylint: disable=W0613 + testrun): # pylint: disable=W0613 + """ Test export profile as PDF with existing device (200)""" + + # Load the profile using load_json utility method + profile = load_json("valid_profile.json", directory=PROFILES_PATH) + + # Assign the profile name to profile_name + profile_name = profile["name"] + + # Load the device using load_json utility method + device = load_json("device_config.json", directory=DEVICE_1_PATH) + + # Assign the device mac address + mac_addr = device["mac_addr"] + + # Payload with device mac address + payload = {"mac_addr": mac_addr} + + print(payload) + + # Send the post request + r = requests.post(f"{API}/profile/{profile_name}", + json=payload, + timeout=5) + + # Check if status code is 200 (OK) + assert r.status_code == 200 + + # Check if the response is a pdf file + assert r.headers["Content-Type"] == "application/pdf" + +@pytest.mark.parametrize("add_profiles", [ + ["valid_profile.json"] +],indirect=True) +def test_export_profile_device_not_found(empty_profiles_dir, add_profiles, # pylint: disable=W0613 + testrun): # pylint: disable=W0613 + """ Test export profile as PDF when device not found (404)""" + + # Load the profile using load_json utility method + profile = load_json("valid_profile.json", directory=PROFILES_PATH) + + # Assign the profile name to profile_name + profile_name = profile["name"] + + # Assign the device mac address + device_mac = {"mac_addr": "non_existing"} + + # Send the post request + r = requests.post(f"{API}/profile/{profile_name}", + json=device_mac, + timeout=5) + + # Check if status code is 404 (Not Found) + assert r.status_code == 404 + + # Parse the response json + response = r.json() + + # Check if "error" in response + assert "error" in response + + # Check if the correct error message returned + error_message = "A device with that mac address could not be found" + assert error_message in response["error"] + # Skipped tests currently not working due to blocking during monitoring period @pytest.mark.skip()