This commit is contained in:
171
tests/BasicTests.cpp
Normal file
171
tests/BasicTests.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
|
||||
class RediskaBasicTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
channel_ = grpc::CreateChannel(grpc::InsecureChannel("localhost:50051"));
|
||||
bool_stub_ = std::make_unique<v1::primitives::boolean::BoolCacheService::Stub>(channel_);
|
||||
int_stub_ = std::make_unique<v1::primitives::integer::IntCacheService::Stub>(channel_);
|
||||
string_stub_ = std::make_unique<v1::primitives::str::StringCacheService::Stub>(channel_);
|
||||
list_stub_ = std::make_unique<v1::collections::list::ListCacheService::Stub>(channel_);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
channel_->Shutdown();
|
||||
}
|
||||
|
||||
std::shared_ptr<grpc::Channel> channel_;
|
||||
std::unique_ptr<v1::primitives::boolean::BoolCacheService::Stub> bool_stub_;
|
||||
std::unique_ptr<v1::primitives::integer::IntCacheService::Stub> int_stub_;
|
||||
std::unique_ptr<v1::primitives::str::StringCacheService::Stub> string_stub_;
|
||||
std::unique_ptr<v1::collections::list::ListCacheService::Stub> list_stub_;
|
||||
};
|
||||
|
||||
TEST_F(RediskaBasicTest, BooleanOperations) {
|
||||
// Test SET operation
|
||||
bool_stub_->Set(v1::primitives::boolean::BoolSetRequest(id="test_bool", value=true));
|
||||
|
||||
// Test GET operation
|
||||
auto response = bool_stub_->Get(v1::primitives::BoolGetRequest(id="test_bool"));
|
||||
EXPECT_TRUE(response.value());
|
||||
|
||||
// Test DELETE operation
|
||||
auto delete_response = bool_stub_->Delete(v1::primitives::BoolDeleteRequest(id="test_bool"));
|
||||
EXPECT_TRUE(delete_response.removed_value());
|
||||
}
|
||||
|
||||
TEST_F(RediskaBasicTest, IntegerOperations) {
|
||||
// Test SET operation
|
||||
int_stub_->Set(v1::primitives::integer::IntSetRequest(id="test_int", value=42));
|
||||
|
||||
// Test GET operation
|
||||
auto response = int_stub_->Get(v1::primitives::integer::IntGetRequest(id="test_int"));
|
||||
EXPECT_EQ(response.value(), 42);
|
||||
|
||||
// Test DELETE operation
|
||||
auto delete_response = int_stub_->Delete(v1::primitives::integer::IntDeleteRequest(id="test_int"));
|
||||
EXPECT_EQ(delete_response.removed_value(), 42);
|
||||
}
|
||||
|
||||
TEST_F(RediskaBasicTest, StringOperations) {
|
||||
// Test SET operation
|
||||
string_stub_->Set(v1::primitives::str::StringSetRequest(id="test_string", value="hello"));
|
||||
|
||||
// Test GET operation
|
||||
auto response = string_stub_->Get(v1::primitives::str::StringGetRequest(id="test_string"));
|
||||
EXPECT_EQ(response.value(), "hello");
|
||||
|
||||
// Test DELETE operation
|
||||
auto delete_response = string_stub_->Delete(v1::primitives::str::StringDeleteRequest(id="test_string"));
|
||||
EXPECT_EQ(delete_response.removed_value(), "hello");
|
||||
}
|
||||
|
||||
TEST_FediskaBasicTest, ListOperations) {
|
||||
// Test CREATE operation
|
||||
auto create_response = list_stub_->Create(v1::collections::list::ListCreateRequest(
|
||||
element_kind=v1::collections::common::ElementKind::INT,
|
||||
ttl_seconds=3600
|
||||
));
|
||||
std::string list_id = create_response.id;
|
||||
|
||||
// Test SET operation
|
||||
std::vector<v1::collections::common::CollectionElement> elements;
|
||||
elements.push_back(v1::collections::common::CollectionElement(integer=1));
|
||||
elements.push_back(v1::collections::common::CollectionElement(integer=2));
|
||||
elements.push_back(v1::collections::common::CollectionElement(integer=3));
|
||||
|
||||
list_stub_->Set(v1::collections::list::ListSetRequest(id=list_id, elements=elements));
|
||||
|
||||
// Test GET operation (streaming)
|
||||
auto get_response = list_stub_->Get(v1::collections::list::ListGetRequest(id=list_id));
|
||||
int count = 0;
|
||||
for (auto element : get_response) {
|
||||
EXPECT_EQ(element.integer(), count + 1);
|
||||
count++;
|
||||
}
|
||||
EXPECT_EQ(count, 3);
|
||||
|
||||
// Test LENGTH operation
|
||||
auto length_response = list_stub_->Length(v1::collections::list::ListLengthRequest(id=list_id));
|
||||
EXPECT_EQ(length_response.length(), 3);
|
||||
|
||||
// Test DELETE operation
|
||||
list_stub_->Delete(v1::collections::DeleteRequest(id=list_id));
|
||||
}
|
||||
|
||||
TEST_FediskaBasicTest, ConcurrentOperations) {
|
||||
const int num_threads = 10;
|
||||
const int operations_per_thread = 100;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::exception_ptr<std::exception>> exceptions;
|
||||
|
||||
// Запускаем несколько потоков одновременно
|
||||
for (int i = 0; i < num_threads; ++i) {
|
||||
threads.emplace_back([&, i, operations_per_thread]() {
|
||||
try {
|
||||
for (int j = 0; j < operations_per_thread; ++j) {
|
||||
std::string key = "test_" + std::to_string(i * operations_per_thread + j);
|
||||
|
||||
if (j % 3 == 0) {
|
||||
bool_stub_->Set(v1::primitives::boolean::BoolSetRequest(key, true));
|
||||
} else if (j % 3 == 1) {
|
||||
int_stub_->Set(v1::primitives::integer::IntSetRequest(key, j));
|
||||
} else {
|
||||
string_stub_->Set(v1::primitives::str::StringSetRequest(key, "value_" + std::to_string(j)));
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
exceptions.emplace_back(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ждем завершения всех потоков
|
||||
for (auto& thread : threads) {
|
||||
if (thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем, что не было исключений
|
||||
EXPECT_TRUE(exceptions.empty());
|
||||
}
|
||||
|
||||
TEST_FediskaBasicTest, ServerStability) {
|
||||
// Тест на стабильность сервера при множественных запросов
|
||||
const int num_requests = 1000;
|
||||
|
||||
for (int i = 0; i < num_requests; ++i) {
|
||||
std::string key = "stability_test_" + std::to_string(i);
|
||||
|
||||
if (i % 4 == 0) {
|
||||
bool_stub_->Set(v1::primitives::boolean::BoolSetRequest(key, true));
|
||||
} else if (i % 4 == 1) {
|
||||
int_stub_->Set(v1::primitives::integer::IntSetRequest(key, i));
|
||||
} else if (i % 4 == 2) {
|
||||
string_stub_->Set(v1::primitives::str::StringSetRequest(key, "value_" + std::to_string(i)));
|
||||
} else {
|
||||
list_stub_->Set(v1::collections::ListSetRequest(
|
||||
id="list_" + std::to_string(i),
|
||||
elements={
|
||||
v1::collections::common::CollectionElement(integer=i)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
if (i % 10 == 0) {
|
||||
auto response = bool_stub_->Get(v1::primitives::boolean::BoolGetRequest(key));
|
||||
EXPECT_TRUE(response.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
39
tests/CMakeLists.txt
Normal file
39
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
add_executable(unit_tests
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(unit_tests
|
||||
PRIVATE
|
||||
worker
|
||||
cache
|
||||
frontend
|
||||
doctest::doctest
|
||||
)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${doctest_SOURCE_DIR}/scripts/cmake")
|
||||
include(doctest)
|
||||
list(POP_BACK CMAKE_MODULE_PATH)
|
||||
|
||||
doctest_discover_tests(unit_tests
|
||||
TEST_PREFIX ""
|
||||
TEST_SUFFIX ""
|
||||
)
|
||||
|
||||
find_program(CPPCHECK_EXECUTABLE cppcheck)
|
||||
if(CPPCHECK_EXECUTABLE)
|
||||
add_test(
|
||||
NAME static_analysis
|
||||
COMMAND ${CPPCHECK_EXECUTABLE}
|
||||
--enable=all
|
||||
--std=c++23
|
||||
--error-exitcode=1
|
||||
--inline-suppr
|
||||
${CMAKE_SOURCE_DIR}/rediska
|
||||
)
|
||||
set_tests_properties(static_analysis PROPERTIES
|
||||
LABELS "static"
|
||||
TIMEOUT 120
|
||||
)
|
||||
else()
|
||||
message(WARNING "Cppcheck not found — skipping static analysis test")
|
||||
endif()
|
||||
39
tests/README.md
Normal file
39
tests/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Creating a test group
|
||||
|
||||
1. **Create a new test file** \
|
||||
Place it under `/tests/frontend/` or `/tests/backend/`, following the [naming convention](#naming-convention) below.
|
||||
|
||||
2. **Include the testing framework** \
|
||||
Add the following at the top of the file:
|
||||
```C++
|
||||
#include <doctest/doctest.h>
|
||||
```
|
||||
|
||||
3. **Register the file in the build system** \
|
||||
Add the new test file to `/tests/CMakeLists.txt` so it’s compiled and executed as part of the test suite.
|
||||
|
||||
4. **Start writing tests!** \
|
||||
See [doctest docs](https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md) for more details.
|
||||
|
||||
# Naming convention
|
||||
|
||||
File name should match against the following pattern:
|
||||
|
||||
`<module>_<feature>_<test_type>_tests.cpp`
|
||||
|
||||
Where:
|
||||
- `<module>` - the component or subsystem under test.
|
||||
- Backend modules: `cache` | `worker`
|
||||
- Frontend modules: `work in progress`
|
||||
- `<feature>` - a concise, kebab-case description of the specific functionality.
|
||||
- `<test_type>` - one of the following:
|
||||
- `unit`
|
||||
- `integration`
|
||||
- `e2e`
|
||||
|
||||
# Examples
|
||||
|
||||
| Path | Description |
|
||||
| :--------------------------------------------- | :------------------------------------------------------------- |
|
||||
| `backend/cache_lru_unit_tests.cpp` | Unit tests for LRU eviction logic in the cache module |
|
||||
| `backend/worker_handle-request_unit_tests.cpp` | Unit tests for the request-handling logic in the worker module |
|
||||
0
tests/backend/.keep
Normal file
0
tests/backend/.keep
Normal file
0
tests/frontend/.keep
Normal file
0
tests/frontend/.keep
Normal file
2
tests/main.cpp
Normal file
2
tests/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
Reference in New Issue
Block a user