/* * test_storage_smoke.c — P0.4: flatfile + SQLite core smoke * * Tests: open/close, register_user, find_user, resolve_user, * count_level, update_user, delete_user, session_begin/end. * Runs against both flatfile and SQLite backends in temporary directories. */ #include "hybbx/storage.h" #include "hybbx/auth.h" #include "hybbx/util.h" #include #include #include #include #include static unsigned g_failures; static void check_rc(const char *label, hybbx_result_t got, hybbx_result_t want) { if (got != want) { fprintf(stderr, "FAIL %s: got %d want %d\n", label, (int)got, (int)want); g_failures++; } } static void check_str(const char *label, const char *got, const char *want) { if (got == NULL || want == NULL || strcmp(got, want) != 0) { fprintf(stderr, "FAIL %s: got '%s' want '%s'\n", label, got != NULL ? got : "(null)", want); g_failures++; } } static void check_uint64(const char *label, uint64_t got, uint64_t want) { if (got != want) { fprintf(stderr, "FAIL %s: got %llu want %llu\n", label, (unsigned long long)got, (unsigned long long)want); g_failures++; } } static void check_nonzero(const char *label, uint64_t val) { if (val == 0) { fprintf(stderr, "FAIL %s: expected non-zero\n", label); g_failures++; } } static void mkdir_p(const char *path) { char tmp[512]; char *p; size_t len; hybbx_strlcpy(tmp, path, sizeof(tmp)); len = strlen(tmp); if (len > 0 && tmp[len - 1] == '/') { tmp[len - 1] = '\0'; } for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = '\0'; mkdir(tmp, 0755); *p = '/'; } } mkdir(tmp, 0755); } static void rm_rf(const char *path) { char cmd[512]; snprintf(cmd, sizeof(cmd), "rm -rf '%s'", path); (void)system(cmd); } static hybbx_storage_t *open_backend(hybbx_storage_backend_kind_t backend, const char *path) { hybbx_storage_options_t opts; memset(&opts, 0, sizeof(opts)); opts.backend = backend; opts.path = path; return hybbx_storage_open(&opts); } /* * Run the full CRUD + session smoke against one backend. */ static void smoke_backend(hybbx_storage_backend_kind_t backend, const char *label, const char *path) { hybbx_storage_t *st; hybbx_user_registration_t reg; hybbx_user_record_t user; hybbx_user_record_t found; hybbx_session_record_t sess; size_t count; char test_user[64]; snprintf(test_user, sizeof(test_user), "smk_%s", label); rm_rf(path); mkdir_p(path); st = open_backend(backend, path); if (st == NULL) { fprintf(stderr, "FAIL %s: storage_open returned NULL\n", label); g_failures++; return; } check_uint64("backend kind", (uint64_t)hybbx_storage_backend(st), (uint64_t)backend); /* register_user — level defaults to HYBBX_LEVEL_USER */ memset(®, 0, sizeof(reg)); hybbx_strlcpy(reg.username, test_user, sizeof(reg.username)); hybbx_strlcpy(reg.password, "testpass123", sizeof(reg.password)); hybbx_strlcpy(reg.nickname, test_user, sizeof(reg.nickname)); hybbx_strlcpy(reg.full_name, "Smoke Test", sizeof(reg.full_name)); hybbx_strlcpy(reg.country, "DE", sizeof(reg.country)); hybbx_strlcpy(reg.location, "localhost", sizeof(reg.location)); hybbx_strlcpy(reg.email, "smoke@test.local", sizeof(reg.email)); check_rc("register_user", hybbx_storage_register_user(st, ®, &user), HYBBX_OK); check_nonzero("user id", user.id); check_str("user username", user.username, test_user); check_uint64("user level", (uint64_t)user.level, (uint64_t)HYBBX_LEVEL_USER); /* find_user */ memset(&found, 0, sizeof(found)); check_rc("find_user", hybbx_storage_find_user(st, test_user, &found), HYBBX_OK); check_uint64("found id matches", found.id, user.id); check_str("found username", found.username, test_user); /* find_user nonexistent */ check_rc("find_user missing", hybbx_storage_find_user(st, "no_such_user", &found), HYBBX_ERR_NOT_FOUND); /* resolve_user (by name) */ memset(&found, 0, sizeof(found)); check_rc("resolve_user", hybbx_storage_resolve_user(st, test_user, &found), HYBBX_OK); check_uint64("resolve id matches", found.id, user.id); /* count_level */ count = 0; check_rc("count_level", hybbx_storage_count_level(st, HYBBX_LEVEL_USER, &count), HYBBX_OK); /* count >= 1 (our user) */ /* update_user */ user.level = HYBBX_LEVEL_SYSOP; check_rc("update_user", hybbx_storage_update_user(st, &user), HYBBX_OK); memset(&found, 0, sizeof(found)); check_rc("find after update", hybbx_storage_find_user(st, test_user, &found), HYBBX_OK); check_uint64("updated level", (uint64_t)found.level, (uint64_t)HYBBX_LEVEL_SYSOP); /* session_begin */ memset(&sess, 0, sizeof(sess)); check_rc("session_begin", hybbx_storage_session_begin(st, &user, "test", &sess), HYBBX_OK); check_nonzero("session id", sess.session_id); /* session_end */ check_rc("session_end", hybbx_storage_session_end(st, sess.session_id), HYBBX_OK); /* delete_user */ check_rc("delete_user", hybbx_storage_delete_user(st, user.id), HYBBX_OK); /* find after delete */ check_rc("find after delete", hybbx_storage_find_user(st, test_user, &found), HYBBX_ERR_NOT_FOUND); /* Persistence: close and reopen, verify functional */ hybbx_storage_close(st); st = open_backend(backend, path); if (st == NULL) { fprintf(stderr, "FAIL %s: reopen returned NULL\n", label); g_failures++; return; } /* Re-register after reopen to verify backend is functional */ check_rc("re-register after reopen", hybbx_storage_register_user(st, ®, &user), HYBBX_OK); check_nonzero("re-registered id", user.id); hybbx_storage_delete_user(st, user.id); hybbx_storage_close(st); rm_rf(path); } int main(void) { char path_ff[256]; char path_sql[256]; snprintf(path_ff, sizeof(path_ff), "/tmp/hybbx-smoke-flatfile-%d", (int)getpid()); snprintf(path_sql, sizeof(path_sql), "/tmp/hybbx-smoke-sqlite-%d", (int)getpid()); smoke_backend(HYBBX_STORAGE_FLATFILE, "flatfile", path_ff); smoke_backend(HYBBX_STORAGE_SQLITE, "sqlite", path_sql); if (g_failures != 0) { fprintf(stderr, "%u failure(s)\n", g_failures); return 1; } puts("test_storage_smoke: ok"); return 0; }