new version with saga
This commit is contained in:
149
tests/integration.lua
Normal file
149
tests/integration.lua
Normal file
@@ -0,0 +1,149 @@
|
||||
-- ============================================================================
|
||||
-- test_integration.lua - Интеграционный тест
|
||||
-- ============================================================================
|
||||
-- Назначение: Проверка взаимодействия между компонентами системы
|
||||
-- Покрывает: API + Storage + Cluster + Transactions + Triggers + Indexes
|
||||
|
||||
local function test_api_storage_integration()
|
||||
print("[Integration] API + Storage integration...")
|
||||
|
||||
-- Create document via API
|
||||
local api_insert = http.request("POST", "http://localhost:8080/api/db/integration_test/orders",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{order_id = "ORD-001", amount = 1500, status = "pending"}
|
||||
)
|
||||
assert(api_insert.success, "Integration: API insert failed")
|
||||
|
||||
-- Verify direct storage access (simulated)
|
||||
local storage_verify = http.request("GET", "http://localhost:8080/api/db/integration_test/orders/ORD-001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(storage_verify.success and storage_verify.data.fields.amount == 1500,
|
||||
"Integration: API-Storage consistency failed")
|
||||
|
||||
print(" ✓ API -> Storage data flow")
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_transaction_index_integration()
|
||||
print("[Integration] Transactions + Indexes integration...")
|
||||
|
||||
-- Start transaction
|
||||
local session_resp = http.request("POST", "http://localhost:8080/api/webui/transactions",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{action = "start_transaction"}
|
||||
)
|
||||
|
||||
-- Create index within transaction
|
||||
local idx_resp = http.request("POST", "http://localhost:8080/api/index/integration_test/orders/create",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{name = "order_amount_idx", fields = {"amount"}, unique = false}
|
||||
)
|
||||
|
||||
-- Insert data
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/integration_test/orders",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{order_id = "ORD-002", amount = 2500, status = "processing"}
|
||||
)
|
||||
|
||||
-- Commit
|
||||
local commit_resp = http.request("POST", "http://localhost:8080/api/webui/transaction/commit",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
-- Query by index after commit
|
||||
local query_resp = http.request("GET", "http://localhost:8080/api/db/integration_test/orders?index=order_amount_idx&value=2500",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
print(" ✓ Transaction + Indexes integration")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_acl_trigger_integration()
|
||||
print("[Integration] ACL + Triggers integration...")
|
||||
|
||||
-- Create role with specific permissions
|
||||
local role_resp = http.request("POST", "http://localhost:8080/api/webui/acl/role/analyst",
|
||||
{["X-Session-ID"] = "admin_session"}
|
||||
)
|
||||
|
||||
-- Grant read permission on integration_test
|
||||
local grant_resp = http.request("POST", "http://localhost:8080/api/webui/acl/role/analyst/grant/integration_test.*:read",
|
||||
{["X-Session-ID"] = "admin_session"}
|
||||
)
|
||||
|
||||
-- Create trigger that logs read operations
|
||||
local trigger_config = {
|
||||
name = "read_logger",
|
||||
event = "AFTER_UPDATE",
|
||||
action = "log",
|
||||
description = "Log all read operations"
|
||||
}
|
||||
|
||||
local trigger_resp = http.request("POST", "http://localhost:8080/api/webui/trigger/integration_test/orders/create",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
trigger_config
|
||||
)
|
||||
print(" ✓ ACL + Triggers integration")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_cluster_persistence_integration()
|
||||
print("[Integration] Cluster + Persistence integration...")
|
||||
|
||||
-- Get cluster status
|
||||
local cluster_resp = http.request("GET", "http://localhost:8080/api/cluster/status",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
-- Create data that should be persisted across restarts
|
||||
local persist_resp = http.request("POST", "http://localhost:8080/api/db/persistence_test/data",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "persist_001", data = "This should survive restart", created = os.time()}
|
||||
)
|
||||
assert(persist_resp.success, "Integration: Persistence test insert failed")
|
||||
|
||||
-- Force checkpoint creation
|
||||
-- (In real scenario, we'd wait for checkpoint or trigger it manually)
|
||||
print(" ✓ Cluster + Persistence ready")
|
||||
print(" ⚠ Manual restart required to verify persistence")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_wal_recovery_integration()
|
||||
print("[Integration] WAL + Recovery integration...")
|
||||
|
||||
-- Create multiple updates to generate WAL entries
|
||||
for i = 1, 10 do
|
||||
local resp = http.request("PUT", "http://localhost:8080/api/db/wal_test/counter/doc_001",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{counter = i, update_time = os.time()}
|
||||
)
|
||||
end
|
||||
|
||||
-- Get transaction list to verify WAL
|
||||
local tx_list = http.request("GET", "http://localhost:8080/api/webui/transactions",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
print(" ✓ WAL entries generated")
|
||||
print(" ✓ WAL + Recovery integrated")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run integration tests
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("INTEGRATION TEST SUITE")
|
||||
print(string.rep("=", 50))
|
||||
|
||||
test_api_storage_integration()
|
||||
test_transaction_index_integration()
|
||||
test_acl_trigger_integration()
|
||||
test_cluster_persistence_integration()
|
||||
test_wal_recovery_integration()
|
||||
|
||||
print("\n✓ ALL INTEGRATION TESTS PASSED ✓\n")
|
||||
199
tests/regression.lua
Normal file
199
tests/regression.lua
Normal file
@@ -0,0 +1,199 @@
|
||||
-- ============================================================================
|
||||
-- test_regression.lua - Регрессионный тест
|
||||
-- ============================================================================
|
||||
-- Назначение: Проверка корректности базовых операций после изменений
|
||||
-- Покрывает: CRUD операции, индексы, транзакции, ограничения
|
||||
|
||||
local function setup_test_data()
|
||||
-- Создаём тестовую базу и коллекцию
|
||||
local response = http.request("POST", "http://localhost:8080/api/db/test_db/test_collection",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{name = "test_doc_1", value = 100}
|
||||
)
|
||||
assert(response.success, "Regression: Failed to insert test document")
|
||||
|
||||
response = http.request("POST", "http://localhost:8080/api/db/test_db/test_collection",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{name = "test_doc_2", value = 200}
|
||||
)
|
||||
assert(response.success, "Regression: Failed to insert second document")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_crud_operations()
|
||||
print("[Regression] Testing CRUD operations...")
|
||||
|
||||
-- CREATE - Insert
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/regress_db/users",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "user_001", name = "John Doe", age = 30, email = "john@example.com"}
|
||||
)
|
||||
assert(insert_resp.success, "CRUD: Insert operation failed")
|
||||
print(" ✓ INSERT operation")
|
||||
|
||||
-- READ - Find by ID
|
||||
local read_resp = http.request("GET", "http://localhost:8080/api/db/regress_db/users/user_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(read_resp.success and read_resp.data._id == "user_001", "CRUD: Read operation failed")
|
||||
assert(read_resp.data.fields.name == "John Doe", "CRUD: Read data mismatch")
|
||||
print(" ✓ READ operation")
|
||||
|
||||
-- UPDATE
|
||||
local update_resp = http.request("PUT", "http://localhost:8080/api/db/regress_db/users/user_001",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{age = 31, city = "New York"}
|
||||
)
|
||||
assert(update_resp.success, "CRUD: Update operation failed")
|
||||
|
||||
-- Verify update
|
||||
local verify_resp = http.request("GET", "http://localhost:8080/api/db/regress_db/users/user_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(verify_resp.data.fields.age == 31, "CRUD: Update data not persisted")
|
||||
print(" ✓ UPDATE operation")
|
||||
|
||||
-- DELETE
|
||||
local delete_resp = http.request("DELETE", "http://localhost:8080/api/db/regress_db/users/user_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(delete_resp.success, "CRUD: Delete operation failed")
|
||||
|
||||
-- Verify deletion
|
||||
local deleted_resp = http.request("GET", "http://localhost:8080/api/db/regress_db/users/user_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(not deleted_resp.success, "CRUD: Document still exists after delete")
|
||||
print(" ✓ DELETE operation")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_index_operations()
|
||||
print("[Regression] Testing index operations...")
|
||||
|
||||
-- Create index
|
||||
local idx_resp = http.request("POST", "http://localhost:8080/api/index/regress_db/users/create",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{name = "idx_name", fields = {"name"}, unique = false}
|
||||
)
|
||||
assert(idx_resp.success, "Index: Create index failed")
|
||||
print(" ✓ Index creation")
|
||||
|
||||
-- List indexes
|
||||
local list_resp = http.request("GET", "http://localhost:8080/api/index/regress_db/users/list",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(list_resp.success and #list_resp.data >= 1, "Index: List indexes failed")
|
||||
print(" ✓ Index listing")
|
||||
|
||||
-- Query by index
|
||||
local query_resp = http.request("GET", "http://localhost:8080/api/db/regress_db/users?index=idx_name&value=John%20Doe",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
print(" ✓ Index query")
|
||||
|
||||
-- Drop index
|
||||
local drop_resp = http.request("DELETE", "http://localhost:8080/api/index/regress_db/users/drop/idx_name",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
print(" ✓ Index drop")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_transaction_operations()
|
||||
print("[Regression] Testing transaction operations...")
|
||||
|
||||
-- Start transaction session
|
||||
local session_resp = http.request("POST", "http://localhost:8080/api/webui/transactions",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{action = "start_session"}
|
||||
)
|
||||
assert(session_resp.success, "Transaction: Session start failed")
|
||||
print(" ✓ Transaction session")
|
||||
|
||||
-- Start transaction
|
||||
local tx_resp = http.request("POST", "http://localhost:8080/api/webui/transactions",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{action = "start_transaction"}
|
||||
)
|
||||
assert(tx_resp.success, "Transaction: Start failed")
|
||||
print(" ✓ Transaction start")
|
||||
|
||||
-- Insert within transaction
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/regress_db/tx_test",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "tx_doc", value = "transactional"}
|
||||
)
|
||||
assert(insert_resp.success, "Transaction: Insert in transaction failed")
|
||||
|
||||
-- Commit transaction
|
||||
local commit_resp = http.request("POST", "http://localhost:8080/api/webui/transaction/commit",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(commit_resp.success, "Transaction: Commit failed")
|
||||
print(" ✓ Transaction commit")
|
||||
|
||||
-- Verify persisted data
|
||||
local verify_resp = http.request("GET", "http://localhost:8080/api/db/regress_db/tx_test/tx_doc",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(verify_resp.success, "Transaction: Data not persisted after commit")
|
||||
print(" ✓ Transaction verification")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_constraints()
|
||||
print("[Regression] Testing constraints...")
|
||||
|
||||
-- Add required field constraint
|
||||
local required_resp = http.request("POST", "http://localhost:8080/api/constraint/regress_db/users/required/email",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(required_resp.success, "Constraints: Add required field failed")
|
||||
print(" ✓ Required constraint")
|
||||
|
||||
-- Test constraint violation
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/regress_db/users",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "bad_user", name = "No Email"}
|
||||
)
|
||||
assert(not insert_resp.success, "Constraints: Required field constraint not enforced")
|
||||
print(" ✓ Required constraint enforcement")
|
||||
|
||||
-- Add unique constraint
|
||||
local unique_resp = http.request("POST", "http://localhost:8080/api/constraint/regress_db/users/unique/email",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(unique_resp.success, "Constraints: Add unique field failed")
|
||||
|
||||
-- Test unique violation
|
||||
local insert1 = http.request("POST", "http://localhost:8080/api/db/regress_db/users",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "user_a", name = "User A", email = "same@email.com"}
|
||||
)
|
||||
local insert2 = http.request("POST", "http://localhost:8080/api/db/regress_db/users",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "user_b", name = "User B", email = "same@email.com"}
|
||||
)
|
||||
assert(insert1.success and not insert2.success, "Constraints: Unique constraint not enforced")
|
||||
print(" ✓ Unique constraint")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run regression tests
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("REGRESSION TEST SUITE")
|
||||
print(string.rep("=", 50))
|
||||
|
||||
setup_test_data()
|
||||
test_crud_operations()
|
||||
test_index_operations()
|
||||
test_transaction_operations()
|
||||
test_constraints()
|
||||
|
||||
print("\n✓ ALL REGRESSION TESTS PASSED ✓\n")
|
||||
104
tests/smoke.lua
Normal file
104
tests/smoke.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
-- ============================================================================
|
||||
-- test_smoke.lua - Smoke-тест (проверка доступности и базовой функциональности)
|
||||
-- ============================================================================
|
||||
-- Назначение: Быстрая проверка того, что система работает и отвечает
|
||||
-- Покрывает: API endpoints, кластеризацию, аутентификацию
|
||||
|
||||
local function test_api_availability()
|
||||
print("[Smoke] Testing API availability...")
|
||||
|
||||
-- Check main endpoints
|
||||
local endpoints = {
|
||||
"/api/webui/stats",
|
||||
"/api/webui/databases",
|
||||
"/api/cluster/status",
|
||||
"/api/webui/transactions"
|
||||
}
|
||||
|
||||
for _, endpoint in ipairs(endpoints) do
|
||||
local resp = http.request("GET", "http://localhost:8080" .. endpoint,
|
||||
{["X-Session-ID"] = "test_session"})
|
||||
if resp and resp.success then
|
||||
print(" ✓ " .. endpoint .. " - available")
|
||||
else
|
||||
print(" ✗ " .. endpoint .. " - unavailable")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_authentication()
|
||||
print("[Smoke] Testing authentication...")
|
||||
|
||||
-- Login with admin credentials
|
||||
local login_resp = http.request("POST", "http://localhost:8080/api/auth/login",
|
||||
{},
|
||||
{username = "admin", password = "admin"}
|
||||
)
|
||||
assert(login_resp.success and login_resp.data.session_id, "Smoke: Login failed")
|
||||
print(" ✓ Authentication works")
|
||||
|
||||
-- Test invalid credentials
|
||||
local invalid_resp = http.request("POST", "http://localhost:8080/api/auth/login",
|
||||
{},
|
||||
{username = "fake", password = "wrong"}
|
||||
)
|
||||
assert(not invalid_resp.success, "Smoke: Invalid credentials accepted")
|
||||
print(" ✓ Invalid credentials rejected")
|
||||
|
||||
return login_resp.data.session_id
|
||||
end
|
||||
|
||||
local function test_cluster_status()
|
||||
print("[Smoke] Testing cluster status...")
|
||||
|
||||
local status_resp = http.request("GET", "http://localhost:8080/api/cluster/status",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
if status_resp and status_resp.success then
|
||||
print(" ✓ Cluster status: " .. (status_resp.data.health or "unknown"))
|
||||
print(" - Total nodes: " .. (status_resp.data.total_nodes or 0))
|
||||
print(" - Active nodes: " .. (status_resp.data.active_nodes or 0))
|
||||
return true
|
||||
else
|
||||
print(" ⚠ Cluster endpoint may be unavailable in single-node mode")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function test_basic_crud()
|
||||
print("[Smoke] Testing basic CRUD...")
|
||||
|
||||
-- Create database and collection via first write
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/smoke_test/users",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "smoke_001", test_field = "smoke_value", timestamp = os.time()}
|
||||
)
|
||||
assert(insert_resp.success, "Smoke: Basic insert failed")
|
||||
print(" ✓ Basic insert")
|
||||
|
||||
-- Read back
|
||||
local read_resp = http.request("GET", "http://localhost:8080/api/db/smoke_test/users/smoke_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
assert(read_resp.success, "Smoke: Basic read failed")
|
||||
assert(read_resp.data.fields.test_field == "smoke_value", "Smoke: Data mismatch")
|
||||
print(" ✓ Basic read")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run smoke tests
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("SMOKE TEST SUITE")
|
||||
print(string.rep("=", 50))
|
||||
|
||||
local session = test_authentication()
|
||||
test_api_availability()
|
||||
test_cluster_status()
|
||||
test_basic_crud()
|
||||
|
||||
print("\n✓ ALL SMOKE TESTS PASSED ✓\n")
|
||||
180
tests/test_functional.lua
Normal file
180
tests/test_functional.lua
Normal file
@@ -0,0 +1,180 @@
|
||||
-- ============================================================================
|
||||
-- test_functional.lua - Функциональный тест
|
||||
-- ============================================================================
|
||||
-- Назначение: Проверка конкретных бизнес-требований и сценариев использования
|
||||
-- Покрывает: Вложенные документы (кортежи), ACL, триггеры, MVCC
|
||||
|
||||
local function test_nested_documents()
|
||||
print("[Functional] Testing nested documents (Tuples)...")
|
||||
|
||||
-- Create document with nested fields
|
||||
local user_data = {
|
||||
_id = "user_full_001",
|
||||
profile = {
|
||||
first_name = "Ivan",
|
||||
last_name = "Petrov",
|
||||
address = {
|
||||
city = "Moscow",
|
||||
street = "Tverskaya",
|
||||
zip = "101000"
|
||||
}
|
||||
},
|
||||
settings = {
|
||||
theme = "dark",
|
||||
notifications = true
|
||||
}
|
||||
}
|
||||
|
||||
local insert_resp = http.request("POST", "http://localhost:8080/api/db/func_test/profiles",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
user_data
|
||||
)
|
||||
assert(insert_resp.success, "Functional: Nested document insert failed")
|
||||
print(" ✓ Nested document creation")
|
||||
|
||||
-- Update nested field
|
||||
local update_resp = http.request("PUT", "http://localhost:8080/api/db/func_test/profiles/user_full_001",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{["profile.address.city"] = "Saint Petersburg"}
|
||||
)
|
||||
assert(update_resp.success, "Functional: Nested field update failed")
|
||||
print(" ✓ Nested field update")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_acl_permissions()
|
||||
print("[Functional] Testing ACL system...")
|
||||
|
||||
-- Create a test user
|
||||
local user_resp = http.request("POST", "http://localhost:8080/api/acl/user/testuser",
|
||||
{["X-Session-ID"] = "admin_session"},
|
||||
{password = "testpass123", roles = {"guest"}}
|
||||
)
|
||||
assert(user_resp.success, "Functional: User creation failed")
|
||||
print(" ✓ User creation")
|
||||
|
||||
-- Grant permission
|
||||
local grant_resp = http.request("POST", "http://localhost:8080/api/acl/role/guest/grant/func_test.*:read",
|
||||
{["X-Session-ID"] = "admin_session"}
|
||||
)
|
||||
print(" ✓ Permission grant")
|
||||
|
||||
-- Verify permission with test user
|
||||
local test_session = auth.login("testuser", "testpass123")
|
||||
assert(test_session, "Functional: Test user login failed")
|
||||
|
||||
local read_resp = http.request("GET", "http://localhost:8080/api/db/func_test/profiles",
|
||||
{["X-Session-ID"] = test_session}
|
||||
)
|
||||
assert(read_resp.success, "Functional: Read permission check failed")
|
||||
print(" ✓ Permission verification")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_triggers()
|
||||
print("[Functional] Testing triggers...")
|
||||
|
||||
-- Create a trigger that auto-updates timestamp
|
||||
local trigger_config = {
|
||||
name = "auto_timestamp",
|
||||
event = "BEFORE_UPDATE",
|
||||
action = "modify",
|
||||
operations = {
|
||||
{type = "set", field = "updated_at", value = "$$NOW"}
|
||||
},
|
||||
description = "Auto-update timestamp on modification"
|
||||
}
|
||||
|
||||
local create_resp = http.request("POST", "http://localhost:8080/api/webui/trigger/func_test/profiles/create",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
trigger_config
|
||||
)
|
||||
print(" ✓ Trigger creation")
|
||||
|
||||
-- Enable trigger
|
||||
local enable_resp = http.request("POST", "http://localhost:8080/api/webui/trigger/func_test/profiles/enable/auto_timestamp",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
print(" ✓ Trigger enable")
|
||||
|
||||
-- Test trigger
|
||||
local update_resp = http.request("PUT", "http://localhost:8080/api/db/func_test/profiles/user_full_001",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{profile.first_name = "Petr"}
|
||||
)
|
||||
assert(update_resp.success, "Functional: Trigger test update failed")
|
||||
|
||||
local read_resp = http.request("GET", "http://localhost:8080/api/db/func_test/profiles/user_full_001",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
if read_resp.data.fields.updated_at then
|
||||
print(" ✓ Trigger executed (updated_at set)")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_mvcc_isolation()
|
||||
print("[Functional] Testing MVCC isolation...")
|
||||
|
||||
-- Create multiple versions of a document
|
||||
for i = 1, 5 do
|
||||
local resp = http.request("PUT", "http://localhost:8080/api/db/func_test/mvcc_test/version_doc",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{counter = i, version = "v" .. i}
|
||||
)
|
||||
assert(resp.success, "Functional: MVCC version update failed")
|
||||
end
|
||||
|
||||
local final_resp = http.request("GET", "http://localhost:8080/api/db/func_test/mvcc_test/version_doc",
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
|
||||
if final_resp.data and final_resp.data.fields.counter == 5 then
|
||||
print(" ✓ MVCC versioning works")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function test_backup_restore()
|
||||
print("[Functional] Testing backup/restore...")
|
||||
|
||||
-- Export data
|
||||
local export_resp = http.request("POST", "http://localhost:8080/api/webui/export",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{database = "func_test", filename = "backup_test.msgpack"}
|
||||
)
|
||||
assert(export_resp.success, "Functional: Export failed")
|
||||
print(" ✓ Data export")
|
||||
|
||||
-- Import to new database
|
||||
local import_resp = http.request("POST", "http://localhost:8080/api/webui/import",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{
|
||||
database = "func_test_restored",
|
||||
data = export_resp.data.data,
|
||||
overwrite = true
|
||||
}
|
||||
)
|
||||
assert(import_resp.success, "Functional: Import failed")
|
||||
print(" ✓ Data import")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run functional tests
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("FUNCTIONAL TEST SUITE")
|
||||
print(string.rep("=", 50))
|
||||
|
||||
test_nested_documents()
|
||||
test_acl_permissions()
|
||||
test_triggers()
|
||||
test_mvcc_isolation()
|
||||
test_backup_restore()
|
||||
|
||||
print("\n✓ ALL FUNCTIONAL TESTS PASSED ✓\n")
|
||||
275
tests/test_perfomance.lua
Normal file
275
tests/test_perfomance.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
-- ============================================================================
|
||||
-- test_performance.lua - Нагрузочный тест
|
||||
-- ============================================================================
|
||||
-- Назначение: Оценка производительности под нагрузкой
|
||||
-- Покрывает: Пропускную способность, латентность, параллелизм
|
||||
|
||||
local function measure_latency(operation, fn)
|
||||
local start_time = os.clock()
|
||||
local result = fn()
|
||||
local end_time = os.clock()
|
||||
local latency_ms = (end_time - start_time) * 1000
|
||||
|
||||
if not result then
|
||||
print(" ✗ " .. operation .. " failed")
|
||||
return nil
|
||||
end
|
||||
|
||||
print(string.format(" ✓ %s: %.2f ms", operation, latency_ms))
|
||||
return latency_ms
|
||||
end
|
||||
|
||||
local function performance_insert_test(count)
|
||||
print(string.format("\n[Performance] Insert %d documents...", count))
|
||||
|
||||
local latencies = {}
|
||||
local successes = 0
|
||||
|
||||
for i = 1, count do
|
||||
local latency = measure_latency("Insert " .. i, function()
|
||||
local resp = http.request("POST", "http://localhost:8080/api/db/perf_test/benchmark",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{
|
||||
_id = "perf_" .. i,
|
||||
value = math.random(1, 1000000),
|
||||
text = string.rep("x", 100),
|
||||
timestamp = os.time()
|
||||
}
|
||||
)
|
||||
if resp and resp.success then
|
||||
successes = successes + 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
if latency then
|
||||
table.insert(latencies, latency)
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate statistics
|
||||
local total_ms = 0
|
||||
for _, lat in ipairs(latencies) do
|
||||
total_ms = total_ms + lat
|
||||
end
|
||||
|
||||
local avg_latency = total_ms / #latencies
|
||||
local throughput = (successes / (total_ms / 1000))
|
||||
|
||||
print(string.format("\n Results: %d/%d successful", successes, count))
|
||||
print(string.format(" Average latency: %.2f ms", avg_latency))
|
||||
print(string.format(" Throughput: %.2f ops/sec", throughput))
|
||||
|
||||
return {successes = successes, avg_latency = avg_latency, throughput = throughput}
|
||||
end
|
||||
|
||||
local function performance_read_test(doc_count)
|
||||
print(string.format("\n[Performance] Read %d documents...", doc_count))
|
||||
|
||||
local latencies = {}
|
||||
local successes = 0
|
||||
|
||||
for i = 1, doc_count do
|
||||
local latency = measure_latency("Read " .. i, function()
|
||||
local resp = http.request("GET", "http://localhost:8080/api/db/perf_test/benchmark/perf_" .. i,
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
if resp and resp.success then
|
||||
successes = successes + 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
if latency then
|
||||
table.insert(latencies, latency)
|
||||
end
|
||||
end
|
||||
|
||||
local total_ms = 0
|
||||
for _, lat in ipairs(latencies) do
|
||||
total_ms = total_ms + lat
|
||||
end
|
||||
|
||||
local avg_latency = total_ms / #latencies
|
||||
local throughput = (successes / (total_ms / 1000))
|
||||
|
||||
print(string.format("\n Results: %d/%d successful", successes, doc_count))
|
||||
print(string.format(" Average latency: %.2f ms", avg_latency))
|
||||
print(string.format(" Throughput: %.2f ops/sec", throughput))
|
||||
|
||||
return {successes = successes, avg_latency = avg_latency, throughput = throughput}
|
||||
end
|
||||
|
||||
local function performance_index_query_test()
|
||||
print("\n[Performance] Index query test...")
|
||||
|
||||
-- Create index
|
||||
local idx_resp = http.request("POST", "http://localhost:8080/api/index/perf_test/benchmark/create",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{name = "value_idx", fields = {"value"}, unique = false}
|
||||
)
|
||||
|
||||
local latencies = {}
|
||||
local queries = 100
|
||||
|
||||
for i = 1, queries do
|
||||
local search_value = math.random(1, 1000000)
|
||||
|
||||
local latency = measure_latency("Query " .. i, function()
|
||||
local resp = http.request("GET",
|
||||
"http://localhost:8080/api/db/perf_test/benchmark?index=value_idx&value=" .. search_value,
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
return resp and resp.success
|
||||
end)
|
||||
|
||||
if latency then
|
||||
table.insert(latencies, latency)
|
||||
end
|
||||
end
|
||||
|
||||
local total_ms = 0
|
||||
for _, lat in ipairs(latencies) do
|
||||
total_ms = total_ms + lat
|
||||
end
|
||||
|
||||
local avg_latency = total_ms / #latencies
|
||||
print(string.format("\n Average index query latency: %.2f ms", avg_latency))
|
||||
|
||||
return avg_latency
|
||||
end
|
||||
|
||||
local function performance_concurrent_test()
|
||||
print("\n[Performance] Concurrent operations test...")
|
||||
print(" ⚠ This test simulates concurrent operations")
|
||||
print(" Note: Lua concurrency is simulated via sequential requests")
|
||||
|
||||
local concurrency_levels = {10, 50, 100}
|
||||
local results = {}
|
||||
|
||||
for _, level in ipairs(concurrency_levels) do
|
||||
print(string.format("\n Testing concurrency level: %d", level))
|
||||
|
||||
local start_time = os.clock()
|
||||
local success_count = 0
|
||||
|
||||
for i = 1, level do
|
||||
local resp = http.request("POST", "http://localhost:8080/api/db/perf_test/concurrent",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{
|
||||
_id = "concurrent_" .. i .. "_" .. os.time(),
|
||||
thread_id = i,
|
||||
timestamp = os.time()
|
||||
}
|
||||
)
|
||||
if resp and resp.success then
|
||||
success_count = success_count + 1
|
||||
end
|
||||
end
|
||||
|
||||
local duration = os.clock() - start_time
|
||||
local throughput = success_count / duration
|
||||
|
||||
table.insert(results, {
|
||||
level = level,
|
||||
duration = duration,
|
||||
throughput = throughput,
|
||||
success_rate = (success_count / level) * 100
|
||||
})
|
||||
|
||||
print(string.format(" Duration: %.2f sec", duration))
|
||||
print(string.format(" Throughput: %.2f ops/sec", throughput))
|
||||
print(string.format(" Success rate: %.1f%%", (success_count / level) * 100))
|
||||
end
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
local function performance_mixed_workload_test()
|
||||
print("\n[Performance] Mixed workload (70% read, 20% write, 10% update)...")
|
||||
|
||||
local operations = 200
|
||||
local reads = 0
|
||||
local writes = 0
|
||||
local updates = 0
|
||||
|
||||
local start_time = os.clock()
|
||||
|
||||
for i = 1, operations do
|
||||
local rand = math.random(1, 100)
|
||||
|
||||
if rand <= 70 then
|
||||
-- Read operation
|
||||
local doc_id = "perf_" .. math.random(1, 100)
|
||||
local resp = http.request("GET", "http://localhost:8080/api/db/perf_test/benchmark/" .. doc_id,
|
||||
{["X-Session-ID"] = "test_session"}
|
||||
)
|
||||
if resp and resp.success then reads = reads + 1 end
|
||||
|
||||
elseif rand <= 90 then
|
||||
-- Write operation
|
||||
local resp = http.request("POST", "http://localhost:8080/api/db/perf_test/benchmark",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{
|
||||
_id = "mixed_" .. i .. "_" .. os.time(),
|
||||
data = "mixed workload test",
|
||||
timestamp = os.time()
|
||||
}
|
||||
)
|
||||
if resp and resp.success then writes = writes + 1 end
|
||||
|
||||
else
|
||||
-- Update operation
|
||||
local doc_id = "perf_" .. math.random(1, 100)
|
||||
local resp = http.request("PUT", "http://localhost:8080/api/db/perf_test/benchmark/" .. doc_id,
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{updated = true, update_time = os.time()}
|
||||
)
|
||||
if resp and resp.success then updates = updates + 1 end
|
||||
end
|
||||
end
|
||||
|
||||
local duration = os.clock() - start_time
|
||||
|
||||
print(string.format("\n Results:"))
|
||||
print(string.format(" Total operations: %d", operations))
|
||||
print(string.format(" Reads: %d (%.1f%%)", reads, (reads/operations)*100))
|
||||
print(string.format(" Writes: %d (%.1f%%)", writes, (writes/operations)*100))
|
||||
print(string.format(" Updates: %d (%.1f%%)", updates, (updates/operations)*100))
|
||||
print(string.format(" Total duration: %.2f sec", duration))
|
||||
print(string.format(" Overall throughput: %.2f ops/sec", operations/duration))
|
||||
|
||||
return {reads = reads, writes = writes, updates = updates, duration = duration}
|
||||
end
|
||||
|
||||
-- Run performance tests
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("PERFORMANCE TEST SUITE")
|
||||
print(string.rep("=", 50))
|
||||
|
||||
-- Warm-up
|
||||
print("\n[Performance] Warming up...")
|
||||
for i = 1, 10 do
|
||||
http.request("POST", "http://localhost:8080/api/db/perf_test/benchmark",
|
||||
{["X-Session-ID"] = "test_session"},
|
||||
{_id = "warmup_" .. i, data = "warmup"}
|
||||
)
|
||||
end
|
||||
|
||||
local insert_results = performance_insert_test(100)
|
||||
local read_results = performance_read_test(100)
|
||||
local index_latency = performance_index_query_test()
|
||||
local concurrent_results = performance_concurrent_test()
|
||||
local mixed_results = performance_mixed_workload_test()
|
||||
|
||||
print("\n" .. string.rep("=", 50))
|
||||
print("PERFORMANCE SUMMARY")
|
||||
print(string.rep("=", 50))
|
||||
print(string.format("Insert throughput: %.2f ops/sec", insert_results.throughput or 0))
|
||||
print(string.format("Read throughput: %.2f ops/sec", read_results.throughput or 0))
|
||||
print(string.format("Index query latency: %.2f ms", index_latency or 0))
|
||||
print(string.format("Mixed workload throughput: %.2f ops/sec", mixed_results.duration and 200/mixed_results.duration or 0))
|
||||
|
||||
print("\n✓ PERFORMANCE TESTS COMPLETED ✓\n")
|
||||
Reference in New Issue
Block a user