test/database.c

changeset 23
b26390e77237
child 24
df671b62538e
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <cx/buffer.h>
+#include <cx/streams.h>
+
+#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);
+        
+    }
+}

mercurial