From 44ef95ba6346365ad0a350a2f518e2e3533d537a Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Fri, 14 Mar 2025 10:43:46 +0100 Subject: [PATCH] carts purchase first half --- backend/prepare.sql | 13 --- backend/src/collections/collection.h | 45 ++++---- backend/src/collections/kv_map.h | 2 +- backend/src/controllers/carts.c | 42 ++++---- backend/src/controllers/controllers.h | 4 +- backend/src/controllers/sessions.c | 6 +- backend/src/db/db.h | 5 +- backend/src/http/packet.h | 2 +- backend/src/http/server.h | 2 +- backend/src/json/json.c | 4 +- backend/src/main.c | 2 +- backend/src/models/models.c | 143 ++++++++++++++------------ backend/src/models/models.h | 29 ++++-- backend/src/models/models_json.h | 4 +- backend/src/util/str.h | 4 +- backend/test/test.ts | 100 ++++++++++++++++++ backend/test/test_authentication.ts | 68 ------------ 17 files changed, 260 insertions(+), 215 deletions(-) create mode 100644 backend/test/test.ts delete mode 100644 backend/test/test_authentication.ts diff --git a/backend/prepare.sql b/backend/prepare.sql index 2f9fce4..d5c2bae 100644 --- a/backend/prepare.sql +++ b/backend/prepare.sql @@ -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); diff --git a/backend/src/collections/collection.h b/backend/src/collections/collection.h index 4bcc58c..7aa0c14 100644 --- a/backend/src/collections/collection.h +++ b/backend/src/collections/collection.h @@ -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 { \ diff --git a/backend/src/collections/kv_map.h b/backend/src/collections/kv_map.h index 5aaf568..be2bf5a 100644 --- a/backend/src/collections/kv_map.h +++ b/backend/src/collections/kv_map.h @@ -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) \ { \ diff --git a/backend/src/controllers/carts.c b/backend/src/controllers/carts.c index fff5f69..2d2f32f 100644 --- a/backend/src/controllers/carts.c +++ b/backend/src/controllers/carts.c @@ -1,32 +1,36 @@ #include "../models/models_json.h" #include "../util/str.h" #include "controllers.h" +#include -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}"); } diff --git a/backend/src/controllers/controllers.h b/backend/src/controllers/controllers.h index 3df36cd..617c9e8 100644 --- a/backend/src/controllers/controllers.h +++ b/backend/src/controllers/controllers.h @@ -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); diff --git a/backend/src/controllers/sessions.c b/backend/src/controllers/sessions.c index 224ca9f..6edc744 100644 --- a/backend/src/controllers/sessions.c +++ b/backend/src/controllers/sessions.c @@ -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) diff --git a/backend/src/db/db.h b/backend/src/db/db.h index 1646fea..b54aef2 100644 --- a/backend/src/db/db.h +++ b/backend/src/db/db.h @@ -4,9 +4,8 @@ #include "../models/models.h" #include -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, diff --git a/backend/src/http/packet.h b/backend/src/http/packet.h index 8ecc2c6..2223190 100644 --- a/backend/src/http/packet.h +++ b/backend/src/http/packet.h @@ -21,4 +21,4 @@ typedef struct { char* value; } Header; -DEFINE_VEC(Header, HeaderVec, header_vec, 8) +DEFINE_VEC(Header, HeaderVec, header_vec) diff --git a/backend/src/http/server.h b/backend/src/http/server.h index b97bb74..b4e0056 100644 --- a/backend/src/http/server.h +++ b/backend/src/http/server.h @@ -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; diff --git a/backend/src/json/json.c b/backend/src/json/json.c index 19e9c12..b25e84e 100644 --- a/backend/src/json/json.c +++ b/backend/src/json/json.c @@ -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; diff --git a/backend/src/main.c b/backend/src/main.c index cde38d9..b8c3175 100644 --- a/backend/src/main.c +++ b/backend/src/main.c @@ -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); diff --git a/backend/src/models/models.c b/backend/src/models/models.c index 93b4c8a..9bc4d61 100644 --- a/backend/src/models/models.c +++ b/backend/src/models/models.c @@ -1,5 +1,6 @@ #include "models.h" #include "../json/json.h" +#include "../util/panic.h" #include "../util/str.h" #include "models_json.h" #include @@ -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, ) diff --git a/backend/src/models/models.h b/backend/src/models/models.h index 219a0ce..ecb18be 100644 --- a/backend/src/models/models.h +++ b/backend/src/models/models.h @@ -1,5 +1,6 @@ #pragma once +#include "../collections/collection.h" #include 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); diff --git a/backend/src/models/models_json.h b/backend/src/models/models_json.h index 7c63df1..37265f4 100644 --- a/backend/src/models/models_json.h +++ b/backend/src/models/models_json.h @@ -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) diff --git a/backend/src/util/str.h b/backend/src/util/str.h index ae26315..4c51e81 100644 --- a/backend/src/util/str.h +++ b/backend/src/util/str.h @@ -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 diff --git a/backend/test/test.ts b/backend/test/test.ts new file mode 100644 index 0000000..06c582c --- /dev/null +++ b/backend/test/test.ts @@ -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( + path: string, + headers: Record, +): Promise { + return fetch(`${url}${path}`, { headers }) + .then((res) => res.json()); +} + +function post( + path: string, + body: Req, + headers: Record = {}, +): Promise { + return fetch(`${url}${path}`, { + method: "post", + headers: { ...headers, "Content-Type": "application/json" }, + body: JSON.stringify(body), + }).then((res) => res.json()); +} diff --git a/backend/test/test_authentication.ts b/backend/test/test_authentication.ts deleted file mode 100644 index e61e92d..0000000 --- a/backend/test/test_authentication.ts +++ /dev/null @@ -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( - path: string, - headers: Record, -): Promise { - return fetch(`${url}${path}`, { headers }) - .then((res) => res.json()); -} - -function post( - path: string, - body: Req, - headers: Record = {}, -): Promise { - return fetch(`${url}${path}`, { - method: "post", - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(body), - }).then((res) => res.json()); -}