@cryptotaxi247 / netdata-1 / commits / 87a7a044d

Initial CMocka testing against web_client.c (issue #7229). (#7264)

* Start of unit-test for http request processing. A CMocka test-driver has been added to replay the live-capture headers against the http request processing code. Enough stubs / mocking code have been written to check that we can inject a chosen raw request into the web_client code and check how it was processed. A parameterised buffer building function can setup requests for testing in `web_buffer` structures. Each endpoint in the API will be checked against a variety of http requests. This PoC demonstrates enough functionality to show building the request on demand for each test-case and cleaning up afterwards.

Andrew Moss committed Nov 7, 2019 at 15:29 UTC 87a7a044d58a518740f2a14e2266b5631c4aee10
2 files changed +190
Makefile.am
+22
@@ -644,10 +644,32 @@ if ENABLE_UNITTESTS
644 check_PROGRAMS = \
645 libnetdata/tests/str2ld_testdriver \
646 libnetdata/storage_number/tests/storage_number_testdriver \
647 + web/api/tests/web_api_testdriver \
648 $(NULL)
649
650 TESTS = $(check_PROGRAMS)
651
652 + web_api_tests_web_api_testdriver_LDFLAGS = \
653 + -Wl,--wrap=rrdhost_find_by_hostname \
654 + -Wl,--wrap=finished_web_request_statistics \
655 + -Wl,--wrap=config_get \
656 + -Wl,--wrap=web_client_api_request_v1 \
657 + -Wl,--wrap=rrdhost_find_by_guid \
658 + -Wl,--wrap=rrdset_find_byname \
659 + -Wl,--wrap=rrdset_find \
660 + -Wl,--wrap=rrdpush_receiver_thread_spawn \
661 + -Wl,--wrap=debug_int \
662 + -Wl,--wrap=error_int \
663 + -Wl,--wrap=info_int \
664 + $(TEST_LDFLAGS) \
665 + $(NULL)
666 + web_api_tests_web_api_testdriver_SOURCES = \
667 + web/api/tests/web_api.c \
668 + web/server/web_client.c \
669 + $(LIBNETDATA_FILES) \
670 + $(NULL)
671 + web_api_tests_web_api_testdriver_LDADD = $(NETDATA_COMMON_LIBS) $(TEST_LIBS)
672 +
673 libnetdata_tests_str2ld_testdriver_SOURCES = \
674 libnetdata/tests/test_str2ld.c \
675 $(LIBNETDATA_FILES) \
web/api/tests/web_api.c new
+168
@@ -0,0 +1,168 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "../../../libnetdata/libnetdata.h"
4 +#include "../../../libnetdata/required_dummies.h"
5 +#include "../../../database/rrd.h"
6 +#include "../../../web/server/web_client.h"
7 +#include <setjmp.h>
8 +#include <cmocka.h>
9 +#include <stdbool.h>
10 +
11 +RRDHOST *__wrap_rrdhost_find_by_hostname(const char *hostname, uint32_t hash)
12 +{
13 + return NULL;
14 +}
15 +
16 +/* Note: we've got some intricate code inside the global statistics module, might be useful to pull it inside the
17 + test set instead of mocking it. */
18 +void __wrap_finished_web_request_statistics(
19 + uint64_t dt, uint64_t bytes_received, uint64_t bytes_sent, uint64_t content_size, uint64_t compressed_content_size)
20 +{
21 +}
22 +
23 +char *__wrap_config_get(struct config *root, const char *section, const char *name, const char *default_value)
24 +{
25 + if (!strcmp(section, CONFIG_SECTION_WEB) && !strcmp(name, "web files owner"))
26 + return "netdata";
27 + return "UNKNOWN FIX ME";
28 +}
29 +
30 +int __wrap_web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url)
31 +{
32 + printf("api requests: %s\n", url);
33 +}
34 +
35 +int __wrap_rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url)
36 +{
37 + return 0;
38 +}
39 +
40 +RRDHOST *__wrap_rrdhost_find_by_guid(const char *guid, uint32_t hash)
41 +{
42 + printf("FIXME: rrdset_find_guid\n");
43 + return NULL;
44 +}
45 +
46 +RRDSET *__wrap_rrdset_find_byname(RRDHOST *host, const char *name)
47 +{
48 + printf("FIXME: rrdset_find_byname\n");
49 + return NULL;
50 +}
51 +
52 +RRDSET *__wrap_rrdset_find(RRDHOST *host, const char *id)
53 +{
54 + printf("FIXME: rrdset_find\n");
55 + return NULL;
56 +}
57 +
58 +void __wrap_debug_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
59 +{
60 + va_list args;
61 + va_start(args, fmt);
62 + printf("DEBUG: ");
63 + vprintf(fmt, args);
64 + printf("\n");
65 + va_end(args);
66 +}
67 +
68 +void __wrap_info_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
69 +{
70 + va_list args;
71 + va_start(args, fmt);
72 + printf("INFO: ");
73 + vprintf(fmt, args);
74 + printf("\n");
75 + va_end(args);
76 +}
77 +
78 +void __wrap_error_int(
79 + const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ...)
80 +{
81 + va_list args;
82 + va_start(args, fmt);
83 + printf("ERROR: ");
84 + vprintf(fmt, args);
85 + printf("\n");
86 + va_end(args);
87 +}
88 +
89 +WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_STATIC_THREADED;
90 +char *netdata_configured_web_dir = "UNKNOWN FIXME";
91 +RRDHOST *localhost = NULL;
92 +
93 +struct config netdata_config = { .sections = NULL,
94 + .mutex = NETDATA_MUTEX_INITIALIZER,
95 + .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
96 + .rwlock = AVL_LOCK_INITIALIZER } };
97 +
98 +const char *http_headers[] = { "Host: 254.254.0.1",
99 + "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_" // No ,
100 + "0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36",
101 + "Connection: keep-alive",
102 + "X-Forwarded-For: 1.254.1.251",
103 + "Cookie: _ga=GA1.1.1227576758.1571113676; _gid=GA1.2.1222321739.1573628979",
104 + "X-Requested-With: XMLHttpRequest",
105 + "Accept-Encoding: gzip, deflate",
106 + "Cache-Control: no-cache, no-store" };
107 +#define MAX_HEADERS (sizeof(http_headers) / (sizeof(const char *)))
108 +
109 +static void build_request(struct web_buffer *wb, const char *url, bool use_cr, size_t num_headers)
110 +{
111 + buffer_reset(wb);
112 + buffer_strcat(wb, "GET ");
113 + buffer_strcat(wb, url);
114 + buffer_strcat(wb, " HTTP/1.1");
115 + if (use_cr)
116 + buffer_strcat(wb, "\r");
117 + buffer_strcat(wb, "\n");
118 + for (size_t i = 0; i < num_headers && i < MAX_HEADERS; i++) {
119 + buffer_strcat(wb, http_headers[i]);
120 + if (use_cr)
121 + buffer_strcat(wb, "\r");
122 + buffer_strcat(wb, "\n");
123 + }
124 + if (use_cr)
125 + buffer_strcat(wb, "\r");
126 + buffer_strcat(wb, "\n");
127 +}
128 +
129 +static struct web_client *pre_test_setup()
130 +{
131 + localhost = malloc(sizeof(RRDHOST));
132 + struct web_client *w = (struct web_client *)malloc(sizeof(struct web_client));
133 + w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
134 + w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
135 + w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
136 + strcpy(w->origin, "*"); // Simulate web_client_create_on_fd()
137 + w->cookie1[0] = 0; // Simulate web_client_create_on_fd()
138 + w->cookie2[0] = 0; // Simulate web_client_create_on_fd()
139 + return w;
140 +}
141 +
142 +static void post_test_cleanup(struct web_client *w)
143 +{
144 + buffer_free(w->response.data);
145 + buffer_free(w->response.header);
146 + buffer_free(w->response.header_output);
147 + free(w);
148 + free(localhost);
149 +}
150 +
151 +static void api_info(void **state)
152 +{
153 + for (size_t i = 0; i < MAX_HEADERS; i++) {
154 + struct web_client *w = pre_test_setup();
155 + build_request(w->response.data, "/api/v1/info", true, i);
156 + web_client_process_request(w);
157 + assert_int_equal(w->flags & WEB_CLIENT_FLAG_WAIT_RECEIVE, 0);
158 + post_test_cleanup(w);
159 + }
160 +}
161 +
162 +int main(void)
163 +{
164 + const struct CMUnitTest tests[] = { cmocka_unit_test(api_info) };
165 + debug_flags = 0xffffffffffff;
166 +
167 + return cmocka_run_group_tests_name("web_api", tests, NULL, NULL);
168 +}