carts purchase first half

This commit is contained in:
SimonFJ20 2025-03-14 10:43:46 +01:00
parent b3d911685e
commit 44ef95ba63
17 changed files with 260 additions and 215 deletions

View File

@ -32,19 +32,6 @@ CREATE TABLE IF NOT EXISTS product_prices (
FOREIGN KEY(product) REFERENCES products(id)
);
CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY,
user INTEGER NOT NULL,
product INTEGER NOT NULL,
amount INTEGER NOT NULL,
FOREIGN KEY(user) REFERENCES users(id),
FOREIGN KEY(product) REFERENCES product(id)
);
INSERT OR REPLACE INTO users (name, email, password_hash, balance_dkk_cent)
VALUES ('User','test@email.com','08ce0220f6d63d85c3ac313e308f4fca35ecfb850baa8ddb924cfab98137b6b18b4a8e027067cb98802757df1337246a0f3aa25c44c2b788517a871086419dcf',10000);

View File

@ -5,18 +5,28 @@
#define MAYBE_UNUSED __attribute__((unused))
#define DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
#define DECLARE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, FN_SPECIFIER) \
typedef TYPE VEC_TYPE##T; \
typedef struct { \
VEC_TYPE##T* data; \
size_t capacity; \
size_t size; \
} VEC_TYPE;
} VEC_TYPE; \
FN_SPECIFIER int FN_PREFIX##_construct(VEC_TYPE* vec); \
FN_SPECIFIER void FN_PREFIX##_destroy(VEC_TYPE* vec); \
FN_SPECIFIER VEC_TYPE* FN_PREFIX##_new(void); \
FN_SPECIFIER void FN_PREFIX##_free(VEC_TYPE* vec); \
FN_SPECIFIER int FN_PREFIX##_push(VEC_TYPE* vec, VEC_TYPE##T value); \
FN_SPECIFIER VEC_TYPE##T* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx); \
FN_SPECIFIER const VEC_TYPE##T* FN_PREFIX##_at_const( \
const VEC_TYPE* vec, size_t idx); \
FN_SPECIFIER VEC_TYPE##T FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx);
#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
MAYBE_UNUSED static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \
#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, FN_SPECIFIER) \
FN_SPECIFIER int FN_PREFIX##_construct(VEC_TYPE* vec) \
{ \
const size_t capacity = INITIAL_CAPACITY; \
const size_t capacity \
= 8 / sizeof(VEC_TYPE##T) > 2 ? 8 / sizeof(VEC_TYPE##T) : 2; \
VEC_TYPE##T* data = malloc(sizeof(VEC_TYPE##T) * capacity); \
if (!data) \
return -1; \
@ -24,12 +34,12 @@
return 0; \
} \
\
MAYBE_UNUSED static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \
FN_SPECIFIER void FN_PREFIX##_destroy(VEC_TYPE* vec) \
{ \
free(vec->data); \
} \
\
MAYBE_UNUSED static inline VEC_TYPE* FN_PREFIX##_new(void) \
FN_SPECIFIER VEC_TYPE* FN_PREFIX##_new(void) \
{ \
VEC_TYPE* vec = malloc(sizeof(VEC_TYPE)); \
if (!vec) \
@ -40,14 +50,13 @@
return vec; \
} \
\
MAYBE_UNUSED static inline void FN_PREFIX##_free(VEC_TYPE* vec) \
FN_SPECIFIER void FN_PREFIX##_free(VEC_TYPE* vec) \
{ \
FN_PREFIX##_destroy(vec); \
free(vec); \
} \
\
MAYBE_UNUSED static inline int FN_PREFIX##_push( \
VEC_TYPE* vec, VEC_TYPE##T value) \
FN_SPECIFIER int FN_PREFIX##_push(VEC_TYPE* vec, VEC_TYPE##T value) \
{ \
if (vec->size + 1 > vec->capacity) { \
size_t new_capacity = vec->capacity * 2; \
@ -62,29 +71,25 @@
return 0; \
} \
\
MAYBE_UNUSED static inline VEC_TYPE##T* FN_PREFIX##_at( \
VEC_TYPE* vec, size_t idx) \
FN_SPECIFIER VEC_TYPE##T* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx) \
{ \
return &vec->data[idx]; \
} \
\
MAYBE_UNUSED static inline const VEC_TYPE##T* FN_PREFIX##_at_const( \
FN_SPECIFIER const VEC_TYPE##T* FN_PREFIX##_at_const( \
const VEC_TYPE* vec, size_t idx) \
{ \
return &vec->data[idx]; \
} \
\
MAYBE_UNUSED static inline VEC_TYPE##T FN_PREFIX##_get( \
const VEC_TYPE* vec, size_t idx) \
FN_SPECIFIER VEC_TYPE##T FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx) \
{ \
return vec->data[idx]; \
}
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY)
#define DEFINE_MAYBE_UNUSED
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX) \
DECLARE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, MAYBE_UNUSED static inline) \
DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, MAYBE_UNUSED static inline)
#define DEFINE_STATIC_QUEUE(TYPE, QUEUE_TYPE, FN_PREFIX) \
typedef struct { \

View File

@ -10,7 +10,7 @@
MAP_TYPE##Value value; \
} MAP_TYPE_Entry; \
\
DEFINE_VEC(MAP_TYPE_Entry, MAP_TYPE, FN_PREFIX##_entry_vec, 8) \
DEFINE_VEC(MAP_TYPE_Entry, MAP_TYPE, FN_PREFIX##_entry_vec) \
\
MAYBE_UNUSED static inline int FN_PREFIX##_construct(MAP_TYPE* map) \
{ \

View File

@ -1,32 +1,36 @@
#include "../models/models_json.h"
#include "../util/str.h"
#include "controllers.h"
#include <stdio.h>
void route_get_cart_items_from_session(HttpCtx* ctx)
void route_post_carts_purchase(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const Session* session = middleware_session(ctx);
if (!session)
return;
CartItemVec cart_items;
cart_item_vec_construct(&cart_items);
String res;
string_construct(&res);
string_push_str(&res, "{\"ok\":true,\"products\":[");
for (size_t i = 0; i < cart_items.size; ++i) {
if (i != 0) {
string_push_str(&res, ",");
}
char* json = cart_item_to_json_string(&cart_items.data[i]);
string_push_str(&res, json);
free(json);
const char* body_str = http_ctx_req_body(ctx);
JsonValue* body_json = json_parse(body_str, strlen(body_str));
if (!body_json) {
RESPOND_BAD_REQUEST(ctx, "bad request");
return;
}
string_push_str(&res, "]}");
cart_item_vec_destroy(&cart_items);
RESPOND_JSON(ctx, 200, "%s", res.data);
string_destroy(&res);
CartsPurchaseReq req;
int parse_result = carts_purchase_req_from_json(&req, body_json);
json_free(body_json);
if (parse_result != 0) {
RESPOND_BAD_REQUEST(ctx, "bad request");
return;
}
printf("product_id\tamount\n");
for (size_t i = 0; i < req.items.size; ++i) {
printf("%ld\t\t%ld\n",
req.items.data[i].product_id,
req.items.data[i].amount);
}
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
}

View File

@ -15,7 +15,7 @@ typedef struct {
void session_construct(Session* session, int64_t user_id);
void session_destroy(Session* session);
DEFINE_VEC(Session, SessionVec, session_vec, 16)
DEFINE_VEC(Session, SessionVec, session_vec)
typedef struct {
pthread_mutex_t mutex;
@ -37,7 +37,7 @@ void route_get_not_found(HttpCtx* ctx);
void route_get_products_all(HttpCtx* ctx);
void route_get_cart_items_from_session(HttpCtx* ctx);
void route_post_carts_purchase(HttpCtx* ctx);
void route_post_users_register(HttpCtx* ctx);

View File

@ -12,8 +12,8 @@ void route_post_sessions_login(HttpCtx* ctx)
JsonValue* body_json = json_parse(body_str, strlen(body_str));
AuthLoginReq req;
int parse_res = auth_login_req_from_json(&req, body_json);
SessionsLoginReq req;
int parse_res = sessions_login_req_from_json(&req, body_json);
json_free(body_json);
if (parse_res != 0) {
@ -48,7 +48,7 @@ void route_post_sessions_login(HttpCtx* ctx)
l2_return:
user_destroy(&user);
l0_return:
auth_login_req_destroy(&req);
sessions_login_req_destroy(&req);
}
void route_post_sessions_logout(HttpCtx* ctx)

View File

@ -4,9 +4,8 @@
#include "../models/models.h"
#include <stdint.h>
DEFINE_VEC(int64_t, Ids, ids, 8)
DEFINE_VEC(Product, ProductVec, product_vec, 32)
DEFINE_VEC(CartItem, CartItemVec, cart_item_vec, 32)
DEFINE_VEC(int64_t, Ids, ids)
DEFINE_VEC(Product, ProductVec, product_vec)
typedef enum {
DbRes_Ok,

View File

@ -21,4 +21,4 @@ typedef struct {
char* value;
} Header;
DEFINE_VEC(Header, HeaderVec, header_vec, 8)
DEFINE_VEC(Header, HeaderVec, header_vec)

View File

@ -21,7 +21,7 @@ typedef struct {
HttpHandlerFn handler;
} Handler;
DEFINE_VEC(Handler, HandlerVec, handler_vec, 8)
DEFINE_VEC(Handler, HandlerVec, handler_vec)
struct HttpServer {
int fd;

View File

@ -11,8 +11,8 @@ typedef struct {
JsonValue* val;
} KV;
DEFINE_VEC(JsonValue*, Arr, arr, 4)
DEFINE_VEC(KV, Obj, obj, 4)
DEFINE_VEC(JsonValue*, Arr, arr)
DEFINE_VEC(KV, Obj, obj)
struct JsonValue {
JsonType type;

View File

@ -39,7 +39,7 @@ int main(void)
http_server_get(server, "/api/products/all", route_get_products_all);
http_server_get(server, "/api/cart", route_get_cart_items_from_session);
http_server_post(server, "/api/carts/purchase", route_post_carts_purchase);
http_server_post(server, "/api/users/register", route_post_users_register);
http_server_post(server, "/api/sessions/login", route_post_sessions_login);

View File

@ -1,5 +1,6 @@
#include "models.h"
#include "../json/json.h"
#include "../util/panic.h"
#include "../util/str.h"
#include "models_json.h"
#include <assert.h>
@ -38,13 +39,6 @@ void product_price_destroy(ProductPrice* m)
(void)m;
}
void cart_item_destroy(CartItem* m)
{
static_assert(sizeof(CartItem) == 32, "model has changed");
(void)m;
}
void users_register_req_destroy(UsersRegisterReq* model)
{
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
@ -54,14 +48,21 @@ void users_register_req_destroy(UsersRegisterReq* model)
free(model->password);
}
void auth_login_req_destroy(AuthLoginReq* model)
void sessions_login_req_destroy(SessionsLoginReq* model)
{
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
free(model->email);
free(model->password);
}
void carts_purchase_req_destroy(CartsPurchaseReq* model)
{
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
carts_item_vec_destroy(&model->items);
}
char* user_to_json_string(const User* m)
{
static_assert(sizeof(User) == 40, "model has changed");
@ -156,29 +157,6 @@ char* product_price_to_json_string(const ProductPrice* m)
return result;
}
char* cart_item_to_json_string(const CartItem* m)
{
static_assert(sizeof(CartItem) == 32, "model has changed");
String string;
string_construct(&string);
string_pushf(&string,
"{"
"\"id\":%ld,"
"\"user_id\":%ld,"
"\"product_id\":%ld,"
"\"amount\":%ld"
"}",
m->id,
m->user_id,
m->product_id,
m->amount);
char* result = string_copy(&string);
string_destroy(&string);
return result;
}
char* users_register_req_to_json(const UsersRegisterReq* m)
{
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
@ -200,9 +178,9 @@ char* users_register_req_to_json(const UsersRegisterReq* m)
return result;
}
char* auth_login_req_to_json(const AuthLoginReq* m)
char* sessions_login_req_to_json(const SessionsLoginReq* m)
{
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
String string;
string_construct(&string);
@ -219,6 +197,13 @@ char* auth_login_req_to_json(const AuthLoginReq* m)
return result;
}
char* carts_purchase_req_to_json(const CartsPurchaseReq* m)
{
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
PANIC("not implemented");
}
typedef struct {
const char* key;
JsonType type;
@ -241,8 +226,14 @@ static inline bool obj_conforms(
return true;
}
#define GET_INT(K) json_int(json_object_get(json, K))
#define GET_STR(K) str_dup(json_string(json_object_get(json, K)))
#define OBJ_GET_INT(JSON, K) json_int(json_object_get(JSON, K))
#define OBJ_GET_STR(JSON, K) str_dup(json_string(json_object_get(JSON, K)))
#define OBJ_CONFORMS(JSON, FIELDS) \
obj_conforms(JSON, FIELDS, sizeof(FIELDS) / sizeof(FIELDS[0]))
#define GET_INT(K) OBJ_GET_INT(json, K)
#define GET_STR(K) OBJ_GET_STR(json, K)
int user_from_json(User* m, const JsonValue* json)
{
@ -255,7 +246,7 @@ int user_from_json(User* m, const JsonValue* json)
{ "password_hash", JsonType_String },
{ "balance_dkk_cent", JsonType_Number },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (User) {
.id = GET_INT("id"),
@ -276,7 +267,7 @@ int coord_from_json(Coord* m, const JsonValue* json)
{ "x", JsonType_Number },
{ "y", JsonType_Number },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (Coord) {
.id = GET_INT("id"),
@ -298,7 +289,7 @@ int product_from_json(Product* m, const JsonValue* json)
{ "coord_id", JsonType_Number },
{ "barcode", JsonType_String },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (Product) {
.id = GET_INT("id"),
@ -320,7 +311,7 @@ int product_price_from_json(ProductPrice* m, const JsonValue* json)
{ "product_id", JsonType_Number },
{ "price_dkk_cent", JsonType_Number },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (ProductPrice) {
.id = GET_INT("id"),
@ -330,27 +321,6 @@ int product_price_from_json(ProductPrice* m, const JsonValue* json)
return 0;
}
int cart_item_from_json(CartItem* m, const JsonValue* json)
{
static_assert(sizeof(CartItem) == 32, "model has changed");
ObjField fields[] = {
{ "id", JsonType_Number },
{ "user_id", JsonType_Number },
{ "product_id", JsonType_Number },
{ "amount", JsonType_Number },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
return -1;
*m = (CartItem) {
.id = GET_INT("id"),
.user_id = GET_INT("user_id"),
.product_id = GET_INT("product_id"),
.amount = GET_INT("amount"),
};
return 0;
}
int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
{
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
@ -360,7 +330,7 @@ int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
{ "email", JsonType_String },
{ "password", JsonType_String },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (UsersRegisterReq) {
.name = GET_STR("name"),
@ -370,19 +340,60 @@ int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
return 0;
}
int auth_login_req_from_json(AuthLoginReq* m, const JsonValue* json)
int sessions_login_req_from_json(SessionsLoginReq* m, const JsonValue* json)
{
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
static_assert(sizeof(SessionsLoginReq) == 16, "model has changed");
ObjField fields[] = {
{ "email", JsonType_String },
{ "password", JsonType_String },
};
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (AuthLoginReq) {
*m = (SessionsLoginReq) {
.email = GET_STR("email"),
.password = GET_STR("password"),
};
return 0;
}
int carts_purchase_req_from_json(CartsPurchaseReq* m, const JsonValue* json)
{
static_assert(sizeof(CartsPurchaseReq) == 24, "model has changed");
ObjField fields[] = {
{ "items", JsonType_Array },
};
if (!OBJ_CONFORMS(json, fields))
return -1;
*m = (CartsPurchaseReq) {
.items = (CartsItemVec) { 0 },
};
carts_item_vec_construct(&m->items);
const JsonValue* items = json_object_get(json, "items");
size_t items_size = json_array_size(items);
for (size_t i = 0; i < items_size; ++i) {
const JsonValue* item = json_array_get(items, i);
ObjField item_fields[] = {
{ "product_id", JsonType_Number },
{ "amount", JsonType_Number },
};
if (!OBJ_CONFORMS(item, item_fields)) {
carts_item_vec_destroy(&m->items);
return -1;
}
carts_item_vec_push(&m->items,
(CartsItem) {
.product_id = OBJ_GET_INT(item, "product_id"),
.amount = OBJ_GET_INT(item, "amount"),
});
}
return 0;
}
DEFINE_VEC_IMPL(CartsItem, CartsItemVec, carts_item_vec, )

View File

@ -1,5 +1,6 @@
#pragma once
#include "../collections/collection.h"
#include <stdint.h>
typedef struct {
@ -31,18 +32,10 @@ typedef struct {
int64_t price_dkk_cent;
} ProductPrice;
typedef struct {
int64_t id;
int64_t user_id;
int64_t product_id;
int64_t amount;
} CartItem;
void user_destroy(User* model);
void coord_destroy(Coord* model);
void product_destroy(Product* model);
void product_price_destroy(ProductPrice* model);
void cart_item_destroy(CartItem* model);
//
@ -52,10 +45,24 @@ typedef struct {
char* password;
} UsersRegisterReq;
void users_register_req_destroy(UsersRegisterReq* model);
typedef struct {
char* email;
char* password;
} AuthLoginReq;
} SessionsLoginReq;
void users_register_req_destroy(UsersRegisterReq* model);
void auth_login_req_destroy(AuthLoginReq* model);
void sessions_login_req_destroy(SessionsLoginReq* model);
typedef struct {
int64_t product_id;
int64_t amount;
} CartsItem;
DECLARE_VEC_TYPE(CartsItem, CartsItemVec, carts_item_vec, )
typedef struct {
CartsItemVec items;
} CartsPurchaseReq;
void carts_purchase_req_destroy(CartsPurchaseReq* model);

View File

@ -9,7 +9,7 @@ DEFINE_MODEL_JSON(User, user)
DEFINE_MODEL_JSON(Coord, coord)
DEFINE_MODEL_JSON(Product, product)
DEFINE_MODEL_JSON(ProductPrice, product_price)
DEFINE_MODEL_JSON(CartItem, cart_item)
DEFINE_MODEL_JSON(UsersRegisterReq, users_register_req)
DEFINE_MODEL_JSON(AuthLoginReq, auth_login_req)
DEFINE_MODEL_JSON(SessionsLoginReq, sessions_login_req)
DEFINE_MODEL_JSON(CartsPurchaseReq, carts_purchase_req)

View File

@ -23,7 +23,7 @@ typedef struct {
StrSplitter str_splitter(const char* text, size_t text_len, const char* split);
StrSlice str_split_next(StrSplitter* splitter);
DEFINE_VEC(char, String, string, 8)
DEFINE_VEC(char, String, string)
void string_push_str(String* string, const char* str);
void string_push_fmt_va(String* string, const char* fmt, ...);
@ -31,7 +31,7 @@ char* string_copy(const String* string);
#define string_pushf(STRING, ...) string_push_fmt_va(STRING, __VA_ARGS__)
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
DEFINE_VEC(char*, RawStrVec, rawstr_vec)
#define MAX_HASH_INPUT_LEN 256 - 1

100
backend/test/test.ts Normal file
View File

@ -0,0 +1,100 @@
import { assertEquals, assertMatch, assertNotEquals } from "jsr:@std/assert";
const url = `http://127.0.0.1:8080`;
// const url = `http://10.135.51.114:8080`;
const name = "Maksim";
const email = `mash.skp_${Math.floor(Math.random() * 100000)}@edu.mercantec.dk`;
const password = "Merc1234";
Deno.test("test backend", async (t) => {
await t.step("test /api/users/register", async () => {
const registerRes = await post<{ ok: boolean }>(
`/api/users/register`,
{ name, email, password },
);
assertEquals(registerRes, { ok: true });
});
let token: string | undefined = undefined;
await t.step("test /api/sessions/login", async () => {
const loginRes = await post<{
ok: true;
token: string;
}>(
"/api/sessions/login",
{ email, password },
);
assertEquals(loginRes.ok, true);
assertMatch(loginRes.token, /^[0-9a-zA-Z]+$/);
token = loginRes.token;
});
if (!token) {
return;
}
await t.step("test /api/sessions/user", async () => {
const sessionUserRes = await get<{
ok: boolean;
user: unknown;
}>(
"/api/sessions/user",
{ "Session-Token": token! },
);
assertEquals(sessionUserRes.ok, true);
// console.log(sessionUserRes.user);
});
await testCarts(t, token);
await t.step("test /api/sessions/logout", async () => {
const logoutRes = await post<{ ok: boolean }>(
"/api/sessions/logout",
{},
{ "Session-Token": token! },
);
assertEquals(logoutRes, { ok: true });
});
});
async function testCarts(t: Deno.TestContext, token: string) {
await t.step("test /api/carts/purchase", async () => {
const res = await post<{ ok: boolean }>(
"/api/carts/purchase",
{
items: [
{ product_id: 1, amount: 2 },
{ product_id: 2, amount: 5 },
],
},
{ "Session-Token": token },
);
assertEquals(res, { ok: true });
});
}
function get<Res>(
path: string,
headers: Record<string, string>,
): Promise<Res> {
return fetch(`${url}${path}`, { headers })
.then((res) => res.json());
}
function post<Res, Req = unknown>(
path: string,
body: Req,
headers: Record<string, string> = {},
): Promise<Res> {
return fetch(`${url}${path}`, {
method: "post",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then((res) => res.json());
}

View File

@ -1,68 +0,0 @@
import { assertEquals, assertMatch } from "jsr:@std/assert";
const url = `http://127.0.0.1:8080`;
// const url = `http://10.135.51.114:8080`;
const name = "Maksim";
const email = `mash.skp_${Math.floor(Math.random() * 100000)}@edu.mercantec.dk`;
const password = "Merc1234";
Deno.test("test", async () => {
const registerRes = await post<{ ok: boolean }>(
`/api/users/register`,
{ name, email, password },
);
assertEquals(registerRes, { ok: true });
const loginRes = await post<{
ok: true;
token: string;
}>(
"/api/sessions/login",
{ email, password },
);
assertEquals(loginRes.ok, true);
assertMatch(loginRes.token, /^[0-9a-zA-Z]+$/);
const token = loginRes.token;
const sessionUserRes = await get<{
ok: boolean;
user: unknown;
}>(
"/api/sessions/user",
{ "Session-Token": token },
);
assertEquals(sessionUserRes.ok, true);
console.log(sessionUserRes.user);
const logoutRes = await post<{ ok: boolean }>(
"/api/sessions/logout",
{},
{ "Session-Token": token },
);
assertEquals(logoutRes, { ok: true });
});
function get<Res>(
path: string,
headers: Record<string, string>,
): Promise<Res> {
return fetch(`${url}${path}`, { headers })
.then((res) => res.json());
}
function post<Res, Req = unknown>(
path: string,
body: Req,
headers: Record<string, string> = {},
): Promise<Res> {
return fetch(`${url}${path}`, {
method: "post",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then((res) => res.json());
}