diff options
Diffstat (limited to '')
-rw-r--r-- | fluent-bit/tests/runtime/filter_modify.c | 1674 |
1 files changed, 1674 insertions, 0 deletions
diff --git a/fluent-bit/tests/runtime/filter_modify.c b/fluent-bit/tests/runtime/filter_modify.c new file mode 100644 index 00000000..ac5a166b --- /dev/null +++ b/fluent-bit/tests/runtime/filter_modify.c @@ -0,0 +1,1674 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <fluent-bit.h> +#include "flb_tests_runtime.h" + +struct filter_test { + flb_ctx_t *flb; /* Fluent Bit library context */ + int i_ffd; /* Input fd */ + int f_ffd; /* Filter fd */ +}; + +/* Callback to check expected results */ +static int cb_check_result(void *record, size_t size, void *data) +{ + char *p; + char *expected; + char *result; + + expected = (char *) data; + result = (char *) record; + + p = strstr(result, expected); + TEST_CHECK(p != NULL); + + if (!p) { + flb_error("Expected to find: '%s' in result '%s'", + expected, result); + } + /* + * If you want to debug your test + * + * printf("Expect: '%s' in result '%s'", expected, result); + */ + + flb_free(record); + return 0; +} + + + +static struct filter_test *filter_test_create(struct flb_lib_out_cb *data) +{ + int i_ffd; + int f_ffd; + int o_ffd; + struct filter_test *ctx; + + ctx = flb_malloc(sizeof(struct filter_test)); + if (!ctx) { + flb_errno(); + return NULL; + } + + /* Service config */ + ctx->flb = flb_create(); + flb_service_set(ctx->flb, + "Flush", "0.200000000", + "Grace", "1", + NULL); + + /* Input */ + i_ffd = flb_input(ctx->flb, (char *) "lib", NULL); + TEST_CHECK(i_ffd >= 0); + flb_input_set(ctx->flb, i_ffd, "tag", "test", NULL); + ctx->i_ffd = i_ffd; + + /* Filter configuration */ + f_ffd = flb_filter(ctx->flb, (char *) "modify", NULL); + TEST_CHECK(f_ffd >= 0); + flb_filter_set(ctx->flb, f_ffd, "match", "*", NULL); + ctx->f_ffd = f_ffd; + + /* Output */ + o_ffd = flb_output(ctx->flb, (char *) "lib", (void *) data); + TEST_CHECK(o_ffd >= 0); + flb_output_set(ctx->flb, o_ffd, + "match", "test", + "format", "json", + NULL); + + return ctx; +} + +static void filter_test_destroy(struct filter_test *ctx) +{ + sleep(1); + flb_stop(ctx->flb); + flb_destroy(ctx->flb); + flb_free(ctx); +} + +/* Operation: SET / append new record */ +static void flb_test_op_set_append() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "set", "test_key test_value", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"test_key\":\"test_value\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0, {\"k\":\"sample\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: SET / replace value of existing key */ +static void flb_test_op_set_replace() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "set", "k test_value", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k\":\"test_value\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0, {\"k\":\"sample\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: REMOVE */ +static void flb_test_op_remove() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "remove", "remove", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k\":\"sample\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0, {\"k\":\"sample\",\"remove\":\"sample to remove\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: REMOVE_WILDCARD */ +static void flb_test_op_remove_wildcard() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "remove_wildcard", "k", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"a3\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"a3\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: REMOVE_REGEX */ +static void flb_test_op_remove_regex() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "remove_regex", "^[a-z][0-9]$", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"A3\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* + * Operation: RENAME / Try to rename Key where 'renamed' key already + * exists: do nothing. + */ +static void flb_test_op_rename_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "rename", "A3 k2", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: RENAME / Rename when key DON'T exists */ +static void flb_test_op_rename_no_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "rename", "A3 a3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\",\"a3\":\"sample3\""; + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\",\"A3\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* + * Operation: HARD_RENAME / Try to rename Key where 'renamed' key already + * exists: do nothing. + */ +static void flb_test_op_hard_rename_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "hard_rename", "k2 k1", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k1\":\"sample2\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: HARD_RENAME / Rename when key DON'T exists */ +static void flb_test_op_hard_rename_no_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "hard_rename", "k2 k3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k3\":\"sample2\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: COPY / Target key already exists, do nothing */ +static void flb_test_op_copy_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "copy", "k1 k2", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample2\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: COPY / Target key no exists, make a copy */ +static void flb_test_op_copy_no_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "copy", "k1 k3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k3\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: HARD_COPY / if target key exists, replace value */ +static void flb_test_op_hard_copy_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "hard_copy", "k1 k2", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k1\":\"sample1\",\"k2\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0, {\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Operation: HARD_COPY / if key don't exists make a copy */ +static void flb_test_op_hard_copy_no_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "hard_copy", "k1 k3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k3\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + + +/* Condition: KEY_EXISTS / If key exists, make a copy */ +static void flb_test_cond_key_exists() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_exists k1", + "copy", "k1 k3_copy", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k3_copy\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_EXISTS / If nested key exists, make a copy */ +static void flb_test_cond_key_exists_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_exists $nest['k1']", + "add", "key found", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"key\":\"found\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\", \"nest\":{\"k1\":\"nest\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_DOES_NOT_EXISTS / If key does not exists, add a dummy key */ +static void flb_test_cond_key_does_not_exist() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_does_not_exist k3", + "copy", "k1 k3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k3\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_DOES_NOT_EXISTS / If key does not exists, add a dummy key */ +static void flb_test_cond_key_does_not_exist_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_does_not_exist $nest['k1']", + "add", "key not_found", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"key\":\"not_found\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"sample2\", \"nest\":{\"k\":\"sample\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: A_KEY_MATCHES / If key matches, add a dummy key */ +static void flb_test_cond_a_key_matches() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "a_key_matches ^[a-z][0-9]$", + "copy", "c1 c1_matched", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"c1_matched\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: NO_KEY_MATCHES / If no key matches, add a dummy key */ +static void flb_test_cond_no_key_matches() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "no_key_matches ^[0-9]$", + "copy", "c1 no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"no_matches\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_EQUALS / If key value matches, add a dummy key */ +static void flb_test_cond_key_value_equals() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_equals bb sample2", + "copy", "c1 no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"no_matches\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_EQUALS / If key value matches, add a dummy key */ +static void flb_test_cond_key_value_equals_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_equals $nest['k1'] sample2", + "add", "key found", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"key\":\"found\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\", \"nest\":{\"k1\":\"sample2\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_DOES_NOT_EQUAL / If key value mismatch, add a key */ +static void flb_test_cond_key_value_does_not_equal() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_does_not_equal bb sample3", + "copy", "c1 no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"no_matches\":\"sample3\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_DOES_NOT_EQUAL / If key value mismatch, add a key */ +static void flb_test_cond_key_value_does_not_equal_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_does_not_equal $nest['k1'] sample2", + "add", "key not_found", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"key\":\"not_found\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"aa\":\"sample1\",\"bb\":\"sample2\",\"c1\":\"sample3\", \"nest\":{\"k1\":\"sample3\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_MATCHES / If key match, add a key */ +static void flb_test_cond_key_value_matches() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_matches k2 ^[a-z][0-9]$", + "copy", "k1 matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"matches\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"z2\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_MATCHES / If key match, add a key */ +static void flb_test_cond_key_value_matches_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_matches $nest['k2'] ^[a-z][0-9]$", + "add", "kv matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"kv\":\"matches\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"z2\", \"nest\":{\"k2\":\"z2\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_DOES_NOT_MATCH / If key mismatch, add a key */ +static void flb_test_cond_key_value_does_not_match() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_does_not_match k2 ^[a-z][0-9]$", + "copy", "k1 no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"no_matches\":\"sample1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"22\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: KEY_VALUE_DOES_NOT_MATCH / If key mismatch, add a key */ +static void flb_test_cond_key_value_does_not_match_nest() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_does_not_match $nest['k2'] ^[a-z][0-9]$", + "add", "kv no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"kv\":\"no_matches\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample1\",\"k2\":\"22\",\"nest\":{\"k2\":\"22\"}}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: MATCHING_KEYS_HAVE_MATCHING_VALUES / If key match, add a key */ +static void flb_test_cond_matching_keys_have_matching_values() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", + "matching_keys_have_matching_values "\ + "^[a-z][0-9]$ ^[a-z][0-9]$", + "copy", "k1 matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"matches\":\"n1\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"n1\",\"k2\":\"n3\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Condition: MATCHING_KEYS_DOES_NOT_HAVE_MATCHING_VALUES */ +static void flb_test_cond_matching_keys_do_not_have_matching_values() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", + "matching_keys_do_not_have_matching_values "\ + "^[a-z][0-9]$ ^[a-z][0-9]$", + "copy", "k1 no_matches", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"no_matches\":\"aa\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"aa\",\"k2\":\"bb\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + +/* Test all operations */ +static void flb_test_cond_chain() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_exists k1", + "condition", "key_does_not_exist k2", + "add", "k2 sample_2", + "condition", "a_key_matches ^[a-z]1$", + "add", "k3 3", + "condition", "no_key_matches ^[0-9]$", + "condition", "key_value_equals k1 sample", + "add", "k4 4", + "condition", "key_value_does_not_equal k1 sampl", + "condition", "key_value_matches k1 ^[a-z]+$", + "condition", "key_value_does_not_match k1 aa", + + "condition", + "matching_keys_have_matching_values " \ + "^[a-z][0-9]$ ^[a-z]+$", + + "condition", + "matching_keys_do_not_have_matching_values " \ + "^[a-z][0-9]$ ^[a-z][0-9]$", + + "add", "k5 5", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"k5\":\"5\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"k1\":\"sample\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + + +pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER; +int num_output = 0; + + +static void add_output_num() +{ + pthread_mutex_lock(&result_mutex); + num_output++; + pthread_mutex_unlock(&result_mutex); +} + +static void clear_output_num() +{ + pthread_mutex_lock(&result_mutex); + num_output = 0; + pthread_mutex_unlock(&result_mutex); +} + +static int get_output_num() +{ + int ret; + pthread_mutex_lock(&result_mutex); + ret = num_output; + pthread_mutex_unlock(&result_mutex); + + return ret; +} + +static int callback_count(void* data, size_t size, void* cb_data) +{ + if (size > 0) { + flb_debug("[test_filter_modify] received message: %s", (char*)data); + add_output_num(); /* success */ + flb_free(data); + } + return 0; +} + + +// to check issue https://github.com/fluent/fluent-bit/issues/1077 +static void flb_test_not_drop_multi_event() +{ + int count = 0; + int expected = 3; + + char *p; + int len; + int ret; + int bytes; + + struct filter_test *ctx; + struct flb_lib_out_cb cb_data; + + + clear_output_num(); + cb_data.cb = callback_count; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_equals cond true", + "add", "data matched", + NULL); + TEST_CHECK(ret == 0); + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest multiple events */ + p = "[0, {\"cond\":\"false\", \"data\": \"something\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + p = "[0, {\"cond\":\"true\", \"data\": \"something\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + p = "[0, {\"data\": \"something\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + sleep(1); /* waiting flush */ + count = get_output_num(); + + TEST_CHECK_(count == expected, "Expected number of events %d, got %d", expected, count ); + + + filter_test_destroy(ctx); + +} + +/* to check issue https://github.com/fluent/fluent-bit/issues/4319 */ +static void flb_test_issue_4319() +{ + char *p; + int len; + int ret; + int bytes; + + struct filter_test *ctx; + struct flb_lib_out_cb cb_data; + + + clear_output_num(); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"ok\":\"sample\""; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + /* set key which doesn't exist */ + "condition", "key_value_does_not_equal aaa sample", + "rename", "ok error", + NULL); + TEST_CHECK(ret == 0); + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest event */ + p = "[0, {\"ok\":\"sample\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + sleep(1); /* waiting flush */ + + filter_test_destroy(ctx); +} + + +/* + * to check issue https://github.com/fluent/fluent-bit/issues/4319 + Key_value_does_not_match case +*/ +static void flb_test_issue_4319_2() +{ + char *p; + int len; + int ret; + int bytes; + + struct filter_test *ctx; + struct flb_lib_out_cb cb_data; + + + clear_output_num(); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"ok\":\"sample\""; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + /* set key which doesn't exist */ + "condition", "key_value_does_not_match aaa sample", + "rename", "ok error", + NULL); + TEST_CHECK(ret == 0); + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest event */ + p = "[0, {\"ok\":\"sample\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + sleep(1); /* waiting flush */ + + filter_test_destroy(ctx); +} + +/* https://github.com/fluent/fluent-bit/issues/1225 */ +static void flb_test_issue_1225() +{ + int len; + int ret; + int bytes; + char *p; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "condition", "key_value_matches \"key 1\" \".*with spaces.*\"", + "add", "\"key 2\" \"second value with spaces\"", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"key 1\":\"first value with spaces\","\ + "\"key 2\":\"second value with spaces\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest data samples */ + p = "[0,{\"key 1\":\"first value with spaces\"}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + filter_test_destroy(ctx); +} + + +/* + * to check issue https://github.com/fluent/fluent-bit/issues/7075 +*/ +static void flb_test_issue_7075() +{ + char *p; + int len; + int ret; + int bytes; + + struct filter_test *ctx; + struct flb_lib_out_cb cb_data; + + clear_output_num(); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"matched\":true"; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + /* set key which doesn't exist */ + "condition", "key_value_matches ok true", + "rename", "ok matched", + NULL); + TEST_CHECK(ret == 0); + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret == 0); + + /* Ingest event */ + p = "[0, {\"ok\":true}]"; + len = strlen(p); + bytes = flb_lib_push(ctx->flb, ctx->i_ffd, p, len); + TEST_CHECK(bytes == len); + + sleep(1); /* waiting flush */ + + filter_test_destroy(ctx); +} + +static void flb_test_issue_7368() +{ + int ret; + struct flb_lib_out_cb cb_data; + struct filter_test *ctx; + + /* Create test context */ + ctx = filter_test_create((void *) &cb_data); + if (!ctx) { + exit(EXIT_FAILURE); + } + + /* Configure filter */ + ret = flb_filter_set(ctx->flb, ctx->f_ffd, + "remove_wildcard", "*s3", + NULL); + TEST_CHECK(ret == 0); + + /* Prepare output callback with expected result */ + cb_data.cb = cb_check_result; + cb_data.data = "\"r1\":\"someval\""; + + /* Start the engine */ + ret = flb_start(ctx->flb); + TEST_CHECK(ret != 0); + + filter_test_destroy(ctx); +} + +TEST_LIST = { + /* Operations / Commands */ + {"op_set_append" , flb_test_op_set_append }, + {"op_set_replace" , flb_test_op_set_replace }, + {"op_remove" , flb_test_op_remove }, + {"op_remove_wildcard" , flb_test_op_remove_wildcard }, + {"op_remove_regex" , flb_test_op_remove_regex }, + {"op_rename_exists" , flb_test_op_rename_exists }, + {"op_rename_no_exists" , flb_test_op_rename_no_exists }, + {"op_hard_rename_exists" , flb_test_op_hard_rename_exists }, + {"op_hard_rename_no_exists" , flb_test_op_hard_rename_no_exists }, + {"op_copy_exists" , flb_test_op_copy_exists }, + {"op_copy_no_exists" , flb_test_op_copy_no_exists }, + {"op_hard_copy_exists" , flb_test_op_hard_copy_exists }, + {"op_hard_copy_no_exists" , flb_test_op_hard_copy_no_exists }, + + /* Conditions (nested) */ + {"cond_key_value_matches_nest", flb_test_cond_key_value_matches_nest }, + {"cond_key_value_does_not_match_nest", flb_test_cond_key_value_does_not_match_nest }, + {"cond_key_exists_nest", flb_test_cond_key_exists_nest }, + {"cond_key_does_not_exist_nest", flb_test_cond_key_does_not_exist_nest }, + {"cond_key_value_equals_nest", flb_test_cond_key_value_equals_nest }, + {"cond_key_value_does_not_equal_nest", flb_test_cond_key_value_does_not_equal_nest }, + + /* Conditions */ + {"cond_key_exists", flb_test_cond_key_exists }, + {"cond_key_does_not_exist", flb_test_cond_key_does_not_exist }, + {"cond_a_key_matches", flb_test_cond_a_key_matches }, + {"cond_no_key_matches", flb_test_cond_no_key_matches }, + {"cond_key_value_equals", flb_test_cond_key_value_equals }, + {"cond_key_value_does_not_equal", flb_test_cond_key_value_does_not_equal }, + {"cond_key_value_matches", flb_test_cond_key_value_matches }, + {"cond_key_value_does_not_match", flb_test_cond_key_value_does_not_match }, + {"cond_matching_keys_have_matching_values", + flb_test_cond_matching_keys_have_matching_values }, + {"cond_matching_keys_do_not_have_matching_values", + flb_test_cond_matching_keys_do_not_have_matching_values }, + {"cond_chain", flb_test_cond_chain }, + + /* Bug fixes */ + {"multiple events are not dropped", flb_test_not_drop_multi_event }, + {"cond_key_value_does_not_equal and key does not exist", flb_test_issue_4319 }, + {"cond_key_value_does_not_matches and key does not exist", flb_test_issue_4319_2 }, + {"Key_value_matches and value is bool type", flb_test_issue_7075}, + {"operation_with_whitespace", flb_test_issue_1225 }, + {"invalid_wildcard", flb_test_issue_7368 }, + + {NULL, NULL} +}; |