347 lines
14 KiB
Python
347 lines
14 KiB
Python
"""
|
|
Test data generator for ATS load testing.
|
|
|
|
This module provides utilities to generate realistic test data
|
|
for load testing scenarios including jobs, users, and applications.
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
from faker import Faker
|
|
from typing import List, Dict, Any
|
|
import django
|
|
from django.conf import settings
|
|
|
|
# Initialize Faker
|
|
fake = Faker()
|
|
|
|
class TestDataGenerator:
|
|
"""Generates test data for ATS load testing."""
|
|
|
|
def __init__(self):
|
|
self.job_titles = [
|
|
"Software Engineer", "Senior Developer", "Frontend Developer",
|
|
"Backend Developer", "Full Stack Developer", "DevOps Engineer",
|
|
"Data Scientist", "Machine Learning Engineer", "Product Manager",
|
|
"UX Designer", "UI Designer", "Business Analyst",
|
|
"Project Manager", "Scrum Master", "QA Engineer",
|
|
"System Administrator", "Network Engineer", "Security Analyst",
|
|
"Database Administrator", "Cloud Engineer", "Mobile Developer"
|
|
]
|
|
|
|
self.departments = [
|
|
"Engineering", "Product", "Design", "Marketing", "Sales",
|
|
"HR", "Finance", "Operations", "Customer Support", "IT"
|
|
]
|
|
|
|
self.locations = [
|
|
"Riyadh", "Jeddah", "Dammam", "Mecca", "Medina",
|
|
"Khobar", "Tabuk", "Abha", "Hail", "Najran"
|
|
]
|
|
|
|
self.skills = [
|
|
"Python", "JavaScript", "Java", "C++", "Go", "Rust",
|
|
"React", "Vue.js", "Angular", "Django", "Flask", "FastAPI",
|
|
"PostgreSQL", "MySQL", "MongoDB", "Redis", "Elasticsearch",
|
|
"Docker", "Kubernetes", "AWS", "Azure", "GCP",
|
|
"Git", "CI/CD", "Agile", "Scrum", "TDD"
|
|
]
|
|
|
|
def generate_job_posting(self, job_id: int = None) -> Dict[str, Any]:
|
|
"""Generate a realistic job posting."""
|
|
if job_id is None:
|
|
job_id = random.randint(1, 1000)
|
|
|
|
title = random.choice(self.job_titles)
|
|
department = random.choice(self.departments)
|
|
location = random.choice(self.locations)
|
|
|
|
# Generate job description
|
|
description = f"""
|
|
We are seeking a talented {title} to join our {department} team in {location}.
|
|
|
|
Responsibilities:
|
|
- Design, develop, and maintain high-quality software solutions
|
|
- Collaborate with cross-functional teams to deliver projects
|
|
- Participate in code reviews and technical discussions
|
|
- Mentor junior developers and share knowledge
|
|
- Stay updated with latest technologies and best practices
|
|
|
|
Requirements:
|
|
- Bachelor's degree in Computer Science or related field
|
|
- {random.randint(3, 8)}+ years of relevant experience
|
|
- Strong programming skills in relevant technologies
|
|
- Excellent problem-solving and communication skills
|
|
- Experience with agile development methodologies
|
|
"""
|
|
|
|
# Generate qualifications
|
|
qualifications = f"""
|
|
Required Skills:
|
|
- {random.choice(self.skills)}
|
|
- {random.choice(self.skills)}
|
|
- {random.choice(self.skills)}
|
|
- Experience with version control (Git)
|
|
- Strong analytical and problem-solving skills
|
|
|
|
Preferred Skills:
|
|
- {random.choice(self.skills)}
|
|
- {random.choice(self.skills)}
|
|
- Cloud computing experience
|
|
- Database design and optimization
|
|
"""
|
|
|
|
# Generate benefits
|
|
benefits = """
|
|
Competitive salary and benefits package
|
|
Health insurance and medical coverage
|
|
Professional development opportunities
|
|
Flexible work arrangements
|
|
Annual performance bonuses
|
|
Employee wellness programs
|
|
"""
|
|
|
|
# Generate application instructions
|
|
application_instructions = """
|
|
To apply for this position:
|
|
1. Submit your updated resume
|
|
2. Include a cover letter explaining your interest
|
|
3. Provide portfolio or GitHub links if applicable
|
|
4. Complete the online assessment
|
|
5. Wait for our recruitment team to contact you
|
|
"""
|
|
|
|
# Generate deadlines and dates
|
|
posted_date = fake.date_between(start_date="-30d", end_date="today")
|
|
application_deadline = posted_date + timedelta(days=random.randint(30, 90))
|
|
|
|
return {
|
|
"id": job_id,
|
|
"title": title,
|
|
"slug": f"{title.lower().replace(' ', '-')}-{job_id}",
|
|
"description": description.strip(),
|
|
"qualifications": qualifications.strip(),
|
|
"benefits": benefits.strip(),
|
|
"application_instructions": application_instructions.strip(),
|
|
"department": department,
|
|
"location": location,
|
|
"employment_type": random.choice(["Full-time", "Part-time", "Contract", "Temporary"]),
|
|
"experience_level": random.choice(["Entry", "Mid", "Senior", "Lead"]),
|
|
"salary_min": random.randint(5000, 15000),
|
|
"salary_max": random.randint(15000, 30000),
|
|
"is_active": True,
|
|
"posted_date": posted_date.isoformat(),
|
|
"application_deadline": application_deadline.isoformat(),
|
|
"internal_job_id": f"JOB-{job_id:06d}",
|
|
"hash_tags": f"#{title.replace(' ', '')},#{department},#{location},#hiring",
|
|
"application_url": f"/jobs/{title.lower().replace(' ', '-')}-{job_id}/apply/"
|
|
}
|
|
|
|
def generate_user_profile(self, user_id: int = None) -> Dict[str, Any]:
|
|
"""Generate a realistic user profile."""
|
|
if user_id is None:
|
|
user_id = random.randint(1, 1000)
|
|
|
|
first_name = fake.first_name()
|
|
last_name = fake.last_name()
|
|
email = fake.email()
|
|
|
|
return {
|
|
"id": user_id,
|
|
"username": f"{first_name.lower()}.{last_name.lower()}{user_id}",
|
|
"email": email,
|
|
"first_name": first_name,
|
|
"last_name": last_name,
|
|
"phone": fake.phone_number(),
|
|
"location": fake.city(),
|
|
"bio": fake.text(max_nb_chars=200),
|
|
"linkedin_profile": f"https://linkedin.com/in/{first_name.lower()}-{last_name.lower()}{user_id}",
|
|
"github_profile": f"https://github.com/{first_name.lower()}{last_name.lower()}{user_id}",
|
|
"portfolio_url": f"https://{first_name.lower()}{last_name.lower()}{user_id}.com",
|
|
"is_staff": random.choice([True, False]),
|
|
"is_active": True,
|
|
"date_joined": fake.date_between(start_date="-2y", end_date="today").isoformat(),
|
|
"last_login": fake.date_between(start_date="-30d", end_date="today").isoformat()
|
|
}
|
|
|
|
def generate_application(self, application_id: int = None, job_id: int = None, user_id: int = None) -> Dict[str, Any]:
|
|
"""Generate a realistic job application."""
|
|
if application_id is None:
|
|
application_id = random.randint(1, 5000)
|
|
if job_id is None:
|
|
job_id = random.randint(1, 100)
|
|
if user_id is None:
|
|
user_id = random.randint(1, 500)
|
|
|
|
statuses = ["PENDING", "REVIEWING", "SHORTLISTED", "INTERVIEW", "OFFER", "HIRED", "REJECTED"]
|
|
status = random.choice(statuses)
|
|
|
|
# Generate application date
|
|
applied_date = fake.date_between(start_date="-60d", end_date="today")
|
|
|
|
# Generate cover letter
|
|
cover_letter = f"""
|
|
Dear Hiring Manager,
|
|
|
|
I am writing to express my strong interest in the position at your organization.
|
|
With my background and experience, I believe I would be a valuable addition to your team.
|
|
|
|
{fake.text(max_nb_chars=300)}
|
|
|
|
I look forward to discussing how my skills and experience align with your needs.
|
|
|
|
Best regards,
|
|
{fake.name()}
|
|
"""
|
|
|
|
return {
|
|
"id": application_id,
|
|
"job_id": job_id,
|
|
"user_id": user_id,
|
|
"status": status,
|
|
"applied_date": applied_date.isoformat(),
|
|
"cover_letter": cover_letter.strip(),
|
|
"resume_file": f"resume_{application_id}.pdf",
|
|
"portfolio_url": fake.url() if random.choice([True, False]) else None,
|
|
"linkedin_url": fake.url() if random.choice([True, False]) else None,
|
|
"github_url": fake.url() if random.choice([True, False]) else None,
|
|
"expected_salary": random.randint(5000, 25000),
|
|
"available_start_date": (fake.date_between(start_date="+1w", end_date="+2m")).isoformat(),
|
|
"notice_period": random.choice(["Immediate", "1 week", "2 weeks", "1 month"]),
|
|
"source": random.choice(["LinkedIn", "Company Website", "Referral", "Job Board", "Social Media"]),
|
|
"notes": fake.text(max_nb_chars=100) if random.choice([True, False]) else None
|
|
}
|
|
|
|
def generate_interview(self, interview_id: int = None, application_id: int = None) -> Dict[str, Any]:
|
|
"""Generate a realistic interview schedule."""
|
|
if interview_id is None:
|
|
interview_id = random.randint(1, 2000)
|
|
if application_id is None:
|
|
application_id = random.randint(1, 500)
|
|
|
|
interview_types = ["Phone Screen", "Technical Interview", "Behavioral Interview", "Final Interview", "HR Interview"]
|
|
interview_type = random.choice(interview_types)
|
|
|
|
# Generate interview date and time
|
|
interview_datetime = fake.date_time_between(start_date="-30d", end_date="+30d")
|
|
|
|
return {
|
|
"id": interview_id,
|
|
"application_id": application_id,
|
|
"type": interview_type,
|
|
"scheduled_date": interview_datetime.isoformat(),
|
|
"duration": random.randint(30, 120), # minutes
|
|
"location": random.choice(["Office", "Video Call", "Phone Call"]),
|
|
"interviewer": fake.name(),
|
|
"interviewer_email": fake.email(),
|
|
"status": random.choice(["SCHEDULED", "COMPLETED", "CANCELLED", "RESCHEDULED"]),
|
|
"notes": fake.text(max_nb_chars=200) if random.choice([True, False]) else None,
|
|
"meeting_id": f"meeting_{interview_id}" if random.choice([True, False]) else None,
|
|
"meeting_url": f"https://zoom.us/j/{interview_id}" if random.choice([True, False]) else None
|
|
}
|
|
|
|
def generate_message(self, message_id: int = None, sender_id: int = None, recipient_id: int = None) -> Dict[str, Any]:
|
|
"""Generate a realistic message between users."""
|
|
if message_id is None:
|
|
message_id = random.randint(1, 3000)
|
|
if sender_id is None:
|
|
sender_id = random.randint(1, 500)
|
|
if recipient_id is None:
|
|
recipient_id = random.randint(1, 500)
|
|
|
|
message_types = ["DIRECT", "SYSTEM", "NOTIFICATION"]
|
|
message_type = random.choice(message_types)
|
|
|
|
return {
|
|
"id": message_id,
|
|
"sender_id": sender_id,
|
|
"recipient_id": recipient_id,
|
|
"subject": fake.sentence(nb_words=6),
|
|
"content": fake.text(max_nb_chars=500),
|
|
"message_type": message_type,
|
|
"is_read": random.choice([True, False]),
|
|
"created_at": fake.date_time_between(start_date="-30d", end_date="today").isoformat(),
|
|
"read_at": fake.date_time_between(start_date="-29d", end_date="today").isoformat() if random.choice([True, False]) else None
|
|
}
|
|
|
|
def generate_bulk_data(self, config: Dict[str, int]) -> Dict[str, List[Dict]]:
|
|
"""Generate bulk test data based on configuration."""
|
|
data = {
|
|
"jobs": [],
|
|
"users": [],
|
|
"applications": [],
|
|
"interviews": [],
|
|
"messages": []
|
|
}
|
|
|
|
# Generate jobs
|
|
for i in range(config.get("job_count", 100)):
|
|
data["jobs"].append(self.generate_job_posting(i + 1))
|
|
|
|
# Generate users
|
|
for i in range(config.get("user_count", 50)):
|
|
data["users"].append(self.generate_user_profile(i + 1))
|
|
|
|
# Generate applications
|
|
for i in range(config.get("application_count", 500)):
|
|
job_id = random.randint(1, len(data["jobs"]))
|
|
user_id = random.randint(1, len(data["users"]))
|
|
data["applications"].append(self.generate_application(i + 1, job_id, user_id))
|
|
|
|
# Generate interviews (for some applications)
|
|
interview_count = len(data["applications"]) // 2 # Half of applications have interviews
|
|
for i in range(interview_count):
|
|
application_id = random.randint(1, len(data["applications"]))
|
|
data["interviews"].append(self.generate_interview(i + 1, application_id))
|
|
|
|
# Generate messages
|
|
message_count = config.get("user_count", 50) * 5 # 5 messages per user on average
|
|
for i in range(message_count):
|
|
sender_id = random.randint(1, len(data["users"]))
|
|
recipient_id = random.randint(1, len(data["users"]))
|
|
data["messages"].append(self.generate_message(i + 1, sender_id, recipient_id))
|
|
|
|
return data
|
|
|
|
def save_test_data(self, data: Dict[str, List[Dict]], output_dir: str = "load_tests/test_data"):
|
|
"""Save generated test data to JSON files."""
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
for data_type, records in data.items():
|
|
filename = os.path.join(output_dir, f"{data_type}.json")
|
|
with open(filename, 'w') as f:
|
|
json.dump(records, f, indent=2, default=str)
|
|
print(f"Saved {len(records)} {data_type} to {filename}")
|
|
|
|
def create_test_files(self, count: int = 100, output_dir: str = "load_tests/test_files"):
|
|
"""Create test files for upload testing."""
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
for i in range(count):
|
|
# Create a simple text file
|
|
content = fake.text(max_nb_chars=1000)
|
|
filename = os.path.join(output_dir, f"test_file_{i + 1}.txt")
|
|
with open(filename, 'w') as f:
|
|
f.write(content)
|
|
|
|
print(f"Created {count} test files in {output_dir}")
|
|
|
|
if __name__ == "__main__":
|
|
# Example usage
|
|
generator = TestDataGenerator()
|
|
|
|
# Generate test data
|
|
config = {
|
|
"job_count": 50,
|
|
"user_count": 25,
|
|
"application_count": 200
|
|
}
|
|
|
|
test_data = generator.generate_bulk_data(config)
|
|
generator.save_test_data(test_data)
|
|
generator.create_test_files(50)
|
|
|
|
print("Test data generation completed!")
|