238 lines
8.3 KiB
C
238 lines
8.3 KiB
C
|
|
#include "test_framework.h"
|
||
|
|
#include "../src/types.h"
|
||
|
|
#include "../src/patch.h"
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
void test_patch_has_rules(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Has Rules");
|
||
|
|
|
||
|
|
patch_config_t empty_config;
|
||
|
|
memset(&empty_config, 0, sizeof(empty_config));
|
||
|
|
empty_config.rule_count = 0;
|
||
|
|
TEST_ASSERT_EQ(0, patch_has_rules(&empty_config), "Empty config has no rules");
|
||
|
|
|
||
|
|
patch_config_t config_with_rules;
|
||
|
|
memset(&config_with_rules, 0, sizeof(config_with_rules));
|
||
|
|
config_with_rules.rule_count = 1;
|
||
|
|
strcpy(config_with_rules.rules[0].key, "test");
|
||
|
|
config_with_rules.rules[0].key_len = 4;
|
||
|
|
TEST_ASSERT_EQ(1, patch_has_rules(&config_with_rules), "Config with rules returns true");
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, patch_has_rules(NULL), "NULL config has no rules");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_check_for_block(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Check For Block");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "blocked-word");
|
||
|
|
config.rules[0].key_len = strlen("blocked-word");
|
||
|
|
config.rules[0].is_null = 1;
|
||
|
|
|
||
|
|
const char *data_with_block = "This content contains blocked-word in it";
|
||
|
|
TEST_ASSERT_EQ(1, patch_check_for_block(&config, data_with_block, strlen(data_with_block)),
|
||
|
|
"Content with blocked word is blocked");
|
||
|
|
|
||
|
|
const char *data_without_block = "This content is clean and allowed";
|
||
|
|
TEST_ASSERT_EQ(0, patch_check_for_block(&config, data_without_block, strlen(data_without_block)),
|
||
|
|
"Clean content is not blocked");
|
||
|
|
|
||
|
|
patch_config_t empty_config;
|
||
|
|
memset(&empty_config, 0, sizeof(empty_config));
|
||
|
|
TEST_ASSERT_EQ(0, patch_check_for_block(&empty_config, data_with_block, strlen(data_with_block)),
|
||
|
|
"Empty config blocks nothing");
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, patch_check_for_block(NULL, data_with_block, strlen(data_with_block)),
|
||
|
|
"NULL config blocks nothing");
|
||
|
|
TEST_ASSERT_EQ(0, patch_check_for_block(&config, NULL, 0),
|
||
|
|
"NULL data is not blocked");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_simple_replace(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Simple Replacement");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "old");
|
||
|
|
config.rules[0].key_len = 3;
|
||
|
|
strcpy(config.rules[0].value, "new");
|
||
|
|
config.rules[0].value_len = 3;
|
||
|
|
config.rules[0].is_null = 0;
|
||
|
|
|
||
|
|
const char *input = "This is old text with old words";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Simple replace does not block");
|
||
|
|
TEST_ASSERT(result.output_len > 0, "Output has content");
|
||
|
|
TEST_ASSERT(strstr(output, "new") != NULL, "Replacement word present");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_size_change(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Size Change");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "short");
|
||
|
|
config.rules[0].key_len = 5;
|
||
|
|
strcpy(config.rules[0].value, "much-longer-replacement");
|
||
|
|
config.rules[0].value_len = strlen("much-longer-replacement");
|
||
|
|
config.rules[0].is_null = 0;
|
||
|
|
|
||
|
|
const char *input = "This is short text";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Size change replace does not block");
|
||
|
|
TEST_ASSERT(result.output_len > strlen(input), "Output is longer than input");
|
||
|
|
TEST_ASSERT(result.size_delta > 0, "Size delta is positive");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_shrink(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Shrink");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "very-long-word");
|
||
|
|
config.rules[0].key_len = strlen("very-long-word");
|
||
|
|
strcpy(config.rules[0].value, "tiny");
|
||
|
|
config.rules[0].value_len = 4;
|
||
|
|
config.rules[0].is_null = 0;
|
||
|
|
|
||
|
|
const char *input = "This has very-long-word in it";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Shrink replace does not block");
|
||
|
|
TEST_ASSERT(result.output_len < strlen(input), "Output is shorter than input");
|
||
|
|
TEST_ASSERT(result.size_delta < 0, "Size delta is negative");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_multiple_rules(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Multiple Rules");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 3;
|
||
|
|
|
||
|
|
strcpy(config.rules[0].key, "foo");
|
||
|
|
config.rules[0].key_len = 3;
|
||
|
|
strcpy(config.rules[0].value, "bar");
|
||
|
|
config.rules[0].value_len = 3;
|
||
|
|
config.rules[0].is_null = 0;
|
||
|
|
|
||
|
|
strcpy(config.rules[1].key, "hello");
|
||
|
|
config.rules[1].key_len = 5;
|
||
|
|
strcpy(config.rules[1].value, "world");
|
||
|
|
config.rules[1].value_len = 5;
|
||
|
|
config.rules[1].is_null = 0;
|
||
|
|
|
||
|
|
strcpy(config.rules[2].key, "test");
|
||
|
|
config.rules[2].key_len = 4;
|
||
|
|
strcpy(config.rules[2].value, "demo");
|
||
|
|
config.rules[2].value_len = 4;
|
||
|
|
config.rules[2].is_null = 0;
|
||
|
|
|
||
|
|
const char *input = "foo says hello during test";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Multiple rule replace does not block");
|
||
|
|
TEST_ASSERT(result.output_len > 0, "Output has content");
|
||
|
|
output[result.output_len] = '\0';
|
||
|
|
TEST_ASSERT(strstr(output, "bar") != NULL, "First replacement applied");
|
||
|
|
TEST_ASSERT(strstr(output, "world") != NULL, "Second replacement applied");
|
||
|
|
TEST_ASSERT(strstr(output, "demo") != NULL, "Third replacement applied");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_no_match(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply No Match");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "nonexistent");
|
||
|
|
config.rules[0].key_len = strlen("nonexistent");
|
||
|
|
strcpy(config.rules[0].value, "replacement");
|
||
|
|
config.rules[0].value_len = strlen("replacement");
|
||
|
|
config.rules[0].is_null = 0;
|
||
|
|
|
||
|
|
const char *input = "This text has no matching patterns";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "No match does not block");
|
||
|
|
TEST_ASSERT_EQ(strlen(input), result.output_len, "Output length equals input length");
|
||
|
|
TEST_ASSERT_EQ(0, result.size_delta, "Size delta is zero for no changes");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_empty_config(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Empty Config");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
|
||
|
|
const char *input = "This is some input data";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, input, strlen(input), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Empty config does not block");
|
||
|
|
TEST_ASSERT_EQ(strlen(input), result.output_len, "Output equals input for empty config");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void test_patch_apply_block_rule(void) {
|
||
|
|
TEST_SUITE_BEGIN("Patch Apply Block Rule");
|
||
|
|
|
||
|
|
patch_config_t config;
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.rule_count = 1;
|
||
|
|
strcpy(config.rules[0].key, "malware");
|
||
|
|
config.rules[0].key_len = strlen("malware");
|
||
|
|
config.rules[0].is_null = 1;
|
||
|
|
|
||
|
|
const char *malicious = "This contains malware content";
|
||
|
|
char output[256];
|
||
|
|
patch_result_t result = patch_apply(&config, malicious, strlen(malicious), output, sizeof(output));
|
||
|
|
|
||
|
|
TEST_ASSERT_EQ(1, result.should_block, "Block rule triggers block");
|
||
|
|
|
||
|
|
const char *clean = "This is clean content";
|
||
|
|
result = patch_apply(&config, clean, strlen(clean), output, sizeof(output));
|
||
|
|
TEST_ASSERT_EQ(0, result.should_block, "Clean content not blocked");
|
||
|
|
|
||
|
|
TEST_SUITE_END();
|
||
|
|
}
|
||
|
|
|
||
|
|
void run_patch_tests(void) {
|
||
|
|
test_patch_has_rules();
|
||
|
|
test_patch_check_for_block();
|
||
|
|
test_patch_apply_simple_replace();
|
||
|
|
test_patch_apply_size_change();
|
||
|
|
test_patch_apply_shrink();
|
||
|
|
test_patch_apply_multiple_rules();
|
||
|
|
test_patch_apply_no_match();
|
||
|
|
test_patch_apply_empty_config();
|
||
|
|
test_patch_apply_block_rule();
|
||
|
|
}
|