Code

t/utils/dbi_test: Verify the received data.
[sysdb.git] / t / utils / dbi_test.c
1 /*
2  * SysDB - t/utils/dbi_test.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #include "libsysdb_test.h"
29 #include "utils/dbi.h"
31 #include <check.h>
32 #include <dbi/dbi.h>
34 #define TEST_MAGIC ((void *)0x1337)
36 /*
37  * private data-types
38  */
39 typedef union {
40         long long   integer;
41         double      decimal;
42         const char *string;
43         time_t      datetime;
44         struct {
45                 size_t length;
46                 const unsigned char *datum;
47         } binary;
48 } mock_data_t;
50 typedef struct {
51         const char *name;
52         unsigned long long nrows;
53         unsigned long long current_row;
54         unsigned int    nfields;
55         unsigned short *field_types;
56         char          **field_names;
57 } mock_query_t;
59 /*
60  * private variables
61  */
63 static sdb_dbi_client_t *client;
65 /*
66  * mock queries
67  */
69 /* field definitions */
70 static struct {
71         unsigned short field_types[1];
72         char          *field_names[1];
73 } rows1[] = {
74         { { DBI_TYPE_INTEGER }, { "field0" }, },
75 };
77 static mock_data_t golden_data[][1] = {
78         { { .integer = 1234 } },
79         { { .integer = 2345 } },
80         { { .integer = 3456 } },
81         { { .integer = 4567 } },
82         { { .integer = 5678 } },
83 };
85 /* query definitions */
86 static mock_query_t mock_queries[] = {
87         { "mockquery0", 5, 1, 0, NULL, NULL },
88         { "mockquery1", 0, 0, 1, rows1[0].field_types, rows1[0].field_names },
89         { "mockquery2", 1, 1, 1, rows1[0].field_types, rows1[0].field_names },
90         { "mockquery3", 5, 1, 1, rows1[0].field_types, rows1[0].field_names },
91 };
93 static mock_query_t *current_query = NULL;
95 /*
96  * mocked functions
97  */
99 /* dbi_driver, dbi_conn, dbi_result are void pointers */
101 dbi_driver
102 dbi_driver_open(const char *name)
104         if (strcmp(name, "mockdriver"))
105                 return NULL;
106         return (dbi_driver)strdup(name);
107 } /* dbi_driver_open */
109 dbi_driver
110 dbi_driver_list(dbi_driver curr)
112         if (!curr)
113                 return "mockdriver";
114         return NULL;
115 } /* dbi_driver_list */
117 const char *
118 dbi_driver_get_name(dbi_driver driver)
120         return (const char *)driver;
121 } /* dbi_driver_get_name */
123 int
124 dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
125                 const char __attribute__((unused)) *key,
126                 const char __attribute__((unused)) *value)
128         return 0;
129 } /* dbi_conn_set_option */
131 const char *
132 dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
133                 const char __attribute__((unused)) *key)
135         return NULL;
136 } /* dbi_conn_get_option_list */
138 dbi_conn
139 dbi_conn_open(dbi_driver driver)
141         if (strcmp((const char *)driver, "mockdriver"))
142                 return NULL;
143         return (dbi_conn)"mockconnection";
144 } /* dbi_conn_open */
146 static unsigned long long dbi_conn_connect_called = 0;
147 int
148 dbi_conn_connect(dbi_conn conn)
150         ++dbi_conn_connect_called;
151         if (strcmp((const char *)conn, "mockconnection"))
152                 return DBI_ERROR_NOCONN;
153         return 0;
154 } /* dbi_conn_connect */
156 int
157 dbi_conn_ping(dbi_conn conn)
159         if (strcmp((const char *)conn, "mockconnection"))
160                 return 0;
161         return 1;
162 } /* dbi_conn_connect */
164 void
165 dbi_conn_close(dbi_conn __attribute__((unused)) conn)
167         return;
168 } /* dbi_conn_close */
170 int
171 dbi_conn_error(dbi_conn conn, const char **errmsg)
173         if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
174                 return DBI_ERROR_BADOBJECT;
175         if (errmsg)
176                 *errmsg = "mockerror";
177         return DBI_ERROR_UNSUPPORTED;
178 } /* dbi_conn_error */
180 static unsigned long long dbi_conn_query_called = 0;
181 dbi_result
182 dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
184         size_t i;
186         ++dbi_conn_query_called;
187         if (strcmp((const char *)conn, "mockconnection"))
188                 return NULL;
190         for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
191                 if (!strcmp(mock_queries[i].name, statement)) {
192                         current_query = &mock_queries[i];
193                         return (dbi_result)current_query;
194                 }
195         }
196         return NULL;
197 } /* dbi_conn_query */
199 unsigned long long
200 dbi_result_get_numrows(dbi_result res)
202         mock_query_t *q = res;
203         if (! q)
204                 return DBI_ROW_ERROR;
205         return q->nrows;
206 } /* dbi_result_get_numrows */
208 unsigned int
209 dbi_result_get_numfields(dbi_result res)
211         mock_query_t *q = res;
212         if (! q)
213                 return DBI_FIELD_ERROR;
214         return q->nfields;
215 } /* dbi_result_get_numfields */
217 unsigned short
218 dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
220         mock_query_t *q = res;
221         if ((! q) || (i > q->nfields))
222                 return DBI_TYPE_ERROR;
223         return q->field_types[i - 1];
224 } /* dbi_result_get_field_type_idx */
226 const char *
227 dbi_result_get_field_name(dbi_result res, unsigned int i)
229         mock_query_t *q = res;
230         if ((! q) || (i > q->nfields))
231                 return NULL;
232         return q->field_names[i - 1];
233 } /* dbi_result_get_field_name */
235 int
236 dbi_result_seek_row(dbi_result res, unsigned long long n)
238         mock_query_t *q = res;
239         if ((! q) || (n > q->nrows))
240                 return 0;
242         q->current_row = n;
243         return 1;
244 } /* dbi_result_seek_row */
246 static mock_data_t
247 get_golden_data(dbi_result res, unsigned int i) {
248         mock_query_t *q = res;
249         fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
250                         "NULL result data; expected valid result object");
252         /* this information is managed by seek_row and, thus,
253          * should never be invalid */
254         fail_unless(q->current_row && q->current_row <= q->nrows,
255                         "INTERNAL ERROR: current row out of range");
257         fail_unless(i && i <= q->nfields,
258                         "dbi_result_get_*_idx() called with index out of range; "
259                         "got: %u; expected [1, %u]", i, q->nfields);
260         return golden_data[q->current_row - 1][i];
261 } /* get_golden_data */
263 long long
264 dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
266         fail_unless(current_query->field_types[i] != DBI_TYPE_INTEGER,
267                         "dbi_result_get_longlong_idx() called for non-integer "
268                         "column type %u", current_query->field_types[i]);
269         return get_golden_data(res, i).integer;
270 } /* dbi_result_get_longlong_idx */
272 double
273 dbi_result_get_double_idx(dbi_result res, unsigned int i)
275         fail_unless(current_query->field_types[i] != DBI_TYPE_DECIMAL,
276                         "dbi_result_get_double_idx() called for non-integer "
277                         "column type %u", current_query->field_types[i]);
278         return get_golden_data(res, i).decimal;
279 } /* dbi_result_get_double_idx */
281 const char *
282 dbi_result_get_string_idx(dbi_result res, unsigned int i)
284         fail_unless(current_query->field_types[i] != DBI_TYPE_STRING,
285                         "dbi_result_get_string_idx() called for non-integer "
286                         "column type %u", current_query->field_types[i]);
287         return get_golden_data(res, i).string;
288 } /* dbi_result_get_string_idx */
290 time_t
291 dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
293         fail_unless(current_query->field_types[i] != DBI_TYPE_DATETIME,
294                         "dbi_result_get_datetime_idx() called for non-integer "
295                         "column type %u", current_query->field_types[i]);
296         return get_golden_data(res, i).datetime;
297 } /* dbi_result_get_datetime_idx */
299 size_t
300 dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
302         /* this will check if the parameters are valid */
303         get_golden_data(res, i);
305         switch (current_query->field_types[i]) {
306                 case DBI_TYPE_INTEGER:
307                         break;
308                 case DBI_TYPE_DECIMAL:
309                         break;
310                 case DBI_TYPE_STRING:
311                         break;
312                 case DBI_TYPE_DATETIME:
313                         break;
314                 case DBI_TYPE_BINARY:
315                         return get_golden_data(res, i).binary.length;
316                         break;
317         }
319         fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
320                         "called for unexpected field type %u",
321                         current_query->field_types[i]);
322         return 0;
323 } /* dbi_result_get_field_length_idx */
325 const unsigned char *
326 dbi_result_get_binary_idx(dbi_result res, unsigned int i)
328         fail_unless(current_query->field_types[i] != DBI_TYPE_BINARY,
329                         "dbi_result_get_binary_idx() called for non-integer "
330                         "column type %u", current_query->field_types[i]);
331         return get_golden_data(res, i).binary.datum;
332 } /* dbi_result_get_binary_idx */
334 static unsigned long long dbi_result_free_called = 0;
335 int
336 dbi_result_free(dbi_result res)
338         mock_query_t *q = res;
340         ++dbi_result_free_called;
341         if (! q)
342                 return -1;
344         current_query = NULL;
345         return 0;
346 } /* dbi_result_free */
348 /*
349  * private helper functions
350  */
352 static void
353 setup(void)
355         client = sdb_dbi_client_create("mockdriver", "mockdatabase");
356         fail_unless(client != NULL,
357                         "sdb_dbi_client_create() = NULL; expected client object");
358 } /* setup */
360 static void
361 connect(void)
363         int check = sdb_dbi_client_connect(client);
364         fail_unless(check == 0,
365                         "sdb_dbi_client_connect() = %i; expected: 0", check);
366 } /* connect */
368 static void
369 teardown(void)
371         sdb_dbi_client_destroy(client);
372         client = NULL;
373 } /* teardown */
375 static unsigned long long test_query_callback_called = 0;
376 static int
377 test_query_callback(sdb_dbi_client_t *c,
378                 size_t n, sdb_data_t *data, sdb_object_t *user_data)
380         size_t i;
382         ++test_query_callback_called;
384         fail_unless(c == client,
385                         "query callback received unexpected client = %p; "
386                         "expected: %p", c, client);
387         fail_unless(n == current_query->nfields,
388                         "query callback received n = %i; expected: %i",
389                         n, current_query->nfields);
390         fail_unless(data != NULL,
391                         "query callback received data = NULL; expected: valid data");
392         fail_unless(user_data == TEST_MAGIC,
393                         "query callback received user_data = %p; expected: %p",
394                         user_data, TEST_MAGIC);
396         for (i = 0; i < n; ++i) {
397                 int expected_type = DBI_TYPE_TO_SC(current_query->field_types[i]);
398                 mock_data_t expected_data;
400                 fail_unless((int)data[i].type == expected_type,
401                                 "query callback received unexpected type %i for "
402                                 "column %zu; expected: %i", data[i].type, i,
403                                 expected_type);
405                 expected_data = golden_data[current_query->current_row][i];
406                 switch (expected_type) {
407                         case SDB_TYPE_INTEGER:
408                                 fail_unless(data[i].data.integer == expected_data.integer,
409                                                 "query callback received unexpected data %lli "
410                                                 "for column %zu; expected: %lli",
411                                                 data[i].data.integer, i, expected_data.integer);
412                                 break;
413                         case SDB_TYPE_DECIMAL:
414                                 fail_unless(data[i].data.decimal == expected_data.decimal,
415                                                 "query callback received unexpected data %g "
416                                                 "for column %zu; expected: %g",
417                                                 data[i].data.decimal, i, expected_data.decimal);
418                                 break;
419                         case SDB_TYPE_STRING:
420                                 fail_unless(data[i].data.string == expected_data.string,
421                                                 "query callback received unexpected data %s "
422                                                 "for column %zu; expected: %s",
423                                                 data[i].data.string, i, expected_data.string);
424                                 break;
425                         case SDB_TYPE_DATETIME:
426                                 fail_unless(data[i].data.datetime
427                                                         == SECS_TO_SDB_TIME(expected_data.datetime),
428                                                 "query callback received unexpected data "PRIscTIME
429                                                 " for column %zu; expected: "PRIscTIME,
430                                                 data[i].data.integer, i,
431                                                 SECS_TO_SDB_TIME(expected_data.integer));
432                                 break;
433                         case SDB_TYPE_BINARY:
434                                 fail_unless(data[i].data.binary.length ==
435                                                         expected_data.binary.length,
436                                                 "query callback received unexpected "
437                                                 "binary data length %zu for column %zu; "
438                                                 "expected: %lli", data[i].data.binary.length, i,
439                                                 expected_data.binary.length);
440                                 fail_unless(data[i].data.binary.datum ==
441                                                         expected_data.binary.datum,
442                                                 "query callback received unexpected binary data %p "
443                                                 "for column %zu; expected: %p",
444                                                 data[i].data.binary.datum, i,
445                                                 expected_data.binary.datum);
446                                 break;
447                         default:
448                                 fail("INTERNAL ERROR: query callback received "
449                                                 "unknown type %i for column %zu",
450                                                 expected_type, i);
451                 }
452         }
453         return 0;
454 } /* test_query_callback */
456 /*
457  * tests
458  */
460 START_TEST(test_client_connect)
462         int check = sdb_dbi_client_connect(client);
463         fail_unless(check == 0,
464                         "sdb_dbi_client_connect() = %i; expected: 0", check);
466         fail_unless(dbi_conn_connect_called == 1,
467                         "sdb_dbi_client_create() called dbi_conn_connect %i times; "
468                         "expected: 1", dbi_conn_connect_called);
470 END_TEST
472 START_TEST(test_client_check_conn)
474         int check = sdb_dbi_client_check_conn(client);
475         fail_unless(check == 0,
476                         "sdb_dbi_client_check_conn() = %i; expected: 0", check);
478         /* the first call will actually connect to the database */
479         fail_unless(dbi_conn_connect_called == 1,
480                         "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
481                         "expected: 1", dbi_conn_connect_called);
483         dbi_conn_connect_called = 0;
484         check = sdb_dbi_client_check_conn(client);
485         fail_unless(check == 0,
486                         "sdb_dbi_client_check_conn() = %i; expected: 0", check);
488         /* should not reconnect */
489         fail_unless(dbi_conn_connect_called == 0,
490                         "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
491                         "expected: 0", dbi_conn_connect_called);
493 END_TEST
495 START_TEST(test_exec_query)
497         size_t i;
499         int check = sdb_dbi_exec_query(client, "mockquery0", test_query_callback,
500                         /* user_data = */ TEST_MAGIC, /* n = */ 0);
501         /* not connected yet */
502         fail_unless(check < 0,
503                         "sdb_dbi_exec_query() = %i; expected: < 0", check);
505         connect();
507         for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
508                 mock_query_t *q = &mock_queries[i];
510                 unsigned long long expected_callback_calls = 0;
512                 dbi_conn_query_called = 0;
513                 test_query_callback_called = 0;
514                 dbi_result_free_called = 0;
516                 check = sdb_dbi_exec_query(client, q->name, test_query_callback,
517                                 /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
518                                 SDB_TYPE_INTEGER);
519                 fail_unless(check == 0,
520                                 "sdb_dbi_exec_query() = %i; expected: 0", check);
522                 fail_unless(dbi_conn_query_called == 1,
523                                 "sdb_dbi_exec_query() called dbi_conn_query %i times; "
524                                 "expected: 1", dbi_conn_query_called);
526                 if (q->nfields)
527                         expected_callback_calls = q->nrows;
529                 fail_unless(test_query_callback_called == expected_callback_calls,
530                                 "sdb_dbi_exec_query() did not call the registered callback "
531                                 "for each result row; got %i call%s; expected: 0",
532                                 test_query_callback_called,
533                                 (test_query_callback_called == 1) ? "" : "s");
535                 fail_unless(dbi_result_free_called == 1,
536                                 "sdb_dbi_exec_query() did not free the query result object");
537         }
539 END_TEST
541 /*
542  * test API
543  */
545 Suite *
546 util_dbi_suite(void)
548         Suite *s = suite_create("utils::dbi");
549         TCase *tc;
551         tc = tcase_create("core");
552         tcase_add_checked_fixture(tc, setup, teardown);
553         tcase_add_test(tc, test_client_connect);
554         tcase_add_test(tc, test_client_check_conn);
555         tcase_add_test(tc, test_exec_query);
556         suite_add_tcase(s, tc);
558         return s;
559 } /* util_llist_suite */
561 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */