Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
Commit 34920e53 authored by Pierre Jarriges's avatar Pierre Jarriges
Browse files

wip admin panel

parent 8574c199
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env node
const { bundle } = require("simple-browser-js-bundler");
const path = require("path");
const dir = process.cwd();
bundle(
`${dir}/src/index.js`,
path.resolve(dir, "../public/views/admin-panel/assets/bundle.js"),
{
minify: !process.argv.includes("debug")
}
);
\ No newline at end of file
This diff is collapsed.
{
"name": "admin-frontend",
"version": "1.0.0",
"description": "An admin panel app for the Mentalo API",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node ./build.js",
"build-debug": "node ./build.js debug"
},
"author": "kuadrado-software",
"license": "GPL-3.0",
"dependencies": {
"object-to-html-renderer": "^1.1.3"
},
"devDependencies": {
"dotenv": "^10.0.0",
"simple-browser-js-bundler": "^0.1.1"
}
}
\ No newline at end of file
"use strict";
const { fetch_post_article } = require("../xhr");
class CreateArticleForm {
constructor() {
this.state = {
output: {
title: "",
subtitle: "",
category: "",
details: [],
images: [],
body: "",
}
}
}
handle_text_input(field, e) {
this.state.output[field] = e.target.value;
}
handle_del_detail(index) {
this.state.output.details.splice(index, 1);
this.refresh_details();
}
handle_add_detail() {
this.state.output.details.push({ label: "", value: "" });
this.refresh_details();
}
handle_del_image(index) {
this.state.output.images.splice(index, 1);
this.refresh_images();
}
handle_add_image() {
this.state.output.images.push("")
this.refresh_images();
}
refresh_details() {
obj2htm.subRender(
this.render_details_inputs(),
document.getElementById("create-article-form-details"),
{ mode: "replace" }
);
}
render_details_inputs() {
return {
tag: "ul",
style_rules: {
gridColumn: "1 / span 2",
display: "flex",
flexDirection: "column",
gap: "10px",
listStyleType: "none",
padding: 0,
},
id: "create-article-form-details",
contents: this.state.output.details.map((detail, i) => {
return {
tag: "li",
style_rules: {
display: "grid",
gridTemplateColumns: "200px auto 60px",
gap: "10px",
},
contents: [
{
tag: "input",
type: "text",
placeholder: "Label",
value: detail.label,
oninput: e => {
this.state.output.details[i].label = e.target.value;
}
},
{
tag: "input",
type: "text",
placeholder: "Value",
value: detail.value,
oninput: e => {
this.state.output.details[i].value = e.target.value;
}
},
{
tag: "button", contents: "DEL",
onclick: this.handle_del_detail.bind(this, i)
}
]
}
}).concat([
{
tag: "li", contents: [{
tag: "button", contents: "ADD DETAIL",
onclick: this.handle_add_detail.bind(this)
}]
}
])
}
}
refresh_images() {
obj2htm.subRender(
this.render_images_inputs(),
document.getElementById("create-article-form-images"),
{ mode: "replace" }
);
}
render_images_inputs() {
return {
tag: "ul",
style_rules: {
gridColumn: "1 / span 2",
display: "flex",
flexDirection: "column",
gap: "10px",
listStyleType: "none",
padding: 0,
},
id: "create-article-form-images",
contents: this.state.output.images.map((img, i) => {
return {
tag: "li",
style_rules: {
display: "grid",
gridTemplateColumns: "300px 60px",
gap: "10px",
},
contents: [
{
tag: "input",
type: "text",
placeholder: "image file name",
value: img,
oninput: e => {
this.state.output.images[i] = e.target.value;
}
},
{
tag: "button", contents: "DEL",
onclick: this.handle_del_image.bind(this, i)
}
]
}
}).concat([
{
tag: "li", contents: [{
tag: "button", contents: "ADD IMAGE",
onclick: this.handle_add_image.bind(this)
}]
}
])
}
}
render() {
return {
tag: "form",
style_rules: {
display: "grid",
maxWidth: "800px",
gridTemplateColumns: "1fr 1fr",
gap: "20px",
},
onsubmit: e => {
e.preventDefault();
console.log("will post output", this.state.output)
// fetch_post_article(this.state.output)
// .then(res => console.log(res))
// .catch(err => console.log(err))
},
contents: [
{
tag: "input", type: "text", placeholder: "category",
value: this.state.output.category,
oninput: this.handle_text_input.bind(this, "category")
},
{
tag: "input", type: "text",
placeholder: "Article title",
value: this.state.output.title,
oninput: this.handle_text_input.bind(this, "title")
},
{
tag: "input", type: "text",
style_rules: {
gridColumn: "1 / span 2"
},
placeholder: "Article subtitle",
value: this.state.output.subtitle,
oninput: this.handle_text_input.bind(this, "subtitle")
},
{
tag: "textarea",
style_rules: {
gridColumn: "1 / span 2",
height: "300px",
},
placeholder: "Article body",
oninput: this.handle_text_input.bind(this, "body")
},
this.render_details_inputs(),
this.render_images_inputs(),
{ tag: "input", type: "submit" }
]
}
}
}
module.exports = CreateArticleForm;
\ No newline at end of file
const renderer = require("object-to-html-renderer");
const CreateArticleForm = require("./components/create-article-form");
class RootComponent {
constructor() {
this.state = {
selected_tab: ""
};
}
handle_nav_click(e) {
this.state.selected_tab = e.target.tab_name;
this.refresh();
}
refresh() {
obj2htm.renderCycle();
}
render_state() {
switch (this.state.selected_tab) {
case "create":
return new CreateArticleForm().render();
case "update":
return undefined;
default:
return undefined;
}
}
render() {
return {
tag: "main",
contents: [
{ tag: "h1", contents: "Kuadrado admin panel" },
{
tag: "nav",
contents: [
{
tag: "span", contents: "Create article", tab_name: "create",
class: this.state.selected_tab === "create" ? "selected" : "",
onclick: this.handle_nav_click.bind(this),
},
{
tag: "span", contents: "Update article", tab_name: "update",
class: this.state.selected_tab === "update" ? "selected" : "",
onclick: this.handle_nav_click.bind(this),
},
],
},
this.render_state(),
],
};
}
}
renderer.register("obj2htm");
obj2htm.setRenderCycleRoot(new RootComponent());
obj2htm.renderCycle();
\ No newline at end of file
function get_text_date(iso_str) {
const date = new Date(iso_str);
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()} - ${date.getHours()}h${date.getMinutes()}mn`
}
module.exports = {
get_text_date
}
\ No newline at end of file
async function fetch_article(article_id) {
return new Promise((resolve, reject) => {
fetch(`/article/${article_id}`).then(async res => {
if (res.status >= 400 && res.status < 600) {
const text = await res.text();
reject(text);
} else {
resolve(await res.json());
}
}).catch(e => reject(e))
})
}
async function fetch_article_by_title(article_title) {
const form_data = new FormData();
form_data.append("title", article_title);
return new Promise((resolve, reject) => {
fetch(`/article-by-title/`, {
body: new URLSearchParams(form_data),
}).then(async res => {
if (res.status >= 400 && res.status < 600) {
const text = await res.text();
reject(text);
} else {
resolve(await res.json());
}
}).catch(e => reject(e))
})
}
async function fetch_articles_by_category(category) {
return new Promise((resolve, reject) => {
fetch(`/articles/${category}`).then(async res => {
if (res.status >= 400 && res.status < 600) {
const text = await res.text();
reject(text);
} else {
resolve(await res.json());
}
}).catch(e => reject(e))
})
}
async function fetch_post_article(article_data) {
return new Promise((resolve, reject) => {
fetch("/post-article", {
credentials: 'include',
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(article_data),
})
.then(async res => {
const text = await res.text();
if (res.status >= 400 && res.status < 600) {
reject(text)
} else {
resolve(text);
}
})
.catch(err => reject(err))
})
}
async function fetch_update_article(article_data) {
return new Promise((resolve, reject) => {
fetch(`/update-article/${article_data._id.$oid}`, {
credentials: 'include',
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(article_data),
})
.then(async res => {
const text = await res.text();
if (res.status >= 400 && res.status < 600) {
reject(text)
} else {
resolve(text);
}
})
.catch(err => reject(err))
})
}
async function fetch_delete_article(article_id) {
return new Promise((resolve, reject) => {
fetch(`/delete_article/${article_id}`, {
credentials: 'include',
method: "DELETE"
})
.then(async res => {
const text = await res.text();
if (res.status >= 400 && res.status < 600) {
reject(text)
} else {
resolve(text);
}
})
.catch(err => reject(err))
});
}
module.exports = {
fetch_article,
fetch_article_by_title,
fetch_articles_by_category,
fetch_post_article,
fetch_update_article,
fetch_delete_article,
}
\ No newline at end of file
{
"name": "kuadrado-website",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}
public/standard/favicon.ico

7.58 KiB

User-agent: *
Disallow: /articles/
Disallow: /style/
Sitemap: https://kuadrado-software.fr/sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://kuadrado-software.fr</loc>
<lastmod>2021-11-21</lastmod>
</url>
<url>
<loc>https://kuadrado-software.fr/games/</loc>
<lastmod>2021-11-21</lastmod>
</url>
<url>
<loc>https://kuadrado-software.fr/education/</loc>
<lastmod>2021-11-21</lastmod>
</url>
<url>
<loc>https://kuadrado-software.fr/software-development/</loc>
<lastmod>2021-11-21</lastmod>
</url>
</urlset>
body * {
font-family: monospace;
box-sizing: border-box;
}
input[type="text"],
textarea {
padding: 8px;
}
button,
input[type="submit"] {
padding: 10px;
cursor: pointer;
}
nav {
display: flex;
gap: 1px;
margin: 20px 0;
}
nav span {
padding: 20px;
font-weight: bold;
background-color: #ddd;
cursor: pointer;
}
nav span:hover,
nav span.selected {
background-color: #555;
color: white;
}
#[cfg(test)]
use chrono::Utc;
use serde::{Deserialize, Serialize};
use wither::{
bson::{doc, oid::ObjectId, DateTime},
prelude::Model,
};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ArticleDetail {
pub label: String,
pub value: String,
}
#[derive(Debug, Serialize, Deserialize, Model, Clone)]
#[model(index(keys = r#"doc!{"title": 1}"#, options = r#"doc!{"unique": true}"#))]
pub struct Article {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
pub id: Option<ObjectId>,
pub title: String,
pub subtitle: String,
pub date: Option<DateTime>,
pub body: String,
pub details: Vec<ArticleDetail>,
pub images: Vec<String>,
pub category: String,
}
impl Article {
#[cfg(test)]
pub fn test_article() -> Self {
Article {
id: None,
title: "Test Article".to_string(),
subtitle: "An article for testing".to_string(),
date: Some(DateTime(Utc::now())),
body: "blablabla".to_string(),
details: vec![
ArticleDetail {
label: "A label".to_string(),
value: "A value".to_string(),
},
ArticleDetail {
label: "Another label".to_string(),
value: "Another value".to_string(),
},
],
images: vec!["an_image.png".to_string()],
category: "testing".to_string(),
}
}
}
use crate::{middleware::AuthenticatedAdminMiddleware, model::Article, AppState};
use actix_web::{
delete, get, post, put,
web::{Data, Form, Json, Path},
HttpRequest, HttpResponse, Responder,
};
use chrono::Utc;
use futures::stream::StreamExt;
use serde::{Deserialize, Serialize};
use wither::{
bson::{doc, oid::ObjectId, DateTime},
mongodb::Collection,
prelude::Model,
};
#[derive(Deserialize, Serialize)]
pub struct ArticleTitleFormData {
pub title: String,
}
fn get_collection(app_state: &AppState) -> Collection<Article> {
app_state.db.collection_with_type::<Article>("articles")
}
#[post("/post-article")]
pub async fn post_article(
app_state: Data<AppState>,
article_data: Json<Article>,
middleware: Data<AuthenticatedAdminMiddleware<'_>>,
req: HttpRequest,
) -> impl Responder {
if middleware.exec(&app_state, &req, None).await.is_err() {
return HttpResponse::Unauthorized().finish();
}
let mut article_data = article_data.into_inner();
article_data.date = Some(DateTime(Utc::now()));
match get_collection(&app_state)
.insert_one(article_data, None)
.await
{
Ok(_) => HttpResponse::Created().body("Article inserted with success"),
Err(e) => {
HttpResponse::InternalServerError().body(format!("Error inserting new article {:?}", e))
}
}
}
#[put("/update-article/{article_id}")]
pub async fn update_article(
app_state: Data<AppState>,
article_data: Json<Article>,
middleware: Data<AuthenticatedAdminMiddleware<'_>>,
article_id: Path<String>,
req: HttpRequest,
) -> impl Responder {
if middleware.exec(&app_state, &req, None).await.is_err() {
return HttpResponse::Unauthorized().finish();
}
let article_id = match ObjectId::with_string(&article_id.into_inner()) {
Ok(id) => id,
Err(_) => {
return HttpResponse::BadRequest()
.body("Failed to convert article_id to ObjectId. String may be malformed")
}
};
let mut article_data = article_data.into_inner();
article_data.date = Some(DateTime(Utc::now()));
match get_collection(&app_state)
.find_one_and_replace(doc! {"_id": &article_id}, article_data, None)
.await
{
Ok(_) => HttpResponse::Ok().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
#[delete("/delete-article/{article_id}")]
pub async fn delete_article(
app_state: Data<AppState>,
middleware: Data<AuthenticatedAdminMiddleware<'_>>,
article_id: Path<String>,
req: HttpRequest,
) -> impl Responder {
if middleware.exec(&app_state, &req, None).await.is_err() {
return HttpResponse::Unauthorized().finish();
}
let article_id = match ObjectId::with_string(&article_id.into_inner()) {
Ok(id) => id,
Err(_) => {
return HttpResponse::BadRequest()
.body("Failed to convert article_id to ObjectId. String may be malformed")
}
};
match get_collection(&app_state)
.find_one_and_delete(doc! {"_id": &article_id}, None)
.await
{
Ok(_) => HttpResponse::Accepted().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
#[get("/articles/{category}")]
pub async fn get_articles_by_category(
app_state: Data<AppState>,
category: Path<String>,
) -> impl Responder {
match get_collection(&app_state)
.find(doc! {"category": category.into_inner()}, None)
.await
{
Ok(mut cursor) => {
let mut results: Vec<Article> = Vec::new();
while let Some(result) = cursor.next().await {
match result {
Ok(article) => {
results.push(article);
}
Err(_) => {
return HttpResponse::InternalServerError().finish();
}
}
}
HttpResponse::Ok().json(results)
}
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
#[get("/article/{article_id}")]
pub async fn get_article(app_state: Data<AppState>, article_id: Path<String>) -> impl Responder {
let article_id = match ObjectId::with_string(&article_id.into_inner()) {
Ok(id) => id,
Err(_) => {
return HttpResponse::BadRequest()
.body("Failed to convert article_id to ObjectId. String may be malformed")
}
};
match Article::find_one(&app_state.db, doc! {"_id":&article_id}, None).await {
Ok(art) => {
if art.is_none() {
return HttpResponse::NotFound().body("Article was not found");
}
HttpResponse::Ok().json(art)
}
Err(e) => HttpResponse::InternalServerError().body(format!("Database error: {:#?}", e)),
}
}
#[get("/article-by-title")]
pub async fn get_article_by_title(
app_state: Data<AppState>,
form_data: Form<ArticleTitleFormData>,
) -> impl Responder {
let title = form_data.into_inner().title;
match Article::find_one(&app_state.db, doc! {"title":title}, None).await {
Ok(art) => {
if art.is_none() {
return HttpResponse::NotFound().body("Article was not found");
}
HttpResponse::Ok().json(art)
}
Err(e) => HttpResponse::InternalServerError().body(format!("Database error: {:#?}", e)),
}
}
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@
* _______ ______ ______ _______ *@@
* |__ __@ | ____@ / ____@ |__ __@ *@@
* | @ | @__ \_ @_ | @ *@@
* | @ | __@ \ @_ | @ *@@
* | @ | @___ ____\ @ | @ *@@
* |__@ |______@ \______@ |__@ *@@
* *@@
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@*/
#[cfg(test)]
mod test_articles {
use super::*;
use crate::middleware::get_auth_cookie;
use crate::model::{AdminAuthCredentials, Administrator};
use actix_web::{
http::{Method, StatusCode},
test,
web::Bytes,
App,
};
use wither::bson::Bson;
async fn insert_test_article(
app_state: &AppState,
test_article: Article,
) -> Result<(ObjectId, String), String> {
let title = test_article.title.to_owned();
match get_collection(&app_state)
.insert_one(test_article, None)
.await
{
Ok(inserted) => match inserted.inserted_id {
Bson::ObjectId(id) => Ok((id, title)),
_ => Err(String::from("Failed to parse inserted_id")),
},
Err(e) => Err(format!("{:?}", e)),
}
}
async fn delete_test_article(
app_state: &AppState,
article_id: &ObjectId,
) -> Result<i64, String> {
match get_collection(&app_state)
.delete_one(doc! {"_id": article_id}, None)
.await
{
Ok(delete_result) => Ok(delete_result.deleted_count),
Err(e) => Err(format!("{:?}", e)),
}
}
async fn get_authenticated_admin(app_state: &AppState) -> Administrator {
Administrator::authenticated(
app_state,
AdminAuthCredentials {
username: app_state.env.default_admin_username.to_owned(),
password: app_state.env.default_admin_password.to_owned(),
},
)
.await
.unwrap()
}
#[tokio::test]
async fn test_post_article() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(post_article),
)
.await;
let article = Article::test_article();
let admin_user = get_authenticated_admin(&app_state).await;
let req = test::TestRequest::with_uri("/post-article")
.method(Method::POST)
.header("Content-Type", "application/json")
.header("Accept", "text/html")
.cookie(get_auth_cookie(
"kuadrado-admin-auth",
app_state
.encryption
.decrypt(&admin_user.auth_token.unwrap())
.to_owned(),
))
.set_payload(Bytes::from(serde_json::to_string(&article).unwrap()))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::CREATED);
let find_inserted = Article::find_one(&app_state.db, doc! {"title": &article.title}, None)
.await
.unwrap();
assert!(find_inserted.is_some());
assert_eq!(find_inserted.unwrap().title, article.title);
get_collection(&app_state)
.delete_one(doc! {"title": article.title}, None)
.await
.unwrap();
}
#[tokio::test]
async fn test_post_article_unauthorized() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(post_article),
)
.await;
let article = Article::test_article();
let req = test::TestRequest::with_uri("/post-article")
.method(Method::POST)
.header("Content-Type", "application/json")
.header("Accept", "text/html")
.cookie(get_auth_cookie(
"wrong-cookie",
app_state.encryption.random_ascii_lc_string(32),
))
.set_payload(Bytes::from(serde_json::to_string(&article).unwrap()))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn test_update_article() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(update_article),
)
.await;
let mut article = Article::test_article();
let (article_id, _) = insert_test_article(&app_state, article.clone())
.await
.unwrap();
article.title = "changed title".to_string();
let admin_user = get_authenticated_admin(&app_state).await;
let req = test::TestRequest::with_uri(
format!("/update-article/{}", article_id.to_hex()).as_str(),
)
.method(Method::PUT)
.header("Content-Type", "application/json")
.header("Accept", "text/html")
.cookie(get_auth_cookie(
"kuadrado-admin-auth",
app_state
.encryption
.decrypt(&admin_user.auth_token.unwrap())
.to_owned(),
))
.set_payload(Bytes::from(serde_json::to_string(&article).unwrap()))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let find_inserted = Article::find_one(&app_state.db, doc! {"_id": &article_id}, None)
.await
.unwrap();
assert!(find_inserted.is_some());
assert_eq!(find_inserted.unwrap().title, "changed title");
let del_count = delete_test_article(&app_state, &article_id).await.unwrap();
assert_eq!(del_count, 1);
}
#[tokio::test]
async fn test_update_article_unauthorized() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(update_article),
)
.await;
let article = Article::test_article();
let req = test::TestRequest::with_uri(
format!("/update-article/{}", ObjectId::new().to_hex()).as_str(),
)
.method(Method::PUT)
.header("Content-Type", "application/json")
.header("Accept", "text/html")
.cookie(get_auth_cookie(
"wrong-cookie",
app_state.encryption.random_ascii_lc_string(32),
))
.set_payload(Bytes::from(serde_json::to_string(&article).unwrap()))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn test_delete_article() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(delete_article),
)
.await;
let article = Article::test_article();
let (article_id, _) = insert_test_article(&app_state, article.clone())
.await
.unwrap();
let admin_user = get_authenticated_admin(&app_state).await;
let req = test::TestRequest::with_uri(
format!("/delete-article/{}", article_id.to_hex()).as_str(),
)
.method(Method::DELETE)
.cookie(get_auth_cookie(
"kuadrado-admin-auth",
app_state
.encryption
.decrypt(&admin_user.auth_token.unwrap())
.to_owned(),
))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::ACCEPTED);
let find_inserted = Article::find_one(&app_state.db, doc! {"_id": &article_id}, None)
.await
.unwrap();
assert!(find_inserted.is_none());
}
#[tokio::test]
async fn test_delete_article_unauthorized() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.app_data(Data::new(AuthenticatedAdminMiddleware::new(
"kuadrado-admin-auth",
)))
.service(delete_article),
)
.await;
let article = Article::test_article();
let req = test::TestRequest::with_uri(
format!("/delete-article/{}", ObjectId::new().to_hex()).as_str(),
)
.method(Method::DELETE)
.cookie(get_auth_cookie(
"wrong-cookie",
app_state.encryption.random_ascii_lc_string(32),
))
.set_payload(Bytes::from(serde_json::to_string(&article).unwrap()))
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn test_get_article() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.service(get_article),
)
.await;
let article = Article::test_article();
let (article_id, article_title) = insert_test_article(&app_state, article.clone())
.await
.unwrap();
let req = test::TestRequest::with_uri(format!("/article/{}", article_id.to_hex()).as_str())
.header("Accept", "application/json")
.method(Method::GET)
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let result: Article = test::read_body_json(resp).await;
assert_eq!(result.title, article_title);
let del_count = delete_test_article(&app_state, &article_id).await.unwrap();
assert_eq!(del_count, 1);
}
#[tokio::test]
async fn test_get_article_by_title() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.service(get_article_by_title),
)
.await;
let article = Article::test_article();
let (article_id, article_title) = insert_test_article(&app_state, article.clone())
.await
.unwrap();
let req = test::TestRequest::with_uri("/article-by-title")
.header("Accept", "application/json")
.method(Method::GET)
.set_form(&ArticleTitleFormData {
title: article_title.to_owned(),
})
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let result: Article = test::read_body_json(resp).await;
assert_eq!(result.title, article_title);
let del_count = delete_test_article(&app_state, &article_id).await.unwrap();
assert_eq!(del_count, 1);
}
#[tokio::test]
async fn test_get_articles_by_category() {
dotenv::dotenv().ok();
let app_state = AppState::for_test().await;
let mut app = test::init_service(
App::new()
.app_data(Data::new(app_state.clone()))
.service(get_articles_by_category),
)
.await;
let article = Article::test_article();
let (article_id, article_title) = insert_test_article(&app_state, article.clone())
.await
.unwrap();
let req = test::TestRequest::with_uri("/articles/testing")
.header("Accept", "application/json")
.method(Method::GET)
.to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let results: Vec<Article> = test::read_body_json(resp).await;
let find_inserted = results.iter().find(|&art| art.title == article_title);
assert!(find_inserted.is_some());
let del_count = delete_test_article(&app_state, &article_id).await.unwrap();
assert_eq!(del_count, 1);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment