From f0282806ce54239681a05d0fb1dfe1adfec9617d Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Tue, 9 Jun 2026 15:35:36 -0500 Subject: [PATCH 1/7] update regression tests and run them in CI tests --- .github/workflows/test-unit.yml | 1 - tests/QARegressionTests/globals.py | 20 +- .../test_Issues/test_CoreIssue1204.py | 80 +-- .../test_Issues/test_CoreIssue1248.py | 372 +++++++------- .../test_Issues/test_CoreIssue1316.py | 116 ++--- .../test_Issues/test_CoreIssue1326.py | 338 ++++++------ .../test_Issues/test_CoreIssue1332.py | 222 ++++---- .../test_Issues/test_CoreIssue1345.py | 334 ++++++------ .../test_Issues/test_CoreIssue1348.py | 468 ++++++++--------- .../test_Issues/test_CoreIssue1387.py | 480 +++++++++--------- .../test_Issues/test_CoreIssue1421.py | 264 +++++----- .../test_Issues/test_CoreIssue1442.py | 182 +++---- .../test_Issues/test_CoreIssue1487.py | 107 ++-- .../test_Issues/test_CoreIssue1501.py | 124 ++--- .../test_Issues/test_CoreIssue1558.py | 200 ++++---- .../test_Issues/test_CoreIssue164.py | 248 +++++---- .../test_Issues/test_CoreIssue1699.py | 112 ++-- .../test_Issues/test_CoreIssue1718.py | 120 ++--- .../test_Issues/test_CoreIssue363.py | 2 +- .../test_Issues/test_CoreIssue576.py | 260 +++++----- .../test_Issues/test_CoreIssue715.py | 278 +++++----- .../test_Issues/test_CoreIssue720.py | 298 +++++------ .../test_Issues/test_CoreIssue747.py | 159 +++--- .../test_Issues/test_CoreIssue890.py | 323 ++++++------ .../test_Issues/test_CoreIssue897.py | 318 ++++++------ .../test_Issues/test_CoreIssue984.py | 134 ++--- .../QARegressionTests/test_core/test_utils.py | 66 +-- .../test_core/test_validate.py | 11 +- 28 files changed, 2817 insertions(+), 2820 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 38a9e55ae..7f1205b9a 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -24,4 +24,3 @@ jobs: run: | python -m pytest tests/unit/ --cov=cdisc_rules_engine --cov-fail-under=75 python -m pytest tests/QARegressionTests/ - python -m pytest -m regression tests/QARegressionTests/test_Issues diff --git a/tests/QARegressionTests/globals.py b/tests/QARegressionTests/globals.py index 1ed0bc456..2512adb2a 100644 --- a/tests/QARegressionTests/globals.py +++ b/tests/QARegressionTests/globals.py @@ -1,10 +1,10 @@ -issue_datails_sheet = "Issue Details" -dataset_details_sheet = "Dataset Details" -entity_details_sheet = "Entity Details" -rules_report_sheet = "Rules Report" -issue_sheet_coreid_column = "A" -dataset_sheet_dataset_column = "A" -rules_sheet_rule_status_column = "F" -issue_sheet_record_column = "G" -issue_sheet_variable_column = "I" -issue_sheet_values_column = "J" +issue_datails_sheet = "Issue Details" +dataset_details_sheet = "Dataset Details" +entity_details_sheet = "Entity Details" +rules_report_sheet = "Rules Report" +issue_sheet_coreid_column = "A" +dataset_sheet_dataset_column = "A" +rules_sheet_rule_status_column = "F" +issue_sheet_record_column = "F" +issue_sheet_variable_column = "H" +issue_sheet_values_column = "I" diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py index 24ec2349c..a1bd1045a 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py @@ -1,40 +1,40 @@ -import subprocess -import pytest -from conftest import get_python_executable - - -@pytest.mark.regression -def test_non_existing_dataset_shows_helpful_error(): - """Test that the engine displays a helpful error message if dataset files are not found""" - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-dp", - "ds.json", - "-dp", - "ds2.json", - ] - - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert process.returncode == 2, "Expected non-zero exit code" - assert ( - "FileNotFoundError" not in stderr_text - ), "Should not show FileNotFoundError crash" - assert ( - "Failed to execute script" not in stderr_text - ), "Should not show script execution failure" - assert "Files ds.json, ds2.json are not found" in stderr_text, ( - f"Expected helpful error message about non-existing dataset files. " - f"stderr: {stderr_text}, stdout: {stdout_text}" - ) +import subprocess +import pytest +from conftest import get_python_executable + + +@pytest.mark.regression +def test_non_existing_dataset_shows_helpful_error(): + """Test that the engine displays a helpful error message if dataset files are not found""" + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-dp", + "ds.json", + "-dp", + "ds2.json", + ] + + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert process.returncode == 2, "Expected non-zero exit code" + assert ( + "FileNotFoundError" not in stderr_text + ), "Should not show FileNotFoundError crash" + assert ( + "Failed to execute script" not in stderr_text + ), "Should not show script execution failure" + assert "Files ds.json, ds2.json are not found" in stderr_text, ( + f"Expected helpful error message about non-existing dataset files. " + f"stderr: {stderr_text}, stdout: {stdout_text}" + ) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py index b5b60572d..ab44096d3 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py @@ -1,186 +1,186 @@ -import os -import subprocess - -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1248: - @pytest.mark.parametrize( - "command,rules_report,num_issues", - [ - # define path provided will lead to successful execution - ( - [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-2", - "-of", - "JSON", - "-lr", - os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), - "-cs", - "-dxp", - os.path.join( - "tests", - "resources", - "CoreIssue1248", - "define_subfolder", - "define.xml", - ), - "-ps", - "1", - "-dp", - os.path.join("tests", "resources", "CoreIssue1248", "data.xlsx"), - ], - [ - { - "core_id": "SD1129", - "version": "1", - "cdisc_rule_id": "", - "fda_rule_id": "", - "message": "TEST", - "status": "ISSUE REPORTED", - } - ], - 2, - ), - # JSON data file and no define.xml in same folder and no -dxp param will provide error - ( - [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-2", - "-of", - "JSON", - "-lr", - os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), - "-cs", - "-dp", - os.path.join("tests", "resources", "CoreIssue1248", "relrec.json"), - "-ps", - "1", - ], - [ - { - "core_id": "SD1129", - "version": "1", - "cdisc_rule_id": "", - "fda_rule_id": "", - "message": "TEST", - "status": "EXECUTION ERROR", - } - ], - 1, - ), - # no define.xml in same path as data.xlsx file will provide error - ( - [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-2", - "-of", - "JSON", - "-lr", - os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), - "-cs", - "-dp", - os.path.join("tests", "resources", "CoreIssue1248", "data.xlsx"), - "-ps", - "1", - ], - [ - { - "core_id": "SD1129", - "version": "1", - "cdisc_rule_id": "", - "fda_rule_id": "", - "message": "TEST", - "status": "EXECUTION ERROR", - } - ], - 1, - ), - # define.xml in same folder as the data.xls and no -dxp provided will provide error until - # in ExcelDataService dataset metadata creation full_path=dataset_name - ( - [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-2", - "-of", - "JSON", - "-lr", - os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), - "-cs", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1248", - "data_and_define", - "data.xlsx", - ), - "-ps", - "1", - ], - [ - { - "core_id": "SD1129", - "version": "1", - "cdisc_rule_id": "", - "fda_rule_id": "", - "message": "TEST", - "status": "EXECUTION ERROR", - } - ], - 1, - ), - ], - ) - def test_define_path_used(self, command, rules_report, num_issues): - subprocess.run(command, check=True) - - # Get the latest created report file - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert len(json_report["Issue_Details"]) == num_issues - assert json_report["Rules_Report"] == rules_report - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1248: + @pytest.mark.parametrize( + "command,rules_report,num_issues", + [ + # define path provided will lead to successful execution + ( + [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-2", + "-of", + "JSON", + "-lr", + os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), + "-cs", + "-dxp", + os.path.join( + "tests", + "resources", + "CoreIssue1248", + "define_subfolder", + "define.xml", + ), + "-ps", + "1", + "-dp", + os.path.join("tests", "resources", "CoreIssue1248", "data.xlsx"), + ], + [ + { + "core_id": "SD1129", + "version": "1", + "cdisc_rule_id": "", + "fda_rule_id": "", + "message": "TEST", + "status": "ISSUE REPORTED", + } + ], + 2, + ), + # JSON data file and no define.xml in same folder and no -dxp param will provide error + ( + [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-2", + "-of", + "JSON", + "-lr", + os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), + "-cs", + "-dp", + os.path.join("tests", "resources", "CoreIssue1248", "relrec.json"), + "-ps", + "1", + ], + [ + { + "core_id": "SD1129", + "version": "1", + "cdisc_rule_id": "", + "fda_rule_id": "", + "message": "TEST", + "status": "EXECUTION ERROR", + } + ], + 1, + ), + # no define.xml in same path as data.xlsx file will provide error + ( + [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-2", + "-of", + "JSON", + "-lr", + os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), + "-cs", + "-dp", + os.path.join("tests", "resources", "CoreIssue1248", "data.xlsx"), + "-ps", + "1", + ], + [ + { + "core_id": "SD1129", + "version": "1", + "cdisc_rule_id": "", + "fda_rule_id": "", + "message": "TEST", + "status": "EXECUTION ERROR", + } + ], + 1, + ), + # define.xml in same folder as the data.xls and no -dxp provided will provide error until + # in ExcelDataService dataset metadata creation full_path=dataset_name + ( + [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-2", + "-of", + "JSON", + "-lr", + os.path.join("tests", "resources", "CoreIssue1248", "sample.yml"), + "-cs", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1248", + "data_and_define", + "data.xlsx", + ), + "-ps", + "1", + ], + [ + { + "core_id": "SD1129", + "version": "1", + "cdisc_rule_id": "", + "fda_rule_id": "", + "message": "TEST", + "status": "EXECUTION ERROR", + } + ], + 1, + ), + ], + ) + def test_define_path_used(self, command, rules_report, num_issues): + subprocess.run(command, check=True) + + # Get the latest created report file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert len(json_report["Issue_Details"]) == num_issues + assert json_report["Rules_Report"] == rules_report + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py index 44fdb8a87..0fdfd78dd 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py @@ -1,58 +1,58 @@ -import os -import subprocess -import unittest - -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1316(unittest.TestCase): - def test_new_rule(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-4", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1316", - "test_dataset.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1316", "rule_new.yml"), - "-ps", - "1", - "-of", - "json", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert json_report["Issue_Summary"][0]["issues"] == 591 - assert json_report["Rules_Report"][0]["status"] == "ISSUE REPORTED" - - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess +import unittest + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1316(unittest.TestCase): + def test_new_rule(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1316", + "test_dataset.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1316", "rule_new.yml"), + "-ps", + "1", + "-of", + "json", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert json_report["Issue_Summary"][0]["issues"] == 591 + assert json_report["Rules_Report"][0]["status"] == "ISSUE REPORTED" + + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py index dfef7a49c..3f55855cc 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py @@ -1,169 +1,169 @@ -import os -import subprocess -import unittest -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - entity_details_sheet, - issue_datails_sheet, - rules_report_sheet, - issue_sheet_record_column, - issue_sheet_variable_column, - issue_sheet_values_column, -) - - -@pytest.mark.regression -class TestPrefTerm(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "3-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1326", - "regression-test-coreid-DDF00015-positive.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1326", "rule.yml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - # Perform the assertion - # Ensure only two negative values are caught - assert rules_values[0][0] == "CORE-000409" - assert rules_values[0][-1] == "SUCCESS" - assert len(record_values) == 0 - assert len(variables_values) == 0 - assert len(values_column_values) == 0 - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - def test_negative_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "3-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1326", - "regression-test-coreid-DDF00015-negative.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1326", "rule.yml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # --- Entity Details --- - dataset_sheet = workbook[entity_details_sheet] - dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] - dataset_values = [row for row in dataset_values if any(row)] - assert len(dataset_values) > 0 - assert dataset_values[0][0] == "StudyVersion" - assert dataset_values[0][-1] == 7 - - # --- Issue Summary --- - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [ - row for row in issue_summary_sheet.iter_rows(values_only=True) - ][1:] - summary_values = [row for row in summary_values if any(row)] - assert len(summary_values) > 0 - assert summary_values[0][1] == "CORE-000409" - assert summary_values[0][4] == 7 - - # --- Issue Details --- - expected_values = [ - "C199989, C199989, Phase II Trial, PHASE II TRIAL, C15601, Phase Ib Trial", - "null, null, Not Applicable, NOT APPLICABLE, C48660, N/A", - "C198366, C198366, null, null, C198366xx, Phase I/II/III Trial", - "C15600, C15600, null, null, C00001x, Phase I Trial", - "C198366, C198366, Phase II Trial, PHASE II TRIAL, C15601, Phase I/II/III Trial", - "C199989, C199989, null, null, C198366xx, PHASE IB TRIAL", - "null, null, Phase Ib Trial, PHASE IB TRIAL, C199989, PHASE 2 TRIAL", - ] - issue_details_sheet = workbook[issue_datails_sheet] - details_values = [ - row for row in issue_details_sheet.iter_rows(values_only=True) - ][1:] - details_values = [row for row in details_values if any(row)] - assert all(row[0] == "CORE-000409" for row in details_values) - actual_values = [row[-1] for row in details_values] - assert actual_values == expected_values - - # --- Rules Report --- - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - assert len(rules_values) > 0 - assert rules_values[0][0] == "CORE-000409" - assert rules_values[0][-1] == "ISSUE REPORTED" - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - -# if __name__ == "__main__": -# unittest.main() +import os +import subprocess +import unittest +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + entity_details_sheet, + issue_datails_sheet, + rules_report_sheet, + issue_sheet_record_column, + issue_sheet_variable_column, + issue_sheet_values_column, +) + + +@pytest.mark.regression +class TestPrefTerm(unittest.TestCase): + def test_positive_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "3-0", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1326", + "regression-test-coreid-DDF00015-positive.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1326", "rule.yml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + # Perform the assertion + # Ensure only two negative values are caught + assert rules_values[0][0] == "CORE-000409" + assert rules_values[0][-1] == "SUCCESS" + assert len(record_values) == 0 + assert len(variables_values) == 0 + assert len(values_column_values) == 0 + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + def test_negative_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "3-0", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1326", + "regression-test-coreid-DDF00015-negative.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1326", "rule.yml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # --- Entity Details --- + dataset_sheet = workbook[entity_details_sheet] + dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] + dataset_values = [row for row in dataset_values if any(row)] + assert len(dataset_values) > 0 + assert dataset_values[0][0] == "StudyVersion" + assert dataset_values[0][-1] == 7 + + # --- Issue Summary --- + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [ + row for row in issue_summary_sheet.iter_rows(values_only=True) + ][1:] + summary_values = [row for row in summary_values if any(row)] + assert len(summary_values) > 0 + assert summary_values[0][1] == "CORE-000409" + assert summary_values[0][4] == 7 + + # --- Issue Details --- + expected_values = [ + "C199989, C199989, Phase II Trial, PHASE II TRIAL, C15601, Phase Ib Trial", + "null, null, Not Applicable, NOT APPLICABLE, C48660, N/A", + "C198366, C198366, null, null, C198366xx, Phase I/II/III Trial", + "C15600, C15600, null, null, C00001x, Phase I Trial", + "C198366, C198366, Phase II Trial, PHASE II TRIAL, C15601, Phase I/II/III Trial", + "C199989, C199989, null, null, C198366xx, PHASE IB TRIAL", + "null, null, Phase Ib Trial, PHASE IB TRIAL, C199989, PHASE 2 TRIAL", + ] + issue_details_sheet = workbook[issue_datails_sheet] + details_values = [ + row for row in issue_details_sheet.iter_rows(values_only=True) + ][1:] + details_values = [row for row in details_values if any(row)] + assert all(row[0] == "CORE-000409" for row in details_values) + actual_values = [row[-1] for row in details_values] + assert actual_values == expected_values + + # --- Rules Report --- + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + assert len(rules_values) > 0 + assert rules_values[0][0] == "CORE-000409" + assert rules_values[0][-1] == "ISSUE REPORTED" + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + +# if __name__ == "__main__": +# unittest.main() diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py index ff98b9148..d121a8e75 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py @@ -1,111 +1,111 @@ -import os -import subprocess -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - rules_report_sheet, -) - - -@pytest.mark.regression -def test_ap_domain_should_be_correctly_substituted(): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-3", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1332", - "Datasets.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1332", "Rule.yml"), - ] - subprocess.run(command, check=True) - - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - details_values = [row for row in sheet.iter_rows(values_only=True)][1:] - details_values = [row for row in details_values if any(row)] - assert len(details_values) == 8 - - # CORE-ID - assert all(row[0] == "CORE-000181" for row in details_values) - - # Message - assert ( - len( - [ - row - for row in details_values - if row[1] == "APMH Domain value length is not equal to 4." - ] - ) - == 4 - ) - assert ( - len( - [ - row - for row in details_values - if row[1] == "APSQ Domain value length is not equal to 4." - ] - ) - == 4 - ) - - # Variable(s) - assert len([row for row in details_values if row[7] == "MHTERM"]) == 4 - assert len([row for row in details_values if row[7] == "SQTERM"]) == 4 - - # Value(s) - assert all(row[8] == "POMPE DISEASE" for row in details_values) - - # Go to the "Issue Summary" sheet - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [row for row in issue_summary_sheet.iter_rows(values_only=True)][ - 1: - ] - summary_values = [row for row in summary_values if any(row)] - assert len(summary_values) == 2 - assert all(row[1] == "CORE-000181" for row in summary_values) # CORE-ID - assert ( - summary_values[0][2] == "APMH Domain value length is not equal to 4." - ) # Message - assert ( - summary_values[1][2] == "APSQ Domain value length is not equal to 4." - ) # Message - assert all(row[3] == 4 for row in summary_values) # Issues - - # --- Rules Report --- - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - assert len(rules_values) == 1 - assert rules_values[0][0] == "CORE-000181" - assert "ISSUE REPORTED" in rules_values[0] - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + rules_report_sheet, +) + + +@pytest.mark.regression +def test_ap_domain_should_be_correctly_substituted(): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-3", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1332", + "Datasets.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1332", "Rule.yml"), + ] + subprocess.run(command, check=True) + + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + details_values = [row for row in sheet.iter_rows(values_only=True)][1:] + details_values = [row for row in details_values if any(row)] + assert len(details_values) == 8 + + # CORE-ID + assert all(row[0] == "CORE-000181" for row in details_values) + + # Message + assert ( + len( + [ + row + for row in details_values + if row[1] == "APMH Domain value length is not equal to 4." + ] + ) + == 4 + ) + assert ( + len( + [ + row + for row in details_values + if row[1] == "APSQ Domain value length is not equal to 4." + ] + ) + == 4 + ) + + # Variable(s) + assert len([row for row in details_values if row[7] == "MHTERM"]) == 4 + assert len([row for row in details_values if row[7] == "SQTERM"]) == 4 + + # Value(s) + assert all(row[8] == "POMPE DISEASE" for row in details_values) + + # Go to the "Issue Summary" sheet + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [row for row in issue_summary_sheet.iter_rows(values_only=True)][ + 1: + ] + summary_values = [row for row in summary_values if any(row)] + assert len(summary_values) == 2 + assert all(row[1] == "CORE-000181" for row in summary_values) # CORE-ID + assert ( + summary_values[0][2] == "APMH Domain value length is not equal to 4." + ) # Message + assert ( + summary_values[1][2] == "APSQ Domain value length is not equal to 4." + ) # Message + assert all(row[3] == 4 for row in summary_values) # Issues + + # --- Rules Report --- + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + assert len(rules_values) == 1 + assert rules_values[0][0] == "CORE-000181" + assert "ISSUE REPORTED" in rules_values[0] + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py index bb160fd3f..54fb0c436 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py @@ -1,167 +1,167 @@ -import os -import subprocess - -import pytest -import json -from conftest import get_python_executable - - -@pytest.fixture -def generate_report(): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-4", - "-dxp", - os.path.join( - "tests", - "resources", - "CoreIssue1345", - "define_msg20_testsupp_core.xml", - ), - "-d", - os.path.join( - "tests", - "resources", - "CoreIssue1345", - ), - "-lr", - os.path.join( - "tests", - "resources", - "CoreIssue1345", - ), - "-r", - "CDISC.SDTMIG.CG0019", - "-l", - "error", - "-ps", - "1", - "-of", - "json", - ] - subprocess.run(command, check=True) - # Get the latest created Excel file - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - json_report = json.load(open(json_report_path)) - return json_report_path, json_report - - -@pytest.mark.regression -class TestCoreIssue1345: - def test_engine_correctly_merges_datasets_and_flags_row_uniqueness_issues( - self, generate_report - ): - json_report_path, json_report = generate_report - dataset_filenames = { - d["filename"].upper() for d in json_report.get("Dataset_Details", []) - } - - assert "DM" in dataset_filenames, "DM dataset is missing from Dataset_Details" - assert ( - "SUPPDM" in dataset_filenames - ), "SUPPDM dataset is missing from Dataset_Details" - - # 2. check for DM / SUPPDM Issue_Details - dm_related_issues = [ - issue - for issue in json_report.get("Issue_Details", []) - if issue.get("dataset", "") in {"DM", "SUPPDM"} - ] - - assert not dm_related_issues, ( - "Found issues related to DM/SUPPDM datasets:\n" f"{dm_related_issues}" - ) - - dm_related_summary = [ - s - for s in json_report.get("Issue_Summary", []) - if s.get("dataset", "") in {"DM", "SUPPDM"} - ] - - assert not dm_related_summary, ( - "Found issue summary entries related to DM/SUPPDM:\n" - f"{dm_related_summary}" - ) - - ec_detail_issues = [ - i - for i in json_report.get("Issue_Details", []) - if i.get("dataset", "") in {"EC"} - ] - - assert ( - ec_detail_issues - ), "Expected EC-related issues in Issue_Details, but none found" - assert ( - len(ec_detail_issues) == 2 - ), f"Expected 2 issues for EC dataset, but {len(ec_detail_issues)} found in Issue_Details" - - ec_summary_issues = [ - s - for s in json_report.get("Issue_Summary", []) - if s.get("dataset", "") in {"EC"} - ] - - assert ( - ec_summary_issues - ), "Expected issues for EC dataset, but none found in Issue_Summary" - - if os.path.exists(json_report_path): - os.remove(json_report_path) - - def test_engine_correctly_processes_relrec_when_supp_datasets_provided( - self, generate_report - ): - json_report_path, json_report = generate_report - # Open the JSON report file - dataset_filenames = { - d["filename"].upper() for d in json_report.get("Dataset_Details", []) - } - - assert "DM" in dataset_filenames, "DM dataset is missing from Dataset_Details" - assert ( - "SUPPDM" in dataset_filenames - ), "SUPPDM dataset is missing from Dataset_Details" - assert "EC" in dataset_filenames, "EC dataset is missing from Dataset_Details" - assert ( - "SUPPEC" in dataset_filenames - ), "SUPPEC dataset is missing from Dataset_Details" - - # check that relrec was processed and rule checked the data - assert ( - "RELREC" in dataset_filenames - ), "RELREC dataset is missing from Dataset_Details" - relrec_issues = [ - i - for i in json_report.get("Issue_Details", []) - if i.get("dataset", "") in {"RELREC"} - ] - assert ( - len(relrec_issues) == 2 - ), f"Expected 2 issues for RELREC dataset, but {len(relrec_issues)} found" - - # to confirm that EC is still processed and contains issues - ec_detail_issues = [ - i - for i in json_report.get("Issue_Details", []) - if i.get("dataset", "") in {"EC"} - ] - assert ( - len(ec_detail_issues) == 2 - ), f"Expected 2 issues for EC dataset, but {len(ec_detail_issues)} found in Issue_Details" - - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess + +import pytest +import json +from conftest import get_python_executable + + +@pytest.fixture +def generate_report(): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-dxp", + os.path.join( + "tests", + "resources", + "CoreIssue1345", + "define_msg20_testsupp_core.xml", + ), + "-d", + os.path.join( + "tests", + "resources", + "CoreIssue1345", + ), + "-lr", + os.path.join( + "tests", + "resources", + "CoreIssue1345", + ), + "-r", + "CDISC.SDTMIG.CG0019", + "-l", + "error", + "-ps", + "1", + "-of", + "json", + ] + subprocess.run(command, check=True) + # Get the latest created Excel file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + json_report = json.load(open(json_report_path)) + return json_report_path, json_report + + +@pytest.mark.regression +class TestCoreIssue1345: + def test_engine_correctly_merges_datasets_and_flags_row_uniqueness_issues( + self, generate_report + ): + json_report_path, json_report = generate_report + dataset_filenames = { + d["filename"].upper() for d in json_report.get("Dataset_Details", []) + } + + assert "DM" in dataset_filenames, "DM dataset is missing from Dataset_Details" + assert ( + "SUPPDM" in dataset_filenames + ), "SUPPDM dataset is missing from Dataset_Details" + + # 2. check for DM / SUPPDM Issue_Details + dm_related_issues = [ + issue + for issue in json_report.get("Issue_Details", []) + if issue.get("dataset", "") in {"DM", "SUPPDM"} + ] + + assert not dm_related_issues, ( + "Found issues related to DM/SUPPDM datasets:\n" f"{dm_related_issues}" + ) + + dm_related_summary = [ + s + for s in json_report.get("Issue_Summary", []) + if s.get("dataset", "") in {"DM", "SUPPDM"} + ] + + assert not dm_related_summary, ( + "Found issue summary entries related to DM/SUPPDM:\n" + f"{dm_related_summary}" + ) + + ec_detail_issues = [ + i + for i in json_report.get("Issue_Details", []) + if i.get("dataset", "") in {"EC"} + ] + + assert ( + ec_detail_issues + ), "Expected EC-related issues in Issue_Details, but none found" + assert ( + len(ec_detail_issues) == 2 + ), f"Expected 2 issues for EC dataset, but {len(ec_detail_issues)} found in Issue_Details" + + ec_summary_issues = [ + s + for s in json_report.get("Issue_Summary", []) + if s.get("dataset", "") in {"EC"} + ] + + assert ( + ec_summary_issues + ), "Expected issues for EC dataset, but none found in Issue_Summary" + + if os.path.exists(json_report_path): + os.remove(json_report_path) + + def test_engine_correctly_processes_relrec_when_supp_datasets_provided( + self, generate_report + ): + json_report_path, json_report = generate_report + # Open the JSON report file + dataset_filenames = { + d["filename"].upper() for d in json_report.get("Dataset_Details", []) + } + + assert "DM" in dataset_filenames, "DM dataset is missing from Dataset_Details" + assert ( + "SUPPDM" in dataset_filenames + ), "SUPPDM dataset is missing from Dataset_Details" + assert "EC" in dataset_filenames, "EC dataset is missing from Dataset_Details" + assert ( + "SUPPEC" in dataset_filenames + ), "SUPPEC dataset is missing from Dataset_Details" + + # check that relrec was processed and rule checked the data + assert ( + "RELREC" in dataset_filenames + ), "RELREC dataset is missing from Dataset_Details" + relrec_issues = [ + i + for i in json_report.get("Issue_Details", []) + if i.get("dataset", "") in {"RELREC"} + ] + assert ( + len(relrec_issues) == 2 + ), f"Expected 2 issues for RELREC dataset, but {len(relrec_issues)} found" + + # to confirm that EC is still processed and contains issues + ec_detail_issues = [ + i + for i in json_report.get("Issue_Details", []) + if i.get("dataset", "") in {"EC"} + ] + assert ( + len(ec_detail_issues) == 2 + ), f"Expected 2 issues for EC dataset, but {len(ec_detail_issues)} found in Issue_Details" + + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py index 74c41a025..484f64833 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py @@ -1,234 +1,234 @@ -import os -import subprocess - -import pytest -import json -from conftest import get_python_executable - -_message = ( - "The study version's study phase is not specified according to the extensible Trial Phase Response" - ' (C66737) SDTM codelist - codeSystem is not "http://www.cdisc.org", codeSystemVersion is not a valid' - " terminology package date, and/or the code or decode is found in the codelist (case insensitive)" - " but the corresponding decode or code does not match the codelist value (case sensitive)." -) -_summary = [ - { - "dataset": "StudyVersion", - "core_id": "CORE-000409", - "message": _message, - "issues": 7, - } -] -_issue_details = [ - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 1, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": [ - "C199989", - "C199989", - "Phase II Trial", - "PHASE II TRIAL", - "C15601", - "Phase Ib Trial", - ], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 2, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": ["null", "null", "Not Applicable", "NOT APPLICABLE", "C48660", "N/A"], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 3, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": [ - "C198366", - "C198366", - "null", - "null", - "C198366xx", - "Phase I/II/III Trial", - ], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 4, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": ["C15600", "C15600", "null", "null", "C00001x", "Phase I Trial"], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 5, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": [ - "C198366", - "C198366", - "Phase II Trial", - "PHASE II TRIAL", - "C15601", - "Phase I/II/III Trial", - ], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 6, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": ["C199989", "C199989", "null", "null", "C198366xx", "PHASE IB TRIAL"], - }, - { - "core_id": "CORE-000409", - "message": _message, - "executability": "fully executable", - "dataset": "StudyVersion", - "USUBJID": "", - "row": 7, - "SEQ": "", - "variables": [ - "$code_for_pref_term", - "$code_for_value", - "$pref_term_for_code", - "$value_for_code", - "studyPhase.standardCode.code", - "studyPhase.standardCode.decode", - ], - "values": [ - "null", - "null", - "Phase Ib Trial", - "PHASE IB TRIAL", - "C199989", - "PHASE 2 TRIAL", - ], - }, -] - - -@pytest.mark.regression -class TestCoreIssue1348: - @pytest.mark.parametrize( - "rule_name, dataset, issue_summary, details", - [ - ("DDF00015_not.yaml", "data_pos", [], []), - ("DDF00015_not.yaml", "data_neg", _summary, _issue_details), - ("DDF00015_all.yaml", "data_pos", [], []), - ("DDF00015_all.yaml", "data_neg", _summary, _issue_details), - ], - ) - def test_not_as_first_condition_same_errors_as_all_condition( - self, rule_name, dataset, issue_summary, details - ): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-4", - "-lr", - os.path.join("tests", "resources", "CoreIssue1348", rule_name), - "-of", - "json", - "--data", - os.path.join("tests", "resources", "CoreIssue1348", dataset), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - - assert details == json_report["Issue_Details"] - assert issue_summary == json_report["Issue_Summary"] - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess + +import pytest +import json +from conftest import get_python_executable + +_message = ( + "The study version's study phase is not specified according to the extensible Trial Phase Response" + ' (C66737) SDTM codelist - codeSystem is not "http://www.cdisc.org", codeSystemVersion is not a valid' + " terminology package date, and/or the code or decode is found in the codelist (case insensitive)" + " but the corresponding decode or code does not match the codelist value (case sensitive)." +) +_summary = [ + { + "dataset": "StudyVersion", + "core_id": "CORE-000409", + "message": _message, + "issues": 7, + } +] +_issue_details = [ + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 1, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": [ + "C199989", + "C199989", + "Phase II Trial", + "PHASE II TRIAL", + "C15601", + "Phase Ib Trial", + ], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 2, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": ["null", "null", "Not Applicable", "NOT APPLICABLE", "C48660", "N/A"], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 3, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": [ + "C198366", + "C198366", + "null", + "null", + "C198366xx", + "Phase I/II/III Trial", + ], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 4, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": ["C15600", "C15600", "null", "null", "C00001x", "Phase I Trial"], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 5, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": [ + "C198366", + "C198366", + "Phase II Trial", + "PHASE II TRIAL", + "C15601", + "Phase I/II/III Trial", + ], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 6, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": ["C199989", "C199989", "null", "null", "C198366xx", "PHASE IB TRIAL"], + }, + { + "core_id": "CORE-000409", + "message": _message, + "executability": "fully executable", + "dataset": "StudyVersion", + "USUBJID": "", + "row": 7, + "SEQ": "", + "variables": [ + "$code_for_pref_term", + "$code_for_value", + "$pref_term_for_code", + "$value_for_code", + "studyPhase.standardCode.code", + "studyPhase.standardCode.decode", + ], + "values": [ + "null", + "null", + "Phase Ib Trial", + "PHASE IB TRIAL", + "C199989", + "PHASE 2 TRIAL", + ], + }, +] + + +@pytest.mark.regression +class TestCoreIssue1348: + @pytest.mark.parametrize( + "rule_name, dataset, issue_summary, details", + [ + ("DDF00015_not.yaml", "data_pos", [], []), + ("DDF00015_not.yaml", "data_neg", _summary, _issue_details), + ("DDF00015_all.yaml", "data_pos", [], []), + ("DDF00015_all.yaml", "data_neg", _summary, _issue_details), + ], + ) + def test_not_as_first_condition_same_errors_as_all_condition( + self, rule_name, dataset, issue_summary, details + ): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-lr", + os.path.join("tests", "resources", "CoreIssue1348", rule_name), + "-of", + "json", + "--data", + os.path.join("tests", "resources", "CoreIssue1348", dataset), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + + assert details == json_report["Issue_Details"] + assert issue_summary == json_report["Issue_Summary"] + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py index 3c94be9e9..24dd97a76 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py @@ -1,240 +1,240 @@ -import subprocess -import os -import tempfile -import shutil -import pytest -from conftest import get_python_executable - - -@pytest.mark.regression -def test_multiple_xlsx_files_shows_helpful_error(): - """Test that multiple XLSX files show a helpful error message about single file limitation""" - with tempfile.TemporaryDirectory() as temp_dir: - xlsx_files = ["ae.xlsx", "dm.xlsx"] - - for filename in xlsx_files: - open(os.path.join(temp_dir, filename), "w").close() - - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-d", - temp_dir, - ] - - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert ( - process.returncode != 0 - ), "Expected non-zero exit code for multiple XLSX files" - assert "KeyError" not in stderr_text, "Should not show KeyError crash" - assert ( - "Failed to execute script" not in stderr_text - ), "Should not show script execution failure" - assert ( - "Multiple XLSX files found" in stderr_text - or "Multiple XLSX files found" in stdout_text - ), f"Expected helpful error message about multiple XLSX files. stderr: {stderr_text}, stdout: {stdout_text}" - assert ( - "Excel format (XLSX) validation only supports single files" in stderr_text - or "Excel format (XLSX) validation only supports single files" - in stdout_text - ), "Expected explanation of XLSX limitation" - - -@pytest.mark.regression -def test_folder_with_xlsx_files_works_with_excel_service(): - """Test that a folder with a single XLSX file now works with ExcelDataService""" - with tempfile.TemporaryDirectory() as temp_dir: - xlsx_files = ["ae.xlsx"] - - for filename in xlsx_files: - open(os.path.join(temp_dir, filename), "w").close() - - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-d", - temp_dir, - ] - - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert "KeyError: 'XLSX'" not in stderr_text, "Should not show KeyError crash" - assert ( - "Failed to execute script" not in stderr_text - ), "Should not show script execution failure" - - assert ( - "No valid dataset files found" not in stderr_text - and "No valid dataset files found" not in stdout_text - ), "XLSX files should now be recognized as valid" - - -@pytest.mark.regression -def test_folder_with_unsupported_formats_shows_helpful_error(): - """Test that folders with truly unsupported formats (like PDF) show helpful error messages""" - with tempfile.TemporaryDirectory() as temp_dir: - unsupported_files = ["ae.pdf", "dm.txt"] - - for filename in unsupported_files: - open(os.path.join(temp_dir, filename), "w").close() - - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-d", - temp_dir, - ] - - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert ( - process.returncode != 0 - ), "Expected non-zero exit code for unsupported files" - assert "KeyError" not in stderr_text, "Should not show KeyError crash" - assert ( - "Failed to execute script" not in stderr_text - ), "Should not show script execution failure" - assert ( - "No valid dataset files found" in stderr_text - or "No valid dataset files found" in stdout_text - ), f"Expected helpful error message. stderr: {stderr_text}, stdout: {stdout_text}" - assert ( - "SAS V5 XPT, Dataset-JSON" in stderr_text - or "SAS V5 XPT, Dataset-JSON" in stdout_text - ), "Expected format guidance in error message" - - -@pytest.mark.regression -def test_empty_folder_shows_helpful_error(): - with tempfile.TemporaryDirectory() as temp_dir: - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-d", - temp_dir, - ] - - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert process.returncode != 0, "Expected non-zero exit code for empty folder" - assert ( - "No valid dataset files found" in stderr_text - or "No valid dataset files found" in stdout_text - ), "Expected helpful error message for empty folder" - - -@pytest.mark.regression -def test_valid_xpt_files_work_normally(): - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-dp", - os.path.join("tests", "resources", "test_dataset.xpt"), - ] - - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - - assert "KeyError: 'XLSX'" not in stderr_text, "Should not show KeyError" - assert "Failed to execute script" not in stderr_text, "Should not crash" - assert ( - "No valid dataset files found" not in stderr_text - ), "Should find valid XPT file" - - -@pytest.mark.regression -def test_mixed_folder_processes_valid_files(): - with tempfile.TemporaryDirectory() as temp_dir: - valid_xpt = os.path.join("tests", "resources", "test_dataset.xpt") - if os.path.exists(valid_xpt): - shutil.copy(valid_xpt, os.path.join(temp_dir, "ae.xpt")) - - xlsx_file = os.path.join(temp_dir, "dm.xlsx") - open(xlsx_file, "w").close() - - command = [ - get_python_executable(), - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-d", - temp_dir, - ] - - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - - stderr_text = stderr.decode() - stdout_text = stdout.decode() - - assert "KeyError" not in stderr_text, "Should not crash on any files" - assert "Failed to execute script" not in stderr_text, "Should not crash" - - assert ( - "No valid dataset files found" not in stderr_text - and "No valid dataset files found" not in stdout_text - ), "Should find the valid XPT file" +import subprocess +import os +import tempfile +import shutil +import pytest +from conftest import get_python_executable + + +@pytest.mark.regression +def test_multiple_xlsx_files_shows_helpful_error(): + """Test that multiple XLSX files show a helpful error message about single file limitation""" + with tempfile.TemporaryDirectory() as temp_dir: + xlsx_files = ["ae.xlsx", "dm.xlsx"] + + for filename in xlsx_files: + open(os.path.join(temp_dir, filename), "w").close() + + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-d", + temp_dir, + ] + + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert ( + process.returncode != 0 + ), "Expected non-zero exit code for multiple XLSX files" + assert "KeyError" not in stderr_text, "Should not show KeyError crash" + assert ( + "Failed to execute script" not in stderr_text + ), "Should not show script execution failure" + assert ( + "Multiple XLSX files found" in stderr_text + or "Multiple XLSX files found" in stdout_text + ), f"Expected helpful error message about multiple XLSX files. stderr: {stderr_text}, stdout: {stdout_text}" + assert ( + "Excel format (XLSX) validation only supports single files" in stderr_text + or "Excel format (XLSX) validation only supports single files" + in stdout_text + ), "Expected explanation of XLSX limitation" + + +@pytest.mark.regression +def test_folder_with_xlsx_files_works_with_excel_service(): + """Test that a folder with a single XLSX file now works with ExcelDataService""" + with tempfile.TemporaryDirectory() as temp_dir: + xlsx_files = ["ae.xlsx"] + + for filename in xlsx_files: + open(os.path.join(temp_dir, filename), "w").close() + + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-d", + temp_dir, + ] + + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert "KeyError: 'XLSX'" not in stderr_text, "Should not show KeyError crash" + assert ( + "Failed to execute script" not in stderr_text + ), "Should not show script execution failure" + + assert ( + "No valid dataset files found" not in stderr_text + and "No valid dataset files found" not in stdout_text + ), "XLSX files should now be recognized as valid" + + +@pytest.mark.regression +def test_folder_with_unsupported_formats_shows_helpful_error(): + """Test that folders with truly unsupported formats (like PDF) show helpful error messages""" + with tempfile.TemporaryDirectory() as temp_dir: + unsupported_files = ["ae.pdf", "dm.txt"] + + for filename in unsupported_files: + open(os.path.join(temp_dir, filename), "w").close() + + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-d", + temp_dir, + ] + + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert ( + process.returncode != 0 + ), "Expected non-zero exit code for unsupported files" + assert "KeyError" not in stderr_text, "Should not show KeyError crash" + assert ( + "Failed to execute script" not in stderr_text + ), "Should not show script execution failure" + assert ( + "No valid dataset files found" in stderr_text + or "No valid dataset files found" in stdout_text + ), f"Expected helpful error message. stderr: {stderr_text}, stdout: {stdout_text}" + assert ( + "SAS V5 XPT, Dataset-JSON" in stderr_text + or "SAS V5 XPT, Dataset-JSON" in stdout_text + ), "Expected format guidance in error message" + + +@pytest.mark.regression +def test_empty_folder_shows_helpful_error(): + with tempfile.TemporaryDirectory() as temp_dir: + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-d", + temp_dir, + ] + + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert process.returncode != 0, "Expected non-zero exit code for empty folder" + assert ( + "No valid dataset files found" in stderr_text + or "No valid dataset files found" in stdout_text + ), "Expected helpful error message for empty folder" + + +@pytest.mark.regression +def test_valid_xpt_files_work_normally(): + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-dp", + os.path.join("tests", "resources", "test_dataset.xpt"), + ] + + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + + assert "KeyError: 'XLSX'" not in stderr_text, "Should not show KeyError" + assert "Failed to execute script" not in stderr_text, "Should not crash" + assert ( + "No valid dataset files found" not in stderr_text + ), "Should find valid XPT file" + + +@pytest.mark.regression +def test_mixed_folder_processes_valid_files(): + with tempfile.TemporaryDirectory() as temp_dir: + valid_xpt = os.path.join("tests", "resources", "test_dataset.xpt") + if os.path.exists(valid_xpt): + shutil.copy(valid_xpt, os.path.join(temp_dir, "ae.xpt")) + + xlsx_file = os.path.join(temp_dir, "dm.xlsx") + open(xlsx_file, "w").close() + + command = [ + get_python_executable(), + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-d", + temp_dir, + ] + + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + stderr_text = stderr.decode() + stdout_text = stdout.decode() + + assert "KeyError" not in stderr_text, "Should not crash on any files" + assert "Failed to execute script" not in stderr_text, "Should not crash" + + assert ( + "No valid dataset files found" not in stderr_text + and "No valid dataset files found" not in stdout_text + ), "Should find the valid XPT file" diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py index 009e7eca0..02ed2aae6 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py @@ -1,132 +1,132 @@ -import os -import subprocess -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - dataset_details_sheet, - issue_datails_sheet, - rules_report_sheet, - issue_sheet_variable_column, - issue_sheet_coreid_column, -) - - -@pytest.mark.regression -def test_validate_define_xml_against_lib_metadata(): - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-4", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1421", - "Dataset.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1421", "Rule.yml"), - "-dxp", - os.path.join("tests", "resources", "CoreIssue1421", "Define.xml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - # Check Variable(s) column (H) - variables_names_column = sheet["H"] - variables_names_values = [ - cell.value for cell in variables_names_column[1:] if cell.value is not None - ] - assert len(variables_names_values) == 3 - for value in variables_names_values: - assert len(value.split(",")) == 6 - - # Check Value(s) column (I) - variables_values_column = sheet[issue_sheet_variable_column] - variables_values = [ - cell.value for cell in variables_values_column[1:] if cell.value is not None - ] - assert len(variables_values) == 3 - for value in variables_values: - assert len(value.split(",")) == 6 - - dataset_column = sheet["D"] - dataset_column_values = [ - cell.value for cell in dataset_column[1:] if cell.value is not None - ] - assert sorted(set(dataset_column_values)) == ["DM", "SUPPEC"] - - core_id_column = sheet[issue_sheet_coreid_column] - core_id_column_values = [ - cell.value for cell in core_id_column[1:] if cell.value is not None - ] - assert set(core_id_column_values) == {"CDISC.SDTMIG.CG0999"} - - # Go to the "Rules Report" sheet - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - assert rules_values[0][0] == "CDISC.SDTMIG.CG0999" - assert "ISSUE REPORTED" in rules_values[0] - assert ( - rules_values[0][4] - == "Issue with codelist definition in the Define-XML document." - ) - - # Go to the "Dataset Details" sheet - dataset_sheet = workbook[dataset_details_sheet] - dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] - dataset_values = [row for row in dataset_values if any(row)] - assert len(dataset_values) > 0 - dataset_names = set(row[0] for row in dataset_values if row[0] is not None) - assert dataset_names == {"AE", "DM", "EC", "EX", "SUPPEC"} - expected_records = { - "AE": 74, - "DM": 18, - "EC": 1590, - "EX": 1583, - "SUPPEC": 13, - } - for row in dataset_values: - dataset_name = row[0] - records_count = row[-1] - assert records_count == expected_records[dataset_name] - - # Go to the "Issue Summary" sheet - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [row for row in issue_summary_sheet.iter_rows(values_only=True)][ - 1: - ] - summary_values = [row for row in summary_values if any(row)] - assert len(summary_values) == 2 - core_ids = set(row[1] for row in summary_values if row[1] is not None) - assert core_ids == {"CDISC.SDTMIG.CG0999"} - # Check Message and dataset columns - for row in summary_values: - assert row[2] == "Issue with codelist definition in the Define-XML document." - datasets_in_summary = set(row[0] for row in summary_values if row[0] is not None) - assert datasets_in_summary == {"DM", "SUPPEC"} - - # Delete the excel file - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + dataset_details_sheet, + issue_datails_sheet, + rules_report_sheet, + issue_sheet_variable_column, + issue_sheet_coreid_column, +) + + +@pytest.mark.regression +def test_validate_define_xml_against_lib_metadata(): + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1421", + "Dataset.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1421", "Rule.yml"), + "-dxp", + os.path.join("tests", "resources", "CoreIssue1421", "Define.xml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + # Check Variable(s) column (H) + variables_names_column = sheet["H"] + variables_names_values = [ + cell.value for cell in variables_names_column[1:] if cell.value is not None + ] + assert len(variables_names_values) == 3 + for value in variables_names_values: + assert len(value.split(",")) == 6 + + # Check Value(s) column (I) + variables_values_column = sheet[issue_sheet_variable_column] + variables_values = [ + cell.value for cell in variables_values_column[1:] if cell.value is not None + ] + assert len(variables_values) == 3 + for value in variables_values: + assert len(value.split(",")) == 6 + + dataset_column = sheet["D"] + dataset_column_values = [ + cell.value for cell in dataset_column[1:] if cell.value is not None + ] + assert sorted(set(dataset_column_values)) == ["DM", "SUPPEC"] + + core_id_column = sheet[issue_sheet_coreid_column] + core_id_column_values = [ + cell.value for cell in core_id_column[1:] if cell.value is not None + ] + assert set(core_id_column_values) == {"CDISC.SDTMIG.CG0999"} + + # Go to the "Rules Report" sheet + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + assert rules_values[0][0] == "CDISC.SDTMIG.CG0999" + assert "ISSUE REPORTED" in rules_values[0] + assert ( + rules_values[0][4] + == "Issue with codelist definition in the Define-XML document." + ) + + # Go to the "Dataset Details" sheet + dataset_sheet = workbook[dataset_details_sheet] + dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] + dataset_values = [row for row in dataset_values if any(row)] + assert len(dataset_values) > 0 + dataset_names = set(row[0] for row in dataset_values if row[0] is not None) + assert dataset_names == {"AE", "DM", "EC", "EX", "SUPPEC"} + expected_records = { + "AE": 74, + "DM": 18, + "EC": 1590, + "EX": 1583, + "SUPPEC": 13, + } + for row in dataset_values: + dataset_name = row[0] + records_count = row[-1] + assert records_count == expected_records[dataset_name] + + # Go to the "Issue Summary" sheet + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [row for row in issue_summary_sheet.iter_rows(values_only=True)][ + 1: + ] + summary_values = [row for row in summary_values if any(row)] + assert len(summary_values) == 2 + core_ids = set(row[1] for row in summary_values if row[1] is not None) + assert core_ids == {"CDISC.SDTMIG.CG0999"} + # Check Message and dataset columns + for row in summary_values: + assert row[2] == "Issue with codelist definition in the Define-XML document." + datasets_in_summary = set(row[0] for row in summary_values if row[0] is not None) + assert datasets_in_summary == {"DM", "SUPPEC"} + + # Delete the excel file + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py index 172516786..a1ae1a304 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py @@ -1,91 +1,91 @@ -import os -import subprocess -import openpyxl -import pytest -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1442: - @pytest.mark.parametrize( - "command", - [ - ( - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join( - "tests", "resources", "CoreIssue1442", "test_adam_dataset.xpt" - ), - "-dp", - os.path.join( - "tests", "resources", "CoreIssue1442", "test_dataset.ndjson" - ), - "-dp", - os.path.join( - "tests", "resources", "CoreIssue1442", "CDISC_Pilot_Study.json" - ), - "-ft", - "json", - "-lr", - os.path.join("tests", "resources", "CoreIssue1442", "rule.yml"), - ), - ( - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-d", - os.path.join("tests", "resources", "CoreIssue1442"), - "-ft", - "json", - "-lr", - os.path.join("tests", "resources", "CoreIssue1442", "rule.yml"), - ), - ], - ) - def test_positive_dataset(self, command): - # Run the command in the terminal - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - if "Conformance Details" in workbook.sheetnames: - conformance_sheet = workbook["Conformance Details"] - found = False - for row in conformance_sheet.iter_rows(min_row=2, values_only=True): - for idx, cell in enumerate(row[:-1]): - if ( - cell == "JSON file name" - and row[idx + 1] == "CDISC_Pilot_Study.json" - ): - found = True - break - if found: - break - assert ( - found - ), "Pair ('JSON file name', 'CDISC_Pilot_Study.json') not found in any row of 'Conformance Details' sheet." - else: - assert False, "'Conformance Details' sheet not found in report." - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import openpyxl +import pytest +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1442: + @pytest.mark.parametrize( + "command", + [ + ( + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join( + "tests", "resources", "CoreIssue1442", "test_adam_dataset.xpt" + ), + "-dp", + os.path.join( + "tests", "resources", "CoreIssue1442", "test_dataset.ndjson" + ), + "-dp", + os.path.join( + "tests", "resources", "CoreIssue1442", "CDISC_Pilot_Study.json" + ), + "-ft", + "json", + "-lr", + os.path.join("tests", "resources", "CoreIssue1442", "rule.yml"), + ), + ( + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-d", + os.path.join("tests", "resources", "CoreIssue1442"), + "-ft", + "json", + "-lr", + os.path.join("tests", "resources", "CoreIssue1442", "rule.yml"), + ), + ], + ) + def test_positive_dataset(self, command): + # Run the command in the terminal + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + if "Conformance Details" in workbook.sheetnames: + conformance_sheet = workbook["Conformance Details"] + found = False + for row in conformance_sheet.iter_rows(min_row=2, values_only=True): + for idx, cell in enumerate(row[:-1]): + if ( + cell == "JSON file name" + and row[idx + 1] == "CDISC_Pilot_Study.json" + ): + found = True + break + if found: + break + assert ( + found + ), "Pair ('JSON file name', 'CDISC_Pilot_Study.json') not found in any row of 'Conformance Details' sheet." + else: + assert False, "'Conformance Details' sheet not found in report." + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py index 932a67f5b..700b0351e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py @@ -1,54 +1,53 @@ -import os -import subprocess -import unittest -import openpyxl -import pytest -from conftest import get_python_executable - - -@pytest.mark.skip(reason="No library metadata found") -class TestCoreIssue1487(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "5-0", - "-d", - os.path.join("tests", "resources", "CoreIssue1487"), - "-r", - "CORE-000354", - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - assert "Rules Report" in workbook.sheetnames - rules_sheet = workbook["Rules Report"] - target_row = None - for row in rules_sheet.iter_rows(min_row=2, values_only=True): - if row[0] == "CORE-000354": - target_row = row - break - assert target_row, "Rule CORE-000354 not present in 'Rules Report' sheet." - assert ( - target_row[4] and "was requested but is not available" in target_row[4] - ), "Expected error message for CORE-000354 not found." - assert target_row[5] == "SKIPPED", "CORE-000354 status should be SKIPPED." - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import unittest +import openpyxl +import pytest +from conftest import get_python_executable + + +class TestCoreIssue1487(unittest.TestCase): + def test_positive_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-d", + os.path.join("tests", "resources", "CoreIssue1487"), + "-r", + "CORE-000354", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + assert "Rules Report" in workbook.sheetnames + rules_sheet = workbook["Rules Report"] + target_row = None + for row in rules_sheet.iter_rows(min_row=2, values_only=True): + if row[0] == "CORE-000354": + target_row = row + break + assert target_row, "Rule CORE-000354 not present in 'Rules Report' sheet." + assert ( + target_row[4] in target_row[4] + ), "Expected error message for CORE-000354 not found." + assert target_row[5] == "ISSUE REPORTED", "CORE-000354 status should be ISSUE REPORTED." + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index d79dbc2a0..38c40214f 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -1,62 +1,62 @@ -import os -import subprocess -import unittest -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1501(unittest.TestCase): - def test_raw_report(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sendig", - "-v", - "3-1", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue1501", - "unit-test-coreid-SENDIG282-negative.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1501", "Rule.yml"), - "-ps", - "1", - "-of", - "json", - "-rr", - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - - if "results_data" not in json_report.keys(): - assert ( - False - ), "'results_data' key not found in report. Expected while using -rr, --raw_report flag" - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess +import unittest +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1501(unittest.TestCase): + def test_raw_report(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sendig", + "-v", + "3-1", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue1501", + "unit-test-coreid-SENDIG282-negative.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1501", "Rule.yml"), + "-ps", + "1", + "-of", + "json", + "-rr", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + + if "results_data" not in json_report.keys(): + assert ( + False + ), "'results_data' key not found in report. Expected while using -rr, --raw_report flag" + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py index 2c424e832..eace7e744 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py @@ -1,100 +1,100 @@ -import os -import subprocess -import unittest - -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1558(unittest.TestCase): - def test_raw_report(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-r", - "CORE-000007", - "-v", - "3.4", - "-d", - os.path.join( - "tests", - "resources", - "CoreIssue1558", - "datasets", - ), - "--output-format", - "json", - "-ps", - "1", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - datasets = {x["filename"] for x in json_report["Dataset_Details"]} - assert {"LB", "DM"}.issubset(datasets) - if os.path.exists(json_report_path): - os.remove(json_report_path) - - def test_env_vars_loaded(self): - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-r", - "CORE-000007", - "--dotenv-path", - os.path.join("tests", "resources", "CoreIssue1558", "test.env"), - "-d", - os.path.join( - "tests", - "resources", - "CoreIssue1558", - "datasets", - ), - "--output-format", - "json", - "-ps", - "1", - ] - subprocess.run(command, capture_output=True, text=True) - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert json_report["Conformance_Details"]["Standard"] == "SDTMIG" - assert json_report["Conformance_Details"]["Version"] == "V3.4" +import os +import subprocess +import unittest + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1558(unittest.TestCase): + def test_raw_report(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-r", + "CORE-000007", + "-v", + "3.4", + "-d", + os.path.join( + "tests", + "resources", + "CoreIssue1558", + "datasets", + ), + "--output-format", + "json", + "-ps", + "1", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + datasets = {x["filename"] for x in json_report["Dataset_Details"]} + assert {"LB", "DM"}.issubset(datasets) + if os.path.exists(json_report_path): + os.remove(json_report_path) + + def test_env_vars_loaded(self): + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-r", + "CORE-000007", + "--dotenv-path", + os.path.join("tests", "resources", "CoreIssue1558", "test.env"), + "-d", + os.path.join( + "tests", + "resources", + "CoreIssue1558", + "datasets", + ), + "--output-format", + "json", + "-ps", + "1", + ] + subprocess.run(command, capture_output=True, text=True) + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert json_report["Conformance_Details"]["Standard"] == "SDTMIG" + assert json_report["Conformance_Details"]["Version"] == "V3.4" diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py index b62fc4456..1624b6a22 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py @@ -1,125 +1,123 @@ -import subprocess -import os -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - issue_sheet_record_column, - issue_sheet_variable_column, - issue_sheet_values_column, -) - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -def test_negative_dataset(): - command = ( - f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -r " - + os.path.join("tests", "resources", "CoreIssue164", "rule.json") - + " -dp " - + os.path.join("tests", "resources", "CoreIssue164", "Negative_Dataset.json") - ) - - # Construct the command - command = command.split(" ") - - # Run the command in the terminal - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - file_name = stdout.decode().strip().split(": ")[1] - # Open the Excel file - workbook = openpyxl.load_workbook(file_name) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - - # Perform the assertion - # Ensure only two negative values are caught - assert len(record_values) == 2 - assert len(variables_values) == 2 - assert len(values_column_values) == 2 - - # Ensure negatives are detected at correct rows in dataset - assert record_values[0] == 1 - assert record_values[1] == 3 - - # Ensure correct variable is marked as negative - assert variables_values[0] == variables_values[1] == "DSDY" - - # Ensure correct values were marked negatives - assert values_column_values[0] == "0" - assert values_column_values[1] == "alolo" - - # Close the workbook - workbook.close() - - # Delete the file - os.remove(file_name) - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -def test_positive_dataset(): - command = ( - f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -r " - + os.path.join("tests", "resources", "CoreIssue164", "rule.json") - + " -dp " - + os.path.join("tests", "resources", "CoreIssue164", "Positive_Dataset.json") - ) - - # Construct the command - command = command.split(" ") - - # Run the command in the terminal - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - file_name = stdout.decode().strip().split(": ")[1] - print(file_name) - # Open the Excel file - workbook = openpyxl.load_workbook(file_name) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - - # Perform the assertion - # Ensure only two negative values are caught - assert len(record_values) == 0 - assert len(variables_values) == 0 - assert len(values_column_values) == 0 - - # Close the workbook - workbook.close() - - # Delete the file - os.remove(file_name) +import subprocess +import os +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + issue_sheet_record_column, + issue_sheet_variable_column, + issue_sheet_values_column, +) + + +def test_negative_dataset(): + command = ( + f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + + os.path.join("tests", "resources", "CoreIssue164", "rule.json") + + " -dp " + + os.path.join("tests", "resources", "CoreIssue164", "Negative_Dataset.json") + ) + + # Construct the command + command = command.split(" ") + + # Run the command in the terminal + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + file_name = stdout.decode().strip().split(": ")[1] + # Open the Excel file + workbook = openpyxl.load_workbook(file_name) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + + # Perform the assertion + # Ensure only two negative values are caught + assert len(record_values) == 2 + assert len(variables_values) == 2 + assert len(values_column_values) == 2 + + # Ensure negatives are detected at correct rows in dataset + assert record_values[0] == 1 + assert record_values[1] == 3 + + # Ensure correct variable is marked as negative + assert variables_values[0] == variables_values[1] == "DSDY" + + # Ensure correct values were marked negatives + assert values_column_values[0] == "0" + assert values_column_values[1] == "alolo" + + # Close the workbook + workbook.close() + + # Delete the file + os.remove(file_name) + + +def test_positive_dataset(): + command = ( + f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + + os.path.join("tests", "resources", "CoreIssue164", "rule.json") + + " -dp " + + os.path.join("tests", "resources", "CoreIssue164", "Positive_Dataset.json") + ) + + # Construct the command + command = command.split(" ") + + # Run the command in the terminal + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + file_name = stdout.decode().strip().split(": ")[1] + print(file_name) + # Open the Excel file + workbook = openpyxl.load_workbook(file_name) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + + # Perform the assertion + # Ensure only two negative values are caught + assert len(record_values) == 0 + assert len(variables_values) == 0 + assert len(values_column_values) == 0 + + # Close the workbook + workbook.close() + + # Delete the file + os.remove(file_name) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py index c2064523a..fcd4fa5cc 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py @@ -1,56 +1,56 @@ -import os -import subprocess -import unittest -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1501(unittest.TestCase): - def test_raw_report(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sendig", - "-v", - "3-1", - "-d", - os.path.join( - "tests", - "resources", - "CoreIssue1699", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue1699", "rule.yml"), - "-ps", - "1", - "-of", - "json", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert json_report["Rules_Report"][0]["status"] == "SUCCESS" - - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess +import unittest +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1501(unittest.TestCase): + def test_raw_report(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sendig", + "-v", + "3-1", + "-d", + os.path.join( + "tests", + "resources", + "CoreIssue1699", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue1699", "rule.yml"), + "-ps", + "1", + "-of", + "json", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert json_report["Rules_Report"][0]["status"] == "SUCCESS" + + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py index 926839bdf..7b5ec90fb 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py @@ -1,60 +1,60 @@ -import os -import subprocess -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue1718: - def test_max_issues(self): - # Run the command in the terminal - max_issues = 3 - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3-4", - "-d", - os.path.join( - "tests", - "resources", - "CoreIssue1718", - ), - "-r", - "CORE-000356", - "-ps", - "1", - "-of", - "json", - "-me", - f"{max_issues}", - "true", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert json_report["Rules_Report"][0]["status"] == "ISSUE REPORTED" - assert json_report["Issue_Summary"][0]["issues"] == 74 - assert len(json_report["Issue_Details"]) == max_issues - - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue1718: + def test_max_issues(self): + # Run the command in the terminal + max_issues = 3 + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3-4", + "-d", + os.path.join( + "tests", + "resources", + "CoreIssue1718", + ), + "-r", + "CORE-000356", + "-ps", + "1", + "-of", + "json", + "-me", + f"{max_issues}", + "true", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert json_report["Rules_Report"][0]["status"] == "ISSUE REPORTED" + assert json_report["Issue_Summary"][0]["issues"] == 74 + assert len(json_report["Issue_Details"]) == max_issues + + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py index 8cc43263f..9ac58fda9 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") +@pytest.mark.skip(reason="Requires API key to run. Run Locally with valid API key to test.") class TestCoreIssue363(unittest.TestCase): def test_post_request(self): # URL to send the POST request to diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py index 6aff31758..9882b6539 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py @@ -1,131 +1,129 @@ -import subprocess -import os -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - dataset_details_sheet, - rules_report_sheet, - issue_sheet_coreid_column, - dataset_sheet_dataset_column, - rules_sheet_rule_status_column, -) - -""" These tests utilize positive and negative dataset -to validate successful working CG0202. Positive -dataset have supp datasets so rule should run -successfully. while Negative datasets have no -supp datasets so rule should be skipped. The -succesfuly running against postive dataset means -the scope skip problem is resolved """ - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -def test_negative_dataset(): - command = ( - f"{get_python_executable()} -m core test -s sdtmig -v 3.4 -r " - + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") - + " -dp " - + os.path.join("tests", "resources", "CoreIssue576", "Datasets_Negative.json") - ) - - # Construct the command - command = command.split(" ") - - # Run the command in the terminal - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - file_name = stdout.decode().strip().split(": ")[1] - # Open the Excel file - workbook = openpyxl.load_workbook(file_name) - - # Go to the "Issue Details" sheet - issues_sheet = workbook[issue_datails_sheet] - dataset_sheet = workbook[dataset_details_sheet] - rules_sheet = workbook[rules_report_sheet] - - coreid_column = issues_sheet[issue_sheet_coreid_column] - dataset_column = dataset_sheet[dataset_sheet_dataset_column] - rule_status_column = rules_sheet[rules_sheet_rule_status_column] - - coreid_values = [cell.value for cell in coreid_column[1:]] - dataset_values = [cell.value for cell in dataset_column[1:]] - rule_status_column_values = [cell.value for cell in rule_status_column[1:]] - - # Remove None values using list comprehension - coreid_values = [value for value in coreid_values if value is not None] - dataset_values = [value for value in dataset_values if value is not None] - rule_status_column_values = [ - value for value in rule_status_column_values if value is not None - ] - - # Perform the assertion - assert dataset_values[0] == "DM" - assert len(dataset_values) == 1 - - assert len(coreid_values) == 0 - - assert rule_status_column_values[0] == "SKIPPED" - - # Close the workbook - workbook.close() - - # Delete the file - os.remove(file_name) - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -def test_positive_dataset(): - command = ( - f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -r " - + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") - + " -dp " - + os.path.join("tests", "resources", "CoreIssue576", "Datasets_positive.json") - ) - - # Construct the command - command = command.split(" ") - - # Run the command in the terminal - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - file_name = stdout.decode().strip().split(": ")[1] - # Open the Excel file - workbook = openpyxl.load_workbook(file_name) - - # Go to the "Issue Details" sheet - issues_sheet = workbook[issue_datails_sheet] - dataset_sheet = workbook[dataset_details_sheet] - rules_sheet = workbook[rules_report_sheet] - - coreid_column = issues_sheet[issue_sheet_coreid_column] - dataset_column = dataset_sheet[dataset_sheet_dataset_column] - rule_status_column = rules_sheet[rules_sheet_rule_status_column] - - coreid_values = [cell.value for cell in coreid_column[1:]] - dataset_values = [cell.value for cell in dataset_column[1:]] - rule_status_column_values = [cell.value for cell in rule_status_column[1:]] - - # Remove None values using list comprehension - coreid_values = [value for value in coreid_values if value is not None] - dataset_values = [value for value in dataset_values if value is not None] - rule_status_column_values = [ - value for value in rule_status_column_values if value is not None - ] - - # Perform the assertion - assert dataset_values[0] == "SUPPAE" - assert dataset_values[1] == "SUPPEC" - - assert len(coreid_values) == 0 - - assert rule_status_column_values[0] == "SUCCESS" - - # Close the workbook - workbook.close() - - # Delete the file - os.remove(file_name) +import subprocess +import os +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + dataset_details_sheet, + rules_report_sheet, + issue_sheet_coreid_column, + dataset_sheet_dataset_column, + rules_sheet_rule_status_column, +) + +""" These tests utilize positive and negative dataset +to validate successful working CG0202. Positive +dataset have supp datasets so rule should run +successfully. while Negative datasets have no +supp datasets so rule should be skipped. The +succesfuly running against postive dataset means +the scope skip problem is resolved """ + + +def test_negative_dataset(): + command = ( + f"{get_python_executable()} -m core validate -s sdtmig -v 3.3 -lr " + + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") + + " -dp " + + os.path.join("tests", "resources", "CoreIssue576", "Datasets_Negative.json") + ) + + # Construct the command + command = command.split(" ") + + # Run the command in the terminal + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + file_name = stdout.decode().strip().split(": ")[1] + # Open the Excel file + workbook = openpyxl.load_workbook(file_name) + + # Go to the "Issue Details" sheet + issues_sheet = workbook[issue_datails_sheet] + dataset_sheet = workbook[dataset_details_sheet] + rules_sheet = workbook[rules_report_sheet] + + coreid_column = issues_sheet[issue_sheet_coreid_column] + dataset_column = dataset_sheet[dataset_sheet_dataset_column] + rule_status_column = rules_sheet[rules_sheet_rule_status_column] + + coreid_values = [cell.value for cell in coreid_column[1:]] + dataset_values = [cell.value for cell in dataset_column[1:]] + rule_status_column_values = [cell.value for cell in rule_status_column[1:]] + + # Remove None values using list comprehension + coreid_values = [value for value in coreid_values if value is not None] + dataset_values = [value for value in dataset_values if value is not None] + rule_status_column_values = [ + value for value in rule_status_column_values if value is not None + ] + + # Perform the assertion + assert dataset_values[0] == "DM" + assert len(dataset_values) == 1 + + assert len(coreid_values) == 0 + + assert rule_status_column_values[0] == "SKIPPED" + + # Close the workbook + workbook.close() + + # Delete the file + os.remove(file_name) + + +def test_positive_dataset(): + command = ( + f"{get_python_executable()} -m core validate -s sdtmig -v 3.3 -lr " + + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") + + " -dp " + + os.path.join("tests", "resources", "CoreIssue576", "Datasets_positive.json") + ) + + # Construct the command + command = command.split(" ") + + # Run the command in the terminal + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + file_name = stdout.decode().strip().split(": ")[1] + # Open the Excel file + workbook = openpyxl.load_workbook(file_name) + + # Go to the "Issue Details" sheet + issues_sheet = workbook[issue_datails_sheet] + dataset_sheet = workbook[dataset_details_sheet] + rules_sheet = workbook[rules_report_sheet] + + coreid_column = issues_sheet[issue_sheet_coreid_column] + dataset_column = dataset_sheet[dataset_sheet_dataset_column] + rule_status_column = rules_sheet[rules_sheet_rule_status_column] + + coreid_values = [cell.value for cell in coreid_column[1:]] + dataset_values = [cell.value for cell in dataset_column[1:]] + rule_status_column_values = [cell.value for cell in rule_status_column[1:]] + + # Remove None values using list comprehension + coreid_values = [value for value in coreid_values if value is not None] + dataset_values = [value for value in dataset_values if value is not None] + rule_status_column_values = [ + value for value in rule_status_column_values if value is not None + ] + + # Perform the assertion + assert dataset_values[0] == "SUPPAE" + assert dataset_values[1] == "SUPPEC" + + assert len(coreid_values) == 0 + + assert rule_status_column_values[0] == "SUCCESS" + + # Close the workbook + workbook.close() + + # Delete the file + os.remove(file_name) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py index a69f3421b..e61ac5839 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py @@ -1,139 +1,139 @@ -import os -import subprocess -import unittest -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - rules_report_sheet, - issue_sheet_record_column, - issue_sheet_variable_column, - issue_sheet_values_column, -) - - -@pytest.mark.regression -class TestCoreIssue715(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join( - "tests", "resources", "CoreIssue715", "CDISC_Pilot_Study.json" - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue715", "rule.yml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - # Perform the assertion - assert rules_values[0][0] == "DDF00081" - assert "SUCCESS" in rules_values[0] - assert len(record_values) == 0 - assert len(variables_values) == 0 - assert len(values_column_values) == 0 - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - def test_negative_dataset(self): - # Run validation for invalid JSON - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join( - "tests", "resources", "CoreIssue715", "CDISC_Pilot_Study_Invalid.json" - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue715", "rule.yml"), - ] - subprocess.run(command, check=True) - - files = os.listdir() - excel_files = [ - f for f in files if f.startswith("CORE-Report-") and f.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - workbook = openpyxl.load_workbook(excel_file_path) - - # Issue Summary basic checks - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [r for r in issue_summary_sheet.iter_rows(values_only=True)][ - 1: - ] - summary_values = [r for r in summary_values if any(r)] - assert summary_values and summary_values[0][1] == "DDF00081" - assert summary_values[0][4] == 1 - - # Issue Details strict checks: now expect one row per error - issue_details_sheet = workbook[issue_datails_sheet] - details_rows = [r for r in issue_details_sheet.iter_rows(values_only=True)][1:] - details_rows = [r for r in details_rows if any(r)] - # Expect exactly 15 rows - assert len(details_rows) == 15 - - # Expected exact strings - for row in details_rows: - assert row[0] == "DDF00081" - assert ( - row[2] - == "The class relationship does not conform with the USDM schema." - ) - assert row[7] == ("json_path, message") - - # Rules Report - rules_rows = [ - r for r in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_rows = [r for r in rules_rows if any(r)] - assert rules_rows and rules_rows[0][0] == "DDF00081" - assert "ISSUE REPORTED" in rules_rows[0] - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import unittest +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + rules_report_sheet, + issue_sheet_record_column, + issue_sheet_variable_column, + issue_sheet_values_column, +) + + +@pytest.mark.regression +class TestCoreIssue715(unittest.TestCase): + def test_positive_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join( + "tests", "resources", "CoreIssue715", "CDISC_Pilot_Study.json" + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue715", "rule.yml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + # Perform the assertion + assert rules_values[0][0] == "DDF00081" + assert "SUCCESS" in rules_values[0] + assert len(record_values) == 0 + assert len(variables_values) == 0 + assert len(values_column_values) == 0 + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + def test_negative_dataset(self): + # Run validation for invalid JSON + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join( + "tests", "resources", "CoreIssue715", "CDISC_Pilot_Study_Invalid.json" + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue715", "rule.yml"), + ] + subprocess.run(command, check=True) + + files = os.listdir() + excel_files = [ + f for f in files if f.startswith("CORE-Report-") and f.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + workbook = openpyxl.load_workbook(excel_file_path) + + # Issue Summary basic checks + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [r for r in issue_summary_sheet.iter_rows(values_only=True)][ + 1: + ] + summary_values = [r for r in summary_values if any(r)] + assert summary_values and summary_values[0][1] == "DDF00081" + assert summary_values[0][4] == 1 + + # Issue Details strict checks: now expect one row per error + issue_details_sheet = workbook[issue_datails_sheet] + details_rows = [r for r in issue_details_sheet.iter_rows(values_only=True)][1:] + details_rows = [r for r in details_rows if any(r)] + # Expect exactly 15 rows + assert len(details_rows) == 15 + + # Expected exact strings + for row in details_rows: + assert row[0] == "DDF00081" + assert ( + row[2] + == "The class relationship does not conform with the USDM schema." + ) + assert row[7] == ("json_path, message") + + # Rules Report + rules_rows = [ + r for r in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_rows = [r for r in rules_rows if any(r)] + assert rules_rows and rules_rows[0][0] == "DDF00081" + assert "ISSUE REPORTED" in rules_rows[0] + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue720.py b/tests/QARegressionTests/test_Issues/test_CoreIssue720.py index a00aa158a..a655deb8d 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue720.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue720.py @@ -1,149 +1,149 @@ -import os -import subprocess -import unittest - -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - rules_report_sheet, -) - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -class TestCoreIssue720(unittest.TestCase): - def test_negative_dataset(self): - """Negative scenario: SPECIES missing -> expect one populated issue.""" - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join("tests", "resources", "CoreIssue720", "Invalid_datasets.json"), - "-lr", - os.path.join("tests", "resources", "CoreIssue720", "Rule.yml"), - ] - subprocess.run(command, check=True) - - excel_files = [ - f - for f in os.listdir() - if f.startswith("CORE-Report-") and f.endswith(".xlsx") - ] - assert excel_files, "No CORE report generated" - excel_file_path = sorted(excel_files)[-1] - wb = openpyxl.load_workbook(excel_file_path) - - # Conformance Details - assert "Conformance Details" in wb.sheetnames - conf_rows = [ - row for row in wb["Conformance Details"].iter_rows(values_only=True) - ] - assert conf_rows[6][0] == "Standard" - assert conf_rows[6][1] == "USDM" - assert conf_rows[7][0] == "Version" - assert conf_rows[7][1] == "V4.0" - - # Entity Details - entity_rows = [row for row in wb["Entity Details"].iter_rows(values_only=True)][ - 1: - ] - entity_rows = [r for r in entity_rows if any(r)] - assert len(entity_rows) >= 2 - assert entity_rows[0][0] == "DM" and entity_rows[0][1] == 4 - assert entity_rows[1][0] == "TS" and entity_rows[1][1] == 4 - - # Issue Summary - summary_rows = [row for row in wb["Issue Summary"].iter_rows(values_only=True)][ - 1: - ] - summary_rows = [r for r in summary_rows if any(r)] - assert summary_rows[0][0] == "dm.xpt" - assert summary_rows[0][1] == "CDISC.SENDIG.105" - assert summary_rows[0][4] == 1 - - # Issue Details - details_rows = [ - row for row in wb[issue_datails_sheet].iter_rows(values_only=True) - ][1:] - details_rows = [r for r in details_rows if any(r)] - assert details_rows, "Issue Details should have at least one populated row" - first_issue = details_rows[0] - assert first_issue[0] == "CDISC.SENDIG.105" - assert first_issue[1] == "SEND105" - assert "SPECIES in not present" in first_issue[2] - assert first_issue[3] == "fully executable" - assert first_issue[4] == "dm.xpt" - assert first_issue[7] == "$distinct_tsparmcd, $distinct_txparmcd, SPECIES" - assert "Not in dataset" in first_issue[8] - - # Rules Report - rules_rows = [ - row for row in wb[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_rows = [r for r in rules_rows if any(r)] - assert rules_rows, "Rules Report must have at least one populated row" - assert rules_rows[0][0] == "CDISC.SENDIG.105" - assert rules_rows[0][-1] == "SUCCESS" - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - def test_positive_dataset(self): - """Positive scenario: SPECIES present -> expect no issue rows.""" - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join("tests", "resources", "CoreIssue720", "Valid_datasets.json"), - "-lr", - os.path.join("tests", "resources", "CoreIssue720", "Rule.yml"), - ] - subprocess.run(command, check=True) - - excel_files = [ - f - for f in os.listdir() - if f.startswith("CORE-Report-") and f.endswith(".xlsx") - ] - assert excel_files, "No CORE report generated" - excel_file_path = sorted(excel_files)[-1] - wb = openpyxl.load_workbook(excel_file_path) - - # Issue Summary empty - summary_rows = [row for row in wb["Issue Summary"].iter_rows(values_only=True)][ - 1: - ] - summary_rows = [r for r in summary_rows if any(r)] - assert summary_rows == [] - - # Issue Details empty - details_rows = [ - row for row in wb[issue_datails_sheet].iter_rows(values_only=True) - ][1:] - details_rows = [r for r in details_rows if any(r)] - assert details_rows == [] - - # Rules Report has success row - rules_rows = [ - row for row in wb[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_rows = [r for r in rules_rows if any(r)] - assert len(rules_rows) == 1 - assert rules_rows[0][0] == "CDISC.SENDIG.105" - assert rules_rows[0][-1] == "SUCCESS" - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) +import os +import subprocess +import unittest + +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + rules_report_sheet, +) + + +@pytest.mark.skip(reason="The test is obsolete and requires modernization.") +class TestCoreIssue720(unittest.TestCase): + def test_negative_dataset(self): + """Negative scenario: SPECIES missing -> expect one populated issue.""" + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join("tests", "resources", "CoreIssue720", "Invalid_datasets.json"), + "-lr", + os.path.join("tests", "resources", "CoreIssue720", "Rule.yml"), + ] + subprocess.run(command, check=True) + + excel_files = [ + f + for f in os.listdir() + if f.startswith("CORE-Report-") and f.endswith(".xlsx") + ] + assert excel_files, "No CORE report generated" + excel_file_path = sorted(excel_files)[-1] + wb = openpyxl.load_workbook(excel_file_path) + + # Conformance Details + assert "Conformance Details" in wb.sheetnames + conf_rows = [ + row for row in wb["Conformance Details"].iter_rows(values_only=True) + ] + assert conf_rows[6][0] == "Standard" + assert conf_rows[6][1] == "USDM" + assert conf_rows[7][0] == "Version" + assert conf_rows[7][1] == "V4.0" + + # Entity Details + entity_rows = [row for row in wb["Entity Details"].iter_rows(values_only=True)][ + 1: + ] + entity_rows = [r for r in entity_rows if any(r)] + assert len(entity_rows) >= 2 + assert entity_rows[0][0] == "DM" and entity_rows[0][1] == 4 + assert entity_rows[1][0] == "TS" and entity_rows[1][1] == 4 + + # Issue Summary + summary_rows = [row for row in wb["Issue Summary"].iter_rows(values_only=True)][ + 1: + ] + summary_rows = [r for r in summary_rows if any(r)] + assert summary_rows[0][0] == "dm.xpt" + assert summary_rows[0][1] == "CDISC.SENDIG.105" + assert summary_rows[0][4] == 1 + + # Issue Details + details_rows = [ + row for row in wb[issue_datails_sheet].iter_rows(values_only=True) + ][1:] + details_rows = [r for r in details_rows if any(r)] + assert details_rows, "Issue Details should have at least one populated row" + first_issue = details_rows[0] + assert first_issue[0] == "CDISC.SENDIG.105" + assert first_issue[1] == "SEND105" + assert "SPECIES in not present" in first_issue[2] + assert first_issue[3] == "fully executable" + assert first_issue[4] == "dm.xpt" + assert first_issue[7] == "$distinct_tsparmcd, $distinct_txparmcd, SPECIES" + assert "Not in dataset" in first_issue[8] + + # Rules Report + rules_rows = [ + row for row in wb[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_rows = [r for r in rules_rows if any(r)] + assert rules_rows, "Rules Report must have at least one populated row" + assert rules_rows[0][0] == "CDISC.SENDIG.105" + assert rules_rows[0][-1] == "SUCCESS" + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + def test_positive_dataset(self): + """Positive scenario: SPECIES present -> expect no issue rows.""" + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join("tests", "resources", "CoreIssue720", "Valid_datasets.json"), + "-lr", + os.path.join("tests", "resources", "CoreIssue720", "Rule.yml"), + ] + subprocess.run(command, check=True) + + excel_files = [ + f + for f in os.listdir() + if f.startswith("CORE-Report-") and f.endswith(".xlsx") + ] + assert excel_files, "No CORE report generated" + excel_file_path = sorted(excel_files)[-1] + wb = openpyxl.load_workbook(excel_file_path) + + # Issue Summary empty + summary_rows = [row for row in wb["Issue Summary"].iter_rows(values_only=True)][ + 1: + ] + summary_rows = [r for r in summary_rows if any(r)] + assert summary_rows == [] + + # Issue Details empty + details_rows = [ + row for row in wb[issue_datails_sheet].iter_rows(values_only=True) + ][1:] + details_rows = [r for r in details_rows if any(r)] + assert details_rows == [] + + # Rules Report has success row + rules_rows = [ + row for row in wb[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_rows = [r for r in rules_rows if any(r)] + assert len(rules_rows) == 1 + assert rules_rows[0][0] == "CDISC.SENDIG.105" + assert rules_rows[0][-1] == "SUCCESS" + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py index 241d26391..996352959 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py @@ -1,80 +1,79 @@ -import subprocess -import os -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - dataset_details_sheet, - rules_report_sheet, - issue_sheet_coreid_column, - dataset_sheet_dataset_column, - rules_sheet_rule_status_column, -) - -""" These tests utilizes a dataset -to validate successful working CG0019. The dataset -have both positive and negative cases, -including the supp and split domains""" - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -def test_CG0019(): - command = ( - f"{get_python_executable()} -m core test -s sdtmig -v 3.4 -r " - + os.path.join("tests", "resources", "CoreIssue747", "Rule_underscores.json") - + " -dp " - + os.path.join("tests", "resources", "CoreIssue747", "Datasets.json") - + " -dxp " - + os.path.join( - "tests", "resources", "CoreIssue747", "define_CG0019_split_and_supp.xml" - ) - ) - - # Construct the command - command = command.split(" ") - - # Run the command in the terminal - process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - file_name = stdout.decode().strip().split(": ")[1] - # Open the Excel file - workbook = openpyxl.load_workbook(file_name) - - # Go to the "Issue Details" sheet - issues_sheet = workbook[issue_datails_sheet] - dataset_sheet = workbook[dataset_details_sheet] - rules_sheet = workbook[rules_report_sheet] - - coreid_column = issues_sheet[issue_sheet_coreid_column] - dataset_column = dataset_sheet[dataset_sheet_dataset_column] - rule_status_column = rules_sheet[rules_sheet_rule_status_column] - - coreid_values = [cell.value for cell in coreid_column[1:]] - dataset_values = [cell.value for cell in dataset_column[1:]] - rule_status_column_values = [cell.value for cell in rule_status_column[1:]] - - # Remove None values using list comprehension - coreid_values = [value for value in coreid_values if value is not None] - dataset_values = [value for value in dataset_values if value is not None] - rule_status_column_values = [ - value for value in rule_status_column_values if value is not None - ] - - # Perform the assertion - assert process.returncode == 0, f"Process failed with error: {stderr.decode()}" - assert dataset_values[0] == "ECAA" - assert dataset_values[1] == "ECBB" - assert dataset_values[2] == "SUPPEC" - assert len(dataset_values) == 3 - - assert len(coreid_values) == 4 - - assert rule_status_column_values[0] == "SUCCESS" - - # Close the workbook - workbook.close() - - # Delete the file - os.remove(file_name) +import subprocess +import os +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + dataset_details_sheet, + rules_report_sheet, + issue_sheet_coreid_column, + dataset_sheet_dataset_column, + rules_sheet_rule_status_column, +) + +""" These tests utilizes a dataset +to validate successful working CG0019. The dataset +have both positive and negative cases, +including the supp and split domains""" + + +def test_CG0019(): + command = ( + f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + + os.path.join("tests", "resources", "CoreIssue747", "Rule_underscores.json") + + " -dp " + + os.path.join("tests", "resources", "CoreIssue747", "Datasets.json") + + " -dxp " + + os.path.join( + "tests", "resources", "CoreIssue747", "define_CG0019_split_and_supp.xml" + ) + ) + + # Construct the command + command = command.split(" ") + + # Run the command in the terminal + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + file_name = stdout.decode().strip().split(": ")[1] + # Open the Excel file + workbook = openpyxl.load_workbook(file_name) + + # Go to the "Issue Details" sheet + issues_sheet = workbook[issue_datails_sheet] + dataset_sheet = workbook[dataset_details_sheet] + rules_sheet = workbook[rules_report_sheet] + + coreid_column = issues_sheet[issue_sheet_coreid_column] + dataset_column = dataset_sheet[dataset_sheet_dataset_column] + rule_status_column = rules_sheet[rules_sheet_rule_status_column] + + coreid_values = [cell.value for cell in coreid_column[1:]] + dataset_values = [cell.value for cell in dataset_column[1:]] + rule_status_column_values = [cell.value for cell in rule_status_column[1:]] + + # Remove None values using list comprehension + coreid_values = [value for value in coreid_values if value is not None] + dataset_values = [value for value in dataset_values if value is not None] + rule_status_column_values = [ + value for value in rule_status_column_values if value is not None + ] + + # Perform the assertion + assert process.returncode == 0, f"Process failed with error: {stderr.decode()}" + assert dataset_values[0] == "ECAA" + assert dataset_values[1] == "ECBB" + assert dataset_values[2] == "SUPPEC" + assert len(dataset_values) == 3 + + assert len(coreid_values) == 4 + + assert rule_status_column_values[0] == "ISSUE REPORTED" + + # Close the workbook + workbook.close() + + # Delete the file + os.remove(file_name) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py index 9e8f8ce27..2a66058c3 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py @@ -1,162 +1,161 @@ -import os -import subprocess -import unittest -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - issue_datails_sheet, - rules_report_sheet, - issue_sheet_record_column, - issue_sheet_variable_column, - issue_sheet_values_column, -) - - -@pytest.mark.skip(reason="The test is obsolete and requires modernization.") -class TestColumnConsistsOfDelimitedCodelists(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "send", - "-v", - "1-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue890", - "unit-test-coreid-SENDIG282-positive.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue890", "Rule.yml"), - "-ct", - "sendct-2025-09-26", - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - # Perform the assertion - # Ensure only two negative values are caught - assert rules_values[0][0] == "CDISC.SENDIG.SEND282" - assert len(record_values) == 0 - assert len(variables_values) == 0 - assert len(values_column_values) == 0 - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - def test_negaive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "send", - "-v", - "1-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue890", - "unit-test-coreid-SENDIG282-negative.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue890", "Rule.yml"), - "-ct", - "sendct-2025-09-26", - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # --- Dataset Details --- - dataset_sheet = workbook["Dataset Details"] - dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] - dataset_values = [row for row in dataset_values if any(row)] - assert len(dataset_values) > 0 - assert dataset_values[0][0] == "pp.xpt" - assert dataset_values[0][1] == "Pharmacokinetics Parameters" - assert dataset_values[0][-1] == 4 - - # --- Issue Summary --- - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [ - row for row in issue_summary_sheet.iter_rows(values_only=True) - ][1:] - summary_values = [row for row in summary_values if any(row)] - assert len(summary_values) > 0 - assert summary_values[0][0] == "pp.xpt" - assert summary_values[0][1] == "CDISC.SENDIG.SEND282" - assert summary_values[0][3] == 2 - - # --- Issue Details --- - issue_details_sheet = workbook["Issue Details"] - details_values = [ - row for row in issue_details_sheet.iter_rows(values_only=True) - ][1:] - details_values = [row for row in details_values if any(row)] - assert all(row[0] == "CDISC.SENDIG.SEND282" for row in details_values) - assert len(details_values) == 2 - - # --- Rules Report --- - rules_values = [ - row for row in workbook["Rules Report"].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - assert len(rules_values) > 0 - assert rules_values[0][0] == "CDISC.SENDIG.SEND282" - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - -# if __name__ == "__main__": -# unittest.main() +import os +import subprocess +import unittest +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + issue_datails_sheet, + rules_report_sheet, + issue_sheet_record_column, + issue_sheet_variable_column, + issue_sheet_values_column, +) + + +class TestColumnConsistsOfDelimitedCodelists(unittest.TestCase): + def test_positive_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sendig", + "-v", + "3-1", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue890", + "unit-test-coreid-SENDIG282-positive.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue890", "Rule.yml"), + "-ct", + "sendct-2025-09-26", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + # Perform the assertion + # Ensure only two negative values are caught + assert rules_values[0][0] == "CDISC.SENDIG.SEND282" + assert len(record_values) == 0 + assert len(variables_values) == 0 + assert len(values_column_values) == 0 + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + def test_negative_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sendig", + "-v", + "3-1", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue890", + "unit-test-coreid-SENDIG282-negative.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue890", "Rule.yml"), + "-ct", + "sendct-2025-09-26", + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # --- Dataset Details --- + dataset_sheet = workbook["Dataset Details"] + dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] + dataset_values = [row for row in dataset_values if any(row)] + assert len(dataset_values) > 0 + assert dataset_values[0][0] == "PP" + assert dataset_values[0][1] == "Pharmacokinetics Parameters" + assert dataset_values[0][-1] == 4 + + # --- Issue Summary --- + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [ + row for row in issue_summary_sheet.iter_rows(values_only=True) + ][1:] + summary_values = [row for row in summary_values if any(row)] + assert len(summary_values) > 0 + assert summary_values[0][0] == "PP" + assert summary_values[0][1] == "CDISC.SENDIG.SEND282" + assert summary_values[0][3] == 2 + + # --- Issue Details --- + issue_details_sheet = workbook["Issue Details"] + details_values = [ + row for row in issue_details_sheet.iter_rows(values_only=True) + ][1:] + details_values = [row for row in details_values if any(row)] + assert all(row[0] == "CDISC.SENDIG.SEND282" for row in details_values) + assert len(details_values) == 2 + + # --- Rules Report --- + rules_values = [ + row for row in workbook["Rules Report"].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + assert len(rules_values) > 0 + assert rules_values[0][0] == "CDISC.SENDIG.SEND282" + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + +# if __name__ == "__main__": +# unittest.main() diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py index 67758617e..8ea9ec399 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py @@ -1,159 +1,159 @@ -import os -import subprocess -import unittest -import openpyxl -import pytest -from conftest import get_python_executable -from QARegressionTests.globals import ( - entity_details_sheet, - issue_datails_sheet, - rules_report_sheet, - issue_sheet_record_column, - issue_sheet_variable_column, - issue_sheet_values_column, -) - - -@pytest.mark.regression -class TestGetXHTMLErrors(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue897", - "Positive_datasets.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue897", "Rule.yml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # Go to the "Issue Details" sheet - sheet = workbook[issue_datails_sheet] - - record_column = sheet[issue_sheet_record_column] - variables_column = sheet[issue_sheet_variable_column] - values_column = sheet[issue_sheet_values_column] - - record_values = [cell.value for cell in record_column[1:]] - variables_values = [cell.value for cell in variables_column[1:]] - values_column_values = [cell.value for cell in values_column[1:]] - - # Remove None values using list comprehension - record_values = [value for value in record_values if value is not None] - variables_values = [value for value in variables_values if value is not None] - values_column_values = [ - value for value in values_column_values if value is not None - ] - rules_values = [ - row for row in workbook[rules_report_sheet].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - # Perform the assertion - # Ensure only two negative values are caught - assert rules_values[0][0] == "CORE-000409" - assert rules_values[0][-1] == "SUCCESS" - assert len(record_values) == 0 - assert len(variables_values) == 0 - assert len(values_column_values) == 0 - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - def test_negative_dataset(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "usdm", - "-v", - "4-0", - "-dp", - os.path.join( - "tests", - "resources", - "CoreIssue897", - "Negative_datasets.json", - ), - "-lr", - os.path.join("tests", "resources", "CoreIssue897", "Rule.yml"), - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) - - # --- Dataset Details --- - dataset_sheet = workbook[entity_details_sheet] - dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] - dataset_values = [row for row in dataset_values if any(row)] - assert len(dataset_values) > 0 - assert dataset_values[0][0] == "NarrativeContentItem" - assert dataset_values[0][-1] == 170 - - # --- Issue Summary --- - issue_summary_sheet = workbook["Issue Summary"] - summary_values = [ - row for row in issue_summary_sheet.iter_rows(values_only=True) - ][1:] - summary_values = [row for row in summary_values if any(row)] - assert len(summary_values) > 0 - assert summary_values[0][1] == "CORE-000409" - assert summary_values[0][4] == 4 - - # --- Issue Details --- - issue_details_sheet = workbook["Issue Details"] - details_values = [ - row for row in issue_details_sheet.iter_rows(values_only=True) - ][1:] - details_values = [row for row in details_values if any(row)] - assert all(row[0] == "CORE-000409" for row in details_values) - assert len(details_values) == 4 - - # --- Rules Report --- - rules_values = [ - row for row in workbook["Rules Report"].iter_rows(values_only=True) - ][1:] - rules_values = [row for row in rules_values if any(row)] - assert len(rules_values) > 0 - assert rules_values[0][0] == "CORE-000409" - assert rules_values[0][-1] == "ISSUE REPORTED" - - if os.path.exists(excel_file_path): - os.remove(excel_file_path) - - -# if __name__ == "__main__": -# unittest.main() +import os +import subprocess +import unittest +import openpyxl +import pytest +from conftest import get_python_executable +from QARegressionTests.globals import ( + entity_details_sheet, + issue_datails_sheet, + rules_report_sheet, + issue_sheet_record_column, + issue_sheet_variable_column, + issue_sheet_values_column, +) + + +@pytest.mark.regression +class TestGetXHTMLErrors(unittest.TestCase): + def test_positive_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue897", + "Positive_datasets.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue897", "Rule.yml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # Go to the "Issue Details" sheet + sheet = workbook[issue_datails_sheet] + + record_column = sheet[issue_sheet_record_column] + variables_column = sheet[issue_sheet_variable_column] + values_column = sheet[issue_sheet_values_column] + + record_values = [cell.value for cell in record_column[1:]] + variables_values = [cell.value for cell in variables_column[1:]] + values_column_values = [cell.value for cell in values_column[1:]] + + # Remove None values using list comprehension + record_values = [value for value in record_values if value is not None] + variables_values = [value for value in variables_values if value is not None] + values_column_values = [ + value for value in values_column_values if value is not None + ] + rules_values = [ + row for row in workbook[rules_report_sheet].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + # Perform the assertion + # Ensure only two negative values are caught + assert rules_values[0][0] == "CORE-000409" + assert rules_values[0][-1] == "SUCCESS" + assert len(record_values) == 0 + assert len(variables_values) == 0 + assert len(values_column_values) == 0 + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + def test_negative_dataset(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "usdm", + "-v", + "4-0", + "-dp", + os.path.join( + "tests", + "resources", + "CoreIssue897", + "Negative_datasets.json", + ), + "-lr", + os.path.join("tests", "resources", "CoreIssue897", "Rule.yml"), + ] + subprocess.run(command, check=True) + + # Get the latest created Excel file + files = os.listdir() + excel_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".xlsx") + ] + excel_file_path = sorted(excel_files)[-1] + # Open the Excel file + workbook = openpyxl.load_workbook(excel_file_path) + + # --- Dataset Details --- + dataset_sheet = workbook[entity_details_sheet] + dataset_values = [row for row in dataset_sheet.iter_rows(values_only=True)][1:] + dataset_values = [row for row in dataset_values if any(row)] + assert len(dataset_values) > 0 + assert dataset_values[0][0] == "NarrativeContentItem" + assert dataset_values[0][-1] == 170 + + # --- Issue Summary --- + issue_summary_sheet = workbook["Issue Summary"] + summary_values = [ + row for row in issue_summary_sheet.iter_rows(values_only=True) + ][1:] + summary_values = [row for row in summary_values if any(row)] + assert len(summary_values) > 0 + assert summary_values[0][1] == "CORE-000409" + assert summary_values[0][4] == 4 + + # --- Issue Details --- + issue_details_sheet = workbook["Issue Details"] + details_values = [ + row for row in issue_details_sheet.iter_rows(values_only=True) + ][1:] + details_values = [row for row in details_values if any(row)] + assert all(row[0] == "CORE-000409" for row in details_values) + assert len(details_values) == 4 + + # --- Rules Report --- + rules_values = [ + row for row in workbook["Rules Report"].iter_rows(values_only=True) + ][1:] + rules_values = [row for row in rules_values if any(row)] + assert len(rules_values) > 0 + assert rules_values[0][0] == "CORE-000409" + assert rules_values[0][-1] == "ISSUE REPORTED" + + if os.path.exists(excel_file_path): + os.remove(excel_file_path) + + +# if __name__ == "__main__": +# unittest.main() diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py index 1bdda31f0..97db38903 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py @@ -1,67 +1,67 @@ -import os -import subprocess - -import pytest -import json -from conftest import get_python_executable - - -@pytest.mark.regression -class TestCoreIssue984: - def test_define_subversion_ignored(self): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-r", - "CORE-000007", - "-v", - "3.4", - "-dp", - os.path.join( - "tests", - "resources", - "test_dataset.json", - ), - "-dxp", - os.path.join("tests", "resources", "CoreIssue984", "define.xml"), - "--output-format", - "json", - "-ps", - "1", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path)) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - assert json_report["Conformance_Details"]["Standard"] == "SDTMIG" - assert json_report["Conformance_Details"]["Define_XML_Version"] == "2.1.5" - - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess + +import pytest +import json +from conftest import get_python_executable + + +@pytest.mark.regression +class TestCoreIssue984: + def test_define_subversion_ignored(self): + # Run the command in the terminal + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-r", + "CORE-000007", + "-v", + "3.4", + "-dp", + os.path.join( + "tests", + "resources", + "test_dataset.json", + ), + "-dxp", + os.path.join("tests", "resources", "CoreIssue984", "define.xml"), + "--output-format", + "json", + "-ps", + "1", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path)) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + assert json_report["Conformance_Details"]["Standard"] == "SDTMIG" + assert json_report["Conformance_Details"]["Define_XML_Version"] == "2.1.5" + + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_core/test_utils.py b/tests/QARegressionTests/test_core/test_utils.py index 89f9fabd6..900ac386e 100644 --- a/tests/QARegressionTests/test_core/test_utils.py +++ b/tests/QARegressionTests/test_core/test_utils.py @@ -1,33 +1,33 @@ -import subprocess -from platform import system -from os import listdir, remove - - -def run_command(args, shell): - try: - completed_process = subprocess.run( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - check=True, - encoding="utf8", - # test_test_command and windows seem to be happy with shell=True - # test_validate on linux needs shell=False - shell=shell or system() == "Windows", - ) - return ( - completed_process.returncode, - completed_process.stdout.lower(), - completed_process.stderr.lower(), - ) - except subprocess.CalledProcessError as e: - return e.returncode, e.stdout.lower(), e.stderr.lower() - - -def tearDown(): - for file_name in listdir("."): - if file_name not in ("host.json", "local.settings.json") and ( - file_name.endswith(".xlsx") or file_name.endswith(".json") - ): - remove(file_name) +import subprocess +from platform import system +from os import listdir, remove + + +def run_command(args, shell): + try: + completed_process = subprocess.run( + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=True, + encoding="utf8", + # test_test_command and windows seem to be happy with shell=True + # test_validate on linux needs shell=False + shell=shell or system() == "Windows", + ) + return ( + completed_process.returncode, + completed_process.stdout.lower(), + completed_process.stderr.lower(), + ) + except subprocess.CalledProcessError as e: + return e.returncode, e.stdout.lower(), e.stderr.lower() + + +def tearDown(): + for file_name in listdir("."): + if file_name not in ("host.json", "local.settings.json") and ( + file_name.endswith(".xlsx") or file_name.endswith(".json") + ): + remove(file_name) diff --git a/tests/QARegressionTests/test_core/test_validate.py b/tests/QARegressionTests/test_core/test_validate.py index b3e6229bc..41c1f4e90 100644 --- a/tests/QARegressionTests/test_core/test_validate.py +++ b/tests/QARegressionTests/test_core/test_validate.py @@ -163,7 +163,7 @@ def test_validate_all_options(self): "--meddra", os.path.join("tests", "resources", "dictionaries", "meddra"), "-r", - os.path.join("tests", "resources", "Rule-CG0027.json"), + "CORE-000237", "-lr", os.path.join("tests", "resources", "CG0272.yml"), "-p", @@ -264,7 +264,10 @@ def test_validate_less_than_minimum_options(self): args = ["python", "core.py", "validate", "-s", "sdtmig"] exit_code, stdout, stderr = run_command(args, False) self.assertNotEqual(exit_code, 0) - self.assertIn("error: missing option", stderr) + self.assertIn( + "\n\nerror: missing option '-v' / '--version'.\n", + stderr, + ) def test_validate_output_format_json(self): args = [ @@ -360,12 +363,14 @@ def test_validate_with_log_level_info(self): os.path.join("tests", "resources", "test_dataset.xpt"), "-l", "info", + "-r", + "CORE-000237" ] exit_code, stdout, stderr = run_command(args, False) self.assertEqual(exit_code, 0) self.assertFalse(self.error_keyword in stdout) - self.assertIn("warning", stderr) + def test_validate_with_log_level_error(self): args = [ From bf76f49276f71653606ef08bcf608b954c382f1f Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Tue, 9 Jun 2026 15:49:08 -0500 Subject: [PATCH 2/7] update formatting --- tests/QARegressionTests/test_Issues/test_CoreIssue1487.py | 5 +++-- tests/QARegressionTests/test_Issues/test_CoreIssue164.py | 1 - tests/QARegressionTests/test_Issues/test_CoreIssue208.py | 1 - tests/QARegressionTests/test_Issues/test_CoreIssue363.py | 4 +++- tests/QARegressionTests/test_Issues/test_CoreIssue576.py | 1 - tests/QARegressionTests/test_Issues/test_CoreIssue747.py | 1 - tests/QARegressionTests/test_Issues/test_CoreIssue890.py | 1 - tests/QARegressionTests/test_core/test_validate.py | 3 +-- 8 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py index 700b0351e..476a60e81 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py @@ -2,7 +2,6 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable @@ -47,7 +46,9 @@ def test_positive_dataset(self): assert ( target_row[4] in target_row[4] ), "Expected error message for CORE-000354 not found." - assert target_row[5] == "ISSUE REPORTED", "CORE-000354 status should be ISSUE REPORTED." + assert ( + target_row[5] == "ISSUE REPORTED" + ), "CORE-000354 status should be ISSUE REPORTED." if os.path.exists(excel_file_path): os.remove(excel_file_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py index 1624b6a22..9d5f0512b 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py @@ -1,7 +1,6 @@ import subprocess import os import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py index acd8e243a..039a51810 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py @@ -5,7 +5,6 @@ import pytest from conftest import get_python_executable - """This regression test is for automating the validation of acceptancce criteria which is "For any variables that come from datasets and appear in the results, the variables should have the same case as the variable names in the dataset". diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py index 9ac58fda9..f0baba5d9 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py @@ -5,7 +5,9 @@ import pytest -@pytest.mark.skip(reason="Requires API key to run. Run Locally with valid API key to test.") +@pytest.mark.skip( + reason="Requires API key to run. Run Locally with valid API key to test." +) class TestCoreIssue363(unittest.TestCase): def test_post_request(self): # URL to send the POST request to diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py index 9882b6539..333351d02 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py @@ -1,7 +1,6 @@ import subprocess import os import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py index 996352959..75ce4d600 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py @@ -1,7 +1,6 @@ import subprocess import os import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py index 2a66058c3..e87d3e763 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py @@ -2,7 +2,6 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_core/test_validate.py b/tests/QARegressionTests/test_core/test_validate.py index 41c1f4e90..6fb759ec5 100644 --- a/tests/QARegressionTests/test_core/test_validate.py +++ b/tests/QARegressionTests/test_core/test_validate.py @@ -364,14 +364,13 @@ def test_validate_with_log_level_info(self): "-l", "info", "-r", - "CORE-000237" + "CORE-000237", ] exit_code, stdout, stderr = run_command(args, False) self.assertEqual(exit_code, 0) self.assertFalse(self.error_keyword in stdout) - def test_validate_with_log_level_error(self): args = [ "python", From 533325a3e7eb6de40ec1ed1889053eb69f0d6a9c Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Wed, 8 Jul 2026 18:03:06 -0500 Subject: [PATCH 3/7] updates --- .../test_Issues/test_CoreIssue1023.py | 1 - .../test_Issues/test_CoreIssue1204.py | 1 - .../test_Issues/test_CoreIssue1248.py | 1 - .../test_Issues/test_CoreIssue1316.py | 1 - .../test_Issues/test_CoreIssue1326.py | 1 - .../test_Issues/test_CoreIssue1332.py | 1 - .../test_Issues/test_CoreIssue1345.py | 4 +- .../test_Issues/test_CoreIssue1348.py | 1 - .../test_Issues/test_CoreIssue1387.py | 6 -- .../test_Issues/test_CoreIssue1421.py | 1 - .../test_Issues/test_CoreIssue1442.py | 1 - .../test_Issues/test_CoreIssue1487.py | 55 ++++++++----------- .../test_Issues/test_CoreIssue1501.py | 1 - .../test_Issues/test_CoreIssue1558.py | 1 - .../test_Issues/test_CoreIssue1699.py | 1 - .../test_Issues/test_CoreIssue1718.py | 1 - .../test_Issues/test_CoreIssue208.py | 1 - .../test_Issues/test_CoreIssue324.py | 2 - .../test_Issues/test_CoreIssue357.py | 1 - .../test_Issues/test_CoreIssue390.py | 1 - .../test_Issues/test_CoreIssue397.py | 1 - .../test_Issues/test_CoreIssue715.py | 1 - .../test_Issues/test_CoreIssue897.py | 1 - .../test_Issues/test_CoreIssue984.py | 1 - 24 files changed, 25 insertions(+), 62 deletions(-) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py index cb8118f64..6289b126e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1023: def test_dataset_utf8(self): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py index a1bd1045a..303ca2299 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py @@ -3,7 +3,6 @@ from conftest import get_python_executable -@pytest.mark.regression def test_non_existing_dataset_shows_helpful_error(): """Test that the engine displays a helpful error message if dataset files are not found""" command = [ diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py index ab44096d3..68e08ea9c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1248: @pytest.mark.parametrize( "command,rules_report,num_issues", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py index 0fdfd78dd..190944a18 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py @@ -7,7 +7,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1316(unittest.TestCase): def test_new_rule(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py index 3f55855cc..a0fbc4f78 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py @@ -14,7 +14,6 @@ ) -@pytest.mark.regression class TestPrefTerm(unittest.TestCase): def test_positive_dataset(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py index d121a8e75..22c5fa901 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py @@ -9,7 +9,6 @@ ) -@pytest.mark.regression def test_ap_domain_should_be_correctly_substituted(): # Run the command in the terminal command = [ diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py index 54fb0c436..2087fdcf0 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py @@ -36,9 +36,8 @@ def generate_report(): "tests", "resources", "CoreIssue1345", + "CG0019.yml" ), - "-r", - "CDISC.SDTMIG.CG0019", "-l", "error", "-ps", @@ -59,7 +58,6 @@ def generate_report(): return json_report_path, json_report -@pytest.mark.regression class TestCoreIssue1345: def test_engine_correctly_merges_datasets_and_flags_row_uniqueness_issues( self, generate_report diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py index 484f64833..9eebec847 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py @@ -177,7 +177,6 @@ ] -@pytest.mark.regression class TestCoreIssue1348: @pytest.mark.parametrize( "rule_name, dataset, issue_summary, details", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py index 24dd97a76..7ebe5018d 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression def test_multiple_xlsx_files_shows_helpful_error(): """Test that multiple XLSX files show a helpful error message about single file limitation""" with tempfile.TemporaryDirectory() as temp_dir: @@ -54,7 +53,6 @@ def test_multiple_xlsx_files_shows_helpful_error(): ), "Expected explanation of XLSX limitation" -@pytest.mark.regression def test_folder_with_xlsx_files_works_with_excel_service(): """Test that a folder with a single XLSX file now works with ExcelDataService""" with tempfile.TemporaryDirectory() as temp_dir: @@ -95,7 +93,6 @@ def test_folder_with_xlsx_files_works_with_excel_service(): ), "XLSX files should now be recognized as valid" -@pytest.mark.regression def test_folder_with_unsupported_formats_shows_helpful_error(): """Test that folders with truly unsupported formats (like PDF) show helpful error messages""" with tempfile.TemporaryDirectory() as temp_dir: @@ -142,7 +139,6 @@ def test_folder_with_unsupported_formats_shows_helpful_error(): ), "Expected format guidance in error message" -@pytest.mark.regression def test_empty_folder_shows_helpful_error(): with tempfile.TemporaryDirectory() as temp_dir: command = [ @@ -173,7 +169,6 @@ def test_empty_folder_shows_helpful_error(): ), "Expected helpful error message for empty folder" -@pytest.mark.regression def test_valid_xpt_files_work_normally(): command = [ get_python_executable(), @@ -200,7 +195,6 @@ def test_valid_xpt_files_work_normally(): ), "Should find valid XPT file" -@pytest.mark.regression def test_mixed_folder_processes_valid_files(): with tempfile.TemporaryDirectory() as temp_dir: valid_xpt = os.path.join("tests", "resources", "test_dataset.xpt") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py index 02ed2aae6..54e1d9385 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py @@ -12,7 +12,6 @@ ) -@pytest.mark.regression def test_validate_define_xml_against_lib_metadata(): command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py index a1ae1a304..4fed91470 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py @@ -5,7 +5,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1442: @pytest.mark.parametrize( "command", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py index 476a60e81..fbcc91f94 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py @@ -1,13 +1,12 @@ import os import subprocess import unittest -import openpyxl from conftest import get_python_executable class TestCoreIssue1487(unittest.TestCase): - def test_positive_dataset(self): - # Run the command in the terminal + "The updated engine throws an error for unsupported version, which is expected behavior." + def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): command = [ f"{get_python_executable()}", "-m", @@ -16,39 +15,33 @@ def test_positive_dataset(self): "-s", "sdtmig", "-v", - "3-4", + "5.0", "-d", os.path.join("tests", "resources", "CoreIssue1487"), "-r", "CORE-000354", ] - subprocess.run(command, check=True) - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - excel_file_path = sorted(excel_files)[-1] - # # Open the Excel file - workbook = openpyxl.load_workbook(excel_file_path) + result = subprocess.run( + command, + capture_output=True, + text=True, + ) - assert "Rules Report" in workbook.sheetnames - rules_sheet = workbook["Rules Report"] - target_row = None - for row in rules_sheet.iter_rows(min_row=2, values_only=True): - if row[0] == "CORE-000354": - target_row = row - break - assert target_row, "Rule CORE-000354 not present in 'Rules Report' sheet." - assert ( - target_row[4] in target_row[4] - ), "Expected error message for CORE-000354 not found." - assert ( - target_row[5] == "ISSUE REPORTED" - ), "CORE-000354 status should be ISSUE REPORTED." + error_output = result.stderr + result.stdout - if os.path.exists(excel_file_path): - os.remove(excel_file_path) + assert result.returncode != 0, ( + "Expected validation command to fail, but it succeeded." + ) + + assert "LibraryMetadataNotFoundError" in error_output, ( + f"Expected LibraryMetadataNotFoundError, but got:\n{error_output}" + ) + + assert ( + "No library metadata found for standard 'sdtmig' version '5.0'." + in error_output + ), ( + "Expected missing library metadata error for SDTMIG 5.0, " + f"but got:\n{error_output}" + ) \ No newline at end of file diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index 38c40214f..7b8a95949 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1501(unittest.TestCase): def test_raw_report(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py index eace7e744..5b072e98d 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py @@ -7,7 +7,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1558(unittest.TestCase): def test_raw_report(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py index fcd4fa5cc..6619885da 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1501(unittest.TestCase): def test_raw_report(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py index 7b5ec90fb..071b60c92 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py @@ -5,7 +5,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue1718: def test_max_issues(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py index 039a51810..cff03c2a7 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py @@ -28,7 +28,6 @@ def find_value(json_data, key): return results -@pytest.mark.regression class JSONSearchTestCase(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py index 62e926b38..564a00287 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py @@ -5,8 +5,6 @@ import pytest from conftest import get_python_executable - -@pytest.mark.regression class TerminalCommandTestCase(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py index e1f223509..ac76a4dd2 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py @@ -5,7 +5,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TerminalCommandTestCase(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py index 5fd25cdd4..9458cca2c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py @@ -8,7 +8,6 @@ import pytest -@pytest.mark.regression class TestCoreIssue390(unittest.TestCase): def test_get_define_version_from_define20(self): path_to_define = os.path.join("tests", "resources", "test_defineV20-SDTM.xml") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py index 716bdd123..076e0c03c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py @@ -4,7 +4,6 @@ import pytest -@pytest.mark.regression class TestFlattenList(unittest.TestCase): def test_flatten_list_with_array_column(self): data = pd.DataFrame({"ARRAY_COLUMN": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py index e61ac5839..8be5d580b 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py @@ -13,7 +13,6 @@ ) -@pytest.mark.regression class TestCoreIssue715(unittest.TestCase): def test_positive_dataset(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py index 8ea9ec399..ebc05a620 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py @@ -14,7 +14,6 @@ ) -@pytest.mark.regression class TestGetXHTMLErrors(unittest.TestCase): def test_positive_dataset(self): # Run the command in the terminal diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py index 97db38903..4b75a88c5 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py @@ -6,7 +6,6 @@ from conftest import get_python_executable -@pytest.mark.regression class TestCoreIssue984: def test_define_subversion_ignored(self): # Run the command in the terminal From 48f0179d45b9d5f26424c385d4c239f3aabde8d6 Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Wed, 8 Jul 2026 18:14:45 -0500 Subject: [PATCH 4/7] lint update --- .../test_Issues/test_CoreIssue1023.py | 1 - .../test_Issues/test_CoreIssue1204.py | 1 - .../test_Issues/test_CoreIssue1316.py | 1 - .../test_Issues/test_CoreIssue1326.py | 1 - .../test_Issues/test_CoreIssue1332.py | 1 - .../test_Issues/test_CoreIssue1345.py | 7 +------ .../test_Issues/test_CoreIssue1387.py | 1 - .../test_Issues/test_CoreIssue1421.py | 1 - .../test_Issues/test_CoreIssue1487.py | 15 ++++++++------- .../test_Issues/test_CoreIssue1501.py | 1 - .../test_Issues/test_CoreIssue1558.py | 1 - .../test_Issues/test_CoreIssue1699.py | 1 - .../test_Issues/test_CoreIssue1718.py | 1 - .../test_Issues/test_CoreIssue208.py | 1 - .../test_Issues/test_CoreIssue324.py | 2 +- .../test_Issues/test_CoreIssue357.py | 1 - .../test_Issues/test_CoreIssue390.py | 1 - .../test_Issues/test_CoreIssue397.py | 1 - .../test_Issues/test_CoreIssue715.py | 1 - .../test_Issues/test_CoreIssue897.py | 1 - .../test_Issues/test_CoreIssue984.py | 1 - 21 files changed, 10 insertions(+), 32 deletions(-) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py index 6289b126e..03287ba20 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py @@ -2,7 +2,6 @@ import subprocess import json -import pytest from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py index 303ca2299..1673fbe5f 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1204.py @@ -1,5 +1,4 @@ import subprocess -import pytest from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py index 190944a18..caddbad6e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py @@ -2,7 +2,6 @@ import subprocess import unittest -import pytest import json from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py index a0fbc4f78..aeba5841c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py @@ -2,7 +2,6 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( entity_details_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py index 22c5fa901..d6a111881 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py @@ -1,7 +1,6 @@ import os import subprocess import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py index 2087fdcf0..169dae77e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py @@ -32,12 +32,7 @@ def generate_report(): "CoreIssue1345", ), "-lr", - os.path.join( - "tests", - "resources", - "CoreIssue1345", - "CG0019.yml" - ), + os.path.join("tests", "resources", "CoreIssue1345", "CG0019.yml"), "-l", "error", "-ps", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py index 7ebe5018d..e3b525de3 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py @@ -2,7 +2,6 @@ import os import tempfile import shutil -import pytest from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py index 54e1d9385..409552022 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py @@ -1,7 +1,6 @@ import os import subprocess import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( dataset_details_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py index fbcc91f94..b8ec77a0a 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py @@ -6,6 +6,7 @@ class TestCoreIssue1487(unittest.TestCase): "The updated engine throws an error for unsupported version, which is expected behavior." + def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): command = [ f"{get_python_executable()}", @@ -30,13 +31,13 @@ def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): error_output = result.stderr + result.stdout - assert result.returncode != 0, ( - "Expected validation command to fail, but it succeeded." - ) + assert ( + result.returncode != 0 + ), "Expected validation command to fail, but it succeeded." - assert "LibraryMetadataNotFoundError" in error_output, ( - f"Expected LibraryMetadataNotFoundError, but got:\n{error_output}" - ) + assert ( + "LibraryMetadataNotFoundError" in error_output + ), f"Expected LibraryMetadataNotFoundError, but got:\n{error_output}" assert ( "No library metadata found for standard 'sdtmig' version '5.0'." @@ -44,4 +45,4 @@ def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): ), ( "Expected missing library metadata error for SDTMIG 5.0, " f"but got:\n{error_output}" - ) \ No newline at end of file + ) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index 7b8a95949..fc1951364 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -1,7 +1,6 @@ import os import subprocess import unittest -import pytest import json from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py index 5b072e98d..d8667888c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py @@ -2,7 +2,6 @@ import subprocess import unittest -import pytest import json from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py index 6619885da..83de7c14e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py @@ -1,7 +1,6 @@ import os import subprocess import unittest -import pytest import json from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py index 071b60c92..15f5cad03 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py @@ -1,6 +1,5 @@ import os import subprocess -import pytest import json from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py index cff03c2a7..6e26caa91 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py @@ -2,7 +2,6 @@ import os import subprocess import unittest -import pytest from conftest import get_python_executable """This regression test is for automating the validation of acceptancce criteria diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py index 564a00287..ceab111ea 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py @@ -2,9 +2,9 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable + class TerminalCommandTestCase(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py index ac76a4dd2..43a59a7b8 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py @@ -1,7 +1,6 @@ import os import subprocess import unittest -import pytest from conftest import get_python_executable diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py index 9458cca2c..37a50a587 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py @@ -5,7 +5,6 @@ from cdisc_rules_engine.services.define_xml.define_xml_reader_factory import ( DefineXMLReaderFactory, ) -import pytest class TestCoreIssue390(unittest.TestCase): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py index 076e0c03c..420c9fe20 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py @@ -1,7 +1,6 @@ import unittest import pandas as pd from cdisc_rules_engine.check_operators.helpers import flatten_list -import pytest class TestFlattenList(unittest.TestCase): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py index 8be5d580b..02ead193f 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py @@ -2,7 +2,6 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( issue_datails_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py index ebc05a620..004263d76 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py @@ -2,7 +2,6 @@ import subprocess import unittest import openpyxl -import pytest from conftest import get_python_executable from QARegressionTests.globals import ( entity_details_sheet, diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py index 4b75a88c5..43ca6d2d2 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py @@ -1,7 +1,6 @@ import os import subprocess -import pytest import json from conftest import get_python_executable From bcb5fe349e09ee04ac059eef2ed64206393265b7 Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Wed, 15 Jul 2026 18:01:37 -0500 Subject: [PATCH 5/7] revert --- tests/QARegressionTests/test_core/test_validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/QARegressionTests/test_core/test_validate.py b/tests/QARegressionTests/test_core/test_validate.py index 6fb759ec5..939887814 100644 --- a/tests/QARegressionTests/test_core/test_validate.py +++ b/tests/QARegressionTests/test_core/test_validate.py @@ -163,7 +163,7 @@ def test_validate_all_options(self): "--meddra", os.path.join("tests", "resources", "dictionaries", "meddra"), "-r", - "CORE-000237", + os.path.join("tests", "resources", "Rule-CG0027.json"), "-lr", os.path.join("tests", "resources", "CG0272.yml"), "-p", From 8266f2f729b4bf693d4b2475e8566f1014346ef0 Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Fri, 24 Jul 2026 16:10:38 -0500 Subject: [PATCH 6/7] update tests to add docstrings and use local rules flag --- .../test_Issues/test_CoreIssue1023.py | 113 ++-- .../test_Issues/test_CoreIssue1248.py | 18 +- .../test_Issues/test_CoreIssue1316.py | 3 + .../test_Issues/test_CoreIssue1326.py | 6 + .../test_Issues/test_CoreIssue1332.py | 3 + .../test_Issues/test_CoreIssue1345.py | 8 + .../test_Issues/test_CoreIssue1348.py | 5 + .../test_Issues/test_CoreIssue1387.py | 8 + .../test_Issues/test_CoreIssue1421.py | 4 + .../test_Issues/test_CoreIssue1442.py | 4 + .../test_Issues/test_CoreIssue1487.py | 8 +- .../test_Issues/test_CoreIssue1501.py | 3 + .../test_Issues/test_CoreIssue1558.py | 15 +- .../test_Issues/test_CoreIssue164.py | 6 + .../test_Issues/test_CoreIssue1699.py | 8 +- .../test_Issues/test_CoreIssue1718.py | 7 +- .../test_Issues/test_CoreIssue208.py | 4 + .../test_Issues/test_CoreIssue324.py | 5 + .../test_Issues/test_CoreIssue357.py | 11 +- .../test_Issues/test_CoreIssue363.py | 4 + .../test_Issues/test_CoreIssue390.py | 6 + .../test_Issues/test_CoreIssue397.py | 11 + .../test_Issues/test_CoreIssue576.py | 5 + .../test_Issues/test_CoreIssue715.py | 6 + .../test_Issues/test_CoreIssue747.py | 4 + .../test_Issues/test_CoreIssue890.py | 6 + .../test_Issues/test_CoreIssue897.py | 5 + .../test_Issues/test_CoreIssue984.py | 7 +- .../test_core/test_list_dataset_metadata.py | 6 + .../test_core/test_list_rule_sets.py | 27 +- .../test_core/test_list_rules.py | 11 +- .../test_core/test_validate.py | 132 ++--- .../CoreIssue357/SENDIG_266_rule.json | 3 +- .../resources/library_rules/CORE-000007.json | 376 ++++++++++++ .../resources/library_rules/CORE-000237.json | 504 ++++++++++++++++ .../resources/library_rules/CORE-000354.json | 260 ++++++++ .../resources/library_rules/CORE-000356.json | 558 ++++++++++++++++++ .../resources/library_rules/CORE-000766.json | 116 ++++ 38 files changed, 2118 insertions(+), 168 deletions(-) create mode 100644 tests/resources/library_rules/CORE-000007.json create mode 100644 tests/resources/library_rules/CORE-000237.json create mode 100644 tests/resources/library_rules/CORE-000354.json create mode 100644 tests/resources/library_rules/CORE-000356.json create mode 100644 tests/resources/library_rules/CORE-000766.json diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py index 03287ba20..5b33620ae 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py @@ -1,55 +1,58 @@ -import os -import subprocess -import json - -from conftest import get_python_executable - - -class TestCoreIssue1023: - - def test_dataset_utf8(self): - """Test that the engine correctly handles dataset files with utf8 encoding""" - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-r", - "CORE-000766", - "-e", - "utf8", - "-dp", - os.path.join("tests", "resources", "CoreIssue1023", "ae_utf8.json"), - "--output-format", - "json", - ] - subprocess.run(command, check=True) - - files = os.listdir() - json_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".json") - ] - json_report_path = sorted(json_files)[-1] - # Open the JSON report file - json_report = json.load(open(json_report_path, "r", encoding="utf8")) - assert { - "Conformance_Details", - "Dataset_Details", - "Issue_Summary", - "Issue_Details", - "Rules_Report", - }.issubset(json_report.keys()) - datasets = {x["filename"] for x in json_report["Dataset_Details"]} - assert datasets == {"AE"} - assert ( - json_report["Issue_Details"][0]["USUBJID"] - == f"utf8:{b'\xe1\xbc\x87'.decode('utf8')}" - ) - if os.path.exists(json_report_path): - os.remove(json_report_path) +import os +import subprocess +import json + +from conftest import get_python_executable + + +class TestCoreIssue1023: + + def test_dataset_utf8(self): + """Test that the engine correctly handles dataset files with utf8 + encoding.""" + command = [ + f"{get_python_executable()}", + "-m", + "core", + "validate", + "-s", + "sdtmig", + "-v", + "3.4", + "-lr", + os.path.join( + "tests", "resources", "library_rules", "CORE-000766.json" + ), + "-e", + "utf8", + "-dp", + os.path.join("tests", "resources", "CoreIssue1023", "ae_utf8.json"), + "--output-format", + "json", + ] + subprocess.run(command, check=True) + + files = os.listdir() + json_files = [ + file + for file in files + if file.startswith("CORE-Report-") and file.endswith(".json") + ] + json_report_path = sorted(json_files)[-1] + # Open the JSON report file + json_report = json.load(open(json_report_path, "r", encoding="utf8")) + assert { + "Conformance_Details", + "Dataset_Details", + "Issue_Summary", + "Issue_Details", + "Rules_Report", + }.issubset(json_report.keys()) + datasets = {x["filename"] for x in json_report["Dataset_Details"]} + assert datasets == {"AE"} + assert ( + json_report["Issue_Details"][0]["USUBJID"] + == f"utf8:{b'\xe1\xbc\x87'.decode('utf8')}" + ) + if os.path.exists(json_report_path): + os.remove(json_report_path) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py index 68e08ea9c..355ad9faf 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1248.py @@ -10,7 +10,8 @@ class TestCoreIssue1248: @pytest.mark.parametrize( "command,rules_report,num_issues", [ - # define path provided will lead to successful execution + # Case 1: -dxp explicitly points to the Define-XML file, even though + # it lives in a subfolder unrelated to -dp -> validation succeeds. ( [ f"{get_python_executable()}", @@ -51,7 +52,8 @@ class TestCoreIssue1248: ], 2, ), - # JSON data file and no define.xml in same folder and no -dxp param will provide error + # Case 2: JSON data with no adjacent define.xml and no -dxp given -> + # engine cannot locate a Define-XML and reports an execution error. ( [ f"{get_python_executable()}", @@ -84,7 +86,8 @@ class TestCoreIssue1248: ], 1, ), - # no define.xml in same path as data.xlsx file will provide error + # Case 3: xlsx data with no define.xml alongside it and no -dxp + # given -> same as case 2, execution error for a different format. ( [ f"{get_python_executable()}", @@ -117,8 +120,11 @@ class TestCoreIssue1248: ], 1, ), - # define.xml in same folder as the data.xls and no -dxp provided will provide error until - # in ExcelDataService dataset metadata creation full_path=dataset_name + # Case 4: define.xml sits in the same folder as data.xlsx but -dxp + # is still not given -> still an execution error (known limitation: + # ExcelDataService dataset metadata creation uses + # full_path=dataset_name, so it does not auto-discover a sibling + # define.xml the way -dp with a JSON/xlsx path might elsewhere). ( [ f"{get_python_executable()}", @@ -160,6 +166,8 @@ class TestCoreIssue1248: ], ) def test_define_path_used(self, command, rules_report, num_issues): + """Verify how the engine resolves the Define-XML path (-dxp) relative + to the data path (-dp) across the four scenarios documented above.""" subprocess.run(command, check=True) # Get the latest created report file diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py index caddbad6e..bbaa14840 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1316.py @@ -8,6 +8,9 @@ class TestCoreIssue1316(unittest.TestCase): def test_new_rule(self): + """Verify a new local rule (checking EXROUTE equals a fixed value and + that library_variable_ccode is non-empty) runs against test_dataset.json + and reports exactly 591 issues with an ISSUE REPORTED status.""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py index aeba5841c..6f4e333f8 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1326.py @@ -15,6 +15,9 @@ class TestPrefTerm(unittest.TestCase): def test_positive_dataset(self): + """Verify the USDM study-phase codelist consistency rule (DDF00015) + reports zero issues (SUCCESS) against a StudyVersion dataset where + the study phase code/decode values are consistent with the codelist.""" # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -80,6 +83,9 @@ def test_positive_dataset(self): os.remove(excel_file_path) def test_negative_dataset(self): + """Verify the same DDF00015 rule flags exactly 7 rows where the study + phase code/decode pairs are mismatched or inconsistent with the + Trial Phase Response codelist (C66737).""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py index d6a111881..0072e2341 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1332.py @@ -9,6 +9,9 @@ def test_ap_domain_should_be_correctly_substituted(): + """Verify the "AP--" domain-prefix placeholder in a rule's scope is + correctly substituted with actual AP domains (APMH, APSQ), producing + 4 issues each on MHTERM/SQTERM with value "POMPE DISEASE".""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py index 169dae77e..15735cc11 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1345.py @@ -8,6 +8,8 @@ @pytest.fixture def generate_report(): + """Run CG0019.yml against the DM/SUPPDM/EC/SUPPEC/RELREC dataset + fixture and return the parsed JSON report for reuse by both tests.""" # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -57,6 +59,9 @@ class TestCoreIssue1345: def test_engine_correctly_merges_datasets_and_flags_row_uniqueness_issues( self, generate_report ): + """Verify merging DM with its SUPPDM does not spuriously flag any + DM/SUPPDM rows, while the unrelated EC dataset still produces its + expected 2 issues (merge logic doesn't affect unrelated datasets).""" json_report_path, json_report = generate_report dataset_filenames = { d["filename"].upper() for d in json_report.get("Dataset_Details", []) @@ -118,6 +123,9 @@ def test_engine_correctly_merges_datasets_and_flags_row_uniqueness_issues( def test_engine_correctly_processes_relrec_when_supp_datasets_provided( self, generate_report ): + """Verify RELREC is processed correctly (yielding exactly 2 issues) + when SUPP-- datasets are also present, and that this doesn't disturb + the EC dataset's own 2 issues from the other test.""" json_report_path, json_report = generate_report # Open the JSON report file dataset_filenames = { diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py index 9eebec847..a15d01a0b 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1348.py @@ -190,6 +190,11 @@ class TestCoreIssue1348: def test_not_as_first_condition_same_errors_as_all_condition( self, rule_name, dataset, issue_summary, details ): + """Verify a rule whose Check starts with a `not:` condition + (DDF00015_not.yaml) produces byte-identical Issue_Details/ + Issue_Summary output to the logically-equivalent rule using `all:` + (DDF00015_all.yaml), for both positive and negative datasets -- + confirming condition-evaluation order doesn't change results.""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py index e3b525de3..7b2a4ec28 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1387.py @@ -139,6 +139,8 @@ def test_folder_with_unsupported_formats_shows_helpful_error(): def test_empty_folder_shows_helpful_error(): + """Test that validating an empty -d folder exits non-zero and surfaces a + helpful "No valid dataset files found" message instead of crashing.""" with tempfile.TemporaryDirectory() as temp_dir: command = [ get_python_executable(), @@ -169,6 +171,9 @@ def test_empty_folder_shows_helpful_error(): def test_valid_xpt_files_work_normally(): + """Regression check that a valid .xpt file passed via -dp still validates + normally, confirming the format-detection logic added for XLSX handling + didn't break existing XPT support.""" command = [ get_python_executable(), "-m", @@ -195,6 +200,9 @@ def test_valid_xpt_files_work_normally(): def test_mixed_folder_processes_valid_files(): + """Test that a -d folder containing one valid .xpt file alongside an + empty/invalid .xlsx file still processes the valid dataset without + crashing.""" with tempfile.TemporaryDirectory() as temp_dir: valid_xpt = os.path.join("tests", "resources", "test_dataset.xpt") if os.path.exists(valid_xpt): diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py index 409552022..47f554231 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1421.py @@ -12,6 +12,10 @@ def test_validate_define_xml_against_lib_metadata(): + """Validates that codelist definitions in a Define-XML are checked + against CDISC library CT metadata via rule CDISC.SDTMIG.CG0999: verifies + issue rows/columns, per-dataset record counts (AE, DM, EC, EX, SUPPEC), + and that only DM and SUPPEC datasets report issues.""" command = [ f"{get_python_executable()}", "-m", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py index 4fed91470..f90697833 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1442.py @@ -54,6 +54,10 @@ class TestCoreIssue1442: ], ) def test_positive_dataset(self, command): + """Test that both ways of targeting mixed-format datasets - explicit + -dp paths mixing .xpt/.ndjson/.json, and a -d directory filtered + with -ft json - correctly identify CDISC_Pilot_Study.json as the + "JSON file name" entry in the Conformance Details sheet.""" # Run the command in the terminal subprocess.run(command, check=True) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py index b8ec77a0a..44ed1df7b 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1487.py @@ -8,6 +8,10 @@ class TestCoreIssue1487(unittest.TestCase): "The updated engine throws an error for unsupported version, which is expected behavior." def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): + """Test that requesting an unsupported standard version (sdtmig 5.0, + for which no library metadata is cached) fails fast with + LibraryMetadataNotFoundError, before rules are even loaded. + """ command = [ f"{get_python_executable()}", "-m", @@ -19,8 +23,8 @@ def test_unsupported_sdtmig_5_0_version_returns_metadata_error(self): "5.0", "-d", os.path.join("tests", "resources", "CoreIssue1487"), - "-r", - "CORE-000354", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000354.json"), ] result = subprocess.run( diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py index fc1951364..43201db25 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1501.py @@ -7,6 +7,9 @@ class TestCoreIssue1501(unittest.TestCase): def test_raw_report(self): + """Test that the --raw-report/-rr CLI flag causes the JSON report + output to include an additional "results_data" key alongside the + standard report sections.""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py index d8667888c..1a96049ee 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1558.py @@ -8,6 +8,9 @@ class TestCoreIssue1558(unittest.TestCase): def test_raw_report(self): + """Test that validating a -d directory with multiple dataset files + includes all of them (LB, DM) in Dataset_Details. + """ # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -16,8 +19,8 @@ def test_raw_report(self): "validate", "-s", "sdtmig", - "-r", - "CORE-000007", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000007.json"), "-v", "3.4", "-d", @@ -56,13 +59,17 @@ def test_raw_report(self): os.remove(json_report_path) def test_env_vars_loaded(self): + """Test that --dotenv-path loads PRODUCT/VERSION env vars so -s/-v + don't need to be passed on the CLI (Conformance_Details still reports + SDTMIG / V3.4). + """ command = [ f"{get_python_executable()}", "-m", "core", "validate", - "-r", - "CORE-000007", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000007.json"), "--dotenv-path", os.path.join("tests", "resources", "CoreIssue1558", "test.env"), "-d", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py index 9d5f0512b..f4932ad64 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue164.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue164.py @@ -11,6 +11,9 @@ def test_negative_dataset(): + """Verify the "All Study Day values are integers" rule flags exactly the + two non-integer DSDY values ("0" and "alolo") in the negative dataset, + at the correct rows and variable.""" command = ( f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + os.path.join("tests", "resources", "CoreIssue164", "rule.json") @@ -72,6 +75,9 @@ def test_negative_dataset(): def test_positive_dataset(): + """Verify the same "All Study Day values are integers" rule reports zero + issues against the positive dataset, confirming valid integer DSDY + values are not flagged (companion to test_negative_dataset).""" command = ( f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + os.path.join("tests", "resources", "CoreIssue164", "rule.json") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py index 83de7c14e..39dbd1da8 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1699.py @@ -5,8 +5,12 @@ from conftest import get_python_executable -class TestCoreIssue1501(unittest.TestCase): - def test_raw_report(self): +class TestCoreIssue1699(unittest.TestCase): + def test_cg0314_supp_rule_reports_success(self): + """Validates the CG0314 SUPP (Supplemental Qualifiers) rule via + rule.yml against the CoreIssue1699 dataset, asserting the rule + reports a SUCCESS status. + """ # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py index 15f5cad03..8939d676a 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1718.py @@ -6,6 +6,9 @@ class TestCoreIssue1718: def test_max_issues(self): + """Test that -me/--max-errors-per-rule (per_dataset_flag=true) caps + Issue_Details rows while Issue_Summary still reports the true total. + """ # Run the command in the terminal max_issues = 3 command = [ @@ -23,8 +26,8 @@ def test_max_issues(self): "resources", "CoreIssue1718", ), - "-r", - "CORE-000356", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000356.json"), "-ps", "1", "-of", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py index 6e26caa91..7aabd6f76 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue208.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue208.py @@ -57,6 +57,10 @@ def setUpClass(cls): cls.json_file_path = sorted(json_files)[-1] def test_searched_values_are_capital(self): + """Verifies that USUBJID/SEQ values surfaced in the generated JSON + report retain the same (uppercase) case as the corresponding values + in the source dataset, rather than being altered by report + generation.""" # Read the generated JSON file with open(self.json_file_path, "r") as file: json_data = json.load(file) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py index ceab111ea..b0e2fd4bf 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue324.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue324.py @@ -33,6 +33,11 @@ def setUpClass(cls): cls.excel_file_path = sorted(excel_files)[-1] def test_excel_file_contents(self): + """Regression check for the Excel report's structure/terminology + migration: the legacy "Bundle Details" sheet no longer exists, a + "Conformance Details" sheet is present, and the Issue Summary, + Issue Details, and Rules Report sheets use the "CORE-ID" column + instead of the legacy "RULE-ID" column.""" # Check if the Excel file is created self.assertTrue( os.path.exists(self.excel_file_path), diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py index 43a59a7b8..5e5ece7d0 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py @@ -5,6 +5,13 @@ class TerminalCommandTestCase(unittest.TestCase): + """Regression test for SEND rule 266 (variable not allowed in SDTM/SEND + model) run against a negative dataset. + + Note: this only asserts that a report file is produced, it does not check + the rule's actual findings/issue content. + """ + @classmethod def setUpClass(cls): # Run the command in the terminal @@ -17,7 +24,7 @@ def setUpClass(cls): "sdtmig", "-v", "3.4", - "-r", + "-lr", "tests/resources/CoreIssue357/SENDIG_266_rule.json", "-dp", "tests/resources/CoreIssue357/SENDIG_266_negative_testdata_datasets.json", @@ -34,6 +41,8 @@ def setUpClass(cls): cls.excel_file_path = sorted(excel_files)[-1] def test_command_execution(self): + """Test that validating against the local SEND-266 rule produces an + Excel report file.""" # Check if the Excel file is created self.assertTrue( os.path.exists(self.excel_file_path), diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py index f0baba5d9..dcd668b6c 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue363.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue363.py @@ -10,6 +10,10 @@ ) class TestCoreIssue363(unittest.TestCase): def test_post_request(self): + """Currently skipped (requires an API key). Hits the external Azure + "TestRule" rule-generation API directly over HTTP; not part of the + local CLI-based validation regression suite covered elsewhere in + this directory.""" # URL to send the POST request to url = ( "https://cdisc-library-conformance-rules-generator-dev.azurewebsites.net" diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py index 37a50a587..a0f16dcce 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue390.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue390.py @@ -8,12 +8,18 @@ class TestCoreIssue390(unittest.TestCase): + # NOTE: This is a plain unit test of DefineXMLReaderFactory's version + # detection, not a CLI regression test, even though it lives in this folder. def test_get_define_version_from_define20(self): + """Verify a Define-XML v2.0 file is correctly identified as version + "2.0.0" by DefineXMLReaderFactory.get_define_version().""" path_to_define = os.path.join("tests", "resources", "test_defineV20-SDTM.xml") reader = DefineXMLReaderFactory.from_filename(path_to_define) self.assertEqual(reader.get_define_version(), "2.0.0") def test_get_define_version_from_define21(self): + """Verify a Define-XML v2.1 file is correctly identified as version + "2.1.0" by DefineXMLReaderFactory.get_define_version().""" path_to_define = os.path.join("tests", "resources", "test_defineV21-SDTM.xml") reader = DefineXMLReaderFactory.from_filename(path_to_define) self.assertEqual(reader.get_define_version(), "2.1.0") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py index 420c9fe20..2eec0b52a 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue397.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue397.py @@ -4,19 +4,28 @@ class TestFlattenList(unittest.TestCase): + # NOTE: These are plain unit tests of the flatten_list() helper, not CLI + # regression tests, even though they live in this folder. def test_flatten_list_with_array_column(self): + """Verify flatten_list() flattens a DataFrame column of list values + into a single flat list of scalars.""" data = pd.DataFrame({"ARRAY_COLUMN": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}) result = list(flatten_list(data, ["ARRAY_COLUMN"])) expected = [1, 2, 3] self.assertEqual(result, expected) def test_flatten_list_with_nonexistent_column(self): + """Verify flatten_list() falls back to yielding the column name + itself when the requested column is not present in the DataFrame.""" data = pd.DataFrame({"COLUMN_A": [1, 2, 3], "COLUMN_B": ["A", "B", "C"]}) result = list(flatten_list(data, ["COLUMN_C"])) expected = ["COLUMN_C"] self.assertEqual(result, expected) def test_flatten_list_with_mixed_columns(self): + """Verify flatten_list() handles a mix of array-valued and scalar + columns, flattening the array column while passing through the + scalar column's name unchanged.""" data = pd.DataFrame( { "ARRAY_COLUMN": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], @@ -28,6 +37,8 @@ def test_flatten_list_with_mixed_columns(self): self.assertEqual(result, expected) def test_flatten_list_with_empty_dataframe(self): + """Verify flatten_list() degrades gracefully on an empty DataFrame, + yielding the requested column name instead of raising an error.""" empty_data = pd.DataFrame() result = list(flatten_list(empty_data, ["COLUMN"])) expected = ["COLUMN"] diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py index 333351d02..1a5a90914 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue576.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue576.py @@ -21,6 +21,8 @@ def test_negative_dataset(): + """Verify CG0202 is SKIPPED (0 issues) when the dataset contains no SUPP + domains — only DM is present, so the rule's scope has nothing to check.""" command = ( f"{get_python_executable()} -m core validate -s sdtmig -v 3.3 -lr " + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") @@ -75,6 +77,9 @@ def test_negative_dataset(): def test_positive_dataset(): + """Verify CG0202 runs to SUCCESS (0 issues) when SUPPAE and SUPPEC are + present, confirming the fix for the scope-skip bug that previously + prevented the rule from evaluating datasets with SUPP domains.""" command = ( f"{get_python_executable()} -m core validate -s sdtmig -v 3.3 -lr " + os.path.join("tests", "resources", "CoreIssue576", "Rule_underscores.json") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py index 02ead193f..fc0982367 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue715.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue715.py @@ -14,6 +14,9 @@ class TestCoreIssue715(unittest.TestCase): def test_positive_dataset(self): + """Verify the USDM DDF00081 class-relationship schema-conformance + rule reports SUCCESS (0 issues) against a valid CDISC Pilot Study + USDM JSON dataset.""" # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -75,6 +78,9 @@ def test_positive_dataset(self): os.remove(excel_file_path) def test_negative_dataset(self): + """Verify the same DDF00081 rule flags exactly 15 issue rows, each + reporting "class relationship does not conform", against an + intentionally invalid USDM JSON dataset.""" # Run validation for invalid JSON command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py index 75ce4d600..24baa6e3e 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue747.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue747.py @@ -18,6 +18,10 @@ def test_CG0019(): + """Verify CG0019 reports 4 issues across split EC domains (ECAA, ECBB) + plus SUPPEC, using a Define-XML that describes the split/supp structure, + confirming the rule correctly evaluates split and supplemental datasets + together.""" command = ( f"{get_python_executable()} -m core validate -s sdtmig -v 3.4 -lr " + os.path.join("tests", "resources", "CoreIssue747", "Rule_underscores.json") diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py index e87d3e763..578b3586f 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue890.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue890.py @@ -14,6 +14,9 @@ class TestColumnConsistsOfDelimitedCodelists(unittest.TestCase): def test_positive_dataset(self): + """Verify the SENDIG SEND282 delimited-codelist rule reports SUCCESS + (0 issues) against a positive dataset when validated with an explicit + CT package (sendct-2025-09-26).""" # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -80,6 +83,9 @@ def test_positive_dataset(self): os.remove(excel_file_path) def test_negative_dataset(self): + """Verify the same SEND282 rule flags 2 issues on the PP domain + (4 records) when validated against a negative dataset with the same + explicit CT package.""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py index 004263d76..150f2f08f 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue897.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue897.py @@ -15,6 +15,8 @@ class TestGetXHTMLErrors(unittest.TestCase): def test_positive_dataset(self): + """Verify the get_xhtml_errors operator (DDF00187) reports SUCCESS + (0 issues) when narrative content is valid, well-formed XHTML.""" # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -80,6 +82,9 @@ def test_positive_dataset(self): os.remove(excel_file_path) def test_negative_dataset(self): + """Verify the same rule flags 4 issues on NarrativeContentItem + (out of 170 total entities) when the narrative content contains + malformed/non-conformant XHTML.""" # Run the command in the terminal command = [ f"{get_python_executable()}", diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py index 43ca6d2d2..7224b084a 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue984.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue984.py @@ -7,6 +7,9 @@ class TestCoreIssue984: def test_define_subversion_ignored(self): + """Test that a Define-XML sub-version string doesn't break standard/ + version matching (Conformance_Details still reports SDTMIG / 2.1.5). + """ # Run the command in the terminal command = [ f"{get_python_executable()}", @@ -15,8 +18,8 @@ def test_define_subversion_ignored(self): "validate", "-s", "sdtmig", - "-r", - "CORE-000007", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000007.json"), "-v", "3.4", "-dp", diff --git a/tests/QARegressionTests/test_core/test_list_dataset_metadata.py b/tests/QARegressionTests/test_core/test_list_dataset_metadata.py index e094cf8e5..64cdb09ab 100644 --- a/tests/QARegressionTests/test_core/test_list_dataset_metadata.py +++ b/tests/QARegressionTests/test_core/test_list_dataset_metadata.py @@ -11,6 +11,8 @@ def setUp(self): self.runner = CliRunner() def test_list_dataset_metadata_with_valid_paths(self): + """Test that list-dataset-metadata on a valid -dp file returns the + expected metadata JSON (domain EX, correct filename).""" result = self.runner.invoke( list_dataset_metadata, ["-dp", os.path.join("tests", "resources", "test_dataset.xpt")], @@ -23,6 +25,8 @@ def test_list_dataset_metadata_with_valid_paths(self): self.assertIn(expected_output, result.output) def test_list_dataset_metadata_with_invalid_paths(self): + """Test that a nonexistent -dp path fails (exit code 1) and does not + produce dataset metadata output.""" result = self.runner.invoke(list_dataset_metadata, ["-dp", "invalid_path"]) expected_output = """[ { @@ -34,6 +38,8 @@ def test_list_dataset_metadata_with_invalid_paths(self): self.assertNotIn(expected_output, result.output) def test_list_dataset_metadata_with_no_paths(self): + """Test that omitting the required -dp option fails with a + missing-option error.""" result = self.runner.invoke(list_dataset_metadata) expected_output = """Error: Missing option '-dp' / '--dataset-path'""" self.assertEqual(result.exit_code, 2) diff --git a/tests/QARegressionTests/test_core/test_list_rule_sets.py b/tests/QARegressionTests/test_core/test_list_rule_sets.py index c248e43bc..693f26584 100644 --- a/tests/QARegressionTests/test_core/test_list_rule_sets.py +++ b/tests/QARegressionTests/test_core/test_list_rule_sets.py @@ -9,28 +9,25 @@ class TestListRuleSets(unittest.TestCase): def setUp(self): self.runner = CliRunner() - def test_list_rule_sets_valid_cache_path(self): - result = self.runner.invoke( - list_rule_sets, ["-c", os.path.join("resources", "cache")] - ) - self.assertIn("sdtmig, 3-2", result.output) - self.assertIn("sdtmig, 3-3", result.output) - self.assertIn("sdtmig, 3-4", result.output) - self.assertIn("sendig, 3-1", result.output) + def test_list_rule_sets_lists_expected_standard_version_pairs(self): + """Test that list-rule-sets lists the expected standard/version pairs + both with an explicit valid -c/--cache-path and with -c omitted + entirely (falls back to the default cache path). + """ + expected_pairs = ["sdtmig, 3-2", "sdtmig, 3-3", "sdtmig, 3-4", "sendig, 3-1"] + for args in (["-c", os.path.join("resources", "cache")], []): + result = self.runner.invoke(list_rule_sets, args) + for pair in expected_pairs: + self.assertIn(pair, result.output) def test_list_rule_sets_invalid_cache_path(self): + """Test that an invalid/incomplete --cache-path (a dir with no rule + cache files) fails with a non-zero exit code.""" result = self.runner.invoke( list_rule_sets, ["--cache-path", os.path.join("resources")] ) self.assertNotEqual(result.exit_code, 0) - def test_list_rule_sets_no_cache_path(self): - result = self.runner.invoke(list_rule_sets, []) - self.assertIn("sdtmig, 3-2", result.output) - self.assertIn("sdtmig, 3-3", result.output) - self.assertIn("sdtmig, 3-4", result.output) - self.assertIn("sendig, 3-1", result.output) - def tearDown(self): tearDown() diff --git a/tests/QARegressionTests/test_core/test_list_rules.py b/tests/QARegressionTests/test_core/test_list_rules.py index 06002b65c..8586935a5 100644 --- a/tests/QARegressionTests/test_core/test_list_rules.py +++ b/tests/QARegressionTests/test_core/test_list_rules.py @@ -12,6 +12,7 @@ def setUp(self): self.runner = CliRunner() def test_list_rules_all_options_provided(self): + """Test that list-rules with -c, -s and -v all provided succeeds.""" result = self.runner.invoke( list_rules, ["-c", os.path.join("resources", "cache"), "-s", "sdtmig", "-v", "3.4"], @@ -19,22 +20,24 @@ def test_list_rules_all_options_provided(self): self.assertEqual(result.exit_code, 0) def test_list_rules_standard_option_provided(self): + """Test that list-rules with only -s provided succeeds.""" result = self.runner.invoke(list_rules, ["-s", "sdtmig"]) self.assertEqual(result.exit_code, 0) def test_list_rules_version_option_provided(self): + """Test that list-rules with only -v provided succeeds.""" result = self.runner.invoke(list_rules, ["-v", "3.4"]) self.assertEqual(result.exit_code, 0) def test_list_rules_no_option_provided(self): + """Test that list-rules with no options at all succeeds (lists all + rules). + """ result = self.runner.invoke(list_rules) self.assertEqual(result.exit_code, 0) - def test_list_rules_required_options_provided(self): - result = self.runner.invoke(list_rules, ["-s", "sdtmig", "-v", "3.4"]) - self.assertEqual(result.exit_code, 0) - def test_list_rules_output_format(self): + """Test that list-rules output is a JSON list of rule dicts.""" result = self.runner.invoke(list_rules) output = json.loads(result.output) self.assertIsInstance(output, list) diff --git a/tests/QARegressionTests/test_core/test_validate.py b/tests/QARegressionTests/test_core/test_validate.py index 939887814..28f4714f4 100644 --- a/tests/QARegressionTests/test_core/test_validate.py +++ b/tests/QARegressionTests/test_core/test_validate.py @@ -35,6 +35,7 @@ def check_issue_summary_tab_empty(self): return True def test_validate_required_s_option_missing(self): + """Test that omitting the required -s/--standard option fails.""" args = [ "python", "core.py", @@ -49,24 +50,8 @@ def test_validate_required_s_option_missing(self): self.assertNotEqual(exit_code, 0) self.assertNotEqual(stderr, "", "Error Not raised for invalid command") - def test_validate_required_s_option_present(self): - args = [ - "python", - "core.py", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-dp", - os.path.join("tests", "resources", "test_dataset.xpt"), - ] - exit_code, stdout, stderr = run_command(args, False) - - self.assertEqual(exit_code, 0) - self.assertFalse(self.error_keyword in stdout) - def test_validate_required_v_option_missing(self): + """Test that omitting the required -v/--version option fails.""" args = [ "python", "core.py", @@ -81,7 +66,10 @@ def test_validate_required_v_option_missing(self): self.assertNotEqual(exit_code, 0) self.assertNotEqual(stderr, "", "Error Not raised for invalid command") - def test_validate_required_v_option_present(self): + def test_validate_with_minimum_required_options(self): + """Test that supplying just the 3 required options (-s, -v, -dp), + with no other flags, succeeds without producing errors. + """ args = [ "python", "core.py", @@ -99,25 +87,9 @@ def test_validate_required_v_option_present(self): self.assertFalse(self.error_keyword in stdout) self.assertNotIn("error", stderr.lower()) - def test_validate_with_all_required_options(self): - args = [ - "python", - "core.py", - "validate", - "-dp", - os.path.join("tests", "resources", "test_dataset.xpt"), - "-s", - "sdtmig", - "-v", - "3.4", - ] - exit_code, stdout, stderr = run_command(args, False) - - self.assertEqual(exit_code, 0) - self.assertFalse(self.error_keyword in stdout) - self.assertNotIn("error", stderr.lower()) - def test_validate_without_all_required_options(self): + """Test that passing only -d (no -s/-v) fails with a missing-option + error.""" args = [ "python", "core.py", @@ -131,6 +103,11 @@ def test_validate_without_all_required_options(self): self.assertIn("error: missing option", stderr.lower()) def test_validate_all_options(self): + """Test that nearly every validate flag can be supplied together + without crashing. Deliberately passes both -d and -dp (mutually + exclusive), so the expected outcome is a non-empty stderr; this does + not assert anything about the rule/report content itself. + """ args = [ "python", "core.py", @@ -162,7 +139,7 @@ def test_validate_all_options(self): os.path.join("tests", "resources", "dictionaries", "whodrug"), "--meddra", os.path.join("tests", "resources", "dictionaries", "meddra"), - "-r", + "-lr", os.path.join("tests", "resources", "Rule-CG0027.json"), "-lr", os.path.join("tests", "resources", "CG0272.yml"), @@ -173,6 +150,9 @@ def test_validate_all_options(self): self.assertNotEqual(stderr, "") def test_validate_local_rule(self): + """Test that -r filters which rule(s) within a -lr local rules + directory run. + """ args = [ "python", "core.py", @@ -194,6 +174,8 @@ def test_validate_local_rule(self): self.assertFalse(self.error_keyword in stdout) def test_validate_local_exclude_rule(self): + """Test that -er excludes a rule within a -lr local rules directory + (counterpart to test_validate_local_rule's include-filter case).""" args = [ "python", "core.py", @@ -217,6 +199,8 @@ def test_validate_local_exclude_rule(self): self.assertFalse(self.error_keyword in stdout) def test_validate_include_exclude(self): + """Test that passing both -r and -er together is rejected with exit + code 2, regardless of whether the referenced rules exist locally.""" args = [ "python", "core.py", @@ -242,25 +226,9 @@ def test_validate_include_exclude(self): "cannot use both --rules and --exclude-rules flags together.", stderr ) - def test_validate_minimum_options(self): - args = [ - "python", - "core.py", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-dp", - os.path.join("tests", "resources", "test_dataset.xpt"), - ] - exit_code, stdout, stderr = run_command(args, False) - - self.assertEqual(exit_code, 0) - self.assertNotIn("error", stderr.lower()) - self.assertFalse(self.error_keyword in stdout) - def test_validate_less_than_minimum_options(self): + """Test that -s alone (no -v, no dataset) fails with a specific + "missing option -v" error message.""" args = ["python", "core.py", "validate", "-s", "sdtmig"] exit_code, stdout, stderr = run_command(args, False) self.assertNotEqual(exit_code, 0) @@ -270,6 +238,7 @@ def test_validate_less_than_minimum_options(self): ) def test_validate_output_format_json(self): + """Test that -of json produces a successful run.""" args = [ "python", "core.py", @@ -290,6 +259,7 @@ def test_validate_output_format_json(self): self.assertNotIn("error", stderr.lower()) def test_validate_output_format_excel(self): + """Test that -of xlsx produces a successful run.""" args = [ "python", "core.py", @@ -310,6 +280,7 @@ def test_validate_output_format_excel(self): self.assertNotIn("error", stderr.lower()) def test_validate_with_invalid_output_format(self): + """Test that an unrecognized -of value fails.""" args = [ "python", "core.py", @@ -331,6 +302,7 @@ def test_validate_with_invalid_output_format(self): self.assertNotEqual(stderr, "") def test_validate_with_log_level_disabled(self): + """Test that -l disabled produces a successful run.""" args = [ "python", "core.py", @@ -351,6 +323,9 @@ def test_validate_with_log_level_disabled(self): self.assertNotIn("error", stderr.lower()) def test_validate_with_log_level_info(self): + """Test that -l info logging doesn't affect exit code/output when a + specific rule is selected. + """ args = [ "python", "core.py", @@ -363,8 +338,8 @@ def test_validate_with_log_level_info(self): os.path.join("tests", "resources", "test_dataset.xpt"), "-l", "info", - "-r", - "CORE-000237", + "-lr", + os.path.join("tests", "resources", "library_rules", "CORE-000237.json"), ] exit_code, stdout, stderr = run_command(args, False) @@ -372,6 +347,8 @@ def test_validate_with_log_level_info(self): self.assertFalse(self.error_keyword in stdout) def test_validate_with_log_level_error(self): + """Test that -l error produces a successful run (with some stderr + output, since "error" level still surfaces log lines here).""" args = [ "python", "core.py", @@ -392,6 +369,7 @@ def test_validate_with_log_level_error(self): self.assertNotEqual(stderr, "") def test_validate_with_log_level_critical(self): + """Test that -l critical produces a successful run.""" args = [ "python", "core.py", @@ -412,6 +390,8 @@ def test_validate_with_log_level_critical(self): self.assertNotIn("error", stderr.lower()) def test_validate_with_log_level_warn(self): + """Test that -l warn produces a successful run with no "warning" text + in stderr.""" args = [ "python", "core.py", @@ -431,6 +411,7 @@ def test_validate_with_log_level_warn(self): self.assertNotIn("warning", stderr) def test_validate_with_invalid_log_level(self): + """Test that an unrecognized -l value fails.""" args = [ "python", "core.py", @@ -449,25 +430,9 @@ def test_validate_with_invalid_log_level(self): self.assertNotEqual(exit_code, 0) self.assertNotEqual(stderr, "") - def test_validate_with_no_log_level(self): - args = [ - "python", - "core.py", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-dp", - os.path.join("tests", "resources", "test_dataset.xpt"), - ] - exit_code, stdout, stderr = run_command(args, False) - - self.assertEqual(exit_code, 0) - self.assertFalse(self.error_keyword in stdout) - self.assertNotIn("error", stderr.lower()) - def test_validate_high_value_ps(self): + """Test that a -ps (pool size) value smaller than the CPU count + doesn't break validation.""" args = [ "python", "core.py", @@ -488,6 +453,8 @@ def test_validate_high_value_ps(self): self.assertNotIn("error", stderr.lower()) def test_validate_define_xml_path(self): + """Test that a valid -dxp/--define-xml-path produces a successful + run.""" args = [ "python", "core.py", @@ -507,6 +474,8 @@ def test_validate_define_xml_path(self): self.assertNotIn("error", stderr.lower()) def test_validate_dummy_with_all_options_one_data_source(self): + """Test the shell-string command-invocation variant (shell=True) with + a single -dp data source and a full option set succeeds.""" args = ( f"python core.py validate " f"-ca {os.path.join('resources', 'cache')} " @@ -526,6 +495,9 @@ def test_validate_dummy_with_all_options_one_data_source(self): self.assertNotIn("error", stderr.lower()) def test_validate_dummy_with_all_options(self): + """Test that passing both -dp and -d (mutually exclusive) via the + shell-string command variant fails with the expected error message + format.""" args = ( f"python core.py validate " f"-ca {os.path.join('resources', 'cache')} " @@ -557,6 +529,8 @@ def test_validate_dummy_with_all_options(self): self.assertTrue(re.match(expected_pattern, stderr), error_msg) def test_validate_dummy_without_dataset_path(self): + """Test that passing neither -dp nor -d fails with the expected + "you must pass one of" error message format.""" args = ( f"python core.py validate " f"-ca {os.path.join('resources', 'cache')} " @@ -579,6 +553,8 @@ def test_validate_dummy_without_dataset_path(self): self.assertTrue(re.match(expected_pattern, stderr), error_msg) def test_validate_dummy_with_default_cache_path(self): + """Test that omitting -ca/--cache-path falls back to the default + cache location and still succeeds.""" args = ( f"python core.py validate " f"-s sdtmig " @@ -592,6 +568,8 @@ def test_validate_dummy_with_default_cache_path(self): self.assertNotIn("error", stderr.lower()) def test_validate_dummy_without_whodrug_and_meddra(self): + """Test that omitting the optional --whodrug/--meddra dictionary + paths still succeeds.""" args = ( f"python core.py validate " f"-s sdtmig " @@ -606,6 +584,7 @@ def test_validate_dummy_without_whodrug_and_meddra(self): self.assertNotIn("error", stderr.lower()) def test_validate_dummy_with_invalid_whodrug_and_meddra(self): + """Test that invalid --whodrug/--meddra dictionary paths fail.""" args = ( f"python core.py validate " f"-ca {os.path.join('resources', 'cache')} " @@ -619,6 +598,7 @@ def test_validate_dummy_with_invalid_whodrug_and_meddra(self): self.assertNotEqual(stderr, "") def test_validate_dummy_with_vx_as_no(self): + """Test that -vx no disables Define-XML validation without error.""" args = ( f"python core.py validate " f"-s sendig " @@ -631,6 +611,8 @@ def test_validate_dummy_with_vx_as_no(self): self.assertNotIn("error", stdout) def test_validate_dummy_with_vx_as_yes(self): + """Test that -vx y (default) enables Define-XML validation and still + succeeds.""" args = ( f"python core.py validate " f"-s sendig " diff --git a/tests/resources/CoreIssue357/SENDIG_266_rule.json b/tests/resources/CoreIssue357/SENDIG_266_rule.json index 996f76f94..dd48b8ebd 100644 --- a/tests/resources/CoreIssue357/SENDIG_266_rule.json +++ b/tests/resources/CoreIssue357/SENDIG_266_rule.json @@ -20,7 +20,8 @@ ], "Core": { "Id": "CDISC.SDTMIG.CG0013", - "Version": "1" + "Version": "1", + "Status": "Draft" }, "Description": "Trigger when variable cannot be found in the SDTM model (v.1.7 for SENDIG-3.1)", "Operations": [ diff --git a/tests/resources/library_rules/CORE-000007.json b/tests/resources/library_rules/CORE-000007.json new file mode 100644 index 000000000..69b7c67d3 --- /dev/null +++ b/tests/resources/library_rules/CORE-000007.json @@ -0,0 +1,376 @@ +{ + "core_id": "CORE-000007", + "author": "CDISC", + "reference": [ + [ + { + "Citations": [ + { + "Cited_Guidance": "DTHDTC[The date or date and time of death, represented in a standardized character format.] DTHFL[An indication that the subject died.]", + "Document": "Model v2.0", + "Item": "DTHDTC|DTHFL", + "Section": "Demographics" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "A value of 'Y' indicates the subject died. Should be Y or null. Should be populated even when the death date is unknown.", + "Document": "Model v1.4", + "Section": "2.2.6" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "A value of \"Y\" indicates the subject died. Should be \"Y\" or null. Should be populated even when the death date is unknown.", + "Document": "Model v1.7", + "Section": "Table 2.2.6.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "DTHDTC[The date or date and time of death, represented in a standardized character format.]|DTHFL[An indication that the subject died.]", + "Document": "SDTM v2.1", + "Item": "DTHDTC|DTHFL", + "Section": "3.2.1 Demographics" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0587", + "Version": "1" + }, + "Version": "1.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ] + ], + "sensitivity": "Record", + "executability": "fully executable", + "description": "When Date/Time of Death (DTHDTC) in the DM dataset is populated then death flag (DTHFL) should be populated as 'Y' in the DM dataset for the corresponding subject.", + "authorities": [ + { + "Organization": "CDISC", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "DTHDTC[The date or date and time of death, represented in a standardized character format.] DTHFL[An indication that the subject died.]", + "Document": "Model v2.0", + "Item": "DTHDTC|DTHFL", + "Section": "Demographics" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.4" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "A value of 'Y' indicates the subject died. Should be Y or null. Should be populated even when the death date is unknown.", + "Document": "Model v1.4", + "Section": "2.2.6" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.2" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "A value of \"Y\" indicates the subject died. Should be \"Y\" or null. Should be populated even when the death date is unknown.", + "Document": "Model v1.7", + "Section": "Table 2.2.6.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0435", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.3" + }, + { + "Name": "TIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "DTHDTC[The date or date and time of death, represented in a standardized character format.]|DTHFL[An indication that the subject died.]", + "Document": "SDTM v2.1", + "Item": "DTHDTC|DTHFL", + "Section": "3.2.1 Demographics" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0587", + "Version": "1" + }, + "Version": "1.0" + } + ], + "Substandard": "SDTM", + "Version": "1.0" + } + ] + }, + { + "Organization": "FDA", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ], + "Version": "3.2" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ], + "Version": "3.3" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "All death information should be populated for subjects that died during the study including any post treatment follow-up.", + "Document": "FDA", + "Section": "FDAB006" + } + ], + "Origin": "FDA Business Rules", + "Rule_Identifier": { + "Id": "FB0606", + "Version": "1" + }, + "Version": "1.5" + } + ], + "Version": "3.4" + } + ] + } + ], + "standards": [ + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.2", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.3", + "Substandard": null + }, + { + "Name": "TIG", + "Version": "1.0", + "Substandard": "SDTM" + }, + { + "Name": "SDTMIG", + "Version": "3.2", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.3", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + } + ], + "classes": { + "Include": [ + "SPECIAL PURPOSE" + ] + }, + "domains": { + "Include": [ + "DM" + ] + }, + "entities": null, + "rule_type": "Record Data", + "conditions": { + "all": [ + { + "name": "get_dataset", + "operator": "non_empty", + "value": { + "comparator": null, + "target": "DTHDTC" + } + }, + { + "name": "get_dataset", + "operator": "not_equal_to", + "value": { + "comparator": "Y", + "target": "DTHFL" + } + } + ] + }, + "actions": [ + { + "name": "generate_dataset_error_objects", + "params": { + "message": "DTHDTC is populated but DTHFL in DM dataset is not \"Y\"." + } + } + ], + "use_case": "INDH", + "data_structures": null, + "status": "Published", + "output_variables": [ + "DTHDTC", + "DTHFL" + ] +} \ No newline at end of file diff --git a/tests/resources/library_rules/CORE-000237.json b/tests/resources/library_rules/CORE-000237.json new file mode 100644 index 000000000..873c1dcf7 --- /dev/null +++ b/tests/resources/library_rules/CORE-000237.json @@ -0,0 +1,504 @@ +{ + "core_id": "CORE-000237", + "author": "CDISC", + "reference": [ + [ + { + "Citations": [ + { + "Cited_Guidance": "A further grouping or classification of the category for the topic of the finding, event, or intervention. The category is in --CAT.", + "Document": "Model v2.0", + "Item": "--SCAT", + "Section": "Interventions" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Used to define a further categorization of --CAT values.", + "Document": "Model v1.7", + "Item": "--SCAT", + "Section": "2.2.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Used to define a further categorization of --CAT values.", + "Document": "Model v1.4", + "Item": "Table 2.2.1, --SCAT", + "Section": "2.2.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.0", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "A further grouping or classification of the category for the topic of the finding, event, or intervention. The category is in --CAT.", + "Document": "SDTM v2.1", + "Item": "--SCAT", + "Section": "3.1.4 Identifiers for All Classes" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0309", + "Version": "1" + }, + "Version": "1.0" + } + ] + ], + "sensitivity": "Record", + "executability": "fully executable", + "description": "Trigger error when --SCAT is not null and --SCAT is equal to --CAT", + "authorities": [ + { + "Organization": "CDISC", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "A further grouping or classification of the category for the topic of the finding, event, or intervention. The category is in --CAT.", + "Document": "Model v2.0", + "Item": "--SCAT", + "Section": "Interventions" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.4" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Used to define a further categorization of --CAT values.", + "Document": "Model v1.7", + "Item": "--SCAT", + "Section": "2.2.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.3" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Used to define a further categorization of --CAT values.", + "Document": "Model v1.4", + "Item": "Table 2.2.1, --SCAT", + "Section": "2.2.1" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0027", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.2" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.0", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.0" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.1" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.1.1" + }, + { + "Name": "SENDIG-GENETOX", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.0" + }, + { + "Name": "SENDIG-DART", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.1" + }, + { + "Name": "SENDIG-DART", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Values for --CAT and --SCAT should not be redundant with the domain or dictionary classification provided by --DECOD and --BODSYS.", + "Document": "IG v.3.1.1", + "Section": "4.2.6" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND43", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.2" + }, + { + "Name": "TIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "A further grouping or classification of the category for the topic of the finding, event, or intervention. The category is in --CAT.", + "Document": "SDTM v2.1", + "Item": "--SCAT", + "Section": "3.1.4 Identifiers for All Classes" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0309", + "Version": "1" + }, + "Version": "1.0" + } + ], + "Substandard": "SDTM", + "Version": "1.0" + } + ] + } + ], + "standards": [ + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.3", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.2", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.0", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.1", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.1.1", + "Substandard": null + }, + { + "Name": "SENDIG-GENETOX", + "Version": "1.0", + "Substandard": null + }, + { + "Name": "SENDIG-DART", + "Version": "1.1", + "Substandard": null + }, + { + "Name": "SENDIG-DART", + "Version": "1.2", + "Substandard": null + }, + { + "Name": "TIG", + "Version": "1.0", + "Substandard": "SDTM" + } + ], + "classes": { + "Include": [ + "ALL" + ] + }, + "domains": { + "Include": [ + "ALL" + ] + }, + "entities": null, + "rule_type": "Record Data", + "conditions": { + "all": [ + { + "name": "get_dataset", + "operator": "non_empty", + "value": { + "comparator": null, + "target": "--SCAT" + } + }, + { + "name": "get_dataset", + "operator": "equal_to", + "value": { + "comparator": "--CAT", + "target": "--SCAT" + } + } + ] + }, + "actions": [ + { + "name": "generate_dataset_error_objects", + "params": { + "message": "--SCAT is equal to --CAT" + } + } + ], + "use_case": "INDH, PROD", + "data_structures": null, + "status": "Published", + "output_variables": [ + "--CAT", + "--SCAT" + ] +} \ No newline at end of file diff --git a/tests/resources/library_rules/CORE-000354.json b/tests/resources/library_rules/CORE-000354.json new file mode 100644 index 000000000..4bcd56660 --- /dev/null +++ b/tests/resources/library_rules/CORE-000354.json @@ -0,0 +1,260 @@ +{ + "core_id": "CORE-000354", + "author": "CDISC", + "reference": [ + [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain)", + "Document": "SDTMIG v3.4", + "Section": "4.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain).", + "Document": "SDTMIG v3.3", + "Section": "4.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain).", + "Document": "SDTMIG v3.2", + "Section": "4.1.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Variables --DY, --STDY, and --ENDY represent the timing of observations in days relative to the reference start date in RFSTDTC where the value of --DY is the relative day of the date populated in --DTC for the observation,--STDTC is the relative day of the date populated in --STDTC for the observation; an--ENDY is the relative day of the of the date populated in --ENDTC for the observation.", + "Document": "TIG 1.0", + "Section": "2.8.7.6" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0292", + "Version": "1" + }, + "Version": "1.0" + } + ] + ], + "sensitivity": "Record", + "executability": "fully executable", + "description": "Raise an error when the date portion of --DTC is an incomplete date or the date portion of DM.RFSTDTC is an incomplete date, but --DY is not empty", + "authorities": [ + { + "Organization": "CDISC", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain)", + "Document": "SDTMIG v3.4", + "Section": "4.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.4" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain).", + "Document": "SDTMIG v3.3", + "Section": "4.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.3" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "The permissible Study Day variables (--DY, --STDY, and --ENDY) . . . are determined by comparing the date portion of the respective date/time variables (--DTC, --STDTC, and --ENDTC) to the date portion of the Subject Reference Start Date (RFSTDTC from the Demographics domain).", + "Document": "SDTMIG v3.2", + "Section": "4.1.4.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0007", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.2" + }, + { + "Name": "TIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Variables --DY, --STDY, and --ENDY represent the timing of observations in days relative to the reference start date in RFSTDTC where the value of --DY is the relative day of the date populated in --DTC for the observation,--STDTC is the relative day of the date populated in --STDTC for the observation; an--ENDY is the relative day of the of the date populated in --ENDTC for the observation.", + "Document": "TIG 1.0", + "Section": "2.8.7.6" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0292", + "Version": "1" + }, + "Version": "1.0" + } + ], + "Substandard": "SDTM", + "Version": "1.0" + } + ] + } + ], + "standards": [ + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.3", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.2", + "Substandard": null + }, + { + "Name": "TIG", + "Version": "1.0", + "Substandard": "SDTM" + } + ], + "classes": { + "Include": [ + "ALL" + ] + }, + "domains": { + "Include": [ + "ALL" + ] + }, + "entities": null, + "rule_type": "Record Data", + "conditions": { + "all": [ + { + "name": "get_dataset", + "operator": "non_empty", + "value": { + "comparator": null, + "target": "--DY" + } + }, + { + "any": [ + { + "name": "get_dataset", + "operator": "is_incomplete_date", + "value": { + "comparator": null, + "target": "--DTC" + } + }, + { + "name": "get_dataset", + "operator": "is_incomplete_date", + "value": { + "comparator": null, + "target": "RFSTDTC" + } + } + ] + } + ] + }, + "actions": [ + { + "name": "generate_dataset_error_objects", + "params": { + "message": "The date portion of --DTC is not complete date or the date portion of DM.RFSTDTC is not complete date, but --DY is not empty" + } + } + ], + "use_case": "INDH", + "data_structures": null, + "status": "Published", + "datasets": [ + { + "domain_name": "DM", + "match_key": [ + "USUBJID" + ], + "wildcard": "**" + } + ], + "output_variables": [ + "--DY", + "--DTC", + "RFSTDTC" + ] +} \ No newline at end of file diff --git a/tests/resources/library_rules/CORE-000356.json b/tests/resources/library_rules/CORE-000356.json new file mode 100644 index 000000000..ac341e02f --- /dev/null +++ b/tests/resources/library_rules/CORE-000356.json @@ -0,0 +1,558 @@ +{ + "core_id": "CORE-000356", + "author": "CDISC", + "reference": [ + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.2", + "Section": "4.1.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.3", + "Section": "4.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.4", + "Section": "4.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.0", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "All Required and Expected tabulation variables are included as columns in the dataset. Required tabulation variables are populated for all records in the dataset.", + "Document": "TIG 1.0", + "Item": "Table 2 Num 6", + "Section": "2.5" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0299", + "Version": "1" + }, + "Version": "1.0" + } + ], + [ + { + "Citations": [ + { + "Cited_Guidance": "Req for variables which are Required and must be included in the resulting dataset and cannot be null for any record. Such variables are basic to the identification of a data record or are necessary to make the record meaningful.", + "Document": "TIG 1.0", + "Item": "Text", + "Section": "2.8.1 How to Read Domain Specifications" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0057", + "Version": "1" + }, + "Version": "1.0" + } + ] + ], + "sensitivity": "Record", + "executability": "fully executable", + "description": "Part B: Raise an error when a Required variable is null.", + "authorities": [ + { + "Organization": "CDISC", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.2", + "Section": "4.1.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.2" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.3", + "Section": "4.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.3" + }, + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.4", + "Section": "4.1.5" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0014", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.4" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.0", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.0" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.1" + }, + { + "Name": "SENDIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "3.1.1" + }, + { + "Name": "SENDIG-DART", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.1" + }, + { + "Name": "SENDIG-DART", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.2" + }, + { + "Name": "SENDIG-GENETOX", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Required variables must always be included in the dataset and cannot be null for any record.", + "Document": "IG v3.1.1", + "Section": "4.1.3" + } + ], + "Origin": "SEND Conformance Rules", + "Rule_Identifier": { + "Id": "SEND12", + "Version": "1" + }, + "Version": "5.0" + } + ], + "Version": "1.0" + }, + { + "Name": "TIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "All Required and Expected tabulation variables are included as columns in the dataset. Required tabulation variables are populated for all records in the dataset.", + "Document": "TIG 1.0", + "Item": "Table 2 Num 6", + "Section": "2.5" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0299", + "Version": "1" + }, + "Version": "1.0" + } + ], + "Substandard": "SDTM", + "Version": "1.0" + }, + { + "Name": "TIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "Req for variables which are Required and must be included in the resulting dataset and cannot be null for any record. Such variables are basic to the identification of a data record or are necessary to make the record meaningful.", + "Document": "TIG 1.0", + "Item": "Text", + "Section": "2.8.1 How to Read Domain Specifications" + } + ], + "Origin": "TIG Conformance Rules", + "Rule_Identifier": { + "Id": "TIG0057", + "Version": "1" + }, + "Version": "1.0" + } + ], + "Substandard": "SEND", + "Version": "1.0" + } + ] + } + ], + "standards": [ + { + "Name": "SDTMIG", + "Version": "3.2", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.3", + "Substandard": null + }, + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.0", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.1", + "Substandard": null + }, + { + "Name": "SENDIG", + "Version": "3.1.1", + "Substandard": null + }, + { + "Name": "SENDIG-DART", + "Version": "1.1", + "Substandard": null + }, + { + "Name": "SENDIG-DART", + "Version": "1.2", + "Substandard": null + }, + { + "Name": "SENDIG-GENETOX", + "Version": "1.0", + "Substandard": null + }, + { + "Name": "TIG", + "Version": "1.0", + "Substandard": "SDTM" + }, + { + "Name": "TIG", + "Version": "1.0", + "Substandard": "SEND" + } + ], + "classes": { + "Include": [ + "ALL" + ] + }, + "domains": { + "Include": [ + "ALL" + ] + }, + "entities": null, + "rule_type": "Value Check with Dataset Metadata", + "conditions": { + "all": [ + { + "name": "get_dataset", + "operator": "exists", + "value": { + "comparator": null, + "target": "$required_variables" + } + }, + { + "name": "get_dataset", + "operator": "is_contained_by", + "value": { + "comparator": "$required_variables", + "target": "variable_name" + } + }, + { + "name": "get_dataset", + "operator": "empty", + "value": { + "comparator": null, + "target": "variable_value" + } + } + ] + }, + "actions": [ + { + "name": "generate_dataset_error_objects", + "params": { + "message": "At least one Required variable has a null value" + } + } + ], + "use_case": "INDH, PROD, NONCLIN", + "data_structures": null, + "status": "Published", + "operations": [ + { + "id": "$required_variables", + "operator": "required_variables" + } + ], + "output_variables": [ + "variable_name", + "variable_value" + ] +} \ No newline at end of file diff --git a/tests/resources/library_rules/CORE-000766.json b/tests/resources/library_rules/CORE-000766.json new file mode 100644 index 000000000..287ab32e3 --- /dev/null +++ b/tests/resources/library_rules/CORE-000766.json @@ -0,0 +1,116 @@ +{ + "core_id": "CORE-000766", + "author": "CDISC", + "reference": [ + [ + { + "Citations": [ + { + "Cited_Guidance": "When findings are collected about an event or intervention, and the name of the event or intervention is collected in an \"Other, specify\" CRF field, the value in --OBJ variable depends on whether the FA record has a parent record and whether the \"Other, specify\" value was coded.", + "Document": "IG v3.4", + "Section": "4.2.7.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0601", + "Version": "1" + }, + "Version": "2.0" + } + ] + ], + "sensitivity": "Record", + "executability": "fully executable", + "description": "When findings are collected, and the Events parent record exists and --DECOD is null, FAOBJ should not be equal to --TERM", + "authorities": [ + { + "Organization": "CDISC", + "Standards": [ + { + "Name": "SDTMIG", + "References": [ + { + "Citations": [ + { + "Cited_Guidance": "When findings are collected about an event or intervention, and the name of the event or intervention is collected in an \"Other, specify\" CRF field, the value in --OBJ variable depends on whether the FA record has a parent record and whether the \"Other, specify\" value was coded.", + "Document": "IG v3.4", + "Section": "4.2.7.4" + } + ], + "Origin": "SDTM and SDTMIG Conformance Rules", + "Rule_Identifier": { + "Id": "CG0601", + "Version": "1" + }, + "Version": "2.0" + } + ], + "Version": "3.4" + } + ] + } + ], + "standards": [ + { + "Name": "SDTMIG", + "Version": "3.4", + "Substandard": null + } + ], + "classes": { + "Include": [ + "EVENTS" + ] + }, + "domains": { + "Include": [ + "ALL" + ] + }, + "entities": null, + "rule_type": "Record Data", + "conditions": { + "all": [ + { + "name": "get_dataset", + "operator": "empty", + "value": { + "comparator": null, + "target": "--DECOD" + } + }, + { + "name": "get_dataset", + "operator": "not_equal_to", + "value": { + "comparator": "RELREC.FAOBJ", + "target": "--TERM" + } + } + ] + }, + "actions": [ + { + "name": "generate_dataset_error_objects", + "params": { + "message": "Related record is present in the parent domain dataset and \"Other, specify\" value was coded." + } + } + ], + "use_case": null, + "data_structures": null, + "status": "Published", + "datasets": [ + { + "domain_name": "RELREC", + "match_key": [], + "wildcard": "FA" + } + ], + "output_variables": [ + "--DECOD", + "--TERM", + "RELREC.FAOBJ" + ] +} \ No newline at end of file From ebacce07910dd7ef434f5d4272ab864688c47367 Mon Sep 17 00:00:00 2001 From: RamilCDISC Date: Fri, 24 Jul 2026 16:14:28 -0500 Subject: [PATCH 7/7] lint updates --- tests/QARegressionTests/test_Issues/test_CoreIssue1023.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py index 5b33620ae..2b9141fa2 100644 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py +++ b/tests/QARegressionTests/test_Issues/test_CoreIssue1023.py @@ -20,9 +20,7 @@ def test_dataset_utf8(self): "-v", "3.4", "-lr", - os.path.join( - "tests", "resources", "library_rules", "CORE-000766.json" - ), + os.path.join("tests", "resources", "library_rules", "CORE-000766.json"), "-e", "utf8", "-dp",