Code

store: Introduced service attributes.
[sysdb.git] / t / unit / core / store_test.c
1 /*
2  * SysDB - t/unit/core/store_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 "core/store.h"
29 #include "core/store-private.h"
30 #include "libsysdb_test.h"
32 #include <check.h>
33 #include <string.h>
35 static void
36 populate(void)
37 {
38         sdb_data_t datum;
40         sdb_store_host("h1", 1);
41         sdb_store_host("h2", 1);
43         datum.type = SDB_TYPE_STRING;
44         datum.data.string = "v1";
45         sdb_store_attribute("h1", "k1", &datum, 1);
46         datum.data.string = "v2";
47         sdb_store_attribute("h1", "k2", &datum, 1);
48         datum.data.string = "v3";
49         sdb_store_attribute("h1", "k3", &datum, 1);
51         sdb_store_service("h2", "s1", 1);
52         sdb_store_service("h2", "s2", 1);
53 } /* populate */
55 START_TEST(test_store_host)
56 {
57         struct {
58                 const char *name;
59                 sdb_time_t  last_update;
60                 int         expected;
61         } golden_data[] = {
62                 { "a", 1, 0 },
63                 { "a", 2, 0 },
64                 { "a", 1, 1 },
65                 { "b", 1, 0 },
66                 { "b", 1, 1 },
67                 { "A", 1, 1 }, /* case-insensitive */
68                 { "A", 3, 0 },
69         };
71         struct {
72                 const char *name;
73                 _Bool       has;
74         } golden_hosts[] = {
75                 { "a", 1 == 1 },
76                 { "b", 1 == 1 },
77                 { "c", 0 == 1 },
78                 { "A", 1 == 1 },
79         };
81         size_t i;
83         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
84                 int status;
86                 status = sdb_store_host(golden_data[i].name,
87                                 golden_data[i].last_update);
88                 fail_unless(status == golden_data[i].expected,
89                                 "sdb_store_host(%s, %d) = %d; expected: %d",
90                                 golden_data[i].name, (int)golden_data[i].last_update,
91                                 status, golden_data[i].expected);
92         }
94         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
95                 _Bool has;
97                 has = sdb_store_has_host(golden_hosts[i].name);
98                 fail_unless(has == golden_hosts[i].has,
99                                 "sdb_store_has_host(%s) = %d; expected: %d",
100                                 golden_hosts[i].name, has, golden_hosts[i].has);
101         }
103 END_TEST
105 START_TEST(test_store_get_host)
107         char *golden_hosts[] = { "a", "b", "c" };
108         char *unknown_hosts[] = { "x", "y", "z" };
109         size_t i;
111         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
112                 int status = sdb_store_host(golden_hosts[i], 1);
113                 fail_unless(status >= 0,
114                                 "sdb_store_host(%s) = %d; expected: >=0",
115                                 golden_hosts[i], status);
116         }
118         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
119                 sdb_store_obj_t *sobj1, *sobj2;
120                 int ref_cnt;
122                 fail_unless(sdb_store_has_host(golden_hosts[i]),
123                                 "sdb_store_has_host(%s) = FALSE; expected: TRUE",
124                                 golden_hosts[i]);
126                 sobj1 = sdb_store_get_host(golden_hosts[i]);
127                 fail_unless(sobj1 != NULL,
128                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
129                                 golden_hosts[i]);
130                 ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
132                 fail_unless(ref_cnt > 1,
133                                 "sdb_store_get_host(%s) did not increment ref count: "
134                                 "got: %d; expected: >1", golden_hosts[i], ref_cnt);
136                 sobj2 = sdb_store_get_host(golden_hosts[i]);
137                 fail_unless(sobj2 != NULL,
138                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
139                                 golden_hosts[i]);
141                 fail_unless(sobj1 == sobj2,
142                                 "sdb_store_get_host(%s) returned different objects "
143                                 "in successive calls", golden_hosts[i]);
144                 fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
145                                 "sdb_store_get_hosts(%s) did not increment ref count "
146                                 "(first call: %d; second call: %d)",
147                                 golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
149                 sdb_object_deref(SDB_OBJ(sobj1));
150                 sdb_object_deref(SDB_OBJ(sobj2));
151         }
152         for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
153                 sdb_store_obj_t *sobj;
155                 fail_unless(!sdb_store_has_host(unknown_hosts[i]),
156                                 "sdb_store_has_host(%s) = TRUE; expected: FALSE",
157                                 unknown_hosts[i]);
159                 sobj = sdb_store_get_host(unknown_hosts[i]);
160                 fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
161                                 unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
162         }
164 END_TEST
166 START_TEST(test_store_attr)
168         struct {
169                 const char *host;
170                 const char *key;
171                 char       *value;
172                 sdb_time_t  last_update;
173                 int         expected;
174         } golden_data[] = {
175                 { "k", "k",  "v",  1, -1 },
176                 { "k", "k",  "v",  1, -1 }, /* retry to ensure the host is not created */
177                 { "l", "k1", "v1", 1,  0 },
178                 { "l", "k1", "v2", 2,  0 },
179                 { "l", "k1", "v3", 2,  1 },
180                 { "l", "k2", "v1", 1,  0 },
181                 { "m", "k",  "v1", 1,  0 },
182                 { "m", "k",  "v2", 1,  1 },
183         };
185         size_t i;
187         sdb_store_host("l", 1);
188         sdb_store_host("m", 1);
189         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
190                 sdb_data_t datum;
191                 int status;
193                 /* XXX: test other types as well */
194                 datum.type = SDB_TYPE_STRING;
195                 datum.data.string = golden_data[i].value;
197                 status = sdb_store_attribute(golden_data[i].host,
198                                 golden_data[i].key, &datum,
199                                 golden_data[i].last_update);
200                 fail_unless(status == golden_data[i].expected,
201                                 "sdb_store_attribute(%s, %s, %s, %d) = %d; expected: %d",
202                                 golden_data[i].host, golden_data[i].key, golden_data[i].value,
203                                 golden_data[i].last_update, status, golden_data[i].expected);
204         }
206 END_TEST
208 START_TEST(test_store_service)
210         struct {
211                 const char *host;
212                 const char *svc;
213                 sdb_time_t  last_update;
214                 int         expected;
215         } golden_data[] = {
216                 { "k", "s",  1, -1 },
217                 { "k", "s",  1, -1 }, /* retry to ensure the host is not created */
218                 { "l", "s1", 1,  0 },
219                 { "l", "s1", 2,  0 },
220                 { "l", "s1", 2,  1 },
221                 { "l", "s2", 1,  0 },
222                 { "m", "s",  1,  0 },
223                 { "m", "s",  1,  1 },
224         };
226         size_t i;
228         sdb_store_host("m", 1);
229         sdb_store_host("l", 1);
230         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
231                 int status;
233                 status = sdb_store_service(golden_data[i].host,
234                                 golden_data[i].svc, golden_data[i].last_update);
235                 fail_unless(status == golden_data[i].expected,
236                                 "sdb_store_service(%s, %s, %d) = %d; expected: %d",
237                                 golden_data[i].host, golden_data[i].svc,
238                                 golden_data[i].last_update, status, golden_data[i].expected);
239         }
241 END_TEST
243 START_TEST(test_store_service_attr)
245         struct {
246                 const char *host;
247                 const char *svc;
248                 const char *attr;
249                 const sdb_data_t value;
250                 sdb_time_t  last_update;
251                 int expected;
252         } golden_data[] = {
253                 { "k", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
254                 /* retry, it should still fail */
255                 { "k", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
256                 { "l", "sX", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
257                 /* retry, it should still fail */
258                 { "l", "sX", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
259                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
260                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  1 },
261                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 2,  0 },
262                 { "l", "s1", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
263                 { "l", "s1", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  1 },
264                 { "l", "s2", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
265                 { "m", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
266         };
268         size_t i;
270         sdb_store_host("m", 1);
271         sdb_store_host("l", 1);
272         sdb_store_service("m", "s1", 1);
273         sdb_store_service("l", "s1", 1);
274         sdb_store_service("l", "s2", 1);
276         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
277                 int status;
279                 status = sdb_store_service_attr(golden_data[i].host,
280                                 golden_data[i].svc, golden_data[i].attr,
281                                 &golden_data[i].value, golden_data[i].last_update);
282                 fail_unless(status == golden_data[i].expected,
283                                 "sdb_store_service_attr(%s, %s, %s, %d, %d) = %d; "
284                                 "expected: %d", golden_data[i].host, golden_data[i].svc,
285                                 golden_data[i].attr, golden_data[i].value.data.integer,
286                                 golden_data[i].last_update, status, golden_data[i].expected);
287         }
289 END_TEST
291 static void
292 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
294         int pos;
295         size_t len1, len2;
296         size_t i;
298         len1 = strlen(sdb_strbuf_string(buf));
299         len2 = strlen(expected);
301         pos = -1;
302         if (len1 != len2)
303                 pos = (int)(len1 <= len2 ? len1 : len2);
305         for (i = 0; i < (len1 <= len2 ? len1 : len2); ++i) {
306                 if (sdb_strbuf_string(buf)[i] != expected[i]) {
307                         pos = (int)i;
308                         break;
309                 }
310         }
312         fail_unless(pos == -1,
313                         "sdb_store_tojson(%x) returned unexpected result\n"
314                         "         got: %s\n              %*s\n    expected: %s",
315                         flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
316 } /* verify_json_output */
318 START_TEST(test_store_tojson)
320         sdb_strbuf_t *buf;
321         size_t i;
323         struct {
324                 int flags;
325                 const char *expected;
326         } golden_data[] = {
327                 { 0, "{\"hosts\":["
328                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
329                                         "\"update_interval\": \"0s\", \"backends\": [], "
330                                         "\"attributes\": ["
331                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
332                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
333                                                         "\"update_interval\": \"0s\", \"backends\": []},"
334                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
335                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
336                                                         "\"update_interval\": \"0s\", \"backends\": []},"
337                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
338                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
339                                                         "\"update_interval\": \"0s\", \"backends\": []}"
340                                         "], "
341                                         "\"services\": []},"
342                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
343                                         "\"update_interval\": \"0s\", \"backends\": [], "
344                                         "\"attributes\": [], "
345                                         "\"services\": ["
346                                                 "{\"name\": \"s1\", "
347                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
348                                                         "\"update_interval\": \"0s\", \"backends\": []},"
349                                                 "{\"name\": \"s2\", "
350                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
351                                                         "\"update_interval\": \"0s\", \"backends\": []}"
352                                         "]}"
353                         "]}" },
354                 { SDB_SKIP_SERVICES,
355                         "{\"hosts\":["
356                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
357                                         "\"update_interval\": \"0s\", \"backends\": [], "
358                                         "\"attributes\": ["
359                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
360                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
361                                                         "\"update_interval\": \"0s\", \"backends\": []},"
362                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
363                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
364                                                         "\"update_interval\": \"0s\", \"backends\": []},"
365                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
366                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
367                                                         "\"update_interval\": \"0s\", \"backends\": []}"
368                                         "]},"
369                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
370                                         "\"update_interval\": \"0s\", \"backends\": [], "
371                                         "\"attributes\": []}"
372                         "]}" },
373                 { SDB_SKIP_ATTRIBUTES,
374                         "{\"hosts\":["
375                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
376                                         "\"update_interval\": \"0s\", \"backends\": [], "
377                                         "\"services\": []},"
378                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
379                                         "\"update_interval\": \"0s\", \"backends\": [], "
380                                         "\"services\": ["
381                                                 "{\"name\": \"s1\", "
382                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
383                                                         "\"update_interval\": \"0s\", \"backends\": []},"
384                                                 "{\"name\": \"s2\", "
385                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
386                                                         "\"update_interval\": \"0s\", \"backends\": []}"
387                                         "]}"
388                         "]}" },
389                 { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
390                         "{\"hosts\":["
391                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
392                                         "\"update_interval\": \"0s\", \"backends\": []},"
393                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
394                                         "\"update_interval\": \"0s\", \"backends\": []}"
395                         "]}" },
396         };
398         buf = sdb_strbuf_create(0);
399         fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
400         populate();
402         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
403                 int status;
405                 sdb_strbuf_clear(buf);
407                 status = sdb_store_tojson(buf, golden_data[i].flags);
408                 fail_unless(status == 0,
409                                 "sdb_store_tojson(%x) = %d; expected: 0",
410                                 golden_data[i].flags, status);
412                 verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
413         }
414         sdb_strbuf_destroy(buf);
416 END_TEST
418 START_TEST(test_interval)
420         sdb_store_obj_t *host;
422         /* 10 us interval */
423         sdb_store_host("host", 10);
424         sdb_store_host("host", 20);
425         sdb_store_host("host", 30);
426         sdb_store_host("host", 40);
428         host = sdb_store_get_host("host");
429         fail_unless(host != NULL,
430                         "INTERNAL ERROR: store doesn't have host after adding it");
432         fail_unless(host->interval == 10,
433                         "sdb_store_host() did not calculate interval correctly: "
434                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 10);
436         /* multiple updates for the same timestamp don't modify the interval */
437         sdb_store_host("host", 40);
438         sdb_store_host("host", 40);
439         sdb_store_host("host", 40);
440         sdb_store_host("host", 40);
442         fail_unless(host->interval == 10,
443                         "sdb_store_host() changed interval when doing multiple updates "
444                         "using the same timestamp; got: %"PRIscTIME"; "
445                         "expected: %"PRIscTIME, host->interval, 10);
447         /* multiple updates using an timestamp don't modify the interval */
448         sdb_store_host("host", 20);
449         sdb_store_host("host", 20);
450         sdb_store_host("host", 20);
451         sdb_store_host("host", 20);
453         fail_unless(host->interval == 10,
454                         "sdb_store_host() changed interval when doing multiple updates "
455                         "using an old timestamp; got: %"PRIscTIME"; expected: %"PRIscTIME,
456                         host->interval, 10);
458         /* new interval: 20 us */
459         sdb_store_host("host", 60);
460         fail_unless(host->interval == 11,
461                         "sdb_store_host() did not calculate interval correctly: "
462                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
464         /* new interval: 40 us */
465         sdb_store_host("host", 100);
466         fail_unless(host->interval == 13,
467                         "sdb_store_host() did not calculate interval correctly: "
468                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
470         sdb_object_deref(SDB_OBJ(host));
472 END_TEST
474 static int
475 iter_incr(sdb_store_obj_t *obj, void *user_data)
477         intptr_t *i = user_data;
479         fail_unless(obj != NULL,
480                         "sdb_store_iterate callback received NULL obj; expected: "
481                         "<store base obj>");
482         fail_unless(i != NULL,
483                         "sdb_store_iterate callback received NULL user_data; "
484                         "expected: <pointer to data>");
486         ++(*i);
487         return 0;
488 } /* iter_incr */
490 static int
491 iter_error(sdb_store_obj_t *obj, void *user_data)
493         intptr_t *i = user_data;
495         fail_unless(obj != NULL,
496                         "sdb_store_iterate callback received NULL obj; expected: "
497                         "<store base obj>");
498         fail_unless(i != NULL,
499                         "sdb_store_iterate callback received NULL user_data; "
500                         "expected: <pointer to data>");
502         ++(*i);
503         return -1;
504 } /* iter_error */
506 START_TEST(test_iterate)
508         intptr_t i = 0;
509         int check;
511         /* empty store */
512         check = sdb_store_iterate(iter_incr, &i);
513         fail_unless(check == -1,
514                         "sdb_store_iterate(), empty store = %d; expected: -1", check);
515         fail_unless(i == 0,
516                         "sdb_store_iterate called callback %d times; expected: 0", (int)i);
518         populate();
520         check = sdb_store_iterate(iter_incr, &i);
521         fail_unless(check == 0,
522                         "sdb_store_iterate() = %d; expected: 0", check);
523         fail_unless(i == 2,
524                         "sdb_store_iterate called callback %d times; expected: 1", (int)i);
526         i = 0;
527         check = sdb_store_iterate(iter_error, &i);
528         fail_unless(check == -1,
529                         "sdb_store_iterate(), error callback = %d; expected: -1", check);
530         fail_unless(i == 1,
531                         "sdb_store_iterate called callback %d times "
532                         "(callback returned error); expected: 1", (int)i);
534 END_TEST
536 Suite *
537 core_store_suite(void)
539         Suite *s = suite_create("core::store");
540         TCase *tc;
542         tc = tcase_create("core");
543         tcase_add_test(tc, test_store_tojson);
544         tcase_add_test(tc, test_store_host);
545         tcase_add_test(tc, test_store_get_host);
546         tcase_add_test(tc, test_store_attr);
547         tcase_add_test(tc, test_store_service);
548         tcase_add_test(tc, test_store_service_attr);
549         tcase_add_test(tc, test_interval);
550         tcase_add_test(tc, test_iterate);
551         tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
552         suite_add_tcase(s, tc);
554         return s;
555 } /* core_store_suite */
557 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */