Code

store: Include service attributes in JSON output.
[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, 2);
48         datum.data.string = "v3";
49         sdb_store_attribute("h1", "k3", &datum, 2);
51         /* make sure that older updates don't overwrite existing values */
52         datum.data.string = "fail";
53         sdb_store_attribute("h1", "k2", &datum, 1);
54         sdb_store_attribute("h1", "k3", &datum, 2);
56         sdb_store_service("h2", "s1", 1);
57         sdb_store_service("h2", "s2", 1);
59         datum.type = SDB_TYPE_INTEGER;
60         datum.data.integer = 123;
61         sdb_store_service_attr("h2", "s2", "k1", &datum, 2);
63         /* don't overwrite */
64         datum.data.integer = 666;
65         sdb_store_service_attr("h2", "s2", "k1", &datum, 2);
66 } /* populate */
68 START_TEST(test_store_host)
69 {
70         struct {
71                 const char *name;
72                 sdb_time_t  last_update;
73                 int         expected;
74         } golden_data[] = {
75                 { "a", 1, 0 },
76                 { "a", 2, 0 },
77                 { "a", 1, 1 },
78                 { "b", 1, 0 },
79                 { "b", 1, 1 },
80                 { "A", 1, 1 }, /* case-insensitive */
81                 { "A", 3, 0 },
82         };
84         struct {
85                 const char *name;
86                 _Bool       has;
87         } golden_hosts[] = {
88                 { "a", 1 == 1 },
89                 { "b", 1 == 1 },
90                 { "c", 0 == 1 },
91                 { "A", 1 == 1 },
92         };
94         size_t i;
96         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
97                 int status;
99                 status = sdb_store_host(golden_data[i].name,
100                                 golden_data[i].last_update);
101                 fail_unless(status == golden_data[i].expected,
102                                 "sdb_store_host(%s, %d) = %d; expected: %d",
103                                 golden_data[i].name, (int)golden_data[i].last_update,
104                                 status, golden_data[i].expected);
105         }
107         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
108                 _Bool has;
110                 has = sdb_store_has_host(golden_hosts[i].name);
111                 fail_unless(has == golden_hosts[i].has,
112                                 "sdb_store_has_host(%s) = %d; expected: %d",
113                                 golden_hosts[i].name, has, golden_hosts[i].has);
114         }
116 END_TEST
118 START_TEST(test_store_get_host)
120         char *golden_hosts[] = { "a", "b", "c" };
121         char *unknown_hosts[] = { "x", "y", "z" };
122         size_t i;
124         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
125                 int status = sdb_store_host(golden_hosts[i], 1);
126                 fail_unless(status >= 0,
127                                 "sdb_store_host(%s) = %d; expected: >=0",
128                                 golden_hosts[i], status);
129         }
131         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_hosts); ++i) {
132                 sdb_store_obj_t *sobj1, *sobj2;
133                 int ref_cnt;
135                 fail_unless(sdb_store_has_host(golden_hosts[i]),
136                                 "sdb_store_has_host(%s) = FALSE; expected: TRUE",
137                                 golden_hosts[i]);
139                 sobj1 = sdb_store_get_host(golden_hosts[i]);
140                 fail_unless(sobj1 != NULL,
141                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
142                                 golden_hosts[i]);
143                 ref_cnt = SDB_OBJ(sobj1)->ref_cnt;
145                 fail_unless(ref_cnt > 1,
146                                 "sdb_store_get_host(%s) did not increment ref count: "
147                                 "got: %d; expected: >1", golden_hosts[i], ref_cnt);
149                 sobj2 = sdb_store_get_host(golden_hosts[i]);
150                 fail_unless(sobj2 != NULL,
151                                 "sdb_store_get_host(%s) = NULL; expected: <host>",
152                                 golden_hosts[i]);
154                 fail_unless(sobj1 == sobj2,
155                                 "sdb_store_get_host(%s) returned different objects "
156                                 "in successive calls", golden_hosts[i]);
157                 fail_unless(SDB_OBJ(sobj2)->ref_cnt == ref_cnt + 1,
158                                 "sdb_store_get_hosts(%s) did not increment ref count "
159                                 "(first call: %d; second call: %d)",
160                                 golden_hosts[i], ref_cnt, SDB_OBJ(sobj2)->ref_cnt);
162                 sdb_object_deref(SDB_OBJ(sobj1));
163                 sdb_object_deref(SDB_OBJ(sobj2));
164         }
165         for (i = 0; i < SDB_STATIC_ARRAY_LEN(unknown_hosts); ++i) {
166                 sdb_store_obj_t *sobj;
168                 fail_unless(!sdb_store_has_host(unknown_hosts[i]),
169                                 "sdb_store_has_host(%s) = TRUE; expected: FALSE",
170                                 unknown_hosts[i]);
172                 sobj = sdb_store_get_host(unknown_hosts[i]);
173                 fail_unless(!sobj, "sdb_store_get_host(%s) = <host:%s>; expected: NULL",
174                                 unknown_hosts[i], sobj ? SDB_OBJ(sobj)->name : "NULL");
175         }
177 END_TEST
179 START_TEST(test_store_attr)
181         struct {
182                 const char *host;
183                 const char *key;
184                 char       *value;
185                 sdb_time_t  last_update;
186                 int         expected;
187         } golden_data[] = {
188                 { "k", "k",  "v",  1, -1 },
189                 { "k", "k",  "v",  1, -1 }, /* retry to ensure the host is not created */
190                 { "l", "k1", "v1", 1,  0 },
191                 { "l", "k1", "v2", 2,  0 },
192                 { "l", "k1", "v3", 2,  1 },
193                 { "l", "k2", "v1", 1,  0 },
194                 { "m", "k",  "v1", 1,  0 },
195                 { "m", "k",  "v2", 1,  1 },
196         };
198         size_t i;
200         sdb_store_host("l", 1);
201         sdb_store_host("m", 1);
202         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
203                 sdb_data_t datum;
204                 int status;
206                 /* XXX: test other types as well */
207                 datum.type = SDB_TYPE_STRING;
208                 datum.data.string = golden_data[i].value;
210                 status = sdb_store_attribute(golden_data[i].host,
211                                 golden_data[i].key, &datum,
212                                 golden_data[i].last_update);
213                 fail_unless(status == golden_data[i].expected,
214                                 "sdb_store_attribute(%s, %s, %s, %d) = %d; expected: %d",
215                                 golden_data[i].host, golden_data[i].key, golden_data[i].value,
216                                 golden_data[i].last_update, status, golden_data[i].expected);
217         }
219 END_TEST
221 START_TEST(test_store_service)
223         struct {
224                 const char *host;
225                 const char *svc;
226                 sdb_time_t  last_update;
227                 int         expected;
228         } golden_data[] = {
229                 { "k", "s",  1, -1 },
230                 { "k", "s",  1, -1 }, /* retry to ensure the host is not created */
231                 { "l", "s1", 1,  0 },
232                 { "l", "s1", 2,  0 },
233                 { "l", "s1", 2,  1 },
234                 { "l", "s2", 1,  0 },
235                 { "m", "s",  1,  0 },
236                 { "m", "s",  1,  1 },
237         };
239         size_t i;
241         sdb_store_host("m", 1);
242         sdb_store_host("l", 1);
243         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
244                 int status;
246                 status = sdb_store_service(golden_data[i].host,
247                                 golden_data[i].svc, golden_data[i].last_update);
248                 fail_unless(status == golden_data[i].expected,
249                                 "sdb_store_service(%s, %s, %d) = %d; expected: %d",
250                                 golden_data[i].host, golden_data[i].svc,
251                                 golden_data[i].last_update, status, golden_data[i].expected);
252         }
254 END_TEST
256 START_TEST(test_store_service_attr)
258         struct {
259                 const char *host;
260                 const char *svc;
261                 const char *attr;
262                 const sdb_data_t value;
263                 sdb_time_t  last_update;
264                 int expected;
265         } golden_data[] = {
266                 { "k", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
267                 /* retry, it should still fail */
268                 { "k", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
269                 { "l", "sX", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
270                 /* retry, it should still fail */
271                 { "l", "sX", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1, -1 },
272                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
273                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  1 },
274                 { "l", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 2,  0 },
275                 { "l", "s1", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
276                 { "l", "s1", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  1 },
277                 { "l", "s2", "a2", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
278                 { "m", "s1", "a1", { SDB_TYPE_INTEGER, { .integer = 123 } }, 1,  0 },
279         };
281         size_t i;
283         sdb_store_host("m", 1);
284         sdb_store_host("l", 1);
285         sdb_store_service("m", "s1", 1);
286         sdb_store_service("l", "s1", 1);
287         sdb_store_service("l", "s2", 1);
289         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
290                 int status;
292                 status = sdb_store_service_attr(golden_data[i].host,
293                                 golden_data[i].svc, golden_data[i].attr,
294                                 &golden_data[i].value, golden_data[i].last_update);
295                 fail_unless(status == golden_data[i].expected,
296                                 "sdb_store_service_attr(%s, %s, %s, %d, %d) = %d; "
297                                 "expected: %d", golden_data[i].host, golden_data[i].svc,
298                                 golden_data[i].attr, golden_data[i].value.data.integer,
299                                 golden_data[i].last_update, status, golden_data[i].expected);
300         }
302 END_TEST
304 static void
305 verify_json_output(sdb_strbuf_t *buf, const char *expected, int flags)
307         int pos;
308         size_t len1, len2;
309         size_t i;
311         len1 = strlen(sdb_strbuf_string(buf));
312         len2 = strlen(expected);
314         pos = -1;
315         if (len1 != len2)
316                 pos = (int)(len1 <= len2 ? len1 : len2);
318         for (i = 0; i < (len1 <= len2 ? len1 : len2); ++i) {
319                 if (sdb_strbuf_string(buf)[i] != expected[i]) {
320                         pos = (int)i;
321                         break;
322                 }
323         }
325         fail_unless(pos == -1,
326                         "sdb_store_tojson(%x) returned unexpected result\n"
327                         "         got: %s\n              %*s\n    expected: %s",
328                         flags, sdb_strbuf_string(buf), pos + 1, "^", expected);
329 } /* verify_json_output */
331 START_TEST(test_store_tojson)
333         sdb_strbuf_t *buf;
334         size_t i;
336         struct {
337                 int flags;
338                 const char *expected;
339         } golden_data[] = {
340                 { 0, "{\"hosts\":["
341                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
342                                         "\"update_interval\": \"0s\", \"backends\": [], "
343                                         "\"attributes\": ["
344                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
345                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
346                                                         "\"update_interval\": \"0s\", \"backends\": []},"
347                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
348                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
349                                                         "\"update_interval\": \"0s\", \"backends\": []},"
350                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
351                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
352                                                         "\"update_interval\": \"0s\", \"backends\": []}"
353                                         "], "
354                                         "\"services\": []},"
355                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
356                                         "\"update_interval\": \"0s\", \"backends\": [], "
357                                         "\"attributes\": [], "
358                                         "\"services\": ["
359                                                 "{\"name\": \"s1\", "
360                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
361                                                         "\"update_interval\": \"0s\", \"backends\": [], "
362                                                         "\"attributes\": []},"
363                                                 "{\"name\": \"s2\", "
364                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
365                                                         "\"update_interval\": \"0s\", \"backends\": [], "
366                                                         "\"attributes\": ["
367                                                                 "{\"name\": \"k1\", \"value\": 123, "
368                                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
369                                                                         "\"update_interval\": \"0s\", \"backends\": []}"
370                                                         "]}"
371                                         "]}"
372                         "]}" },
373                 { SDB_SKIP_SERVICES,
374                         "{\"hosts\":["
375                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
376                                         "\"update_interval\": \"0s\", \"backends\": [], "
377                                         "\"attributes\": ["
378                                                 "{\"name\": \"k1\", \"value\": \"v1\", "
379                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
380                                                         "\"update_interval\": \"0s\", \"backends\": []},"
381                                                 "{\"name\": \"k2\", \"value\": \"v2\", "
382                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
383                                                         "\"update_interval\": \"0s\", \"backends\": []},"
384                                                 "{\"name\": \"k3\", \"value\": \"v3\", "
385                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
386                                                         "\"update_interval\": \"0s\", \"backends\": []}"
387                                         "]},"
388                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
389                                         "\"update_interval\": \"0s\", \"backends\": [], "
390                                         "\"attributes\": []}"
391                         "]}" },
392                 { SDB_SKIP_ATTRIBUTES,
393                         "{\"hosts\":["
394                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
395                                         "\"update_interval\": \"0s\", \"backends\": [], "
396                                         "\"services\": []},"
397                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
398                                         "\"update_interval\": \"0s\", \"backends\": [], "
399                                         "\"services\": ["
400                                                 "{\"name\": \"s1\", "
401                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
402                                                         "\"update_interval\": \"0s\", \"backends\": []},"
403                                                 "{\"name\": \"s2\", "
404                                                         "\"last_update\": \"1970-01-01 00:00:00 +0000\", "
405                                                         "\"update_interval\": \"0s\", \"backends\": []}"
406                                         "]}"
407                         "]}" },
408                 { SDB_SKIP_SERVICES | SDB_SKIP_ATTRIBUTES,
409                         "{\"hosts\":["
410                                 "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
411                                         "\"update_interval\": \"0s\", \"backends\": []},"
412                                 "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", "
413                                         "\"update_interval\": \"0s\", \"backends\": []}"
414                         "]}" },
415         };
417         buf = sdb_strbuf_create(0);
418         fail_unless(buf != NULL, "INTERNAL ERROR: failed to create string buffer");
419         populate();
421         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
422                 int status;
424                 sdb_strbuf_clear(buf);
426                 status = sdb_store_tojson(buf, golden_data[i].flags);
427                 fail_unless(status == 0,
428                                 "sdb_store_tojson(%x) = %d; expected: 0",
429                                 golden_data[i].flags, status);
431                 verify_json_output(buf, golden_data[i].expected, golden_data[i].flags);
432         }
433         sdb_strbuf_destroy(buf);
435 END_TEST
437 START_TEST(test_interval)
439         sdb_store_obj_t *host;
441         /* 10 us interval */
442         sdb_store_host("host", 10);
443         sdb_store_host("host", 20);
444         sdb_store_host("host", 30);
445         sdb_store_host("host", 40);
447         host = sdb_store_get_host("host");
448         fail_unless(host != NULL,
449                         "INTERNAL ERROR: store doesn't have host after adding it");
451         fail_unless(host->interval == 10,
452                         "sdb_store_host() did not calculate interval correctly: "
453                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 10);
455         /* multiple updates for the same timestamp don't modify the interval */
456         sdb_store_host("host", 40);
457         sdb_store_host("host", 40);
458         sdb_store_host("host", 40);
459         sdb_store_host("host", 40);
461         fail_unless(host->interval == 10,
462                         "sdb_store_host() changed interval when doing multiple updates "
463                         "using the same timestamp; got: %"PRIscTIME"; "
464                         "expected: %"PRIscTIME, host->interval, 10);
466         /* multiple updates using an timestamp don't modify the interval */
467         sdb_store_host("host", 20);
468         sdb_store_host("host", 20);
469         sdb_store_host("host", 20);
470         sdb_store_host("host", 20);
472         fail_unless(host->interval == 10,
473                         "sdb_store_host() changed interval when doing multiple updates "
474                         "using an old timestamp; got: %"PRIscTIME"; expected: %"PRIscTIME,
475                         host->interval, 10);
477         /* new interval: 20 us */
478         sdb_store_host("host", 60);
479         fail_unless(host->interval == 11,
480                         "sdb_store_host() did not calculate interval correctly: "
481                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
483         /* new interval: 40 us */
484         sdb_store_host("host", 100);
485         fail_unless(host->interval == 13,
486                         "sdb_store_host() did not calculate interval correctly: "
487                         "got: %"PRIscTIME"; expected: %"PRIscTIME, host->interval, 11);
489         sdb_object_deref(SDB_OBJ(host));
491 END_TEST
493 static int
494 iter_incr(sdb_store_obj_t *obj, void *user_data)
496         intptr_t *i = user_data;
498         fail_unless(obj != NULL,
499                         "sdb_store_iterate callback received NULL obj; expected: "
500                         "<store base obj>");
501         fail_unless(i != NULL,
502                         "sdb_store_iterate callback received NULL user_data; "
503                         "expected: <pointer to data>");
505         ++(*i);
506         return 0;
507 } /* iter_incr */
509 static int
510 iter_error(sdb_store_obj_t *obj, void *user_data)
512         intptr_t *i = user_data;
514         fail_unless(obj != NULL,
515                         "sdb_store_iterate callback received NULL obj; expected: "
516                         "<store base obj>");
517         fail_unless(i != NULL,
518                         "sdb_store_iterate callback received NULL user_data; "
519                         "expected: <pointer to data>");
521         ++(*i);
522         return -1;
523 } /* iter_error */
525 START_TEST(test_iterate)
527         intptr_t i = 0;
528         int check;
530         /* empty store */
531         check = sdb_store_iterate(iter_incr, &i);
532         fail_unless(check == -1,
533                         "sdb_store_iterate(), empty store = %d; expected: -1", check);
534         fail_unless(i == 0,
535                         "sdb_store_iterate called callback %d times; expected: 0", (int)i);
537         populate();
539         check = sdb_store_iterate(iter_incr, &i);
540         fail_unless(check == 0,
541                         "sdb_store_iterate() = %d; expected: 0", check);
542         fail_unless(i == 2,
543                         "sdb_store_iterate called callback %d times; expected: 1", (int)i);
545         i = 0;
546         check = sdb_store_iterate(iter_error, &i);
547         fail_unless(check == -1,
548                         "sdb_store_iterate(), error callback = %d; expected: -1", check);
549         fail_unless(i == 1,
550                         "sdb_store_iterate called callback %d times "
551                         "(callback returned error); expected: 1", (int)i);
553 END_TEST
555 Suite *
556 core_store_suite(void)
558         Suite *s = suite_create("core::store");
559         TCase *tc;
561         tc = tcase_create("core");
562         tcase_add_test(tc, test_store_tojson);
563         tcase_add_test(tc, test_store_host);
564         tcase_add_test(tc, test_store_get_host);
565         tcase_add_test(tc, test_store_attr);
566         tcase_add_test(tc, test_store_service);
567         tcase_add_test(tc, test_store_service_attr);
568         tcase_add_test(tc, test_interval);
569         tcase_add_test(tc, test_iterate);
570         tcase_add_unchecked_fixture(tc, NULL, sdb_store_clear);
571         suite_add_tcase(s, tc);
573         return s;
574 } /* core_store_suite */
576 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */