Code

store: Store a service/metric's parent hostname as an attribute.
[sysdb.git] / t / unit / frontend / query_test.c
1 /*
2  * SysDB - t/unit/frontend/query_test.c
3  * Copyright (C) 2015 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 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "frontend/connection.h"
33 #include "frontend/parser.h"
34 #include "frontend/connection-private.h"
35 #include "testutils.h"
37 #include <check.h>
39 /*
40  * private helpers
41  */
43 static void
44 populate(void)
45 {
46         sdb_data_t datum;
48         sdb_store_host("h1", 1);
49         sdb_store_host("h2", 3);
51         datum.type = SDB_TYPE_STRING;
52         datum.data.string = "v1";
53         sdb_store_attribute("h1", "k1", &datum, 1);
54         datum.data.string = "v2";
55         sdb_store_attribute("h1", "k2", &datum, 2);
56         datum.data.string = "v3";
57         sdb_store_attribute("h1", "k3", &datum, 2);
59         sdb_store_metric("h1", "m1", /* store */ NULL, 2);
60         sdb_store_metric("h1", "m2", /* store */ NULL, 1);
61         sdb_store_metric("h2", "m1", /* store */ NULL, 1);
63         datum.type = SDB_TYPE_INTEGER;
64         datum.data.integer = 42;
65         sdb_store_metric_attr("h1", "m1", "k3", &datum, 2);
67         sdb_store_service("h2", "s1", 1);
68         sdb_store_service("h2", "s2", 2);
70         datum.data.integer = 123;
71         sdb_store_service_attr("h2", "s2", "k1", &datum, 2);
72         datum.data.integer = 4711;
73         sdb_store_service_attr("h2", "s2", "k2", &datum, 1);
74 } /* populate */
76 #define HOST_H1 \
77         "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \
78                         "\"update_interval\": \"0s\", \"backends\": [], " \
79                 "\"attributes\": [" \
80                         "{\"name\": \"k1\", \"value\": \"v1\", " \
81                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
82                                 "\"update_interval\": \"0s\", \"backends\": []}," \
83                         "{\"name\": \"k2\", \"value\": \"v2\", " \
84                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
85                                 "\"update_interval\": \"0s\", \"backends\": []}," \
86                         "{\"name\": \"k3\", \"value\": \"v3\", " \
87                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
88                                 "\"update_interval\": \"0s\", \"backends\": []}], " \
89                 "\"metrics\": [" \
90                         "{\"name\": \"m1\", \"timeseries\": false, " \
91                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
92                                 "\"update_interval\": \"0s\", \"backends\": [], " \
93                                 "\"attributes\": [" \
94                                         "{\"name\": \"hostname\", \"value\": \"h1\", " \
95                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
96                                                 "\"update_interval\": \"0s\", \"backends\": []}," \
97                                         "{\"name\": \"k3\", \"value\": 42, " \
98                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
99                                                 "\"update_interval\": \"0s\", \"backends\": []}]}," \
100                         "{\"name\": \"m2\", \"timeseries\": false, " \
101                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
102                                 "\"update_interval\": \"0s\", \"backends\": [], " \
103                                 "\"attributes\": [" \
104                                         "{\"name\": \"hostname\", \"value\": \"h1\", " \
105                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
106                                                 "\"update_interval\": \"0s\", \"backends\": []}]}]}"
108 #define SERVICE_H2_S1 \
109         "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \
110                         "\"update_interval\": \"0s\", \"backends\": [], " \
111                 "\"services\": [" \
112                         "{\"name\": \"s1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \
113                                 "\"update_interval\": \"0s\", \"backends\": [], " \
114                                 "\"attributes\": [" \
115                                         "{\"name\": \"hostname\", \"value\": \"h2\", " \
116                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
117                                                 "\"update_interval\": \"0s\", \"backends\": []}]}]}"
119 #define METRIC_H1_M1 \
120         "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " \
121                         "\"update_interval\": \"0s\", \"backends\": [], " \
122                 "\"metrics\": [" \
123                         "{\"name\": \"m1\", \"timeseries\": false, " \
124                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
125                                 "\"update_interval\": \"0s\", \"backends\": [], " \
126                                 "\"attributes\": [" \
127                                         "{\"name\": \"hostname\", \"value\": \"h1\", " \
128                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
129                                                 "\"update_interval\": \"0s\", \"backends\": []}," \
130                                         "{\"name\": \"k3\", \"value\": 42, " \
131                                                 "\"last_update\": \"1970-01-01 00:00:00 +0000\", " \
132                                                 "\"update_interval\": \"0s\", \"backends\": []}]}]}"
134 typedef struct {
135         sdb_conn_t conn;
136         sdb_strbuf_t *write_buf;
137 } mock_conn_t;
138 #define MOCK_CONN(obj) ((mock_conn_t *)(obj))
139 #define CONN(obj) ((sdb_conn_t *)(obj))
141 static void
142 mock_conn_destroy(sdb_conn_t *conn)
144         sdb_strbuf_destroy(conn->buf);
145         sdb_strbuf_destroy(conn->errbuf);
146         sdb_strbuf_destroy(MOCK_CONN(conn)->write_buf);
147         free(conn);
148 } /* mock_conn_destroy */
150 static ssize_t
151 mock_conn_read(sdb_conn_t *conn, size_t len)
153         if (! conn)
154                 return -1;
155         /* unused so far */
156         return len;
157 } /* conn_read */
159 static ssize_t
160 mock_conn_write(sdb_conn_t *conn, const void *buf, size_t len)
162         if (! conn)
163                 return -1;
164         return sdb_strbuf_memappend(MOCK_CONN(conn)->write_buf, buf, len);
165 } /* conn_write */
167 static sdb_conn_t *
168 mock_conn_create(void)
170         mock_conn_t *conn;
172         conn = calloc(1, sizeof(*conn));
173         if (! conn) {
174                 fail("INTERNAL ERROR: failed to allocate connection object");
175                 return NULL;
176         }
178         SDB_OBJ(conn)->name = "mock_connection";
179         SDB_OBJ(conn)->ref_cnt = 1;
181         conn->conn.buf = sdb_strbuf_create(0);
182         conn->conn.errbuf = sdb_strbuf_create(0);
183         conn->write_buf = sdb_strbuf_create(64);
184         if ((! conn->conn.buf) || (! conn->conn.errbuf) || (! conn->write_buf)) {
185                 mock_conn_destroy(CONN(conn));
186                 fail("INTERNAL ERROR: failed to allocate connection object");
187                 return NULL;
188         }
190         conn->conn.read = mock_conn_read;
191         conn->conn.write = mock_conn_write;
193         conn->conn.username = "mock_user";
194         conn->conn.cmd = SDB_CONNECTION_IDLE;
195         conn->conn.cmd_len = 0;
196         return CONN(conn);
197 } /* mock_conn_create */
199 /* TODO: move this into a test helper module */
200 static void
201 fail_if_strneq(const char *got, const char *expected, size_t n, const char *fmt, ...)
203         sdb_strbuf_t *buf;
204         va_list ap;
206         size_t len1 = strlen(got);
207         size_t len2 = strlen(expected);
209         size_t i;
210         int pos = -1;
212         if (n) {
213                 len1 = SDB_MIN(len1, n);
214                 len2 = SDB_MIN(len2, n);
215         }
217         if (len1 != len2)
218                 pos = (int)SDB_MIN(len1, len2);
220         for (i = 0; i < SDB_MIN(len1, len2); ++i) {
221                 if (got[i] != expected[i]) {
222                         pos = (int)i;
223                         break;
224                 }
225         }
227         if (pos == -1)
228                 return;
230         buf = sdb_strbuf_create(64);
231         va_start(ap, fmt);
232         sdb_strbuf_vsprintf(buf, fmt, ap);
234         fail("%s\n         got: %s\n              %*s\n    expected: %s",
235                         sdb_strbuf_string(buf), got, pos + 1, "^", expected);
236 } /* fail_if_strneq */
238 /*
239  * tests
240  */
242 static struct {
243         int type;
244         const char *hostname;
245         const char *name;
246         const char *filter;
248         int expected;
249         uint32_t code;
250         size_t len;
251         const char *data;
252 } exec_fetch_data[] = {
253         /* hosts */
254         {
255                 SDB_HOST, "h1", NULL, NULL,
256                 0, SDB_CONNECTION_DATA, 1110, HOST_H1,
257         },
258         {
259                 SDB_HOST, "h1", NULL, "age >= 0s", /* always matches */
260                 0, SDB_CONNECTION_DATA, 1110, HOST_H1,
261         },
262         {
263                 SDB_HOST, "h1", NULL, "age < 0s", /* never matches */
264                 -1, UINT32_MAX, 0, NULL, /* FETCH fails if the object doesn't exist */
265         },
266         {
267                 SDB_HOST, "x1", NULL, NULL, /* does not exist */
268                 -1, UINT32_MAX, 0, NULL,
269         },
270         {
271                 SDB_HOST, "h1", "s1", NULL, /* invalid args */
272                 -1, UINT32_MAX, 0, NULL,
273         },
274         /* services */
275         {
276                 SDB_SERVICE, "h2", "s1", NULL,
277                 0, SDB_CONNECTION_DATA, 356, SERVICE_H2_S1,
278         },
279         {
280                 SDB_SERVICE, "h2", "s1", "age >= 0s", /* always matches */
281                 0, SDB_CONNECTION_DATA, 356, SERVICE_H2_S1,
282         },
283         {
284                 SDB_SERVICE, "h2", "s1", "age < 0s", /* never matches */
285                 -1, UINT32_MAX, 0, NULL,
286         },
287         {
288                 SDB_SERVICE, "h2", "s1", "name = 'h2'", /* only matches host */
289                 -1, UINT32_MAX, 0, NULL,
290         },
291         {
292                 SDB_SERVICE, "h2", "x1", NULL, /* does not exist */
293                 -1, UINT32_MAX, 0, NULL,
294         },
295         {
296                 SDB_SERVICE, "x2", "s1", NULL, /* does not exist */
297                 -1, UINT32_MAX, 0, NULL,
298         },
299         {
300                 SDB_SERVICE, "h2", NULL, NULL, /* invalid args */
301                 -1, UINT32_MAX, 0, NULL,
302         },
303         /* metrics */
304         {
305                 SDB_METRIC, "h1", "m1", NULL,
306                 0, SDB_CONNECTION_DATA, 489, METRIC_H1_M1,
307         },
308         {
309                 SDB_METRIC, "h1", "m1", "age >= 0s", /* always matches */
310                 0, SDB_CONNECTION_DATA, 489, METRIC_H1_M1,
311         },
312         {
313                 SDB_METRIC, "h1", "m1", "age < 0s", /* never matches */
314                 -1, UINT32_MAX, 0, NULL,
315         },
316         {
317                 SDB_METRIC, "h1", "m1", "name = 'h1'", /* only matches host */
318                 -1, UINT32_MAX, 0, NULL,
319         },
320         {
321                 SDB_METRIC, "h1", "x1", NULL, /* does not exist */
322                 -1, UINT32_MAX, 0, NULL,
323         },
324         {
325                 SDB_METRIC, "x1", "m1", NULL, /* does not exist */
326                 -1, UINT32_MAX, 0, NULL,
327         },
328         {
329                 SDB_METRIC, "x1", NULL, NULL, /* invalid args */
330                 -1, UINT32_MAX, 0, NULL,
331         },
332 };
334 START_TEST(test_exec_fetch)
336         sdb_conn_t *conn = mock_conn_create();
337         sdb_store_matcher_t *filter = NULL;
339         uint32_t code = UINT32_MAX, msg_len = UINT32_MAX;
340         const char *data;
341         ssize_t tmp;
342         size_t len;
343         int check;
345         if (exec_fetch_data[_i].filter) {
346                 filter = sdb_fe_parse_matcher(exec_fetch_data[_i].filter, -1, NULL);
347                 ck_assert_msg(filter != NULL);
348         }
350         check = sdb_fe_exec_fetch(conn, exec_fetch_data[_i].type,
351                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name, filter);
352         fail_unless(check == exec_fetch_data[_i].expected,
353                         "sdb_fe_exec_fetch(%s, %s, %s, %s) = %d; expected: %d",
354                         SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type),
355                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name,
356                         exec_fetch_data[_i].filter, check, exec_fetch_data[_i].expected);
358         len = sdb_strbuf_len(MOCK_CONN(conn)->write_buf);
360         if (exec_fetch_data[_i].code == UINT32_MAX) {
361                 fail_unless(len == 0,
362                                 "sdb_fe_exec_fetch(%s, %s, %s, %s) returned data on error",
363                         SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type),
364                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name,
365                         exec_fetch_data[_i].filter);
366                 sdb_object_deref(SDB_OBJ(filter));
367                 mock_conn_destroy(conn);
368                 return;
369         }
371         data = sdb_strbuf_string(MOCK_CONN(conn)->write_buf);
372         tmp = sdb_proto_unmarshal_header(data, len, &code, &msg_len);
373         ck_assert_msg(tmp == (ssize_t)(2 * sizeof(uint32_t)));
374         data += tmp;
375         len -= tmp;
377         fail_unless((code == exec_fetch_data[_i].code)
378                                 && ((size_t)msg_len == exec_fetch_data[_i].len),
379                         "sdb_fe_exec_fetch(%s, %s, %s, %s) returned %u, %u; expected: %u, %zu",
380                         SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type),
381                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name,
382                         exec_fetch_data[_i].filter, code, msg_len,
383                         exec_fetch_data[_i].code, exec_fetch_data[_i].len);
385         tmp = sdb_proto_unmarshal_int32(data, len, &code);
386         fail_unless(code == SDB_CONNECTION_FETCH,
387                         "sdb_fe_exec_fetch(%s, %s, %s, %s) returned %s object; expected: FETCH",
388                         SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type),
389                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name,
390                         exec_fetch_data[_i].filter, SDB_CONN_MSGTYPE_TO_STRING((int)code));
391         data += tmp;
392         len -= tmp;
394         fail_if_strneq(data, exec_fetch_data[_i].data, (size_t)msg_len,
395                         "sdb_fe_exec_fetch(%s, %s, %s, %s) returned '%s'; expected: '%s'",
396                         SDB_STORE_TYPE_TO_NAME(exec_fetch_data[_i].type),
397                         exec_fetch_data[_i].hostname, exec_fetch_data[_i].name,
398                         exec_fetch_data[_i].filter, data, exec_fetch_data[_i].data);
400         sdb_object_deref(SDB_OBJ(filter));
401         mock_conn_destroy(conn);
403 END_TEST
405 TEST_MAIN("frontend::query")
407         TCase *tc = tcase_create("core");
408         tcase_add_checked_fixture(tc, populate, sdb_store_clear);
409         tcase_add_loop_test(tc, test_exec_fetch, 0, SDB_STATIC_ARRAY_LEN(exec_fetch_data));
410         ADD_TCASE(tc);
412 TEST_MAIN_END
414 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */