Improve tests
This commit is contained in:
parent
38bed6f835
commit
4097e92268
1
.env
1
.env
@ -1,3 +1,4 @@
|
|||||||
DATABASE_URL=postgres://postgres@localhost/bazzar
|
DATABASE_URL=postgres://postgres@localhost/bazzar
|
||||||
PASS_SALT=18CHwV7eGFAea16z+qMKZg
|
PASS_SALT=18CHwV7eGFAea16z+qMKZg
|
||||||
RUST_LOG=debug
|
RUST_LOG=debug
|
||||||
|
KEY_SECRET="NEPJs#8jjn8SK8GC7QEC^*P844UgsyEbQB8mRWXkT%3mPrwewZoc25MMby9H#R*w2KzaQgMkk#Pif$kxrLy*N5L!Ch%jxbWoa%gb"
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset > label {
|
fieldset > label {
|
||||||
width: 45%;
|
width: 45%;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset > input, fieldset > textarea {
|
fieldset > input, fieldset > textarea {
|
||||||
width: 54%;
|
width: 54%;
|
||||||
}
|
}
|
||||||
@ -31,7 +33,15 @@
|
|||||||
<body>
|
<body>
|
||||||
<div style="display: flex;justify-content: space-between;">
|
<div style="display: flex;justify-content: space-between;">
|
||||||
<div style="width: 49%">
|
<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>
|
<fieldset>
|
||||||
<label for="method">Method</label>
|
<label for="method">Method</label>
|
||||||
<select id="method">
|
<select id="method">
|
||||||
@ -51,7 +61,11 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 49%">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
@ -62,6 +76,46 @@
|
|||||||
const urlEl = form.querySelector('#path');
|
const urlEl = form.querySelector('#path');
|
||||||
const paramsEl = form.querySelector('#params');
|
const paramsEl = form.querySelector('#params');
|
||||||
const mthEl = form.querySelector('#method');
|
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) => {
|
form.addEventListener('submit', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -74,21 +128,15 @@
|
|||||||
paramsEl.value.split("\n").forEach(s => {
|
paramsEl.value.split("\n").forEach(s => {
|
||||||
if (!s.length) return;
|
if (!s.length) return;
|
||||||
let [k, ...v] = s.split("=");
|
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'
|
send(method, path, params)
|
||||||
? {}
|
|
||||||
: { 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');
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -152,7 +152,11 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn server(opts: ServerOpts) -> Result<()> {
|
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 redis_connection_string = "127.0.0.1:6379";
|
||||||
|
|
||||||
let config = Arc::new(Config::load());
|
let config = Arc::new(Config::load());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
use derive_more::{Deref, Display};
|
use derive_more::{Deref, Display, From};
|
||||||
use serde::de::{Error, Visitor};
|
use serde::de::{Error, Visitor};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
@ -61,29 +61,21 @@ pub enum ShoppingCartState {
|
|||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct PriceMajor(NonNegative);
|
pub struct PriceMajor(NonNegative);
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct PriceMinor(NonNegative);
|
pub struct PriceMinor(NonNegative);
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Deref, From)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Quantity(NonNegative);
|
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 {
|
impl TryFrom<i32> for Quantity {
|
||||||
type Error = TransformError;
|
type Error = TransformError;
|
||||||
|
|
||||||
@ -112,7 +104,7 @@ impl<'de> serde::Deserialize<'de> for Email {
|
|||||||
type Value = String;
|
type Value = String;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
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>
|
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;
|
type Value = i32;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
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>
|
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
|
||||||
|
@ -14,7 +14,7 @@ use actix_web::web::{Data, Json, ServiceConfig};
|
|||||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[get("products")]
|
#[get("/products")]
|
||||||
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
session.require_admin()?;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ pub struct UpdateProduct {
|
|||||||
pub price_minor: PriceMinor,
|
pub price_minor: PriceMinor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[patch("product")]
|
#[patch("/product")]
|
||||||
async fn update_product(
|
async fn update_product(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
@ -56,7 +56,6 @@ async fn update_product(
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct CreateProduct {
|
pub struct CreateProduct {
|
||||||
pub id: ProductId,
|
|
||||||
pub name: ProductName,
|
pub name: ProductName,
|
||||||
pub short_description: ProductShortDesc,
|
pub short_description: ProductShortDesc,
|
||||||
pub long_description: ProductLongDesc,
|
pub long_description: ProductLongDesc,
|
||||||
@ -65,7 +64,7 @@ pub struct CreateProduct {
|
|||||||
pub price_minor: PriceMinor,
|
pub price_minor: PriceMinor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("product")]
|
#[post("/product")]
|
||||||
async fn create_product(
|
async fn create_product(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
@ -91,7 +90,7 @@ pub struct DeleteProduct {
|
|||||||
pub id: ProductId,
|
pub id: ProductId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("product")]
|
#[delete("/product")]
|
||||||
async fn delete_product(
|
async fn delete_product(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
|
@ -11,7 +11,7 @@ use actix_web::web::{Data, Json, ServiceConfig};
|
|||||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[get("stocks")]
|
#[get("/stocks")]
|
||||||
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
session.require_admin()?;
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ pub struct UpdateStock {
|
|||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[patch("stock")]
|
#[patch("/stock")]
|
||||||
async fn update_stock(
|
async fn update_stock(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
@ -47,13 +47,12 @@ async fn update_stock(
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct CreateStock {
|
pub struct CreateStock {
|
||||||
pub id: StockId,
|
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("stock")]
|
#[post("/stock")]
|
||||||
async fn create_stock(
|
async fn create_stock(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
@ -76,7 +75,7 @@ pub struct DeleteStock {
|
|||||||
pub id: StockId,
|
pub id: StockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("stock")]
|
#[delete("/stock")]
|
||||||
async fn delete_stock(
|
async fn delete_stock(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
|
Loading…
Reference in New Issue
Block a user