// SPDX-License-Identifier: GPL-3.0-or-later #include "dyncfg-internals.h" #include "dyncfg.h" // ---------------------------------------------------------------------------- // unit test #define LINE_FILE_STR TOSTRING(__LINE__) "@" __FILE__ struct dyncfg_unittest { bool enabled; size_t errors; DICTIONARY *nodes; SPINLOCK spinlock; struct dyncfg_unittest_action *queue; } dyncfg_unittest_data = { 0 }; typedef struct { bool enabled; bool removed; struct { double dbl; bool bln; } value; } TEST_CFG; typedef struct { const char *id; const char *source; bool sync; DYNCFG_TYPE type; DYNCFG_CMDS cmds; DYNCFG_SOURCE_TYPE source_type; TEST_CFG current; TEST_CFG expected; bool received; bool finished; size_t last_saves; bool needs_save; } TEST; struct dyncfg_unittest_action { TEST *t; BUFFER *result; BUFFER *payload; DYNCFG_CMDS cmd; const char *add_name; const char *source; rrd_function_result_callback_t result_cb; void *result_cb_data; struct dyncfg_unittest_action *prev, *next; }; static void dyncfg_unittest_register_error(const char *id, const char *msg) { if(msg) nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: error on id '%s': %s", id ? id : "", msg); __atomic_add_fetch(&dyncfg_unittest_data.errors, 1, __ATOMIC_RELAXED); } static int dyncfg_unittest_execute_cb(struct rrd_function_execute *rfe, void *data); bool dyncfg_unittest_parse_payload(BUFFER *payload, TEST *t, DYNCFG_CMDS cmd, const char *add_name, const char *source) { CLEAN_JSON_OBJECT *jobj = json_tokener_parse(buffer_tostring(payload)); if(!jobj) { dyncfg_unittest_register_error(t->id, "cannot parse json payload"); return false; } struct json_object *json_double; struct json_object *json_boolean; json_object_object_get_ex(jobj, "double", &json_double); double value_double = json_object_get_double(json_double); json_object_object_get_ex(jobj, "boolean", &json_boolean); int value_boolean = json_object_get_boolean(json_boolean); if(cmd == DYNCFG_CMD_UPDATE) { t->current.value.dbl = value_double; t->current.value.bln = value_boolean; } else if(cmd == DYNCFG_CMD_ADD) { char buf[strlen(t->id) + strlen(add_name) + 20]; snprintfz(buf, sizeof(buf), "%s:%s", t->id, add_name); TEST tmp = { .id = strdupz(buf), .source = strdupz(source), .cmds = (t->cmds & ~DYNCFG_CMD_ADD) | DYNCFG_CMD_GET | DYNCFG_CMD_REMOVE | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_TEST, .sync = t->sync, .type = DYNCFG_TYPE_JOB, .source_type = DYNCFG_SOURCE_TYPE_DYNCFG, .received = true, .finished = true, .current = {.enabled = true, .removed = false, .value = { .dbl = value_double, .bln = value_boolean, }}, .expected = { .enabled = true, .removed = false, .value = { .dbl = 3.14, .bln = true, } }, .needs_save = true, }; const DICTIONARY_ITEM *item = dictionary_set_and_acquire_item(dyncfg_unittest_data.nodes, buf, &tmp, sizeof(tmp)); TEST *t2 = dictionary_acquired_item_value(item); dictionary_acquired_item_release(dyncfg_unittest_data.nodes, item); dyncfg_add_low_level(localhost, t2->id, "/unittests", DYNCFG_STATUS_RUNNING, t2->type, t2->source_type, t2->source, t2->cmds, 0, 0, t2->sync, HTTP_ACCESS_NONE, HTTP_ACCESS_NONE, dyncfg_unittest_execute_cb, t2); } else { dyncfg_unittest_register_error(t->id, "invalid command received to parse payload"); return false; } return true; } static int dyncfg_unittest_action(struct dyncfg_unittest_action *a) { TEST *t = a->t; int rc = HTTP_RESP_OK; if(a->cmd == DYNCFG_CMD_ENABLE) t->current.enabled = true; else if(a->cmd == DYNCFG_CMD_DISABLE) t->current.enabled = false; else if(a->cmd == DYNCFG_CMD_ADD || a->cmd == DYNCFG_CMD_UPDATE) rc = dyncfg_unittest_parse_payload(a->payload, a->t, a->cmd, a->add_name, a->source) ? HTTP_RESP_OK : HTTP_RESP_BAD_REQUEST; else if(a->cmd == DYNCFG_CMD_REMOVE) t->current.removed = true; else rc = HTTP_RESP_BAD_REQUEST; dyncfg_default_response(a->result, rc, NULL); a->result_cb(a->result, rc, a->result_cb_data); buffer_free(a->payload); freez((void *)a->add_name); freez(a); __atomic_store_n(&t->finished, true, __ATOMIC_RELAXED); return rc; } static void *dyncfg_unittest_thread_action(void *ptr) { while(!nd_thread_signaled_to_cancel()) { struct dyncfg_unittest_action *a = NULL; spinlock_lock(&dyncfg_unittest_data.spinlock); a = dyncfg_unittest_data.queue; if(a) DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(dyncfg_unittest_data.queue, a, prev, next); spinlock_unlock(&dyncfg_unittest_data.spinlock); if(a) dyncfg_unittest_action(a); else sleep_usec(10 * USEC_PER_MS); } return ptr; } static int dyncfg_unittest_execute_cb(struct rrd_function_execute *rfe, void *data) { int rc; bool run_the_callback = true; TEST *t = data; t->received = true; char buf[strlen(rfe->function) + 1]; memcpy(buf, rfe->function, sizeof(buf)); char *words[MAX_FUNCTION_PARAMETERS]; // an array of pointers for the words in this line size_t num_words = quoted_strings_splitter_pluginsd(buf, words, MAX_FUNCTION_PARAMETERS); const char *config = get_word(words, num_words, 0); const char *id = get_word(words, num_words, 1); const char *action = get_word(words, num_words, 2); const char *add_name = get_word(words, num_words, 3); if(!config || !*config || strcmp(config, PLUGINSD_FUNCTION_CONFIG) != 0) { char *msg = "did not receive a config call"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } if(!id || !*id) { char *msg = "did not receive an id"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } if(t->type != DYNCFG_TYPE_TEMPLATE && strcmp(t->id, id) != 0) { char *msg = "id received is not the expected"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } if(!action || !*action) { char *msg = "did not receive an action"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } DYNCFG_CMDS cmd = dyncfg_cmds2id(action); if(cmd == DYNCFG_CMD_NONE) { char *msg = "action received is not known"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } if(!(t->cmds & cmd)) { char *msg = "received a command that is not supported"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } if(t->current.removed && cmd != DYNCFG_CMD_ADD) { char *msg = "received a command for a removed entry"; dyncfg_unittest_register_error(id, msg); rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST, msg); goto cleanup; } struct dyncfg_unittest_action *a = callocz(1, sizeof(*a)); a->t = t; a->add_name = add_name ? strdupz(add_name) : NULL; a->source = rfe->source, a->result = rfe->result.wb; a->payload = buffer_dup(rfe->payload); a->cmd = cmd; a->result_cb = rfe->result.cb; a->result_cb_data = rfe->result.data; run_the_callback = false; if(t->sync) rc = dyncfg_unittest_action(a); else { spinlock_lock(&dyncfg_unittest_data.spinlock); DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(dyncfg_unittest_data.queue, a, prev, next); spinlock_unlock(&dyncfg_unittest_data.spinlock); rc = HTTP_RESP_OK; } cleanup: if(run_the_callback) { __atomic_store_n(&t->finished, true, __ATOMIC_RELAXED); if (rfe->result.cb) rfe->result.cb(rfe->result.wb, rc, rfe->result.data); } return rc; } static bool dyncfg_unittest_check(TEST *t, DYNCFG_CMDS c, const char *cmd, bool received) { size_t errors = 0; fprintf(stderr, "CHECK '%s' after cmd '%s'...", t->id, cmd); if(t->received != received) { fprintf(stderr, "\n - received flag found '%s', expected '%s'", t->received?"true":"false", received?"true":"false"); errors++; goto cleanup; } if(!received) goto cleanup; usec_t give_up_ut = now_monotonic_usec() + 2 * USEC_PER_SEC; while(!__atomic_load_n(&t->finished, __ATOMIC_RELAXED)) { tinysleep(); if(now_monotonic_usec() > give_up_ut) { fprintf(stderr, "\n - gave up waiting for the plugin to process this!"); errors++; goto cleanup; } } if(t->type != DYNCFG_TYPE_TEMPLATE && t->current.enabled != t->expected.enabled) { fprintf(stderr, "\n - enabled flag found '%s', expected '%s'", t->current.enabled?"true":"false", t->expected.enabled?"true":"false"); errors++; } if(t->current.removed != t->expected.removed) { fprintf(stderr, "\n - removed flag found '%s', expected '%s'", t->current.removed?"true":"false", t->expected.removed?"true":"false"); errors++; } if(t->current.value.bln != t->expected.value.bln) { fprintf(stderr, "\n - boolean value found '%s', expected '%s'", t->current.value.bln?"true":"false", t->expected.value.bln?"true":"false"); errors++; } if(t->current.value.dbl != t->expected.value.dbl) { fprintf(stderr, "\n - double value found '%f', expected '%f'", t->current.value.dbl, t->expected.value.dbl); errors++; } DYNCFG *df = dictionary_get(dyncfg_globals.nodes, t->id); if(!df) { fprintf(stderr, "\n - not found in DYNCFG nodes dictionary!"); errors++; } else if(df->cmds != t->cmds) { fprintf(stderr, "\n - has different cmds in DYNCFG nodes dictionary; found: "); dyncfg_cmds2fp(df->cmds, stderr); fprintf(stderr, ", expected: "); dyncfg_cmds2fp(t->cmds, stderr); fprintf(stderr, "\n"); errors++; } else if(df->type == DYNCFG_TYPE_JOB && df->current.source_type == DYNCFG_SOURCE_TYPE_DYNCFG && !df->dyncfg.saves) { fprintf(stderr, "\n - DYNCFG job has no saves!"); errors++; } else if(df->type == DYNCFG_TYPE_JOB && df->current.source_type == DYNCFG_SOURCE_TYPE_DYNCFG && (!df->dyncfg.payload || !buffer_strlen(df->dyncfg.payload))) { fprintf(stderr, "\n - DYNCFG job has no payload!"); errors++; } else if(df->dyncfg.user_disabled && !df->dyncfg.saves) { fprintf(stderr, "\n - DYNCFG disabled config has no saves!"); errors++; } else if((c & (DYNCFG_CMD_ADD | DYNCFG_CMD_UPDATE)) && t->source && string_strcmp(df->current.source, t->source) != 0) { fprintf(stderr, "\n - source does not match!"); errors++; } else if((c & (DYNCFG_CMD_ADD | DYNCFG_CMD_UPDATE)) && df->current.source && !t->source) { fprintf(stderr, "\n - there is a source but it shouldn't be any!"); errors++; } else if(t->needs_save && df->dyncfg.saves <= t->last_saves) { fprintf(stderr, "\n - should be saved, but it is not saved!"); errors++; } else if(!t->needs_save && df->dyncfg.saves > t->last_saves) { fprintf(stderr, "\n - should be not be saved, but it saved!"); errors++; } cleanup: if(errors) { fprintf(stderr, "\n >>> FAILED\n\n"); dyncfg_unittest_register_error(NULL, NULL); return false; } fprintf(stderr, " OK\n"); return true; } static void dyncfg_unittest_reset(void) { TEST *t; dfe_start_read(dyncfg_unittest_data.nodes, t) { t->received = t->finished = false; t->needs_save = false; DYNCFG *df = dictionary_get(dyncfg_globals.nodes, t->id); if(!df) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: cannot find id '%s'", t->id); dyncfg_unittest_register_error(NULL, NULL); } else t->last_saves = df->dyncfg.saves; } dfe_done(t); } void should_be_saved(TEST *t, DYNCFG_CMDS c) { DYNCFG *df; if(t->type == DYNCFG_TYPE_TEMPLATE) { df = dictionary_get(dyncfg_globals.nodes, t->id); t->current.enabled = !df->dyncfg.user_disabled; } t->needs_save = c == DYNCFG_CMD_UPDATE || (t->current.enabled && c == DYNCFG_CMD_DISABLE) || (!t->current.enabled && c == DYNCFG_CMD_ENABLE); } static int dyncfg_unittest_run(const char *cmd, BUFFER *wb, const char *payload, const char *source) { dyncfg_unittest_reset(); char buf[strlen(cmd) + 1]; memcpy(buf, cmd, sizeof(buf)); char *words[MAX_FUNCTION_PARAMETERS]; // an array of pointers for the words in this line size_t num_words = quoted_strings_splitter_pluginsd(buf, words, MAX_FUNCTION_PARAMETERS); // const char *config = get_word(words, num_words, 0); const char *id = get_word(words, num_words, 1); char *action = get_word(words, num_words, 2); const char *add_name = get_word(words, num_words, 3); DYNCFG_CMDS c = dyncfg_cmds2id(action); TEST *t = dictionary_get(dyncfg_unittest_data.nodes, id); if(!t) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: cannot find id '%s' from cmd: %s", id, cmd); dyncfg_unittest_register_error(NULL, NULL); return HTTP_RESP_NOT_FOUND; } if(t->type == DYNCFG_TYPE_TEMPLATE) t->received = t->finished = true; if(c == DYNCFG_CMD_DISABLE) t->expected.enabled = false; if(c == DYNCFG_CMD_ENABLE) t->expected.enabled = true; if(c == DYNCFG_CMD_UPDATE) memset(&t->current.value, 0, sizeof(t->current.value)); if(c & (DYNCFG_CMD_UPDATE) || (c & (DYNCFG_CMD_DISABLE|DYNCFG_CMD_ENABLE) && t->type != DYNCFG_TYPE_TEMPLATE)) { freez((void *)t->source); t->source = strdupz(source); } buffer_flush(wb); CLEAN_BUFFER *pld = NULL; if(payload) { pld = buffer_create(1024, NULL); buffer_strcat(pld, payload); } should_be_saved(t, c); int rc = rrd_function_run(localhost, wb, 10, HTTP_ACCESS_ALL, cmd, true, NULL, NULL, NULL, NULL, NULL, NULL, NULL, pld, source); if(!DYNCFG_RESP_SUCCESS(rc)) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: failed to run: %s; returned code %d", cmd, rc); dyncfg_unittest_register_error(NULL, NULL); } dyncfg_unittest_check(t, c, cmd, true); if(rc == HTTP_RESP_OK && t->type == DYNCFG_TYPE_TEMPLATE) { if(c == DYNCFG_CMD_ADD) { char buf2[strlen(id) + strlen(add_name) + 2]; snprintfz(buf2, sizeof(buf2), "%s:%s", id, add_name); TEST *tt = dictionary_get(dyncfg_unittest_data.nodes, buf2); if (!tt) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: failed to find newly added id '%s' of command: %s", id, cmd); dyncfg_unittest_register_error(NULL, NULL); } dyncfg_unittest_check(tt, c, cmd, true); } else { STRING *template = string_strdupz(t->id); DYNCFG *df; dfe_start_read(dyncfg_globals.nodes, df) { if(df->type == DYNCFG_TYPE_JOB && df->template == template) { TEST *tt = dictionary_get(dyncfg_unittest_data.nodes, df_dfe.name); if (!tt) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: failed to find id '%s' while running command: %s", df_dfe.name, cmd); dyncfg_unittest_register_error(NULL, NULL); } else { if(c == DYNCFG_CMD_DISABLE) tt->expected.enabled = false; if(c == DYNCFG_CMD_ENABLE) tt->expected.enabled = true; dyncfg_unittest_check(tt, c, cmd, true); } } } dfe_done(df); string_freez(template); } } return rc; } static void dyncfg_unittest_cleanup_files(void) { char path[FILENAME_MAX]; snprintfz(path, sizeof(path) - 1, "%s/%s", netdata_configured_varlib_dir, "config"); DIR *dir = opendir(path); if (!dir) { nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG UNITTEST: cannot open directory '%s'", path); return; } struct dirent *entry; char filename[FILENAME_MAX + sizeof(entry->d_name)]; while ((entry = readdir(dir)) != NULL) { if ((entry->d_type == DT_REG || entry->d_type == DT_LNK) && strstartswith(entry->d_name, "unittest:") && strendswith(entry->d_name, ".dyncfg")) { snprintf(filename, sizeof(filename), "%s/%s", path, entry->d_name); nd_log(NDLS_DAEMON, NDLP_INFO, "DYNCFG UNITTEST: deleting file '%s'", filename); unlink(filename); } } closedir(dir); } static TEST *dyncfg_unittest_add(TEST t) { dyncfg_unittest_reset(); TEST *ret = dictionary_set(dyncfg_unittest_data.nodes, t.id, &t, sizeof(t)); if(!dyncfg_add_low_level(localhost, t.id, "/unittests", DYNCFG_STATUS_RUNNING, t.type, t.source_type, t.source, t.cmds, 0, 0, t.sync, HTTP_ACCESS_NONE, HTTP_ACCESS_NONE, dyncfg_unittest_execute_cb, ret)) { dyncfg_unittest_register_error(t.id, "addition of job failed"); } dyncfg_unittest_check(ret, DYNCFG_CMD_NONE, "plugin create", t.type != DYNCFG_TYPE_TEMPLATE); return ret; } void dyncfg_unittest_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { TEST *v = value; freez((void *)v->id); freez((void *)v->source); } int dyncfg_unittest(void) { dyncfg_unittest_data.nodes = dictionary_create(DICT_OPTION_NONE); dictionary_register_delete_callback(dyncfg_unittest_data.nodes, dyncfg_unittest_delete_cb, NULL); dyncfg_unittest_cleanup_files(); rrd_functions_inflight_init(); dyncfg_init(false); // ------------------------------------------------------------------------ // create the thread for testing async communication ND_THREAD *thread = nd_thread_create("unittest", NETDATA_THREAD_OPTION_JOINABLE, dyncfg_unittest_thread_action, NULL); // ------------------------------------------------------------------------ // single TEST *single1 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:sync:single1"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_SINGLE, .cmds = DYNCFG_CMD_GET | DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_INTERNAL, .sync = true, .current = { .enabled = true, }, .expected = { .enabled = true, } }); (void)single1; TEST *single2 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:async:single2"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_SINGLE, .cmds = DYNCFG_CMD_GET | DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_INTERNAL, .sync = false, .current = { .enabled = true, }, .expected = { .enabled = true, } }); (void)single2; // ------------------------------------------------------------------------ // template TEST *template1 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:sync:template1"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_TEMPLATE, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_ADD | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_INTERNAL, .sync = true, }); (void)template1; TEST *template2 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:async:template2"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_TEMPLATE, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_ADD | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_INTERNAL, .sync = false, }); (void)template2; // ------------------------------------------------------------------------ // job TEST *user1 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:sync:template1:user1"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = true, .current = { .enabled = true, }, .expected = { .enabled = true, } }); (void)user1; TEST *user2 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:async:template2:user2"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = false, .expected = { .enabled = true, } }); (void)user2; // ------------------------------------------------------------------------ int rc; (void)rc; BUFFER *wb = buffer_create(0, NULL); // ------------------------------------------------------------------------ // dynamic job dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 add dyn1", wb, "{\"double\":3.14,\"boolean\":true}", LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 add dyn2", wb, "{\"double\":3.14,\"boolean\":true}", LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 add dyn3", wb, "{\"double\":3.14,\"boolean\":true}", LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 add dyn4", wb, "{\"double\":3.14,\"boolean\":true}", LINE_FILE_STR); // ------------------------------------------------------------------------ // saving of user_disabled dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:single1 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:single2 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:user1 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:user2 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:dyn1 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:dyn2 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:dyn3 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:dyn4 disable", wb, NULL, LINE_FILE_STR); // ------------------------------------------------------------------------ // enabling dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:single1 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:single2 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:user1 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:user2 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:dyn1 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:dyn2 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:dyn3 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:dyn4 enable", wb, NULL, LINE_FILE_STR); // ------------------------------------------------------------------------ // disabling template dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 disable", wb, NULL, LINE_FILE_STR); // ------------------------------------------------------------------------ // enabling template dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 enable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 enable", wb, NULL, LINE_FILE_STR); // ------------------------------------------------------------------------ // adding job on disabled template dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 disable", wb, NULL, LINE_FILE_STR); dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 disable", wb, NULL, LINE_FILE_STR); TEST *user3 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:sync:template1:user3"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = true, .expected = { .enabled = false, } }); (void)user3; TEST *user4 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:async:template2:user4"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = false, .expected = { .enabled = false, } }); (void)user4; TEST *user5 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:sync:template1:user5"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = true, .expected = { .enabled = false, } }); (void)user5; TEST *user6 = dyncfg_unittest_add((TEST){ .id = strdupz("unittest:async:template2:user6"), .source = strdupz(LINE_FILE_STR), .type = DYNCFG_TYPE_JOB, .cmds = DYNCFG_CMD_SCHEMA | DYNCFG_CMD_UPDATE | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE, .source_type = DYNCFG_SOURCE_TYPE_USER, .sync = false, .expected = { .enabled = false, } }); (void)user6; // dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1:user5 disable", wb, NULL, LINE_FILE_STR); // dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2:user6 disable", wb, NULL, LINE_FILE_STR); // // ------------------------------------------------------------------------ // // enable template with disabled jobs // // user3->expected.enabled = true; // user5->expected.enabled = false; // dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:sync:template1 enable", wb, NULL, LINE_FILE_STR); // // user4->expected.enabled = true; // user6->expected.enabled = false; // dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " unittest:async:template2 enable", wb, NULL, LINE_FILE_STR); // // ------------------------------------------------------------------------ // // rc = dyncfg_unittest_run(PLUGINSD_FUNCTION_CONFIG " tree", wb, NULL); // if(rc == HTTP_RESP_OK) // fprintf(stderr, "%s\n", buffer_tostring(wb)); nd_thread_signal_cancel(thread); nd_thread_join(thread); dyncfg_unittest_cleanup_files(); dictionary_destroy(dyncfg_unittest_data.nodes); buffer_free(wb); return __atomic_load_n(&dyncfg_unittest_data.errors, __ATOMIC_RELAXED) > 0 ? 1 : 0; }