use bytes::Bytes;
use http::{HeaderMap, Method, StatusCode};
use rjango::http::{HttpResponse, request::HttpRequest};
use rjango::views::csrf::csrf_failure;
use rjango::views::generic::dates::{
DateDetailView, DayArchiveView, MonthArchiveView, TodayArchiveView, WeekArchiveView,
YearArchiveView,
};
use rjango::views::mixins::{
ContextMixin, FormMixin, MultipleObjectMixin, SingleObjectMixin, TemplateResponseMixin,
};
use serde_json::json;
fn build_request(path: &str) -> HttpRequest {
HttpRequest::from_axum(
Method::GET,
path.parse().expect("valid path"),
HeaderMap::new(),
Bytes::new(),
)
}
struct EmptyContext;
impl ContextMixin for EmptyContext {}
struct TemplateExample;
impl TemplateResponseMixin for TemplateExample {
fn get_template_names(&self) -> Vec<String> {
vec!["articles/detail.html".to_string()]
}
}
struct FormExample;
impl FormMixin for FormExample {
fn get_form_class(&self) -> &str {
"ArticleForm"
}
fn get_success_url(&self) -> &str {
"/articles/"
}
fn form_valid(&self) -> HttpResponse {
HttpResponse::with_status(StatusCode::CREATED, "created")
}
fn form_invalid(&self) -> HttpResponse {
HttpResponse::with_status(StatusCode::BAD_REQUEST, "invalid")
}
}
struct EmptySingleObject;
impl SingleObjectMixin for EmptySingleObject {}
struct EmptyMultipleObject;
impl MultipleObjectMixin for EmptyMultipleObject {}
#[test]
fn csrf_failure_returns_forbidden_response_with_reason_and_path() {
let request = build_request("/forms/contact/");
let response = csrf_failure(&request, "Missing token");
assert_eq!(response.status_code, StatusCode::FORBIDDEN);
let body = std::str::from_utf8(&response.content).expect("utf8 body");
assert!(body.contains("Missing token"));
assert!(body.contains("/forms/contact/"));
}
#[test]
fn context_mixin_default_context_is_empty() {
let mixin = EmptyContext;
assert!(mixin.get_context_data().is_empty());
}
#[test]
fn context_mixin_supports_trait_objects() {
let mixin: &dyn ContextMixin = &EmptyContext;
assert!(mixin.get_context_data().is_empty());
}
#[test]
fn template_response_mixin_returns_template_names() {
let mixin = TemplateExample;
assert_eq!(
mixin.get_template_names(),
vec!["articles/detail.html".to_string()]
);
}
#[test]
fn template_response_mixin_defaults_content_type_to_html() {
let mixin = TemplateExample;
assert_eq!(mixin.get_content_type(), "text/html");
}
#[test]
fn template_response_mixin_supports_trait_objects() {
let mixin: &dyn TemplateResponseMixin = &TemplateExample;
assert_eq!(
mixin.get_template_names(),
vec!["articles/detail.html".to_string()]
);
assert_eq!(mixin.get_content_type(), "text/html");
}
#[test]
fn form_mixin_exposes_form_configuration() {
let mixin = FormExample;
assert_eq!(mixin.get_form_class(), "ArticleForm");
assert_eq!(mixin.get_success_url(), "/articles/");
}
#[test]
fn form_mixin_supports_trait_objects() {
let mixin: &dyn FormMixin = &FormExample;
assert_eq!(mixin.get_form_class(), "ArticleForm");
assert_eq!(mixin.get_success_url(), "/articles/");
assert_eq!(mixin.form_valid().status_code, StatusCode::CREATED);
assert_eq!(mixin.form_invalid().status_code, StatusCode::BAD_REQUEST);
}
#[test]
fn single_object_mixin_defaults_to_none_and_slug_field() {
let mixin = EmptySingleObject;
assert_eq!(mixin.get_object(), None);
assert_eq!(mixin.get_slug_field(), "slug");
}
#[test]
fn multiple_object_mixin_defaults_to_empty_queryset_and_no_pagination() {
let mixin = EmptyMultipleObject;
assert!(mixin.get_queryset().is_empty());
assert_eq!(mixin.get_paginate_by(), None);
}
#[test]
fn year_archive_view_new_sets_fields() {
let view = YearArchiveView::new(2024);
assert_eq!(view.year, 2024);
assert_eq!(view.template_name, "");
}
#[test]
fn year_archive_view_builder_updates_fields() {
let view = YearArchiveView::new(2024)
.with_year(2025)
.with_template_name("archive/year.html");
assert_eq!(view.year, 2025);
assert_eq!(view.template_name, "archive/year.html");
}
#[test]
fn month_archive_view_new_sets_fields() {
let view = MonthArchiveView::new(2024, 3);
assert_eq!(view.year, 2024);
assert_eq!(view.month, 3);
assert_eq!(view.template_name, "");
}
#[test]
fn week_archive_view_builder_updates_fields() {
let view = WeekArchiveView::new(2024, 12)
.with_year(2025)
.with_week(13)
.with_template_name("archive/week.html");
assert_eq!(view.year, 2025);
assert_eq!(view.week, 13);
assert_eq!(view.template_name, "archive/week.html");
}
#[test]
fn day_archive_view_builder_updates_fields() {
let view = DayArchiveView::new(2024, 3, 14)
.with_year(2025)
.with_month(4)
.with_day(15)
.with_template_name("archive/day.html");
assert_eq!(view.year, 2025);
assert_eq!(view.month, 4);
assert_eq!(view.day, 15);
assert_eq!(view.template_name, "archive/day.html");
}
#[test]
fn today_archive_view_new_starts_with_empty_template_name() {
let view = TodayArchiveView::new();
assert_eq!(view.template_name, "");
}
#[test]
fn today_archive_view_builder_updates_template_name() {
let view = TodayArchiveView::new().with_template_name("archive/today.html");
assert_eq!(view.template_name, "archive/today.html");
}
#[test]
fn date_detail_view_new_sets_fields() {
let view = DateDetailView::new(2024, 3, 14, "42");
assert_eq!(view.year, 2024);
assert_eq!(view.month, 3);
assert_eq!(view.day, 14);
assert_eq!(view.pk, "42");
assert_eq!(view.template_name, "");
}
#[test]
fn date_detail_view_builder_updates_fields() {
let view = DateDetailView::new(2024, 3, 14, "42")
.with_year(2025)
.with_month(4)
.with_day(15)
.with_pk("84")
.with_template_name("archive/detail.html");
assert_eq!(view.year, 2025);
assert_eq!(view.month, 4);
assert_eq!(view.day, 15);
assert_eq!(view.pk, "84");
assert_eq!(view.template_name, "archive/detail.html");
}
#[test]
fn single_and_multiple_object_mixins_can_be_overridden() {
struct CustomMixins;
impl SingleObjectMixin for CustomMixins {
fn get_object(&self) -> Option<serde_json::Value> {
Some(json!({"id": 1}))
}
}
impl MultipleObjectMixin for CustomMixins {
fn get_queryset(&self) -> Vec<serde_json::Value> {
vec![json!({"id": 1}), json!({"id": 2})]
}
fn get_paginate_by(&self) -> Option<usize> {
Some(20)
}
}
let mixin = CustomMixins;
assert_eq!(mixin.get_object(), Some(json!({"id": 1})));
assert_eq!(mixin.get_queryset().len(), 2);
assert_eq!(mixin.get_paginate_by(), Some(20));
}