- Implemented tests for creating, retrieving, updating, and deleting hackathons. - Added validation tests for hackathon creation and updates. - Included tests for hackathon events and timelines, ensuring proper handling of edge cases. - Created tests for hackathon submissions, including validation and submission status updates. - Organized tests into a dedicated module for better structure and maintainability.
401 lines
15 KiB
Rust
401 lines
15 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use utoipa::{ToSchema, schema};
|
|
use validator::Validate;
|
|
|
|
use crate::v1::hackathon::hackathon_schema::{
|
|
HackathonEventType, HackathonEventsSchema, HackathonPhase, HackathonSchema,
|
|
HackathonStatus, HackathonSubmissionsSchema, HackathonTimelineSchema,
|
|
SubmissionStatus,
|
|
};
|
|
|
|
// Hackathon DTOs
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonCreateRequestDto {
|
|
#[validate(length(min = 1, max = 100, message = "Hackathon name must be between 1 and 100 characters"))]
|
|
pub name: String,
|
|
#[validate(length(min = 1, max = 1000, message = "Description must be between 1 and 1000 characters"))]
|
|
pub description: String,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub registration_deadline: DateTime<Utc>,
|
|
#[validate(range(min = 1, max = 10000, message = "Max participants must be between 1 and 10000"))]
|
|
pub max_participants: Option<u32>,
|
|
pub theme: Option<String>,
|
|
pub rules: Option<String>,
|
|
pub prizes: Option<Vec<PrizeDto>>,
|
|
pub organizers: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonUpdateRequestDto {
|
|
#[validate(length(min = 1, max = 100, message = "Hackathon name must be between 1 and 100 characters"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub name: Option<String>,
|
|
#[validate(length(min = 1, max = 1000, message = "Description must be between 1 and 1000 characters"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: Option<DateTime<Utc>>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: Option<DateTime<Utc>>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub registration_deadline: Option<DateTime<Utc>>,
|
|
#[validate(range(min = 1, max = 10000, message = "Max participants must be between 1 and 10000"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub max_participants: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub theme: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub rules: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub prizes: Option<Vec<PrizeDto>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub organizers: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
pub struct HackathonDto {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub description: String,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub registration_deadline: DateTime<Utc>,
|
|
pub max_participants: Option<u32>,
|
|
pub status: HackathonStatus,
|
|
pub theme: Option<String>,
|
|
pub rules: Option<String>,
|
|
pub prizes: Option<Vec<PrizeDto>>,
|
|
pub organizers: Vec<String>,
|
|
pub is_deleted: bool,
|
|
pub created_at: Option<String>,
|
|
pub updated_at: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct PrizeDto {
|
|
#[validate(range(min = 1, message = "Position must be at least 1"))]
|
|
pub position: u32,
|
|
#[validate(length(min = 1, message = "Prize title cannot be empty"))]
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
pub value: Option<String>,
|
|
}
|
|
|
|
// Hackathon Events DTOs
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonEventCreateRequestDto {
|
|
#[validate(length(min = 1, message = "Event title cannot be empty"))]
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
pub event_type: HackathonEventType,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_time: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_time: DateTime<Utc>,
|
|
pub location: Option<String>,
|
|
pub virtual_link: Option<String>,
|
|
#[validate(range(min = 1, message = "Max attendees must be at least 1"))]
|
|
pub max_attendees: Option<u32>,
|
|
pub is_mandatory: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonEventUpdateRequestDto {
|
|
#[validate(length(min = 1, message = "Event title cannot be empty"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub title: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub event_type: Option<HackathonEventType>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_time: Option<DateTime<Utc>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_time: Option<DateTime<Utc>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub location: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub virtual_link: Option<String>,
|
|
#[validate(range(min = 1, message = "Max attendees must be at least 1"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub max_attendees: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub is_mandatory: Option<bool>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
pub struct HackathonEventDto {
|
|
pub id: String,
|
|
pub hackathon_id: String,
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
pub event_type: HackathonEventType,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_time: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_time: DateTime<Utc>,
|
|
pub location: Option<String>,
|
|
pub virtual_link: Option<String>,
|
|
pub max_attendees: Option<u32>,
|
|
pub is_mandatory: bool,
|
|
pub is_deleted: bool,
|
|
pub created_at: Option<String>,
|
|
pub updated_at: Option<String>,
|
|
}
|
|
|
|
// Hackathon Timeline DTOs
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonTimelineCreateRequestDto {
|
|
pub phase: HackathonPhase,
|
|
#[validate(length(min = 1, message = "Timeline title cannot be empty"))]
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: DateTime<Utc>,
|
|
pub is_active: bool,
|
|
#[validate(range(min = 0, message = "Order must be non-negative"))]
|
|
pub order: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonTimelineUpdateRequestDto {
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub phase: Option<HackathonPhase>,
|
|
#[validate(length(min = 1, message = "Timeline title cannot be empty"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub title: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: Option<DateTime<Utc>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: Option<DateTime<Utc>>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub is_active: Option<bool>,
|
|
#[validate(range(min = 0, message = "Order must be non-negative"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub order: Option<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
pub struct HackathonTimelineDto {
|
|
pub id: String,
|
|
pub hackathon_id: String,
|
|
pub phase: HackathonPhase,
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub start_date: DateTime<Utc>,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub end_date: DateTime<Utc>,
|
|
pub is_active: bool,
|
|
pub order: u32,
|
|
pub is_deleted: bool,
|
|
pub created_at: Option<String>,
|
|
pub updated_at: Option<String>,
|
|
}
|
|
|
|
// Hackathon Submissions DTOs
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonSubmissionCreateRequestDto {
|
|
#[validate(length(min = 1, message = "Project name cannot be empty"))]
|
|
pub project_name: String,
|
|
#[validate(length(min = 1, message = "Description cannot be empty"))]
|
|
pub description: String,
|
|
pub repository_url: Option<String>,
|
|
pub demo_url: Option<String>,
|
|
pub slides_url: Option<String>,
|
|
pub technologies: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonSubmissionUpdateRequestDto {
|
|
#[validate(length(min = 1, message = "Project name cannot be empty"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub project_name: Option<String>,
|
|
#[validate(length(min = 1, message = "Description cannot be empty"))]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub repository_url: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub demo_url: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub slides_url: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub technologies: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
pub struct HackathonSubmissionDto {
|
|
pub id: String,
|
|
pub hackathon_id: String,
|
|
pub team_id: String,
|
|
pub project_name: String,
|
|
pub description: String,
|
|
pub repository_url: Option<String>,
|
|
pub demo_url: Option<String>,
|
|
pub slides_url: Option<String>,
|
|
pub technologies: Vec<String>,
|
|
pub submission_status: SubmissionStatus,
|
|
#[schema(value_type = String, format = DateTime)]
|
|
pub submitted_at: DateTime<Utc>,
|
|
pub is_deleted: bool,
|
|
pub created_at: Option<String>,
|
|
pub updated_at: Option<String>,
|
|
}
|
|
|
|
// Query DTOs
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonQueryDto {
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub status: Option<HackathonStatus>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub organizer_id: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub limit: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub offset: Option<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonEventQueryDto {
|
|
pub hackathon_id: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub event_type: Option<HackathonEventType>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub limit: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub offset: Option<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonTimelineQueryDto {
|
|
pub hackathon_id: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub phase: Option<HackathonPhase>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub is_active: Option<bool>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
pub struct HackathonSubmissionQueryDto {
|
|
pub hackathon_id: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub team_id: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub submission_status: Option<SubmissionStatus>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub limit: Option<u32>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub offset: Option<u32>,
|
|
}
|
|
|
|
// Conversion implementations
|
|
impl From<HackathonSchema> for HackathonDto {
|
|
fn from(schema: HackathonSchema) -> Self {
|
|
Self {
|
|
id: schema.id.id.to_raw(),
|
|
name: schema.name,
|
|
description: schema.description,
|
|
start_date: schema.start_date,
|
|
end_date: schema.end_date,
|
|
registration_deadline: schema.registration_deadline,
|
|
max_participants: schema.max_participants,
|
|
status: schema.status,
|
|
theme: schema.theme,
|
|
rules: schema.rules,
|
|
prizes: schema.prizes.map(|prizes| {
|
|
prizes
|
|
.into_iter()
|
|
.map(|p| PrizeDto {
|
|
position: p.position,
|
|
title: p.title,
|
|
description: p.description,
|
|
value: p.value,
|
|
})
|
|
.collect()
|
|
}),
|
|
organizers: schema.organizers,
|
|
is_deleted: schema.is_deleted,
|
|
created_at: schema.created_at,
|
|
updated_at: schema.updated_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<HackathonEventsSchema> for HackathonEventDto {
|
|
fn from(schema: HackathonEventsSchema) -> Self {
|
|
Self {
|
|
id: schema.id.id.to_raw(),
|
|
hackathon_id: schema.hackathon_id.id.to_raw(),
|
|
title: schema.title,
|
|
description: schema.description,
|
|
event_type: schema.event_type,
|
|
start_time: schema.start_time,
|
|
end_time: schema.end_time,
|
|
location: schema.location,
|
|
virtual_link: schema.virtual_link,
|
|
max_attendees: schema.max_attendees,
|
|
is_mandatory: schema.is_mandatory,
|
|
is_deleted: schema.is_deleted,
|
|
created_at: schema.created_at,
|
|
updated_at: schema.updated_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<HackathonTimelineSchema> for HackathonTimelineDto {
|
|
fn from(schema: HackathonTimelineSchema) -> Self {
|
|
Self {
|
|
id: schema.id.id.to_raw(),
|
|
hackathon_id: schema.hackathon_id.id.to_raw(),
|
|
phase: schema.phase,
|
|
title: schema.title,
|
|
description: schema.description,
|
|
start_date: schema.start_date,
|
|
end_date: schema.end_date,
|
|
is_active: schema.is_active,
|
|
order: schema.order,
|
|
is_deleted: schema.is_deleted,
|
|
created_at: schema.created_at,
|
|
updated_at: schema.updated_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<HackathonSubmissionsSchema> for HackathonSubmissionDto {
|
|
fn from(schema: HackathonSubmissionsSchema) -> Self {
|
|
Self {
|
|
id: schema.id.id.to_raw(),
|
|
hackathon_id: schema.hackathon_id.id.to_raw(),
|
|
team_id: schema.team_id.id.to_raw(),
|
|
project_name: schema.project_name,
|
|
description: schema.description,
|
|
repository_url: schema.repository_url,
|
|
demo_url: schema.demo_url,
|
|
slides_url: schema.slides_url,
|
|
technologies: schema.technologies,
|
|
submission_status: schema.submission_status,
|
|
submitted_at: schema.submitted_at,
|
|
is_deleted: schema.is_deleted,
|
|
created_at: schema.created_at,
|
|
updated_at: schema.updated_at,
|
|
}
|
|
}
|
|
} |