To ensure the Project Review Scheduler (PRS) met the highest quality and security standards, I implemented a comprehensive suite of unit, integration, and user acceptance testing (UAT) practices throughout the development process. These practices were guided by agile software development principles that emphasize early defect identification, iterative refinement, and feedback loops (Hoda et al., 2017).
Unit testing was performed using Python’s unittest and mock libraries to validate isolated functional components. Core logic such as due date calculations, reviewer assignments, email notifications, and CSV-based reporting were independently tested. For example, the calculate_due_date() function was evaluated with varying frequencies (e.g., 0.5 years, 1 year) to ensure correct classification of project status into “Overdue,” “Due Soon,” or “Up to Date.” These tests reduced logical errors and improved the traceability of issues. As Shaw (n.d.) emphasizes, structured test automation allows for continuous, formal quality assurance throughout the lifecycle.
Integration testing focused on the interaction between modules, especially where data flowed between components. The assign_all_reviewers() function was tested to ensure that projects marked as “Overdue” or “Due Soon” were passed to the reviewer assignment logic and correctly updated in both Users.csv and Reviews.csv. Mocked file reads and SMTP calls simulated external dependencies, confirming that transitions from status updates to notifications occurred without failure (Ganeshan, 2020). These tests validated that the system’s internal contracts between modules were being honored.
User Acceptance Testing (UAT) was achieved through .command files (run_reports.command and run_everything.command) that emulate real-world batch operations. By resetting data, executing assignments, and generating reports in a single automated workflow, these scripts enabled testers to observe system behavior end-to-end. Manual inspection of output files (e.g., overdue_alerts_20250512.csv, monthly_schedule_05-2025.csv) confirmed that data was presented in the expected format, reviews were assigned appropriately, and report sorting logic was effective. This confirmed the system’s readiness for non-technical stakeholders and aligned with acceptance criteria.
From a security perspective, several best practices were implemented:
Input CSV fields were validated for structure, completeness, and logical accuracy.
Exception handling was integrated across batch processes to ensure graceful degradation.
All write operations were preceded by versioned backups to protect data integrity.
SMTP credentials were passed securely via runtime arguments instead of hardcoded values.
Department-aware reviewer filtering upheld principles of data segregation.
For instance, the validate_project_data() function detects malformed dates, empty fields, or negative values that could otherwise lead to runtime errors—an important step given historical error rates in manually tracked review systems. Furthermore, reviewer assignment logic prevents conflicts of interest by excluding reviewers from the same department, upholding the principle of least privilege (McCarthy, 2023).
Together, this multi-layered testing strategy reinforced the robustness, security, and functional reliability of the Project Review Scheduler.
To ensure the reliability, and security of the Project Review Scheduler (PRS), the system was developed using a layered and structured testing strategy. This strategy combined white-box and black-box testing techniques, modular unit testing, defensive coding, and secure data handling practices consistent with modern secure software development lifecycles (SDLC) (Souppaya et al., 2022).
The system incorporated multiple levels of testing techniques:
Black-box testing: Applied to verify outputs against known input conditions, without accessing the internal codebase. Key techniques included:
Equivalence Class Partitioning and Boundary Value Analysis (BVA): These techniques were used when validating date-based computations for review intervals, ensuring valid partitions for overdue, due soon, and up-to-date project statuses.
frequency_days = int(float(project_data['Review_Frequency_Years']) * 365)
next_review = last_review + timedelta(days=frequency_days)
days_until_review = (next_review - current_date).days
if days_until_review < 0:
status = 'Overdue'
elif days_until_review <= 30:
status = 'Due Soon'
else:
status = 'Up to Date'
This logic tests boundaries such as exactly 30 days or just below/above review thresholds (Real Python, 2023).
White-box testing: Used with Python’s unittest to explicitly test internal control flow, logic branches, and function-level accuracy (Ganeshan, 2020). For example, assign_reviewer() tests both department filtering and load balancing branches.
### Example: Department-based logic in assign_reviewer
other_dept_reviewers = [r for r in reviewers_sorted if r.get('Department') != project_dept]
if other_dept_reviewers:
assigned_reviewer = other_dept_reviewers[0]
else:
assigned_reviewer = reviewers_sorted[0]
This test does the following:
The calculate_due_date(project_data, current_date=None) function is responsible for determining when projects are due for review and assigning appropriate status labels. Testing this component is critical as incorrect calculations could lead to missed reviews or false urgency indicators, undermining the entire scheduling system.
Accurate date calculation is fundamental to the system’s functionality. By testing calculation logic, we ensure the automation correctly applies business rules for different review frequencies.
class TestDueDateCalculator(unittest.TestCase):
@patch('scheduler.read_csv')
@patch('scheduler.write_csv')
def test_due_date_six_months(self, mock_write_csv, mock_read_csv):
# Setup mock project data
projects = [{
"Project_ID": "P001",
"Project_Name": "Security Review",
"Start_Date": "2025-01-01",
"Last_Review_Date": "2025-01-01",
"Review_Frequency_Years": "0.5",
"Department": "IT",
"Status": "",
"Next_Review_Date": ""
}]
# Mock return value for read_csv
mock_read_csv.return_value = projects
# Execute calculation with a fixed current date
result = scheduler.calculate_all_reviews("Projects.csv", current_date="2025-06-01")
# Extract updated projects written to CSV
written_projects = mock_write_csv.call_args[0][1]
updated = written_projects[0]
# Validate the calculated date and status
self.assertEqual(updated['Next_Review_Date'], '2025-07-01')
self.assertEqual(updated['Status'], 'Due Soon')
self.assertEqual(result['due_soon'], 1)
self.assertEqual(result['total_projects'], 1)
This test does the following:
The assign_reviewer() function implements security control by prioritizing reviewers from different departments than the project being reviewed. This separation of duties minimizes conflicts of interest and ensures objective evaluation.
As McCarthy (2023) emphasizes the principle of least privilege is necessary for a secure system design. By testing our implementation of departmental separation, we verify this security principle is correctly applied to the review process, enhancing the integrity of project assessments.
@patch('scheduler.update_user')
@patch('scheduler.add_review')
def test_avoid_same_department_reviewer(self, mock_add_review, mock_update_user):
# Project is from IT department
project = {
"Project_ID": "P001",
"Department": "IT"
}
# Reviewers with different departments
reviewers = [
{"User_ID": "U001", "Name": "Alice", "Department": "IT", "Current_Load": 1},
{"User_ID": "U002", "Name": "Bob", "Department": "HR", "Current_Load": 2},
{"User_ID": "U003", "Name": "Charlie", "Department": "Finance", "Current_Load": 1}
]
review = assign_reviewer(project, reviewers)
assigned_id = review['Reviewer_ID']
# U001 is from same department (IT) and should NOT be selected if others exist
self.assertIn(assigned_id, ["U002", "U003"], "Reviewer from different department should be assigned")
This test does the following:
The assign_all_reviewers() function must handle edge cases where no reviewers exist in the system. This defensive programming technique prevents system failures when operating with incomplete data.
Defensive programming requires anticipating and handling exceptional scenarios. According to Ganeshan (2020), effective quality control demands testing at the boundaries of normal operation. This test verifies the system’s resilience when faced with a complete absence of available reviewers.
@patch('scheduler.read_csv')
@patch('scheduler.write_csv')
def test_graceful_handling_when_no_reviewers_exist(self, mock_write_csv, mock_read_csv):
# Only one project needing review
projects = [{
"Project_ID": "P006",
"Project_Name": "Compliance Audit",
"Start_Date": "2025-01-01",
"Last_Review_Date": "2025-01-01",
"Review_Frequency_Years": "1",
"Department": "Finance",
"Status": "Overdue",
"Next_Review_Date": "2025-05-01"
}]
# No reviewers exist
users = [] # Intentionally empty
# Simulate read_csv returning projects, then users twice
mock_read_csv.side_effect = [projects, users, users]
# Run the reviewer assignment
result = assign_all_reviewers("Projects.csv", "Users.csv", "Reviews.csv")
# Assertions
self.assertEqual(result['total_assigned'], 0)
self.assertEqual(len(result['assignments']), 0)
self.assertIn('assignments', result)
This test does the following:
The send_notifications() function is responsible for emailing reviewers when their assigned projects are due or overdue. Since interacting with a live SMTP server during testing can cause unintended emails to be sent—or fail due to configuration issues—mocking is used to simulate the email-sending behavior.
Mocking external dependencies is an established best practice in unit testing. According to Shaw (n.d.), mocking allows developers to replace complex, stateful, or external systems with dummy implementations that can be easily controlled and inspected during tests. In this case, the smtplib.SMTP class is mocked to avoid sending real emails.
@patch("scheduler.read_csv")
@patch("scheduler.smtplib.SMTP")
def test_overdue_email_notification(self, mock_smtp, mock_read_csv):
# Mocked data setup
projects = [{
"Project_ID": "P002",
"Project_Name": "Critical Security Review",
"Status": "Overdue",
"Next_Review_Date": "2025-04-15",
"Department": "IT"
}]
reviews = [{
"Review_ID": "R001",
"Project_ID": "P002",
"Reviewer_ID": "U002",
"Scheduled_Date": "2025-04-15",
"Status": "Scheduled",
"Completion_Date": ""
}]
users = [{
"User_ID": "U002",
"Name": "Jane Doe",
"Email": "reviewer@example.com",
"Department": "IT",
"Current_Load": "1"
}]
mock_read_csv.side_effect = [projects, reviews, users]
mock_server = MagicMock()
mock_smtp.return_value.__enter__.return_value = mock_server
result = send_notifications(status_filter="Overdue", smtp_server="localhost", smtp_port=587)
self.assertEqual(result["sent"], 1)
self.assertIn("reviewer@example.com", result["log"][0]["reviewer_email"])
self.assertIn("URGENT", result["log"][0]["subject"].upper())
This test does the following:
The generate_monthly_schedule() function produces formatted reports showing all reviews scheduled for a specific month. This reporting capability is essential for management oversight and resource planning.
Reporting functionality must produce consistently formatted, accurate outputs for decision-making. Testing this component ensures that reporting logic correctly filters, sorts, and presents review data, providing stakeholders with reliable information for planning purposes as recommended by Souppaya et al. (2022).
@patch('scheduler.write_csv')
@patch('scheduler.get_reviewer')
@patch('scheduler.get_all_projects')
@patch('scheduler.get_all_reviews')
def test_generate_monthly_review_schedule(self, mock_get_all_reviews, mock_get_all_projects,
mock_get_reviewer, mock_write_csv):
# Mock Reviews
mock_get_all_reviews.return_value = [
{
"Review_ID": "R001",
"Project_ID": "P001",
"Reviewer_ID": "U001",
"Scheduled_Date": "2025-05-10",
"Status": "Scheduled"
},
{
"Review_ID": "R002",
"Project_ID": "P002",
"Reviewer_ID": "U002",
"Scheduled_Date": "2025-05-15",
"Status": "In Progress"
},
{
"Review_ID": "R003",
"Project_ID": "P003",
"Reviewer_ID": "U003",
"Scheduled_Date": "2025-06-10",
"Status": "Scheduled"
}
]
# Run the report for May 2025
result = generate_monthly_schedule("05", "2025", output_file="test_monthly_schedule.csv")
# Assertions
self.assertEqual(result['review_count'], 2)
written_data = mock_write_csv.call_args[0][1]
scheduled_dates = [r['Scheduled_Date'] for r in written_data]
self.assertEqual(scheduled_dates, sorted(scheduled_dates))
This test does the following:
Implementing these comprehensive testing practices validates functional requirements and establishes robust error handling and security controls while maintaining data integrity across all system components (Defensive Programming, 2024).
Security was embedded throughout the development process of the Project Review Scheduler by secure software development lifecycle principles. The implementation focused on multiple layers of protection to ensure data integrity, proper access controls, and error handling (Alenezi & Almuairfi, 2019).
All CSV-based data underwent validation using schema templates and field type checks before processing. According to Souppaya et al. (2022), input validation is a fundamental security practice that prevents malformed or malicious inputs from compromising system integrity.
def validate_project_data(project):
"""Validate project data against required schema and types."""
required_fields = ["Project_ID", "Project_Name", "Department", "Review_Frequency_Years"]
# Check for required fields
for field in required_fields:
if field not in project or not project[field]:
raise ValueError(f"Missing required field: {field}")
# Type and format validation
try:
# Ensure review frequency is a positive number
freq = float(project["Review_Frequency_Years"])
if freq <= 0:
raise ValueError("Review frequency must be positive")
# Validate date formats
for date_field in ["Start_Date", "Last_Review_Date", "Next_Review_Date"]:
if date_field in project and project[date_field]:
datetime.strptime(project[date_field], "%Y-%m-%d")
except ValueError as e:
raise ValueError(f"Invalid data format: {str(e)}")
return True
The validate_project_data() function:
This validation layer is a defense for accidental corruption and potential injection attacks that could compromise the scheduling system. ### Cross-Departmental Assignment Enforcement
The reviewer assignment algorithm implemented the separation of duties as a core security principle (McCarthy, 2023). By programmatically enforcing cross-departmental reviews, the system created an architectural security control.
def assign_reviewer(project, reviewers):
"""Assign reviewer with cross-departmental separation when possible."""
# Sort reviewers by current load (ascending)
reviewers_sorted = sorted(reviewers, key=lambda r: int(r.get('Current_Load', 0)))
# Extract the project's department
project_dept = project.get('Department')
# First try to find reviewers from other departments
other_dept_reviewers = [r for r in reviewers_sorted if r.get('Department') != project_dept]
if other_dept_reviewers:
# Security principle: Use cross-departmental reviewer when available
assigned_reviewer = other_dept_reviewers[0]
else:
# Fallback only when cross-departmental reviewers unavailable
assigned_reviewer = reviewers_sorted[0]
# Create the review assignment
review = {
"Review_ID": generate_id(),
"Project_ID": project["Project_ID"],
"Reviewer_ID": assigned_reviewer["User_ID"],
"Scheduled_Date": project["Next_Review_Date"],
"Status": "Scheduled",
"Completion_Date": ""
}
return review
Security aspects of the assign_reviewer() function:
This approach aligns with the principle of least privilege by restricting reviewers from evaluating projects within their sphere of influence, significantly reducing the risk of biased assessments (McCarthy, 2023).
The notification subsystem implemented security measures for all external communications:
def send_notifications(status_filter="Overdue", smtp_server=None, smtp_port=None):
"""Send secure email notifications for reviews with specified status."""
results = {"sent": 0, "failed": 0, "total": 0, "log": []}
try:
# Load data securely with error handling
projects = read_csv("Projects.csv")
reviews = read_csv("Reviews.csv")
users = read_csv("Users.csv")
# Filter projects by status
filtered_projects = [p for p in projects if p.get("Status") == status_filter]
# Find matching reviews and reviewers
for project in filtered_projects:
# Find the review for this project
project_reviews = [r for r in reviews if r.get("Project_ID") == project.get("Project_ID")]
if not project_reviews:
continue
review = project_reviews[0]
reviewer_id = review.get("Reviewer_ID")
# Find the reviewer
reviewers = [u for u in users if u.get("User_ID") == reviewer_id]
if not reviewers:
continue
reviewer = reviewers[0]
# Security: Validate email format before sending
reviewer_email = reviewer.get("Email", "").strip()
if not is_valid_email(reviewer_email):
results["failed"] += 1
continue
# Create email with urgency indicators for overdue projects
subject = f"URGENT: Review Required - {project.get('Project_Name')}" if status_filter == "Overdue" else f"Review Scheduled - {project.get('Project_Name')}"
# Send email using secure connection when available
with smtplib.SMTP(smtp_server, smtp_port) as server:
try:
# Security: Use TLS when available
if supports_starttls(server):
server.starttls()
# Security: Credentials passed as parameters not hardcoded
if smtp_user and smtp_password:
server.login(smtp_user, smtp_password)
message = create_notification_email(reviewer, project, review, subject)
server.sendmail("scheduler@example.com", reviewer_email, message)
results["sent"] += 1
results["log"].append({
"project_id": project.get("Project_ID"),
"reviewer_email": reviewer_email,
"subject": subject,
"status": "sent"
})
except Exception as e:
results["failed"] += 1
results["log"].append({
"project_id": project.get("Project_ID"),
"reviewer_email": reviewer_email,
"error": str(e),
"status": "failed"
})
results["total"] += 1
except Exception as e:
# Log exception but don't expose details
logging.error(f"Error in send_notifications: {str(e)}")
return results
Security features of the send_notifications() function:
External system interactions represent security boundaries that require special attention to prevent information disclosure (Defensive Programming, 2024).
Error handling was implemented as a functional requirement and a security measure. According to Shaw (n.d.), exception handling prevents information leakage and system state corruption.
def calculate_all_reviews(projects_file, current_date=None):
"""Calculate reviews with defensive error handling and data protection."""
results = {"updated": 0, "overdue": 0, "due_soon": 0, "total_projects": 0}
try:
# Create backup before modification
backup_file(projects_file)
# Load projects with validation
projects = read_csv(projects_file)
results["total_projects"] = len(projects)
# Use current date or today
if current_date:
today = datetime.strptime(current_date, "%Y-%m-%d").date()
else:
today = datetime.now().date()
updated_projects = []
for project in projects:
try:
# Defensive validation of each project
if not validate_project_data(project):
# Log invalid project but continue processing
logging.warning(f"Skipping invalid project: {project.get('Project_ID')}")
updated_projects.append(project)
continue
# Calculate next review date
if project.get("Last_Review_Date"):
last_review = datetime.strptime(project["Last_Review_Date"], "%Y-%m-%d").date()
freq_years = float(project["Review_Frequency_Years"])
# Calculate review cycle in days
cycle_days = int(freq_years * 365)
next_review = last_review + timedelta(days=cycle_days)
# Format date for storage
project["Next_Review_Date"] = next_review.strftime("%Y-%m-%d")
# Determine status with 30-day threshold for "Due Soon"
days_until_due = (next_review - today).days
if days_until_due < 0:
project["Status"] = "Overdue"
results["overdue"] += 1
elif days_until_due <= 30:
project["Status"] = "Due Soon"
results["due_soon"] += 1
else:
project["Status"] = "Up to Date"
results["updated"] += 1
updated_projects.append(project)
except Exception as e:
# Handle per-project exceptions without breaking entire batch
logging.error(f"Error processing project {project.get('Project_ID')}: {str(e)}")
# Preserve original project data
updated_projects.append(project)
# Write updated projects back to file
write_csv(projects_file, updated_projects)
except Exception as e:
# Recover from backup if overall process fails
restore_from_backup(projects_file)
logging.critical(f"Failed to update projects: {str(e)}")
raise
return results
Security features of the calculate_all_reviews() function:
These defensive programming techniques ensured that even under exceptional conditions, the system maintained a secure operational posture with proper data integrity controls.
The security architecture of the Project Review Scheduler demonstrates the application of security-by-design principles (Hoda et al., 2017), where protection mechanisms are integrated throughout the application rather than added as an afterthought. This approach resulted in a system that maintained security controls even when handling edge cases or unexpected inputs.
image.png
# TC_DR_001 Verify due date is correctly calculated for 6-month review frequency and status is correctly assigned
from pathlib import Path
# Path to save the test file
test_path = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_due_date_calculator.py"
# The unit test code
test_code = """
import unittest
from unittest.mock import patch
from datetime import datetime
import scheduler
class TestDueDateCalculator(unittest.TestCase):
@patch('scheduler.read_csv')
@patch('scheduler.write_csv')
def test_due_date_six_months(self, mock_write_csv, mock_read_csv):
# Setup mock project data
projects = [{
"Project_ID": "P001",
"Project_Name": "Security Review",
"Start_Date": "2025-01-01",
"Last_Review_Date": "2025-01-01",
"Review_Frequency_Years": "0.5",
"Department": "IT",
"Status": "",
"Next_Review_Date": ""
}]
# Mock return value for read_csv
mock_read_csv.return_value = projects
# Execute calculation with a fixed current date
result = scheduler.calculate_all_reviews("Projects.csv", current_date="2025-06-01")
# Extract updated projects written to CSV
written_projects = mock_write_csv.call_args[0][1]
updated = written_projects[0]
# Validate the calculated date and status
self.assertEqual(updated['Next_Review_Date'], '2025-07-01')
self.assertEqual(updated['Status'], 'Due Soon')
self.assertEqual(result['due_soon'], 1)
self.assertEqual(result['total_projects'], 1)
if __name__ == '__main__':
unittest.main()
"""
# Save the file locally
test_path.parent.mkdir(parents=True, exist_ok=True)
test_path.write_text(test_code)
print(f"Unit test saved to: {test_path}")
Unit test saved to: /Users/cynthiamcginnis/Documents/ProjectReviewScheduler/test_due_date_calculator.py
Screenshot 2025-05-14 at 2.23.25 PM.png
Screenshot 2025-05-14 at 2.24.03 PM.png
Screenshot 2025-05-14 at 2.25.41 PM.png
Screenshot 2025-05-14 at 2.32.35 PM.png
Ensure reviewer is not from the same department as the project when alternatives are available
# TC_RA_003 Ensure reviewer is not from the same department as the project when alternatives are available
from pathlib import Path
# Define the path for the test file
test_file_path = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_reviewer_department.py"
# Create the test code content
test_code = '''"""
Test Case: TC_RA_003 – Reviewer Department Separation
This unit test verifies that the reviewer assignment algorithm avoids selecting
a reviewer from the same department as the project when reviewers from other departments are available.
"""
import unittest
from unittest.mock import patch
from scheduler import assign_reviewer
class TestReviewerDepartmentSeparation(unittest.TestCase):
@patch('scheduler.update_user')
@patch('scheduler.add_review')
def test_avoid_same_department_reviewer(self, mock_add_review, mock_update_user):
# Project is from IT department
project = {
"Project_ID": "P001",
"Department": "IT"
}
# Reviewers with different departments
reviewers = [
{"User_ID": "U001", "Name": "Alice", "Department": "IT", "Current_Load": 1},
{"User_ID": "U002", "Name": "Bob", "Department": "HR", "Current_Load": 2},
{"User_ID": "U003", "Name": "Charlie", "Department": "Finance", "Current_Load": 1}
]
review = assign_reviewer(project, reviewers)
assigned_id = review['Reviewer_ID']
# U001 is from same department (IT) and should NOT be selected if others exist
self.assertIn(assigned_id, ["U002", "U003"], "Reviewer from different department should be assigned")
if __name__ == '__main__':
unittest.main()
'''
# Ensure the directory exists and write the test script
test_file_path.parent.mkdir(parents=True, exist_ok=True)
test_file_path.write_text(test_code)
test_file_path.name
'test_reviewer_department.py'
Screenshot 2025-05-14 at 2.37.40 PM.png
Screenshot 2025-05-14 at 2.38.11 PM.png
Screenshot 2025-05-14 at 2.39.45 PM.png
Ensure fallback to same-department reviewer and validate reviewer load balancing logic
# TC_RA_005
from pathlib import Path
# Define the path to save the test case file
test_file_path = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_tc_ra_005.py"
# Define the test case code for TC_RA_005
test_code = '''
import unittest
from unittest.mock import patch
from scheduler import assign_reviewer
class TestReviewerAssignmentFallback(unittest.TestCase):
@patch('scheduler.update_user') # Mock update_user to avoid file write
@patch('scheduler.add_review') # Mock add_review to avoid writing reviews
def test_fallback_same_department_and_load_balancing(self, mock_add_review, mock_update_user):
project = {
"Project_ID": "P1001",
"Project_Name": "Fallback Logic Test",
"Department": "IT",
"Status": "Overdue"
}
reviewers = [
{"User_ID": "U001", "Department": "IT", "Current_Load": "3"},
{"User_ID": "U002", "Department": "IT", "Current_Load": "1"},
{"User_ID": "U003", "Department": "IT", "Current_Load": "2"},
]
assignment = assign_reviewer(project, reviewers)
self.assertIsNotNone(assignment)
self.assertEqual(assignment['Reviewer_ID'], 'U002')
self.assertEqual(assignment['Project_ID'], 'P1001')
self.assertEqual(assignment['Status'], 'Scheduled')
if __name__ == "__main__":
unittest.main()
'''
# Save the test case file
test_file_path.parent.mkdir(parents=True, exist_ok=True)
test_file_path.write_text(test_code)
test_file_path.name
'test_tc_ra_005.py'
Screenshot 2025-05-14 at 2.58.29 PM.png
Gracefully handle cases where no reviewers are available
# TC_RA_006 Gracefully handle cases where no reviewers are available
from pathlib import Path
# Define the correct and isolated unit test file for TC_RA_006
test_path = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_tc_ra_006_no_reviewers.py"
unit_test_code = '''\
"""
Test Case: TC_RA_006
Purpose: Ensure the reviewer assignment handles the scenario where no reviewers are available gracefully.
"""
import unittest
from unittest.mock import patch
from scheduler import assign_all_reviewers
class TestNoReviewersAvailable(unittest.TestCase):
@patch('scheduler.read_csv')
@patch('scheduler.write_csv')
def test_graceful_handling_when_no_reviewers_exist(self, mock_write_csv, mock_read_csv):
# Only one project needing review
projects = [{
"Project_ID": "P006",
"Project_Name": "Compliance Audit",
"Start_Date": "2025-01-01",
"Last_Review_Date": "2025-01-01",
"Review_Frequency_Years": "1",
"Department": "Finance",
"Status": "Overdue",
"Next_Review_Date": "2025-05-01"
}]
# No reviewers exist
users = [] # Intentionally empty
# Simulate read_csv returning projects, then users twice (initial + update_user)
mock_read_csv.side_effect = [projects, users, users]
# Run the reviewer assignment
result = assign_all_reviewers("Projects.csv", "Users.csv", "Reviews.csv")
# Assertions
self.assertEqual(result['total_assigned'], 0)
self.assertEqual(len(result['assignments']), 0)
self.assertIn('assignments', result)
print(" TC_RA_006 passed: No reviewers available handled correctly.")
if __name__ == '__main__':
unittest.main()
'''
# Write the test file to disk
test_path.write_text(unit_test_code)
test_path.name
'test_tc_ra_006_no_reviewers.py'
Screenshot 2025-05-14 at 2.43.07 PM.png
Screenshot 2025-05-14 at 2.43.48 PM.png
Verify overdue review notifications include correct urgency indicators and are sent to the right reviewer
# unit test for Notification System TC_NT_002
from pathlib import Path
# Correct test path
notif_test_path_corrected = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_notification_system.py"
# Corrected unit test code with exact mock read_csv order
corrected_test_code = '''"""
Test Case: TC_NT_002 – Notification System
Verify overdue review notifications include correct urgency indicators and are sent to the right reviewer.
"""
import unittest
from unittest.mock import patch, MagicMock
from scheduler import send_notifications
class TestNotificationSystem(unittest.TestCase):
@patch("scheduler.read_csv")
@patch("scheduler.smtplib.SMTP")
def test_overdue_email_notification(self, mock_smtp, mock_read_csv):
# Mocked data setup
projects = [{
"Project_ID": "P002",
"Project_Name": "Critical Security Review",
"Status": "Overdue",
"Next_Review_Date": "2025-04-15",
"Department": "IT"
}]
reviews = [{
"Review_ID": "R001",
"Project_ID": "P002",
"Reviewer_ID": "U002",
"Scheduled_Date": "2025-04-15",
"Status": "Scheduled",
"Completion_Date": ""
}]
users = [{
"User_ID": "U002",
"Name": "Jane Doe",
"Email": "reviewer@example.com",
"Department": "IT",
"Current_Load": "1"
}]
# Ensure correct read_csv call order
mock_read_csv.side_effect = [projects, reviews, users]
# Mock SMTP server behavior
mock_server = MagicMock()
mock_smtp.return_value.__enter__.return_value = mock_server
# Run the function
result = send_notifications(status_filter="Overdue", smtp_server="localhost", smtp_port=587)
print("DEBUG result:", result)
# Assertions
self.assertEqual(result["sent"], 1)
self.assertEqual(result["failed"], 0)
self.assertEqual(result["total"], 1)
self.assertIn("reviewer@example.com", result["log"][0]["reviewer_email"])
self.assertIn("URGENT", result["log"][0]["subject"].upper())
self.assertIn("REVIEW REQUIRED", result["log"][0]["subject"].upper())
if __name__ == "__main__":
unittest.main()
'''
# Save the updated test
notif_test_path_corrected.write_text(corrected_test_code)
notif_test_path_corrected.name
'test_notification_system.py'
Screenshot 2025-05-14 at 3.08.52 PM.png
Validate generation of review schedule with correct formatting and sorting
from pathlib import Path
# Define test code path
test_path = Path.home() / "Documents" / "ProjectReviewScheduler" / "test_reporting_module.py"
# Define the rewritten unit test code
test_code = '''\
# test_reporting_module.py
# Test Case: TC_RP_004 - Validate generation of review schedule with correct formatting and sorting
import unittest
from unittest.mock import patch
from scheduler import generate_monthly_schedule
class TestReportingModule(unittest.TestCase):
@patch("scheduler.get_all_projects")
@patch("scheduler.get_all_reviews")
@patch("scheduler.get_reviewer")
@patch("scheduler.write_csv")
def test_generate_monthly_review_schedule(self, mock_write_csv, mock_get_reviewer, mock_get_all_reviews, mock_get_all_projects):
# Sample data for May 2025
mock_get_all_reviews.return_value = [
{"Review_ID": "R001", "Project_ID": "P001", "Reviewer_ID": "U001", "Scheduled_Date": "2025-05-10", "Status": "Scheduled"},
{"Review_ID": "R002", "Project_ID": "P002", "Reviewer_ID": "U002", "Scheduled_Date": "2025-05-05", "Status": "In Progress"},
{"Review_ID": "R003", "Project_ID": "P003", "Reviewer_ID": "U003", "Scheduled_Date": "2025-06-01", "Status": "Scheduled"},
{"Review_ID": "R004", "Project_ID": "P004", "Reviewer_ID": "U004", "Scheduled_Date": "2025-05-20", "Status": "Completed"}
]
mock_get_all_projects.return_value = [
{"Project_ID": "P001", "Project_Name": "AI Audit"},
{"Project_ID": "P002", "Project_Name": "Cyber Compliance"},
{"Project_ID": "P003", "Project_Name": "Cloud Update"},
{"Project_ID": "P004", "Project_Name": "DevOps Review"},
]
mock_get_reviewer.side_effect = lambda uid: {"U001": {"Name": "Alice"},
"U002": {"Name": "Bob"},
"U003": {"Name": "Carol"},
"U004": {"Name": "Dana"}}.get(uid, {"Name": "Unknown"})
# Call the function
result = generate_monthly_schedule(month="05", year="2025", output_file="test_monthly_schedule.csv")
# Ensure only two reviews included
self.assertEqual(result["review_count"], 2)
mock_write_csv.assert_called_once()
# Capture the written data
_, written_data, fieldnames = mock_write_csv.call_args[0]
dates = [row["Scheduled_Date"] for row in written_data]
self.assertEqual(dates, sorted(dates)) # Ensure sorted
self.assertEqual(fieldnames, ['Project_ID', 'Project_Name', 'Reviewer_Name', 'Scheduled_Date', 'Status'])
if __name__ == '__main__':
unittest.main()
'''
# Create folder if needed and write the file
test_path.parent.mkdir(parents=True, exist_ok=True)
test_path.write_text(test_code)
test_path.name
'test_reporting_module.py'
Screenshot 2025-05-14 at 3.21.54 PM.png
The development and validation of the Project Review Scheduler (PRS) reflect the importance of integrating software testing practices and proactive security measures into every stage of the software development lifecycle. Throughout this project, black-box and white-box testing strategies were employed to ensure functional correctness, performance stability, and maintainability. These testing techniques—supported by unit tests, mock-based validation, and exception handling—helped identify edge cases, verify logic under varying scenarios, and confirm system behavior aligned with stakeholder requirements.
The test cases implemented covered critical aspects of the system:
Each test case passed or was iteratively refined until passing, and all outcomes were documented with terminal logs, and screenshots.
Security was addressed through measures such as .env
-based credentials management, input validation on CSV fields, and logical fallback structures to prevent system crashes or data corruption. As recommended by Alenezi & Almuairfi (2019), these practices support a Secure Software Development Lifecycle (SSDLC) by embedding safety checks at every layer of the application.
Collectively, the testing suite and security mechanisms implemented ensure that PRS is functional, resilient, secure, and maintainable. The attention to testing fidelity, security alignment, and traceable validation provides a foundation for future scalability and operational reliability.
Alenezi, M., & Almuairfi, S. (2019). Security risks in the software development lifecycle. International Journal of Recent Technology and Engineering, 8(3), 7048–7055. https://doi.org/10.35940/ijrte.c5374.098319
Defensive Programming. (2024, December 10). UCL Python Novice GDP – Penguins. https://github-pages.arc.ucl.ac.uk/python-novice-gdp-penguins/10-defensive.html
Ganeshan, D. (2020, April 27). Software testing techniques. MST Solutions. https://www.mstsolutions.com/technical/software-testing-techniques/
Hoda, R., Salleh, N., Grundy, J., & Tee, H. M. (2017). Systematic literature reviews in agile software development: A tertiary study. Information and Software Technology, 85, 60–70. https://doi.org/10.1016/j.infsof.2017.01.007
McCarthy, M. (2023, September 13). Principle of least privilege (PoLP) | What is it & why is it important. strongDM. https://www.strongdm.com/blog/principle-of-least-privilege
Shaw, A. (n.d.). Getting started with testing in Python. Real Python. https://realpython.com/python-testing/
Souppaya, M., Scarfone, K., & Dodson, D. (2022). Secure software development framework (SSDF) version 1.1 (NIST SP 800-218). https://doi.org/10.6028/nist.sp.800-218