blob: a6f240998fe31500605fa3ba4a29a82bb11aa7d7 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#pragma once
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <random>
#include <string>
#include <gtest/gtest.h>
#include "lib.rs.h"
inline iggy::ffi::Identifier make_string_identifier(const std::string &value) {
iggy::ffi::Identifier identifier;
identifier.set_string(value);
return identifier;
}
inline iggy::ffi::Identifier make_numeric_identifier(const std::uint32_t value) {
iggy::ffi::Identifier identifier;
identifier.set_numeric(value);
return identifier;
}
inline rust::Vec<std::uint8_t> to_payload(const std::string &s) {
rust::Vec<std::uint8_t> v;
for (const char c : s) {
v.push_back(static_cast<std::uint8_t>(c));
}
return v;
}
inline rust::Vec<std::uint8_t> partition_id_bytes(std::uint32_t id) {
rust::Vec<std::uint8_t> v;
v.push_back(static_cast<std::uint8_t>(id & 0xFF));
v.push_back(static_cast<std::uint8_t>((id >> 8) & 0xFF));
v.push_back(static_cast<std::uint8_t>((id >> 16) & 0xFF));
v.push_back(static_cast<std::uint8_t>((id >> 24) & 0xFF));
return v;
}
inline rust::Vec<rust::String> make_snapshot_types(std::initializer_list<const char *> values) {
rust::Vec<rust::String> snapshot_types;
for (const auto value : values) {
snapshot_types.push_back(value);
}
return snapshot_types;
}
struct TrackedConsumerGroup {
std::string stream_name;
std::string topic_name;
std::string group_name;
};
class E2ETestFixture : public ::testing::Test {
public:
~E2ETestFixture() { CleanupBestEffort(); }
void TearDown() override { Cleanup(); }
protected:
void TrackClient(iggy::ffi::Client *client) {
ASSERT_NE(client, nullptr);
clients_.push_back(client);
}
iggy::ffi::Client *GetLoggedOutClient() {
iggy::ffi::Client *client = nullptr;
EXPECT_NO_THROW({ client = iggy::ffi::new_connection(""); });
EXPECT_NE(client, nullptr);
if (client == nullptr) {
return nullptr;
}
TrackClient(client);
return client;
}
iggy::ffi::Client *GetLoggedInClient() {
iggy::ffi::Client *client = GetLoggedOutClient();
if (client == nullptr) {
return nullptr;
}
EXPECT_NO_THROW(client->connect());
EXPECT_NO_THROW(client->login_user("iggy", "iggy"));
return client;
}
std::string GetRandomName() {
static constexpr char alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static thread_local std::mt19937 generator(std::random_device{}());
std::uniform_int_distribution<std::size_t> length_distribution(8, 255);
std::uniform_int_distribution<std::size_t> distribution(0, sizeof(alphabet) - 2);
const std::size_t length = length_distribution(generator);
std::string name;
name.reserve(length);
name.push_back('a');
for (std::size_t i = 1; i < length; ++i) {
name.push_back(alphabet[distribution(generator)]);
}
return name;
}
void TrackStream(const std::string &stream_name) { tracked_stream_names_.push_back(stream_name); }
void TrackStream(const std::uint32_t stream_id) { tracked_stream_ids_.push_back(stream_id); }
void TrackConsumerGroup(const std::string &stream_name,
const std::string &topic_name,
const std::string &group_name) {
tracked_consumer_groups_.push_back({stream_name, topic_name, group_name});
}
void ForgetTrackedStream(const std::string &stream_name) {
tracked_stream_names_.erase(
std::remove(tracked_stream_names_.begin(), tracked_stream_names_.end(), stream_name),
tracked_stream_names_.end());
for (std::size_t i = 0; i < tracked_consumer_groups_.size();) {
const auto &group = tracked_consumer_groups_[i];
if (group.stream_name == stream_name) {
ForgetTrackedConsumerGroup(group.stream_name, group.topic_name, group.group_name);
continue;
}
++i;
}
}
void ForgetTrackedStream(const std::uint32_t stream_id) {
tracked_stream_ids_.erase(std::remove(tracked_stream_ids_.begin(), tracked_stream_ids_.end(), stream_id),
tracked_stream_ids_.end());
}
void ForgetTrackedConsumerGroup(const std::string &stream_name,
const std::string &topic_name,
const std::string &group_name) {
for (std::size_t i = 0; i < tracked_consumer_groups_.size();) {
const auto &tracked_group = tracked_consumer_groups_[i];
if (tracked_group.stream_name == stream_name && tracked_group.topic_name == topic_name &&
tracked_group.group_name == group_name) {
tracked_consumer_groups_.erase(tracked_consumer_groups_.begin() + i);
continue;
}
++i;
}
}
void DeleteClient(iggy::ffi::Client *&client) {
iggy::ffi::Client *client_to_delete = client;
client = nullptr;
ForgetClient(client_to_delete);
EXPECT_NO_THROW(iggy::ffi::delete_client(client_to_delete));
}
void Cleanup() {
CleanupConsumerGroups();
CleanupStreams();
CleanupClients();
}
private:
void CleanupBestEffort() noexcept {
CleanupConsumerGroupsBestEffort();
CleanupStreamsBestEffort();
CleanupClientsBestEffort();
}
void ForgetClient(iggy::ffi::Client *client) {
const auto found = std::find(clients_.begin(), clients_.end(), client);
if (found != clients_.end()) {
*found = nullptr;
}
}
void CleanupStreams() {
if (tracked_stream_names_.empty() && tracked_stream_ids_.empty()) {
return;
}
iggy::ffi::Client *cleanup_client = nullptr;
EXPECT_NO_THROW({ cleanup_client = iggy::ffi::new_connection(""); });
EXPECT_NE(cleanup_client, nullptr);
if (cleanup_client != nullptr) {
EXPECT_NO_THROW(cleanup_client->connect());
EXPECT_NO_THROW(cleanup_client->login_user("iggy", "iggy"));
for (const auto &stream_name : tracked_stream_names_) {
EXPECT_NO_THROW(cleanup_client->delete_stream(make_string_identifier(stream_name)));
}
for (const auto stream_id : tracked_stream_ids_) {
EXPECT_NO_THROW(cleanup_client->delete_stream(make_numeric_identifier(stream_id)));
}
EXPECT_NO_THROW(iggy::ffi::delete_client(cleanup_client));
}
tracked_stream_names_.clear();
tracked_stream_ids_.clear();
}
void CleanupConsumerGroups() {
if (tracked_consumer_groups_.empty()) {
return;
}
iggy::ffi::Client *cleanup_client = nullptr;
EXPECT_NO_THROW({ cleanup_client = iggy::ffi::new_connection(""); });
EXPECT_NE(cleanup_client, nullptr);
if (cleanup_client != nullptr) {
EXPECT_NO_THROW(cleanup_client->connect());
EXPECT_NO_THROW(cleanup_client->login_user("iggy", "iggy"));
for (const auto &group : tracked_consumer_groups_) {
EXPECT_NO_THROW(cleanup_client->delete_consumer_group(make_string_identifier(group.stream_name),
make_string_identifier(group.topic_name),
make_string_identifier(group.group_name)));
}
EXPECT_NO_THROW(iggy::ffi::delete_client(cleanup_client));
}
tracked_consumer_groups_.clear();
}
void CleanupStreamsBestEffort() noexcept {
if (tracked_stream_names_.empty() && tracked_stream_ids_.empty()) {
return;
}
iggy::ffi::Client *cleanup_client = nullptr;
try {
cleanup_client = iggy::ffi::new_connection("");
if (cleanup_client != nullptr) {
cleanup_client->connect();
cleanup_client->login_user("iggy", "iggy");
for (const auto &stream_name : tracked_stream_names_) {
cleanup_client->delete_stream(make_string_identifier(stream_name));
}
for (const auto stream_id : tracked_stream_ids_) {
cleanup_client->delete_stream(make_numeric_identifier(stream_id));
}
}
} catch (...) {
}
if (cleanup_client != nullptr) {
try {
iggy::ffi::delete_client(cleanup_client);
} catch (...) {
}
}
tracked_stream_names_.clear();
tracked_stream_ids_.clear();
}
void CleanupConsumerGroupsBestEffort() noexcept {
if (tracked_consumer_groups_.empty()) {
return;
}
iggy::ffi::Client *cleanup_client = nullptr;
try {
cleanup_client = iggy::ffi::new_connection("");
if (cleanup_client != nullptr) {
cleanup_client->connect();
cleanup_client->login_user("iggy", "iggy");
for (const auto &group : tracked_consumer_groups_) {
cleanup_client->delete_consumer_group(make_string_identifier(group.stream_name),
make_string_identifier(group.topic_name),
make_string_identifier(group.group_name));
}
}
} catch (...) {
}
if (cleanup_client != nullptr) {
try {
iggy::ffi::delete_client(cleanup_client);
} catch (...) {
}
}
tracked_consumer_groups_.clear();
}
void CleanupClients() {
for (iggy::ffi::Client *&client : clients_) {
iggy::ffi::Client *client_to_delete = client;
client = nullptr;
EXPECT_NO_THROW(iggy::ffi::delete_client(client_to_delete));
}
clients_.clear();
}
void CleanupClientsBestEffort() noexcept {
for (iggy::ffi::Client *&client : clients_) {
iggy::ffi::Client *client_to_delete = client;
client = nullptr;
try {
iggy::ffi::delete_client(client_to_delete);
} catch (...) {
}
}
clients_.clear();
}
std::vector<iggy::ffi::Client *> clients_;
std::vector<std::string> tracked_stream_names_;
std::vector<std::uint32_t> tracked_stream_ids_;
std::vector<TrackedConsumerGroup> tracked_consumer_groups_;
};