diff -r 112b85020dc9 -r b26390e77237 test/database.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/database.c Tue Dec 09 12:13:43 2025 +0100 @@ -0,0 +1,196 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBLIITY OF SUCH DAMAGE. + */ + +#include "database.h" + +#include +#include +#include +#include + +#include +#include + +#define TEST_DB "test.db" +#define TEST_DATA_FILE "testdata.sql" + + +// create test.db and execute testdata script +int init_test_db(void) { + sqlite3 *db; + char *err_msg = NULL; + + FILE *f = fopen(TEST_DATA_FILE, "r"); + if(!f) { + fprintf(stderr, "Cannot open test data file %s: %s\n", TEST_DATA_FILE, strerror(errno)); + } + + if(unlink(TEST_DB)) { + if(errno != ENOENT) { + fprintf(stderr, "Cannot unlink %s: %s\n", TEST_DB, strerror(errno)); + fclose(f); + return 1; + } + } + if(sqlite3_open(TEST_DB, &db) != SQLITE_OK) { + fprintf(stderr, "Cannot open database %s: %s\n", TEST_DB, sqlite3_errmsg(db)); + fclose(f); + return 1; + } + + CxBuffer *buf = cxBufferCreate(NULL, 2048, NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS); + cx_stream_copy(f, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); + int err = 0; + if(buf->size > 0) { + cxBufferTerminate(buf); + + if(sqlite3_exec(db, buf->space, 0, 0, &err_msg) != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + err = 1; + } + } else { + fprintf(stderr, "Error: no file content\n"); + err = 1; + } + cxBufferFree(buf); + sqlite3_close(db); + + return err; +} + + + +static DBUContext *ctx; +static DBUConnection *conn; + +typedef struct Address { + int64_t address_id; + + cxmutstr street; + cxmutstr zip; + cxmutstr city; +} Address; + +typedef struct Person { + int64_t person_id; + + cxmutstr name; + cxmutstr email; + int age; + bool iscustomer; + uint64_t hash; + + Address *address; + + CxList *roles; +} Person; + +typedef struct Role { + int64_t role_id; + int64_t person_id; + cxmutstr name; +} Role; + +int init_db_tests(void) { + ctx = dbuContextCreate(); + + DBUClass *address = dbuRegisterClass(ctx, "address", Address, address_id); + dbuClassAdd(address, Address, street); + dbuClassAdd(address, Address, zip); + dbuClassAdd(address, Address, city); + + DBUClass *role = dbuRegisterClass(ctx, "role", Role, role_id); + + DBUClass *person = dbuRegisterClass(ctx, "person", Person, person_id); + dbuClassAdd(person, Person, name); + dbuClassAdd(person, Person, email); + dbuClassAdd(person, Person, age); + dbuClassAdd(person, Person, iscustomer); + dbuClassAdd(person, Person, hash); + dbuClassAddObj(person, "address_id", offsetof(Person, address), address); + dbuClassAddCxLinkedList(person, "person_id", offsetof(Person, roles), role); + + dbuClassAddForeignKey(role, Role, person_id, person); + dbuClassAdd(role, Role, name); + + return 0; +} + +void cleanup_db_tests(void) { + if(conn) { + dbuConnectionFree(conn); + } + dbuContextFree(ctx); +} + +CX_TEST(testSqliteConnection) { + CX_TEST_DO { + conn = dbuSQLiteConnection(TEST_DB); + CX_TEST_ASSERT(conn); + CX_TEST_ASSERT(dbuConnectionIsActive(conn)); + } +} + +CX_TEST(testConnectionExec) { + CX_TEST_DO { + CX_TEST_ASSERT(conn); + + int t1 = dbuConnectionExec(conn, "create table ExecTest1(a int);"); + CX_TEST_ASSERT(t1 == 0); + + int t2 = dbuConnectionExec(conn, "insert into ExecTest1(a) values (1);"); + CX_TEST_ASSERT(t2 == 0); + + int t3 = dbuConnectionExec(conn, "drop table ExecTest1;"); + CX_TEST_ASSERT(t3 == 0); + + int fail = dbuConnectionExec(conn, "select * from Fail;"); + CX_TEST_ASSERT(fail != 0); + } +} + +CX_TEST(testSingleValueQuery) { + CX_TEST_DO { + CX_TEST_ASSERT(conn); + + DBUQuery *q = dbuConnectionQuery(conn, NULL); + CX_TEST_ASSERT(q); + CX_TEST_ASSERT(dbuQuerySetSQL(q, "select 12;") == 0); + CX_TEST_ASSERT(dbuQueryExec(q) == 0); + + DBUResult *r = dbuQueryGetResult(q); + CX_TEST_ASSERT(r); + int value; + CX_TEST_ASSERT(dbuResultAsValue(r, &value) == 0); + CX_TEST_ASSERT(value == 12); + + dbuQueryFree(q); + + } +}