#include "test_framework.h" #include "../src/types.h" #include "../src/patch.h" #include 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(); }