blob: e58592bca900751730c7c659463fc9115e8de7b6 [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.
*/
// Unique step-class prefix so the generated CukeObject<n> symbols do not clash with the other
// step file when linked together (see background_steps.cpp for details).
#define CUKE_OBJECT_PREFIX IggyBddMessaging
#include <gtest/gtest.h>
#include <cucumber-cpp/autodetect.hpp>
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include "iggy.hpp"
#include "world.hpp"
GIVEN("^I have no streams in the system$") {
cucumber::ScenarioScope<bdd::GlobalContext> context;
ASSERT_NE(context->client, nullptr);
const auto streams = context->client->get_streams();
EXPECT_EQ(streams.size(), static_cast<std::size_t>(0));
}
WHEN("^I create a stream with name \"([^\"]{1,255})\"$") {
REGEX_PARAM(std::string, name);
cucumber::ScenarioScope<bdd::GlobalContext> context;
context->client->create_stream(name);
}
THEN("^the stream should be created successfully$") {
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto streams = context->client->get_streams();
EXPECT_EQ(streams.size(), static_cast<std::size_t>(1));
}
THEN("^the stream should have name \"([^\"]{1,255})\"$") {
REGEX_PARAM(std::string, name);
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto stream = context->client->get_stream(bdd::make_numeric_identifier(0));
EXPECT_EQ(std::string(stream.name), name);
}
WHEN("^I create a topic with name \"([^\"]{1,255})\" in stream ([0-9]+) with ([0-9]+) partitions$") {
REGEX_PARAM(std::string, topic_name);
REGEX_PARAM(int, stream_id);
REGEX_PARAM(int, partitions_count);
cucumber::ScenarioScope<bdd::GlobalContext> context;
// Drive the SDK through the typed helpers in iggy.hpp rather than raw strings. Options that
// still take plain strings below (partitioning kind, consumer kind) mark where the C++ SDK
// does not yet expose a typed wrapper.
const auto compression = iggy::CompressionAlgorithm::none();
const auto message_expiry = iggy::Expiry::never_expire();
const auto max_topic_size = iggy::MaxTopicSize::server_default();
context->client->create_topic(bdd::make_numeric_identifier(static_cast<std::uint32_t>(stream_id)), topic_name,
static_cast<std::uint32_t>(partitions_count),
std::string(compression.compression_algorithm_value()), 0,
std::string(message_expiry.expiry_kind()), message_expiry.expiry_value(),
std::string(max_topic_size.max_topic_size()));
}
THEN("^the topic should be created successfully$") {
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto stream = context->client->get_stream(bdd::make_numeric_identifier(0));
EXPECT_FALSE(stream.topics.empty());
}
THEN("^the topic should have name \"([^\"]{1,255})\"$") {
REGEX_PARAM(std::string, name);
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto stream = context->client->get_stream(bdd::make_numeric_identifier(0));
ASSERT_FALSE(stream.topics.empty());
EXPECT_EQ(std::string(stream.topics[0].name), name);
}
THEN("^the topic should have ([0-9]+) partitions$") {
REGEX_PARAM(int, partitions_count);
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto stream = context->client->get_stream(bdd::make_numeric_identifier(0));
ASSERT_FALSE(stream.topics.empty());
EXPECT_EQ(stream.topics[0].partitions_count, static_cast<std::uint32_t>(partitions_count));
}
WHEN("^I send ([0-9]+) messages to stream ([0-9]+), topic ([0-9]+), partition ([0-9]+)$") {
REGEX_PARAM(int, message_count);
REGEX_PARAM(int, stream_id);
REGEX_PARAM(int, topic_id);
REGEX_PARAM(int, partition_id);
cucumber::ScenarioScope<bdd::GlobalContext> context;
rust::Vec<iggy::ffi::IggyMessageToSend> messages;
for (int index = 0; index < message_count; ++index) {
iggy::ffi::IggyMessageToSend message =
iggy::ffi::make_message(bdd::to_payload(bdd::expected_payload(static_cast<std::uint32_t>(index))));
// Assign an explicit, 1-based id so the last-sent/last-polled comparison is meaningful.
message.id_lo = static_cast<std::uint64_t>(index + 1);
messages.push_back(std::move(message));
}
context->client->send_messages(bdd::make_numeric_identifier(static_cast<std::uint32_t>(stream_id)),
bdd::make_numeric_identifier(static_cast<std::uint32_t>(topic_id)), "partition_id",
bdd::partition_id_bytes(static_cast<std::uint32_t>(partition_id)),
std::move(messages));
context->last_sent_payload = bdd::expected_payload(static_cast<std::uint32_t>(message_count - 1));
context->last_sent_id_lo = static_cast<std::uint64_t>(message_count);
}
THEN("^all messages should be sent successfully$") {
// send_messages() in the WHEN step throws on failure, so reaching this step means the batch
// was accepted; there is no client-side count to assert (the Rust suite is a no-op here too).
}
WHEN("^I poll messages from stream ([0-9]+), topic ([0-9]+), partition ([0-9]+) starting from offset ([0-9]+)$") {
REGEX_PARAM(int, stream_id);
REGEX_PARAM(int, topic_id);
REGEX_PARAM(int, partition_id);
REGEX_PARAM(int, offset);
cucumber::ScenarioScope<bdd::GlobalContext> context;
const auto polling_strategy = iggy::PollingStrategy::offset(static_cast<std::uint64_t>(offset));
const auto polled = context->client->poll_messages(
bdd::make_numeric_identifier(static_cast<std::uint32_t>(stream_id)),
bdd::make_numeric_identifier(static_cast<std::uint32_t>(topic_id)), static_cast<std::uint32_t>(partition_id),
"consumer", bdd::make_numeric_identifier(1), std::string(polling_strategy.polling_strategy_kind()),
polling_strategy.polling_strategy_value(), 100, false);
context->polled.count = polled.count;
context->polled.offsets.clear();
context->polled.id_los.clear();
context->polled.payloads.clear();
for (const auto &message : polled.messages) {
context->polled.offsets.push_back(message.offset);
context->polled.id_los.push_back(message.id_lo);
context->polled.payloads.push_back(bdd::payload_to_string(message.payload));
}
}
THEN("^I should receive ([0-9]+) messages$") {
REGEX_PARAM(int, expected_count);
cucumber::ScenarioScope<bdd::GlobalContext> context;
EXPECT_EQ(context->polled.count, static_cast<std::uint32_t>(expected_count));
EXPECT_EQ(context->polled.payloads.size(), static_cast<std::size_t>(expected_count));
}
THEN("^the messages should have sequential offsets from ([0-9]+) to ([0-9]+)$") {
REGEX_PARAM(int, from);
REGEX_PARAM(int, to);
cucumber::ScenarioScope<bdd::GlobalContext> context;
ASSERT_EQ(context->polled.offsets.size(), static_cast<std::size_t>(to - from + 1));
for (int offset = from; offset <= to; ++offset) {
EXPECT_EQ(context->polled.offsets[static_cast<std::size_t>(offset - from)], static_cast<std::uint64_t>(offset));
}
}
THEN("^each message should have the expected payload content$") {
cucumber::ScenarioScope<bdd::GlobalContext> context;
for (std::size_t index = 0; index < context->polled.payloads.size(); ++index) {
EXPECT_EQ(context->polled.payloads[index], bdd::expected_payload(static_cast<std::uint32_t>(index)));
}
}
THEN("^the last polled message should match the last sent message$") {
cucumber::ScenarioScope<bdd::GlobalContext> context;
ASSERT_FALSE(context->polled.payloads.empty());
EXPECT_EQ(context->polled.payloads.back(), context->last_sent_payload);
EXPECT_EQ(context->polled.id_los.back(), context->last_sent_id_lo);
}