test(bdd): add C++ SDK BDD tests for basic messaging#3554
Conversation
|
@seokjin0414 the test should be in he root bdd/ folder, not inside of foreign/cpp |
9d6f66a to
d670d13
Compare
|
ignore the above, i didn't read the description😅 |
d670d13 to
dfc8f20
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3554 +/- ##
============================================
- Coverage 74.10% 74.08% -0.03%
Complexity 937 937
============================================
Files 1258 1258
Lines 131485 131485
Branches 107354 107398 +44
============================================
- Hits 97435 97405 -30
+ Misses 30963 30941 -22
- Partials 3087 3139 +52
🚀 New features to boost your workflow:
|
dfc8f20 to
0e70305
Compare
|
@slbotbm |
|
@seokjin0414 I didn't understand why did you use cmake instead of bazel. I think it would be possible to use bazel in bdd/cpp. you could define |
|
/author |
0e70305 to
533c9b0
Compare
|
@slbotbm the cmake route was just to keep the test self-contained in bdd/cpp without touching foreign/cpp — i didn't realize local_path_override could pull the sdk in cleanly. it can, so i switched to bazel as you suggested: bdd/cpp is its own module now, cucumber-cpp from the registry + iggy_cpp via local_path_override(../../foreign/cpp). cmake is gone. the only change to foreign/cpp is one line — making the iggy-cpp target public so the harness can depend on it. build + the full wire e2e (17 steps) pass. |
|
Let's also commit the ruby lockfile as well. |
f168a4d to
023d49d
Compare
|
@slbotbm |
|
/ready |
023d49d to
61e1c76
Compare
|
/ready |
slbotbm
left a comment
There was a problem hiding this comment.
Some comments, otherwise looks lgtm
|
|
||
| WORKDIR /workspace/bdd/cpp | ||
| COPY bdd/cpp/Gemfile bdd/cpp/Gemfile.lock ./ | ||
| RUN gem install bundler --no-document \ |
There was a problem hiding this comment.
Let's pin bundler to a specific version
There was a problem hiding this comment.
pinned to 2.6.9 (matches BUNDLED WITH in Gemfile.lock).
|
|
||
| #include "world.hpp" | ||
|
|
||
| using cucumber::ScenarioScope; |
There was a problem hiding this comment.
Let's remove this line and explicitly use cucumber::ScenarioScope whever it is called.
There was a problem hiding this comment.
dropped the using alias in both step files; every call site now spells out cucumber::ScenarioScope.
| EXPECT_EQ(streams.size(), static_cast<std::size_t>(0)); | ||
| } | ||
|
|
||
| WHEN("^I create a stream with name \"(.*)\"$") { |
There was a problem hiding this comment.
Let's change the regex from (.*) to ([^"]{1,255}) to reflect actual stream name restrictions
There was a problem hiding this comment.
tightened the name captures to ([^"]{1,255}) for both stream and topic (create + should-have-name steps).
| WHEN("^I create a topic with name \"(.*)\" in stream ([0-9]+) with ([0-9]+) partitions$") { | ||
| REGEX_PARAM(std::string, topic_name); | ||
| REGEX_PARAM(int, stream_id); | ||
| REGEX_PARAM(int, partitions_count); | ||
| ScenarioScope<bdd::GlobalContext> context; | ||
|
|
||
| context->client->create_topic(bdd::make_numeric_identifier(static_cast<std::uint32_t>(stream_id)), topic_name, | ||
| static_cast<std::uint32_t>(partitions_count), "none", 0, "never_expire", 0, | ||
| "server_default"); |
There was a problem hiding this comment.
Instead of passing strings like "server_default" and "never_expire" here, I think we can use the functions and classes in iggy.hpp
There was a problem hiding this comment.
can also be done for the rest of the functions. This will help us understand what the sdk is not providing right now.
There was a problem hiding this comment.
switched create_topic to CompressionAlgorithm::none() / Expiry::never_expire() / MaxTopicSize::server_default(), and poll to PollingStrategy::offset(). the consumer kind and partitioning are still raw strings — iggy.hpp has no typed wrapper for those yet, so they mark exactly the gaps you mentioned.
| THEN("^all messages should be sent successfully$") { | ||
| ScenarioScope<bdd::GlobalContext> context; | ||
|
|
||
| EXPECT_GT(context->sent_count, static_cast<std::uint32_t>(0)); |
There was a problem hiding this comment.
can we tighten this to expect the exact message count?
There was a problem hiding this comment.
now asserts the exact count: the send step records how many messages it dispatched and this step checks sent == requested instead of just > 0.
The oversized-payload test grew its 64 MB buffer one byte at a time and a comment claimed cxx::Vec had no public reserve API. It does, so reserve the capacity once before filling it. Signed-off-by: seokjin0414 <sars21@hanmail.net>
The C++ SDK had no BDD coverage. Add cucumber-cpp step definitions under bdd/cpp that drive the shared basic_messaging.feature through the C++ FFI client. cucumber-cpp uses the wire protocol, so bdd/cpp is a self-contained Bazel module: it builds the step definitions into a wire server (pulling cucumber-cpp from the Bazel registry and the Iggy C++ SDK from foreign/cpp via local_path_override) that a Ruby Cucumber runner connects to. The SDK's iggy-cpp target is made public so the harness can depend on it. A per-file CUKE_OBJECT_PREFIX keeps the generated step classes from clashing across translation units. The Gemfile and cucumber.wire comment styles are registered with the license checker. Signed-off-by: seokjin0414 <sars21@hanmail.net>
A Ruby cucumber runner with cucumber-wire drives the wire server over the wire protocol against a real iggy-server. Register the cpp-bdd compose service, the run-bdd-tests entry, and the bdd-cpp CI component so it runs like the other SDK BDD suites. Signed-off-by: seokjin0414 <sars21@hanmail.net>
61e1c76 to
78c3ba4
Compare
|
/ready |
| cd /workspace/bdd/cpp || exit 1 | ||
|
|
||
| set +e | ||
| bundle exec cucumber |
There was a problem hiding this comment.
cucumber 7.1.0 defaults to non-strict, so an undefined step exits 0 - if the shared feature file ever gains a step without a matching C++ regex, this suite silently goes green. bundle exec cucumber --strict closes that.
fwiw rust (cucumber-rs reports an unmatched step as skipped -> exit 0) and go (godog, non-strict by default) have the same hole and would also pass silently - node/java/python/csharp runners are strict by default. so worth adding --strict here, and probably to rust/go too (separate PRs).
separately, the image bakes no .feature files and the copy on line 24 is masked with 2>/dev/null || true, so a missing mount means 0 scenarios and exit 0 even with --strict. worth a guard like ls features/*.feature >/dev/null || exit 1 before running. both fixes needed, they cover different holes.
|
|
||
| bdd_wire_server & | ||
| server_pid=$! | ||
| sleep 2 |
There was a problem hiding this comment.
fixed sleep 2 instead of waiting for port 3902. fails safe (connection refused = red), so worst case is a flake on a slow machine - a small wait-for-port loop would remove that.
| THEN("^all messages should be sent successfully$") { | ||
| cucumber::ScenarioScope<bdd::GlobalContext> context; | ||
|
|
||
| EXPECT_EQ(context->sent_count, context->messages_to_send); |
There was a problem hiding this comment.
this assert can never fail - sent_count and messages_to_send are both derived from the same message_count in the WHEN step. the real signal is send_messages() throwing there. rust and python versions of this step are equally weak, so fine to keep for parity, but the sent_count/messages_to_send bookkeeping could be dropped.
| // (rust::Vec) across steps. | ||
| struct PolledData { | ||
| std::uint32_t count = 0; | ||
| std::uint64_t current_offset = 0; |
There was a problem hiding this comment.
current_offset is write-only - set in the poll step (messaging_steps.cpp:160) but never read by any THEN. field and assignment can go.
| ffi (~> 1.1) | ||
| memoist3 (~> 1.0.0) | ||
|
|
||
| PLATFORMS |
There was a problem hiding this comment.
PLATFORMS is missing x86_64-linux (the CI runner arch), so in the image ffi installs via the generic ruby platform and compiles from source. checked empirically with the pinned bundler 2.6.9: bundle install leaves the lock byte-identical (generic ruby entry already satisfies x86_64-linux, nothing gets appended), and a frozen install succeeds against the current lock as-is - so no mutation, no correctness issue. the only gain from bundle lock --add-platform x86_64-linux + a frozen install is turning future Gemfile/lock drift into a hard error instead of a silent re-resolve. minor, optional.
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| common --enable_bzlmod |
There was a problem hiding this comment.
the committed MODULE.bazel.lock is never enforced - bazel 9 defaults to lockfile_mode=update, so it silently drifts. foreign/cpp/.bazelrc sets --lockfile_mode=error under its ci config and CI builds with --config=ci; same setup here would keep the lock honest.
@slbotbm please confirm
There was a problem hiding this comment.
we can add the required config here as well then
|
|
||
| # Stage 2: install the Ruby Cucumber runner. build-essential is needed to compile the ffi | ||
| # native extension and never reaches the runtime image. | ||
| FROM ruby:3.3-slim-trixie AS deps |
There was a problem hiding this comment.
heads up: the ruby 3.3 pin is load-bearing - cucumber 7.1.0 crashes on ruby 3.4 (unknown keywords: :strict, :proc from the Hash.new keyword change). worth a comment here so a future base-image bump doesn't trip on it.
NOTE: i didnt verify it personally
What
Adds BDD coverage for the C++ SDK by implementing the shared
bdd/scenarios/basic_messaging.featurewith cucumber-cpp and wiring it into theDocker-based BDD harness and CI, the same way the other SDKs run. This is the
second part of #2965; the messaging FFI it relies on landed in #3046.
How it works
cucumber-cpp only supports the Cucumber wire protocol, so the step definitions
(
bdd/cpp/features/step_definitions) build with CMake into a wire server, and aRuby Cucumber runner (
cucumber+cucumber-wire, pinned to the versionscucumber-cpp itself tests against) drives the feature file over the wire against a
real
iggy-server. The step logic and assertions are all C++ and exercise theSDK's FFI (
get_streams,create_stream,create_topic,send_messages,poll_messages). The Iggy C++ SDK is built with cargo and linked into the wireserver as a staticlib.
Heads-up for reviewers
Because cucumber-cpp is wire-protocol-only, the C++ BDD image pulls in a Ruby
runtime (
cucumber+cucumber-wire). Flagging this explicitly in case itchanges how you'd like the harness shaped.
Notes
Everything lives under
bdd/cpp, next to the other SDK BDD suites;foreign/cppis unchanged apart from the reserve() fix to the existing test.
The TearDown fixture and the magic-number cleanup discussed on Discord are tracked
separately by @slbotbm.
Refs #2965