-- tests/backup_test.lua -- Тестирование бэкапов и восстановления local function setup_backup_test() print("=== Setup Backup Test ===") db.execute("CREATE DATABASE IF NOT EXISTS backup_test") db.execute("USE backup_test") db.execute([[ CREATE COLLECTION IF NOT EXISTS users ]]) -- Вставляем тестовые данные for i = 1, 100 do db.execute(string.format([[ INSERT INTO users VALUES { _id = "user_%d", name = "User %d", email = "user%d@test.com", score = %d, created_at = %d } ]], i, i, i, math.random(1, 1000), os.time() * 1000)) end print("Inserted 100 test documents") -- Включаем планировщик бэкапов db.execute([[ SET BACKUP SCHEDULE daily_backup WITH CRON "0 0 * * *" TYPE full RETENTION 7 ]]) return true end local function test_full_backup() print("\n=== Test 1: Full Backup ===") -- Запускаем полный бэкап local result = db.execute("BACKUP DATABASE backup_test TO full_backup.msgpack") print("Backup result: " .. tostring(result)) -- Проверяем, что файл бэкапа создан local backup_info = db.execute("SHOW BACKUP INFO") local info = db.json_decode(backup_info) print(string.format("Backup ID: %s", info.id or "unknown")) print(string.format("Backup size: %d bytes", info.size_bytes or 0)) print(string.format("Backup time: %s", info.end_time or "unknown")) if info.id and info.size_bytes > 0 then print("✅ Full backup completed successfully") return info.id else print("❌ Full backup failed") return nil end end local function test_incremental_backup() print("\n=== Test 2: Incremental Backup ===") -- Добавляем новые документы после полного бэкапа for i = 101, 150 do db.execute(string.format([[ INSERT INTO users VALUES { _id = "user_%d", name = "New User %d", email = "newuser%d@test.com", score = %d, created_at = %d } ]], i, i, i, math.random(1, 1000), os.time() * 1000)) end print("Added 50 new documents") -- Запускаем инкрементальный бэкап local result = db.execute("BACKUP DATABASE backup_test INCREMENTAL TO inc_backup.msgpack") print("Incremental backup result: " .. tostring(result)) local backup_info = db.execute("SHOW BACKUP INFO inc_backup") local info = db.json_decode(backup_info) if info and info.type == "incremental" then print(string.format("Incremental backup: %d entries", info.entries_count or 0)) print("✅ Incremental backup completed successfully") return true else print("❌ Incremental backup failed") return false end end local function test_backup_restore() print("\n=== Test 3: Backup Restore ===") -- Создаём новую БД для восстановления db.execute("CREATE DATABASE IF NOT EXISTS restore_test") -- Восстанавливаем из полного бэкапа local result = db.execute("RESTORE DATABASE restore_test FROM full_backup.msgpack") print("Restore result: " .. tostring(result)) -- Проверяем количество документов db.execute("USE restore_test") local count = db.execute("SELECT COUNT(*) FROM users") local count_data = db.json_decode(count) print(string.format("Restored documents: %d", count_data[1].count or 0)) if count_data[1].count == 100 then print("✅ Full restore successful") return true else print("❌ Restore failed: expected 100 documents") return false end end local function test_incremental_restore() print("\n=== Test 4: Incremental Restore ===") -- Создаём другую БД для теста инкрементального восстановления db.execute("CREATE DATABASE IF NOT EXISTS inc_restore_test") -- Восстанавливаем сначала полный бэкап db.execute("USE inc_restore_test") db.execute("RESTORE DATABASE inc_restore_test FROM full_backup.msgpack") local before_count = db.execute("SELECT COUNT(*) FROM users") local before_data = db.json_decode(before_count) print(string.format("After full restore: %d documents", before_data[1].count or 0)) -- Затем применяем инкрементальный db.execute("RESTORE DATABASE inc_restore_test INCREMENTAL FROM inc_backup.msgpack") local after_count = db.execute("SELECT COUNT(*) FROM users") local after_data = db.json_decode(after_count) print(string.format("After incremental restore: %d documents", after_data[1].count or 0)) if after_data[1].count == 150 then print("✅ Incremental restore successful") return true else print(string.format("❌ Expected 150 documents, got %d", after_data[1].count or 0)) return false end end local function test_backup_schedule() print("\n=== Test 5: Backup Schedule ===") local schedules = db.execute("SHOW BACKUP SCHEDULES") local schedules_data = db.json_decode(schedules) print("Current backup schedules:") for _, schedule in ipairs(schedules_data) do print(string.format(" %s: cron='%s', type='%s', enabled=%s", schedule.name, schedule.cron_expr, schedule.type, tostring(schedule.enabled))) end if #schedules_data > 0 then print("✅ Backup schedules configured correctly") return true else print("⚠️ No backup schedules found") return false end end local function test_backup_compression() print("\n=== Test 6: Backup Compression ===") -- Включаем сжатие db.execute("SET BACKUP COMPRESSION true") -- Создаём много данных для бэкапа for i = 1, 1000 do db.execute(string.format([[ INSERT INTO users VALUES { _id = "compress_%d", data = "%s" } ]], i, string.rep("x", 1000))) end local start_time = os.clock() db.execute("BACKUP DATABASE backup_test TO compressed_backup.msgpack WITH COMPRESSION") local duration = os.clock() - start_time local backup_info = db.execute("SHOW BACKUP INFO compressed_backup") local info = db.json_decode(backup_info) print(string.format("Compressed backup size: %d bytes", info.size_bytes or 0)) print(string.format("Compression time: %.2fs", duration)) if info.size_bytes and info.size_bytes > 0 then print("✅ Backup compression working") return true else print("❌ Backup compression failed") return false end end local function test_backup_retention() print("\n=== Test 7: Backup Retention ===") -- Создаём несколько старых бэкапов (симуляция) for i = 1, 10 do db.execute(string.format("BACKUP DATABASE backup_test TO old_backup_%d.msgpack", i)) end -- Устанавливаем retention на 5 дней db.execute("SET BACKUP RETENTION 5") -- Запускаем очистку db.execute("CLEANUP BACKUPS") local backups = db.execute("SHOW BACKUPS") local backups_data = db.json_decode(backups) print(string.format("Remaining backups after cleanup: %d", #backups_data)) -- Проверяем, что не все бэкапы удалены if #backups_data > 0 then print("✅ Backup retention working") return true else print("⚠️ All backups cleaned up, retention may be too aggressive") return false end end local function test_backup_verify() print("\n=== Test 8: Backup Verification ===") -- Проверяем целостность бэкапа local result = db.execute("VERIFY BACKUP full_backup.msgpack") local verify_result = db.json_decode(result) print(string.format("Verification result: %s", verify_result.valid and "VALID" or "INVALID")) print(string.format("Checksum match: %s", tostring(verify_result.checksum_match))) print(string.format("Corruption detected: %s", tostring(verify_result.corrupted))) if verify_result.valid then print("✅ Backup verification passed") return true else print("❌ Backup verification failed") return false end end local function test_backup_parallel() print("\n=== Test 9: Parallel Backups ===") -- Проверяем, что параллельные бэкапы блокируются local results = {} local function run_backup(name) local success, err = pcall(function() return db.execute(string.format("BACKUP DATABASE backup_test TO parallel_%s.msgpack", name)) end) return success end -- Запускаем 3 параллельных бэкапа local threads = {} for i = 1, 3 do table.insert(threads, coroutine.create(function() local result = run_backup(tostring(i)) coroutine.yield(result) end)) end for _, thread in ipairs(threads) do coroutine.resume(thread) end local success_count = 0 for _, thread in ipairs(threads) do local success = coroutine.resume(thread) if success then success_count = success_count + 1 end end print(string.format("Parallel backups completed: %d/3 succeeded", success_count)) if success_count == 1 then print("✅ Parallel backups correctly limited to 1") return true else print("⚠️ Parallel backup behavior may need tuning") return false end end local function cleanup_backup() print("\n=== Cleanup ===") db.execute("DROP DATABASE IF EXISTS backup_test") db.execute("DROP DATABASE IF EXISTS restore_test") db.execute("DROP DATABASE IF EXISTS inc_restore_test") print("Cleanup completed") end local function run_all_backup_tests() print("╔══════════════════════════════════════════════════════════════╗") print("║ BACKUP TESTS SUITE ║") print("╚══════════════════════════════════════════════════════════════╝") setup_backup_test() local full_backup_id = test_full_backup() local results = { full_backup_id ~= nil, test_incremental_backup(), test_backup_restore(), test_incremental_restore(), test_backup_schedule(), test_backup_compression(), test_backup_retention(), test_backup_verify(), test_backup_parallel() } print("\n╔══════════════════════════════════════════════════════════════╗") print("║ TEST RESULTS ║") print("╚══════════════════════════════════════════════════════════════╝") local passed = 0 for i, result in ipairs(results) do local status = result and "✅ PASSED" or "⚠️ SKIPPED/FAILED" print(string.format("Test %d: %s", i, status)) if result then passed = passed + 1 end end print(string.format("\nTotal: %d/%d tests passed", passed, #results)) cleanup_backup() return passed == #results end return run_all_backup_tests()