diff --git a/tests/tests.lua b/tests/tests.lua new file mode 100644 index 0000000..af141e0 --- /dev/null +++ b/tests/tests.lua @@ -0,0 +1,281 @@ +-- tests.lua - Комплексные тесты для СУБД futriix + +local test_utils = {} + +function test_utils.assert(condition, message) + if not condition then + error("TEST FAILED: " .. (message or "Assertion failed")) + end +end + +function test_utils.run_test(test_name, test_func) + print("Running test: " .. test_name) + local success, result = pcall(test_func) + if success then + print("✓ " .. test_name .. " - PASSED") + return true + else + print("✗ " .. test_name .. " - FAILED: " .. result) + return false + end +end + +-- 1. Регрессионный тест +function regression_test() + -- Проверяем, что базовые операции работают корректно + create_space("regression_test_space") + + -- Тест создания документа + local test_data = {name = "test", value = 42, tags = {"a", "b", "c"}} + insert("regression_test_space", "doc1", json_encode(test_data)) + + -- Тест чтения + local result = get("regression_test_space", "doc1") + test_utils.assert(result ~= "null", "Document should exist") + + local parsed = json_decode(result) + test_utils.assert(parsed.name == "test", "Name should match") + test_utils.assert(parsed.value == 42, "Value should match") + + -- Тест обновления + local update_data = {name = "updated", value = 100, new_field = "added"} + update("regression_test_space", "doc1", json_encode(update_data)) + + local updated = json_decode(get("regression_test_space", "doc1")) + test_utils.assert(updated.name == "updated", "Name should be updated") + test_utils.assert(updated.new_field == "added", "New field should exist") + + -- Тест удаления + delete("regression_test_space", "doc1") + local deleted = get("regression_test_space", "doc1") + test_utils.assert(deleted == "null", "Document should be deleted") + + -- Очистка + delete_space("regression_test_space") + + return "Regression test completed successfully" +end + +-- 2. Smoke-тест (проверка работоспособности) +function smoke_test() + -- Проверяем базовую функциональность системы + test_utils.assert(pcall(create_space, "smoke_test_space"), "Should create space") + test_utils.assert(pcall(delete_space, "smoke_test_space"), "Should delete space") + + -- Проверяем функции JSON + local test_obj = {test = true, number = 123} + local encoded = json_encode(test_obj) + local decoded = json_decode(encoded) + test_utils.assert(decoded.test == true, "JSON encode/decode should work") + test_utils.assert(decoded.number == 123, "JSON numbers should work") + + -- Проверяем MD5 + local hash = md5("test") + test_utils.assert(type(hash) == "string", "MD5 should return string") + test_utils.assert(#hash == 32, "MD5 hash should be 32 characters") + + return "Smoke test passed - system is operational" +end + +-- 3. Unit-тест (тестирование отдельных функций) +function unit_test_md5() + -- Тестируем функцию MD5 хеширования + local test_cases = { + {"", "d41d8cd98f00b204e9800998ecf8427e"}, + {"hello", "5d41402abc4b2a76b9719d911017c592"}, + {"futriix", "3e8c8d2e7e7a6e8f7b6c8a9d0e1f2a3b"} -- пример хеша + } + + for i, case in ipairs(test_cases) do + local input, expected = case[1], case[2] + local result = md5(input) + test_utils.assert(result == expected, + string.format("MD5('%s') should be '%s', got '%s'", input, expected, result)) + end + + return "MD5 unit test passed" +end + +function unit_test_json() + -- Тестируем JSON функции + local complex_obj = { + string = "test", + number = 42.5, + boolean = true, + array = {1, 2, 3}, + nested = {key = "value"} + } + + local encoded = json_encode(complex_obj) + local decoded = json_decode(encoded) + + test_utils.assert(decoded.string == "test", "String should match") + test_utils.assert(decoded.number == 42.5, "Number should match") + test_utils.assert(decoded.boolean == true, "Boolean should match") + test_utils.assert(#decoded.array == 3, "Array length should match") + test_utils.assert(decoded.nested.key == "value", "Nested object should match") + + return "JSON unit test passed" +end + +-- 4. Нагрузочный тест +function load_test() + local space_name = "load_test_space" + create_space(space_name) + + local start_time = os.time() + local operations_count = 100 -- Уменьшено для демонстрации + + -- Тест записи + for i = 1, operations_count do + local data = { + id = i, + timestamp = os.time(), + data = string.rep("X", 100) -- 100 байт данных + } + insert(space_name, "key_" .. i, json_encode(data)) + end + + -- Тест чтения + for i = 1, operations_count do + local result = get(space_name, "key_" .. i) + test_utils.assert(result ~= "null", "Should read written data") + end + + local end_time = os.time() + local duration = end_time - start_time + + -- Очистка + for i = 1, operations_count do + delete(space_name, "key_" .. i) + end + delete_space(space_name) + + local ops_per_second = operations_count * 2 / duration -- запись + чтение + + return string.format("Load test completed: %d operations in %d seconds (%.2f ops/sec)", + operations_count * 2, duration, ops_per_second) +end + +-- 5. Интеграционный тест +function integration_test() + -- Тестируем взаимодействие различных компонентов + create_space("users") + create_space("profiles") + + -- Создаем пользователя с профилем + local user_data = { + username = "testuser", + email = "test@example.com", + password = md5("password123"), + created_at = os.date() + } + + local profile_data = { + user_id = "testuser", + full_name = "Test User", + preferences = {theme = "dark", notifications = true} + } + + insert("users", "testuser", json_encode(user_data)) + insert("profiles", "testuser_profile", json_encode(profile_data)) + + -- Проверяем связность данных + local user = json_decode(get("users", "testuser")) + local profile = json_decode(get("profiles", "testuser_profile")) + + test_utils.assert(user.username == profile.user_id, "User ID should match") + test_utils.assert(user.password == md5("password123"), "Password should be hashed") + + -- Тестируем транзакционность (имитация) + local tx_id = "test_tx_" .. os.time() + begin_transaction(tx_id) + + -- В реальной системе здесь были бы операции в транзакции + print("Transaction started: " .. tx_id) + + -- Очистка + delete("users", "testuser") + delete("profiles", "testuser_profile") + delete_space("users") + delete_space("profiles") + + return "Integration test passed - components work together correctly" +end + +-- 6. API тест +function api_test() + -- Тестируем API-подобные операции через быстрые команды + -- (это имитация HTTP API вызовов) + + create_space("api_test_space") + + -- Имитация POST /document/space/key + local post_data = {action = "create", data = {test = "value"}} + insert("api_test_space", "api_doc", json_encode(post_data)) + + -- Имитация GET /document/space/key + local get_result = get("api_test_space", "api_doc") + test_utils.assert(get_result ~= "null", "API should retrieve document") + + local parsed_get = json_decode(get_result) + test_utils.assert(parsed_get.action == "create", "API data should match") + + -- Имитация PUT /document/space/key + local put_data = {action = "update", data = {test = "new_value"}} + update("api_test_space", "api_doc", json_encode(put_data)) + + local updated = json_decode(get("api_test_space", "api_doc")) + test_utils.assert(updated.action == "update", "API update should work") + + -- Имитация DELETE /document/space/key + delete("api_test_space", "api_doc") + test_utils.assert(get("api_test_space", "api_doc") == "null", "API delete should work") + + delete_space("api_test_space") + + return "API test passed - all HTTP verb simulations work correctly" +end + +-- Главная функция запуска всех тестов +function run_all_tests() + print("=== Running futriix Database Tests ===\n") + + local tests = { + {"Smoke Test", smoke_test}, + {"Unit Test - MD5", unit_test_md5}, + {"Unit Test - JSON", unit_test_json}, + {"Regression Test", regression_test}, + {"Integration Test", integration_test}, + {"API Test", api_test}, + {"Load Test", load_test} + } + + local passed = 0 + local total = #tests + + for _, test_info in ipairs(tests) do + local test_name, test_func = test_info[1], test_info[2] + if test_utils.run_test(test_name, test_func) then + passed = passed + 1 + end + print() + end + + print(string.format("=== Test Results: %d/%d passed ===", passed, total)) + + if passed == total then + print(" ALL TESTS PASSED! ") + return true + else + print("❌ SOME TESTS FAILED") + return false + end +end + +-- Запуск тестов при прямом выполнении файла +if arg and arg[0] and string.match(arg[0], "tests.lua") then + run_all_tests() +else + print("futriix test library loaded. Call 'run_all_tests()' to execute tests.") +end