1 /* Copyright (c) 2020, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Test lib/metrics and feature/metrics functionalities
10 #define CONNECTION_PRIVATE
11 #define MAINLOOP_PRIVATE
12 #define METRICS_STORE_ENTRY_PRIVATE
14 #include "test/test.h"
15 #include "test/test_helpers.h"
16 #include "test/log_test_helpers.h"
18 #include "app/config/config.h"
20 #include "core/mainloop/connection.h"
21 #include "core/mainloop/mainloop.h"
22 #include "core/or/connection_st.h"
23 #include "core/or/policies.h"
24 #include "core/or/port_cfg_st.h"
26 #include "feature/metrics/metrics.h"
28 #include "lib/encoding/confline.h"
29 #include "lib/metrics/metrics_store.h"
31 #define TEST_METRICS_ENTRY_NAME "entryA"
32 #define TEST_METRICS_ENTRY_HELP "Description of entryA"
33 #define TEST_METRICS_ENTRY_LABEL_1 "label=farfadet"
34 #define TEST_METRICS_ENTRY_LABEL_2 "label=ponki"
37 set_metrics_port(or_options_t
*options
)
39 const char *port
= "MetricsPort 9035"; /* Default to 127.0.0.1 */
40 const char *policy
= "MetricsPortPolicy accept 1.2.3.4";
42 config_get_lines(port
, &options
->MetricsPort_lines
, 0);
43 config_get_lines(policy
, &options
->MetricsPortPolicy
, 0);
45 /* Parse and validate policy. */
46 policies_parse_from_options(options
);
50 test_config(void *arg
)
54 smartlist_t
*ports
= smartlist_new();
55 or_options_t
*options
= get_options_mutable();
59 set_metrics_port(options
);
61 int ret
= metrics_parse_ports(options
, ports
, &err_msg
);
62 tt_int_op(ret
, OP_EQ
, 0);
63 tt_int_op(smartlist_len(ports
), OP_EQ
, 1);
65 /* Validate the configured port. */
66 const port_cfg_t
*cfg
= smartlist_get(ports
, 0);
67 tt_assert(tor_addr_eq_ipv4h(&cfg
->addr
, 0x7f000001));
68 tt_int_op(cfg
->port
, OP_EQ
, 9035);
69 tt_int_op(cfg
->type
, OP_EQ
, CONN_TYPE_METRICS_LISTENER
);
71 /* Address of the policy should be permitted. */
72 tor_addr_from_ipv4h(&addr
, 0x01020304); /* 1.2.3.4 */
73 ret
= metrics_policy_permits_address(&addr
);
74 tt_int_op(ret
, OP_EQ
, true);
76 /* Anything else, should not. */
77 tor_addr_from_ipv4h(&addr
, 0x01020305); /* 1.2.3.5 */
78 ret
= metrics_policy_permits_address(&addr
);
79 tt_int_op(ret
, OP_EQ
, false);
82 SMARTLIST_FOREACH(ports
, port_cfg_t
*, c
, port_cfg_free(c
));
83 smartlist_free(ports
);
84 or_options_free(options
);
88 static char _c_buf
[256];
89 #define CONTAINS(conn, msg) \
91 tt_int_op(buf_datalen(conn->outbuf), OP_EQ, (strlen(msg))); \
92 memset(_c_buf, 0, sizeof(_c_buf)); \
93 buf_get_bytes(conn->outbuf, _c_buf, (strlen(msg))); \
94 tt_str_op(_c_buf, OP_EQ, (msg)); \
95 tt_int_op(buf_datalen(conn->outbuf), OP_EQ, 0); \
98 #define WRITE(conn, msg) \
99 buf_add(conn->inbuf, (msg), (strlen(msg)));
101 /* Free the previous conn object if any and allocate a new connection. In
102 * order to be allowed, set its address to 1.2.3.4 as per the policy. */
103 #define NEW_ALLOWED_CONN() \
105 close_closeable_connections(); \
106 conn = connection_new(CONN_TYPE_METRICS, AF_INET); \
107 tor_addr_from_ipv4h(&conn->addr, 0x01020304); \
111 test_connection(void *arg
)
114 connection_t
*conn
= NULL
;
115 or_options_t
*options
= get_options_mutable();
119 /* Notice that in this test, we will allocate a new connection at every test
120 * case. This is because the metrics_connection_process_inbuf() marks for
121 * close the connection in case of an error and thus we can't call again an
122 * inbuf process function on a marked for close connection. */
124 tor_init_connection_lists();
127 set_metrics_port(options
);
129 /* Set 1.2.3.5 IP, we should get rejected. */
131 tor_addr_from_ipv4h(&conn
->addr
, 0x01020305);
132 ret
= metrics_connection_process_inbuf(conn
);
133 tt_int_op(ret
, OP_EQ
, -1);
135 /* No HTTP request yet. */
137 ret
= metrics_connection_process_inbuf(conn
);
138 tt_int_op(ret
, OP_EQ
, 0);
139 connection_free_minimal(conn
);
143 WRITE(conn
, "HTTP 4.7\r\n\r\n");
144 ret
= metrics_connection_process_inbuf(conn
);
145 tt_int_op(ret
, OP_EQ
, -1);
146 CONTAINS(conn
, "HTTP/1.0 400 Bad Request\r\n\r\n");
148 /* Path not found. */
150 WRITE(conn
, "GET /badpath HTTP/1.0\r\n\r\n");
151 ret
= metrics_connection_process_inbuf(conn
);
152 tt_int_op(ret
, OP_EQ
, -1);
153 CONTAINS(conn
, "HTTP/1.0 404 Not Found\r\n\r\n");
155 /* Method not allowed. */
157 WRITE(conn
, "POST /something HTTP/1.0\r\n\r\n");
158 ret
= metrics_connection_process_inbuf(conn
);
159 tt_int_op(ret
, OP_EQ
, -1);
160 CONTAINS(conn
, "HTTP/1.0 405 Method Not Allowed\r\n\r\n");
162 /* Ask for metrics. The content should be above 0. We don't test the
163 * validity of the returned content but it is certainly not an error. */
165 WRITE(conn
, "GET /metrics HTTP/1.0\r\n\r\n");
166 ret
= metrics_connection_process_inbuf(conn
);
167 tt_int_op(ret
, OP_EQ
, 0);
168 tt_int_op(buf_datalen(conn
->outbuf
), OP_GT
, 0);
171 or_options_free(options
);
172 connection_free_minimal(conn
);
176 test_prometheus(void *arg
)
178 metrics_store_t
*store
= NULL
;
179 metrics_store_entry_t
*entry
= NULL
;
180 buf_t
*buf
= buf_new();
185 /* Fresh new store. No entries. */
186 store
= metrics_store_new();
189 /* Add entry and validate its content. */
190 entry
= metrics_store_add(store
, METRICS_TYPE_COUNTER
,
191 TEST_METRICS_ENTRY_NAME
,
192 TEST_METRICS_ENTRY_HELP
);
194 metrics_store_entry_add_label(entry
, TEST_METRICS_ENTRY_LABEL_1
);
196 static const char *expected
=
197 "# HELP " TEST_METRICS_ENTRY_NAME
" " TEST_METRICS_ENTRY_HELP
"\n"
198 "# TYPE " TEST_METRICS_ENTRY_NAME
" counter\n"
199 TEST_METRICS_ENTRY_NAME
"{" TEST_METRICS_ENTRY_LABEL_1
"} 0\n";
201 metrics_store_get_output(METRICS_FORMAT_PROMETHEUS
, store
, buf
);
202 output
= buf_extract(buf
, NULL
);
203 tt_str_op(expected
, OP_EQ
, output
);
208 metrics_store_free(store
);
212 test_store(void *arg
)
214 metrics_store_t
*store
= NULL
;
215 metrics_store_entry_t
*entry
= NULL
;
219 /* Fresh new store. No entries. */
220 store
= metrics_store_new();
222 tt_assert(!metrics_store_get_all(store
, TEST_METRICS_ENTRY_NAME
));
224 /* Add entry and validate its content. */
225 entry
= metrics_store_add(store
, METRICS_TYPE_COUNTER
,
226 TEST_METRICS_ENTRY_NAME
,
227 TEST_METRICS_ENTRY_HELP
);
229 tt_int_op(entry
->type
, OP_EQ
, METRICS_TYPE_COUNTER
);
230 tt_str_op(entry
->name
, OP_EQ
, TEST_METRICS_ENTRY_NAME
);
231 tt_str_op(entry
->help
, OP_EQ
, TEST_METRICS_ENTRY_HELP
);
232 tt_uint_op(entry
->u
.counter
.value
, OP_EQ
, 0);
234 /* Access the entry. */
235 tt_assert(metrics_store_get_all(store
, TEST_METRICS_ENTRY_NAME
));
237 /* Add a label to the entry to make it unique. */
238 metrics_store_entry_add_label(entry
, TEST_METRICS_ENTRY_LABEL_1
);
239 tt_int_op(metrics_store_entry_has_label(entry
, TEST_METRICS_ENTRY_LABEL_1
),
242 /* Update entry's value. */
243 metrics_store_entry_update(entry
, 42);
244 tt_int_op(metrics_store_entry_get_value(entry
), OP_EQ
, 42);
245 metrics_store_entry_update(entry
, 42);
246 tt_int_op(metrics_store_entry_get_value(entry
), OP_EQ
, 84);
247 metrics_store_entry_reset(entry
);
248 tt_int_op(metrics_store_entry_get_value(entry
), OP_EQ
, 0);
250 /* Add a new entry of same name but different label. */
251 /* Add entry and validate its content. */
252 entry
= metrics_store_add(store
, METRICS_TYPE_COUNTER
,
253 TEST_METRICS_ENTRY_NAME
,
254 TEST_METRICS_ENTRY_HELP
);
256 metrics_store_entry_add_label(entry
, TEST_METRICS_ENTRY_LABEL_2
);
258 /* Make sure _both_ entries are there. */
259 const smartlist_t
*entries
=
260 metrics_store_get_all(store
, TEST_METRICS_ENTRY_NAME
);
262 tt_int_op(smartlist_len(entries
), OP_EQ
, 2);
265 metrics_store_free(store
);
268 struct testcase_t metrics_tests
[] = {
270 { "config", test_config
, TT_FORK
, NULL
, NULL
},
271 { "connection", test_connection
, TT_FORK
, NULL
, NULL
},
272 { "prometheus", test_prometheus
, TT_FORK
, NULL
, NULL
},
273 { "store", test_store
, TT_FORK
, NULL
, NULL
},