Improve tests

This commit is contained in:
eraden 2022-04-17 22:15:09 +02:00
parent 38bed6f835
commit 4097e92268
6 changed files with 83 additions and 40 deletions

1
.env
View File

@ -1,3 +1,4 @@
DATABASE_URL=postgres://postgres@localhost/bazzar
PASS_SALT=18CHwV7eGFAea16z+qMKZg
RUST_LOG=debug
KEY_SECRET="NEPJs#8jjn8SK8GC7QEC^*P844UgsyEbQB8mRWXkT%3mPrwewZoc25MMby9H#R*w2KzaQgMkk#Pif$kxrLy*N5L!Ch%jxbWoa%gb"

View File

@ -17,9 +17,11 @@
justify-content: space-between;
width: 600px;
}
fieldset > label {
width: 45%;
}
fieldset > input, fieldset > textarea {
width: 54%;
}
@ -31,7 +33,15 @@
<body>
<div style="display: flex;justify-content: space-between;">
<div style="width: 49%">
<form>
<form style="width: 100%">
<fieldset>
<label for="op">Operation</label>
<select name="op" id="op">
<option value="auto-login">Auto login</option>
<option value="get-products">Get products</option>
<option value="create-product">Create product</option>
</select>
</fieldset>
<fieldset>
<label for="method">Method</label>
<select id="method">
@ -51,7 +61,11 @@
</form>
</div>
<div style="width: 49%">
<pre style="background: black; width: 100%; min-height: 300px"><code id="output" class="language-json"></code></pre>
<pre style="background: black; width: 100%; min-height: 300px"><code
id="output"
class="language-json"
style="overflow: auto"
></code></pre>
</div>
</div>
<script>
@ -62,6 +76,46 @@
const urlEl = form.querySelector('#path');
const paramsEl = form.querySelector('#params');
const mthEl = form.querySelector('#method');
const opEL = form.querySelector('#op');
const send = (method, path, params) => {
const rest = method === 'GET'
? {}
: { body: JSON.stringify(params), headers: { 'Content-Type': 'application/json' } };
path = method === 'GET'
? `${ path }?${ JSON.stringify(params) }`
: path;
fetch(`${ path }`, { ...rest, method })
.then(async (res) => {
if (res.status < 400) {
const json = await res.json();
out.innerHTML = Prism.highlight(JSON.stringify(json, null, 4), Prism.languages.json, 'json');
} else {
out.innerHTML = await res.text();
}
})
}
opEL.addEventListener('change', () => {
switch (opEL.value) {
case 'auto-login':
paramsEl.value = `login=Eraden\npassword=text`
return send(mthEl.value = 'POST', urlEl.value = '/admin/sign-in', { login: 'Eraden', password: 'test' })
case 'get-products':
return send(mthEl.value = 'GET', urlEl.value = '/admin/api/v1/products', {})
case 'create-product':
const p = {
name: 'Foo',
short_description: 'asd',
long_description: 'asjdoiajd ajio djaso idja s',
price_major: 12,
price_minor: 0,
};
paramsEl.value = Object.entries(p).map((k, v) => `${ k }=${ v }}`).join('\n');
return send(mthEl.value = 'POST', urlEl.value = '/admin/api/v1/product', p)
}
})
form.addEventListener('submit', (ev) => {
ev.preventDefault();
@ -74,21 +128,15 @@
paramsEl.value.split("\n").forEach(s => {
if (!s.length) return;
let [k, ...v] = s.split("=");
params[k] = Array(v).join('=');
v = Array(v).join('=')
try {
v = JSON.parse(v);
} catch (_) {
}
params[k] = v;
});
const rest = method === 'GET'
? {}
: { body: JSON.stringify(params), headers: { 'Content-Type': 'application/json' } };
path = method === 'GET'
? `${ path }?${ JSON.stringify(params) }`
: path;
fetch(`${ path }`, { ...rest, method })
.then(res => res.json())
.then(json => {
out.innerHTML = Prism.highlight(JSON.stringify(json), Prism.languages.json, 'json');
})
send(method, path, params)
});
</script>
</body>

View File

@ -152,7 +152,11 @@ impl Config {
}
async fn server(opts: ServerOpts) -> Result<()> {
let secret_key = Key::generate();
let secret_key = {
let key_secret = std::env::var("KEY_SECRET")
.expect("session requires secret key with 64 or more characters");
Key::from(key_secret.as_bytes())
};
let redis_connection_string = "127.0.0.1:6379";
let config = Arc::new(Config::load());

View File

@ -1,6 +1,6 @@
use std::fmt::Formatter;
use derive_more::{Deref, Display};
use derive_more::{Deref, Display, From};
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
@ -61,29 +61,21 @@ pub enum ShoppingCartState {
Closed,
}
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
#[sqlx(transparent)]
#[serde(transparent)]
pub struct PriceMajor(NonNegative);
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
#[sqlx(transparent)]
#[serde(transparent)]
pub struct PriceMinor(NonNegative);
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
#[sqlx(transparent)]
#[serde(transparent)]
pub struct Quantity(NonNegative);
impl TryFrom<NonNegative> for Quantity {
type Error = TransformError;
fn try_from(value: NonNegative) -> Result<Self, Self::Error> {
Ok(Self(value))
}
}
impl TryFrom<i32> for Quantity {
type Error = TransformError;
@ -112,7 +104,7 @@ impl<'de> serde::Deserialize<'de> for Email {
type Value = String;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("this is not valid e-mail address")
formatter.write_str("valid e-mail address")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
@ -158,7 +150,7 @@ impl<'de> serde::Deserialize<'de> for NonNegative {
type Value = i32;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("this is not valid e-mail address")
formatter.write_str("value equal or greater than 0")
}
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>

View File

@ -14,7 +14,7 @@ use actix_web::web::{Data, Json, ServiceConfig};
use actix_web::{delete, get, patch, post, HttpResponse};
use serde::Deserialize;
#[get("products")]
#[get("/products")]
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
session.require_admin()?;
@ -32,7 +32,7 @@ pub struct UpdateProduct {
pub price_minor: PriceMinor,
}
#[patch("product")]
#[patch("/product")]
async fn update_product(
session: Session,
db: Data<Addr<Database>>,
@ -56,7 +56,6 @@ async fn update_product(
#[derive(Deserialize)]
pub struct CreateProduct {
pub id: ProductId,
pub name: ProductName,
pub short_description: ProductShortDesc,
pub long_description: ProductLongDesc,
@ -65,7 +64,7 @@ pub struct CreateProduct {
pub price_minor: PriceMinor,
}
#[post("product")]
#[post("/product")]
async fn create_product(
session: Session,
db: Data<Addr<Database>>,
@ -91,7 +90,7 @@ pub struct DeleteProduct {
pub id: ProductId,
}
#[delete("product")]
#[delete("/product")]
async fn delete_product(
session: Session,
db: Data<Addr<Database>>,

View File

@ -11,7 +11,7 @@ use actix_web::web::{Data, Json, ServiceConfig};
use actix_web::{delete, get, patch, post, HttpResponse};
use serde::Deserialize;
#[get("stocks")]
#[get("/stocks")]
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
session.require_admin()?;
@ -26,7 +26,7 @@ pub struct UpdateStock {
pub quantity_unit: QuantityUnit,
}
#[patch("stock")]
#[patch("/stock")]
async fn update_stock(
session: Session,
db: Data<Addr<Database>>,
@ -47,13 +47,12 @@ async fn update_stock(
#[derive(Deserialize)]
pub struct CreateStock {
pub id: StockId,
pub product_id: ProductId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}
#[post("stock")]
#[post("/stock")]
async fn create_stock(
session: Session,
db: Data<Addr<Database>>,
@ -76,7 +75,7 @@ pub struct DeleteStock {
pub id: StockId,
}
#[delete("stock")]
#[delete("/stock")]
async fn delete_stock(
session: Session,
db: Data<Addr<Database>>,