You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by pi...@apache.org on 2025/07/08 19:59:04 UTC
(iggy) branch master updated: feat(csharp): add BDD tests (#1980)
This is an automated email from the ASF dual-hosted git repository.
piotr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new c0a871b1 feat(csharp): add BDD tests (#1980)
c0a871b1 is described below
commit c0a871b1bb8e998784b1dd601366dcfd49014d77
Author: Łukasz Zborek <lu...@gmail.com>
AuthorDate: Tue Jul 8 21:59:00 2025 +0200
feat(csharp): add BDD tests (#1980)
Added BDD tests for csharp SDK
---
.github/changed-files-config.json | 3 +-
.github/workflows/ci-check-csharp-sdk.yml | 4 +-
.github/workflows/ci-test-bdd.yml | 2 +-
bdd/README.md | 7 +
.../ci-test-bdd.yml => bdd/csharp/Dockerfile | 34 +-
bdd/docker-compose.yml | 10 +
foreign/csharp/.dockerignore | 400 +++++++++++++++++++++
foreign/csharp/DEPENDENCIES.md | 1 +
foreign/csharp/Directory.Packages.props | 43 +--
.../Iggy_SDK.Tests.BDD/Context/TestContext.cs | 32 ++
.../csharp/Iggy_SDK.Tests.BDD/Context/TestHooks.cs | 55 +++
.../Iggy_SDK.Tests.BDD/Iggy_SDK.Tests.BDD.csproj | 44 +++
foreign/csharp/Iggy_SDK.Tests.BDD/README.md | 21 ++
.../BasicMessagingOperationsSteps.cs | 224 ++++++++++++
foreign/csharp/Iggy_SDK.sln | 6 +
foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs | 3 +-
foreign/csharp/Iggy_SDK/Messages/Message.cs | 13 +
.../Iggy_SDK_Tests/MapperTests/BinaryMapper.cs | 1 +
scripts/run-bdd-tests.sh | 12 +-
19 files changed, 860 insertions(+), 55 deletions(-)
diff --git a/.github/changed-files-config.json b/.github/changed-files-config.json
index 3b429b74..a96ea4e2 100644
--- a/.github/changed-files-config.json
+++ b/.github/changed-files-config.json
@@ -73,6 +73,7 @@
"bdd/docker-compose.yml",
"scripts/run-bdd-tests.sh",
".github/workflows/ci-test-bdd.yml",
- "core/.*\\.rs"
+ "core/.*\\.rs",
+ "foreign/csharp/Iggy_SDK.Tests.BDD/.*\\.cs"
]
}
diff --git a/.github/workflows/ci-check-csharp-sdk.yml b/.github/workflows/ci-check-csharp-sdk.yml
index 5944b7a3..5cb85711 100644
--- a/.github/workflows/ci-check-csharp-sdk.yml
+++ b/.github/workflows/ci-check-csharp-sdk.yml
@@ -58,4 +58,6 @@ jobs:
run: dotnet build --no-restore
- name: Test
working-directory: foreign/csharp
- run: dotnet test --no-build --verbosity normal
+ run: |
+ dotnet test Iggy_SDK_Tests --no-build --verbosity normal
+ dotnet test Iggy_SDK.Tests.Integration --no-build --verbosity normal
diff --git a/.github/workflows/ci-test-bdd.yml b/.github/workflows/ci-test-bdd.yml
index 036f69c3..2f024d04 100644
--- a/.github/workflows/ci-test-bdd.yml
+++ b/.github/workflows/ci-test-bdd.yml
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- sdk: [rust, python, go, node]
+ sdk: [rust, python, go, node, csharp]
steps:
- name: Checkout code
uses: actions/checkout@v4
diff --git a/bdd/README.md b/bdd/README.md
index c96156e1..4e065c53 100644
--- a/bdd/README.md
+++ b/bdd/README.md
@@ -18,6 +18,8 @@ bdd/
│ └── requirements.txt
├── node/ # Node SDK BDD implementation
│ └── Dockerfile # Node BDD test container
+├── csharp/ # csharp SDK BDD implementation
+│ └── Dockerfile # csharp BDD test container
├── docker-compose.yml # Orchestrates server + SDK containers
├── Dockerfile # Debug build of Iggy server
└── README.md
@@ -36,6 +38,7 @@ bdd/
../scripts/run-bdd-tests.sh python
../scripts/run-bdd-tests.sh go
../scripts/run-bdd-tests.sh node
+../scripts/run-bdd-tests.sh csharp
# Clean up Docker resources
../scripts/run-bdd-tests.sh clean
@@ -82,6 +85,10 @@ The Python implementation is in `bdd/python/tests/` and needs to be updated as t
The node.js BDD test are run by cucumber-js, bdd test code is located at [foreign/node/src/bdd](../foreign/node/src/bdd/)
+### For csharp SDK
+
+The csharp implementation is located at [foreign/csharp/Iggy_SDK.Tests.BDD](../foreign/csharp/Iggy_SDK.Tests.BDD)
+
### Adding New Scenarios
Add new `.feature` files to the `bdd/scenarios/` directory and implement the corresponding step definitions in each SDK's test directory.
diff --git a/.github/workflows/ci-test-bdd.yml b/bdd/csharp/Dockerfile
similarity index 51%
copy from .github/workflows/ci-test-bdd.yml
copy to bdd/csharp/Dockerfile
index 036f69c3..8b771805 100644
--- a/.github/workflows/ci-test-bdd.yml
+++ b/bdd/csharp/Dockerfile
@@ -15,34 +15,12 @@
# specific language governing permissions and limitations
# under the License.
-name: Cross-SDK BDD Tests
+FROM mcr.microsoft.com/dotnet/sdk:8.0
-on:
- workflow_call:
+COPY ./foreign/csharp ./foreign/csharp
+COPY ./bdd/scenarios ./bdd/scenarios
-jobs:
- bdd-tests:
- name: BDD tests (${{ matrix.sdk }})
- runs-on: ubuntu-latest
- strategy:
- matrix:
- sdk: [rust, python, go, node]
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Cache Rust dependencies
- uses: Swatinem/rust-cache@v2
- with:
- workspaces: |
- . -> target
- foreign/python -> target
- cache-on-failure: true
- key: bdd-${{ matrix.sdk }}
+WORKDIR /foreign/csharp/Iggy_SDK.Tests.BDD
+RUN dotnet build -c Release
- - name: Run BDD tests for ${{ matrix.sdk }}
- run: ./scripts/run-bdd-tests.sh ${{ matrix.sdk }}
-
- - name: Clean up Docker resources
- if: always()
- run: ./scripts/run-bdd-tests.sh clean
+CMD ["dotnet", "test"]
diff --git a/bdd/docker-compose.yml b/bdd/docker-compose.yml
index a52f1e19..1fc1b488 100644
--- a/bdd/docker-compose.yml
+++ b/bdd/docker-compose.yml
@@ -84,6 +84,16 @@ services:
- IGGY_TCP_ADDRESS=iggy-server:8090
command: ["npm", "run", "test:bdd"]
+ csharp-bdd:
+ build:
+ context: ..
+ dockerfile: bdd/csharp/Dockerfile
+ depends_on:
+ iggy-server:
+ condition: service_healthy
+ environment:
+ - IGGY_TCP_ADDRESS=iggy-server:8090
+ command: ["dotnet", "test"]
volumes:
iggy_data:
diff --git a/foreign/csharp/.dockerignore b/foreign/csharp/.dockerignore
new file mode 100644
index 00000000..931afb93
--- /dev/null
+++ b/foreign/csharp/.dockerignore
@@ -0,0 +1,400 @@
+### Csharp template
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+
diff --git a/foreign/csharp/DEPENDENCIES.md b/foreign/csharp/DEPENDENCIES.md
index c311316d..6419947d 100644
--- a/foreign/csharp/DEPENDENCIES.md
+++ b/foreign/csharp/DEPENDENCIES.md
@@ -8,6 +8,7 @@ Microsoft.NET.Test.Sdk: "17.14.1", "MIT",
Microsoft.Testing.Extensions.CodeCoverage: "17.14.2", "MIT",
Microsoft.Testing.Extensions.TrxReport: "1.7.3", "MIT",
Moq: "4.20.72", "BSD-3-Clause",
+Reqnroll.xUnit: "2.4.1", "BSD-3-Clause",
Shouldly: "4.3.0", "BSD-3-Clause",
System.IO.Hashing: "8.0.0", "MIT",
Testcontainers: "4.6.0", "MIT",
diff --git a/foreign/csharp/Directory.Packages.props b/foreign/csharp/Directory.Packages.props
index 02c84e79..941c89d2 100644
--- a/foreign/csharp/Directory.Packages.props
+++ b/foreign/csharp/Directory.Packages.props
@@ -1,22 +1,23 @@
<Project>
- <PropertyGroup>
- <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
- <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
- </PropertyGroup>
- <ItemGroup>
- <PackageVersion Include="coverlet.collector" Version="6.0.4" />
- <PackageVersion Include="FluentAssertions" Version="6.12.0" />
- <PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
- <PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
- <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
- <PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.14.2" />
- <PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="1.7.3" />
- <PackageVersion Include="Moq" Version="4.20.72" />
- <PackageVersion Include="Shouldly" Version="4.3.0" />
- <PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
- <PackageVersion Include="Testcontainers" Version="4.6.0" />
- <PackageVersion Include="TUnit" Version="0.25.21" />
- <PackageVersion Include="xunit" Version="2.9.3" />
- <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" />
- </ItemGroup>
-</Project>
+ <PropertyGroup>
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
+ <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageVersion Include="coverlet.collector" Version="6.0.4" />
+ <PackageVersion Include="FluentAssertions" Version="6.12.0" />
+ <PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
+ <PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
+ <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
+ <PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.14.2" />
+ <PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="1.7.3" />
+ <PackageVersion Include="Moq" Version="4.20.72" />
+ <PackageVersion Include="Reqnroll.xUnit" Version="2.4.1" />
+ <PackageVersion Include="Shouldly" Version="4.3.0" />
+ <PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
+ <PackageVersion Include="Testcontainers" Version="4.6.0" />
+ <PackageVersion Include="TUnit" Version="0.25.21" />
+ <PackageVersion Include="xunit" Version="2.9.3" />
+ <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" />
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestContext.cs b/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestContext.cs
new file mode 100644
index 00000000..aa7dc5dc
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestContext.cs
@@ -0,0 +1,32 @@
+// // 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.
+
+using Apache.Iggy.Contracts.Http;
+using Apache.Iggy.IggyClient;
+using Apache.Iggy.Messages;
+
+namespace Apache.Iggy.Tests.BDD.Context;
+
+public class TestContext
+{
+ public IIggyClient IggyClient { get; set; } = null!;
+ public string TcpUrl { get; set; } = string.Empty;
+ public StreamResponse? CreatedStream { get; set; }
+ public TopicResponse? CreatedTopic { get; set; }
+ public List<MessageResponse> PolledMessages { get; set; } = new();
+ public Message? LastSendMessage { get; set; }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestHooks.cs b/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestHooks.cs
new file mode 100644
index 00000000..954dbe62
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.BDD/Context/TestHooks.cs
@@ -0,0 +1,55 @@
+// // 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.
+
+using Reqnroll;
+
+namespace Apache.Iggy.Tests.BDD.Context;
+
+[Binding]
+public class TestHooks
+{
+ private readonly TestContext _context;
+
+ public TestHooks(TestContext context)
+ {
+ _context = context;
+ }
+
+ [BeforeScenario]
+ public void BeforeScenario()
+ {
+ _context.TcpUrl = Environment.GetEnvironmentVariable("IGGY_TCP_ADDRESS") ?? "127.0.0.1:8090";
+ }
+
+ [AfterScenario]
+ public void AfterScenario()
+ {
+ //
+ }
+
+ [BeforeFeature]
+ public static void BeforeFeature()
+ {
+ //
+ }
+
+ [AfterFeature]
+ public static void AfterFeature()
+ {
+ //
+ }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.BDD/Iggy_SDK.Tests.BDD.csproj b/foreign/csharp/Iggy_SDK.Tests.BDD/Iggy_SDK.Tests.BDD.csproj
new file mode 100644
index 00000000..09dbafd8
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.BDD/Iggy_SDK.Tests.BDD.csproj
@@ -0,0 +1,44 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net8.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <IsPackable>false</IsPackable>
+ <AssemblyName>Apache.Iggy.Tests.BDD</AssemblyName>
+ <RootNamespace>Apache.Iggy.Tests.BDD</RootNamespace>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <Description>Behavior-Driven Development (BDD) tests in csharp SDK for Apache Iggy</Description>
+ <Authors>Apache Iggy</Authors>
+ <RepositoryUrl>https://github.com/apache/iggy</RepositoryUrl>
+ <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
+ <PackageReadmeFile>README.md</PackageReadmeFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="coverlet.collector" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" />
+ <PackageReference Include="Reqnroll.xUnit" />
+ <PackageReference Include="Shouldly" />
+ <PackageReference Include="xunit" />
+ <PackageReference Include="xunit.runner.visualstudio" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Using Include="Xunit" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="..\..\..\bdd\scenarios\basic_messaging.feature">
+ <Link>Features\basic_messaging.feature</Link>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Iggy_SDK\Iggy_SDK.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/foreign/csharp/Iggy_SDK.Tests.BDD/README.md b/foreign/csharp/Iggy_SDK.Tests.BDD/README.md
new file mode 100644
index 00000000..a92723b2
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.BDD/README.md
@@ -0,0 +1,21 @@
+# csharp bdd test
+
+Scenario are located at [/bdd/scenarios](../../../bdd/scenarios)
+
+## env var
+
+use env var `IGGY_TCP_ADDRESS="host:port"` to set expected server address for bdd test suite.
+
+## Run via docker
+
+see [/bdd/README.md](../../../bdd/README.md)
+
+## Run locally
+
+note: bdd test expect an iggy-server at tcp://127.0.0.1:8090
+
+from [/foreign/csharp/Iggy_SDK.Tests.BDD](.) run
+
+```bash
+dotnet test
+```
diff --git a/foreign/csharp/Iggy_SDK.Tests.BDD/StepDefinitions/BasicMessagingOperationsSteps.cs b/foreign/csharp/Iggy_SDK.Tests.BDD/StepDefinitions/BasicMessagingOperationsSteps.cs
new file mode 100644
index 00000000..f0d2ba5c
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.BDD/StepDefinitions/BasicMessagingOperationsSteps.cs
@@ -0,0 +1,224 @@
+// // 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.
+
+using System.Text;
+using Apache.Iggy.Contracts.Http;
+using Apache.Iggy.Contracts.Http.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Factory;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.BDD.Context;
+using Microsoft.Extensions.Logging.Abstractions;
+using Reqnroll;
+using Shouldly;
+using Partitioning = Apache.Iggy.Kinds.Partitioning;
+
+namespace Apache.Iggy.Tests.BDD.StepDefinitions;
+
+[Binding]
+public class BasicMessagingOperationsSteps
+{
+ private readonly TestContext _context;
+
+ public BasicMessagingOperationsSteps(TestContext context)
+ {
+ _context = context;
+ }
+
+ [Given(@"I have a running Iggy server")]
+ public async Task GivenIHaveARunningIggyServer()
+ {
+ _context.IggyClient = MessageStreamFactory.CreateMessageStream(configurator =>
+ {
+ configurator.BaseAdress = _context.TcpUrl;
+ configurator.Protocol = Protocol.Tcp;
+ configurator.MessageBatchingSettings = settings =>
+ {
+ settings.Enabled = false;
+ };
+ }, NullLoggerFactory.Instance);
+
+ await _context.IggyClient.PingAsync();
+ }
+
+ [Given(@"I am authenticated as the root user")]
+ public async Task GivenIAmAuthenticatedAsTheRootUser()
+ {
+ var loginResult = await _context.IggyClient.LoginUser(new LoginUserRequest()
+ {
+ Username = "iggy",
+ Password = "iggy"
+ });
+
+ loginResult.ShouldNotBeNull();
+ loginResult.UserId.ShouldBe(1);
+ }
+
+ [Given(@"I have no streams in the system")]
+ public async Task GivenIHaveNoStreamsInTheSystem()
+ {
+ var streams = await _context.IggyClient.GetStreamsAsync();
+
+ streams.ShouldNotBeNull();
+ streams.Count.ShouldBe(0);
+ }
+
+ [When(@"I create a stream with ID (\d+) and name (.*)")]
+ public async Task WhenICreateAStreamWithIdAndName(int streamId, string streamName)
+ {
+ _context.CreatedStream = await _context.IggyClient.CreateStreamAsync(new StreamRequest()
+ {
+ Name = streamName,
+ StreamId = streamId
+ });
+ }
+
+ [Then(@"the stream should be created successfully")]
+ public void ThenTheStreamShouldBeCreatedSuccessfully()
+ {
+ _context.CreatedStream.ShouldNotBeNull();
+ _context.CreatedStream.Id.ShouldNotBe(0);
+ _context.CreatedStream.Name.ShouldNotBeNullOrEmpty();
+ }
+
+ [Then(@"the stream should have ID (\d+) and name (.*)")]
+ public void ThenTheStreamShouldHaveIdAndName(int expectedId, string expectedName)
+ {
+ _context.CreatedStream!.Id.ShouldBe(expectedId);
+ _context.CreatedStream!.Name.ShouldBe(expectedName);
+ }
+
+ [When(@"I create a topic with ID (\d+) and name (.*) in stream (\d+) with (\d+) partitions")]
+ public async Task WhenICreateATopicWithIdAndNameInStreamWithPartitions(int topicId, string topicName, int streamId, int partitions)
+ {
+ _context.CreatedTopic = await _context.IggyClient.CreateTopicAsync(Identifier.Numeric(streamId), new TopicRequest()
+ {
+ TopicId = topicId,
+ Name = topicName,
+ PartitionsCount = partitions
+ });
+ }
+
+ [Then(@"the topic should be created successfully")]
+ public void ThenTheTopicShouldBeCreatedSuccessfully()
+ {
+ _context.CreatedTopic.ShouldNotBeNull();
+ _context.CreatedTopic!.Id.ShouldNotBe(0);
+ _context.CreatedTopic.Name.ShouldNotBeNullOrEmpty();
+ }
+
+ [Then(@"the topic should have ID (\d+) and name (.*)")]
+ public void ThenTheTopicShouldHaveIdAndName(int expectedId, string expectedName)
+ {
+ _context.CreatedTopic!.Id.ShouldBe(expectedId);
+ _context.CreatedTopic!.Name.ShouldBe(expectedName);
+
+ }
+
+ [Then(@"the topic should have (.*) partitions")]
+ public void ThenTheTopicShouldHavePartitions(int expectedPartitions)
+ {
+ _context.CreatedTopic!.Partitions!.Count().ShouldBe(expectedPartitions);
+ _context.CreatedTopic.PartitionsCount.ShouldBe(expectedPartitions);
+ }
+
+ [When(@"I send (\d+) messages to stream (\d+), topic (\d+), partition (\d+)")]
+ public async Task WhenISendMessagesToStreamTopicPartition(int messageCount, int streamId, int topicId, int partitionId)
+ {
+ var messages = Enumerable.Range(1, messageCount)
+ .Select(i => new Message(1, Encoding.UTF8.GetBytes($"Test message {i}")))
+ .ToList();
+
+ await _context.IggyClient.SendMessagesAsync(new MessageSendRequest()
+ {
+ StreamId = Identifier.Numeric(streamId),
+ TopicId = Identifier.Numeric(topicId),
+ Partitioning = Partitioning.PartitionId(partitionId),
+ Messages = messages
+ });
+
+ _context.LastSendMessage = messages.Last();
+ }
+
+ [Then(@"all messages should be sent successfully")]
+ public void ThenAllMessagesShouldBeSentSuccessfully()
+ {
+ _context.LastSendMessage.ShouldNotBeNull();
+ }
+
+ [When(@"I poll messages from stream (\d+), topic (\d+), partition (\d+) starting from offset (\d+)")]
+ public async Task WhenIPollMessagesFromStreamTopicPartitionStartingFromOffset(int streamId, int topicId, int partitionId, ulong startOffset)
+ {
+ var messages = await _context.IggyClient.FetchMessagesAsync(new MessageFetchRequest()
+ {
+ StreamId = Identifier.Numeric(streamId),
+ TopicId = Identifier.Numeric(topicId),
+ PartitionId = partitionId,
+ PollingStrategy = PollingStrategy.Offset(startOffset),
+ Consumer = Consumer.New(1),
+ Count = 100,
+ AutoCommit = false
+ });
+
+ _context.PolledMessages = messages.Messages.ToList();
+ }
+
+ [Then(@"I should receive (.*) messages")]
+ public void ThenIShouldReceiveMessages(int expectedCount)
+ {
+ _context.PolledMessages.Count.ShouldBe(expectedCount);
+ }
+
+ [Then(@"the messages should have sequential offsets from (\d+) to (\d+)")]
+ public void ThenTheMessagesShouldHaveSequentialOffsetsFromTo(int startOffset, int endOffset)
+ {
+ for (int i = startOffset; i < endOffset; i++)
+ {
+ _context.PolledMessages[i].Header.Offset.ShouldBe((ulong)i);
+
+ }
+ }
+
+ [Then(@"each message should have the expected payload content")]
+ public void ThenEachMessageShouldHaveTheExpectedPayloadContent()
+ {
+ for (int i = 0; i < _context.PolledMessages.Count; i++)
+ {
+ var message = _context.PolledMessages[i];
+ message.Payload.ShouldNotBeNull();
+ message.Payload.Length.ShouldBeGreaterThan(0);
+
+ var payloadText = Encoding.UTF8.GetString(message.Payload);
+ payloadText.ShouldBe($"Test message {i + 1}");
+ }
+ }
+
+ [Then(@"the last polled message should match the last sent message")]
+ public void ThenTheLastPolledMessageShouldMatchTheLastSentMessage()
+ {
+ var lastPolled = _context.PolledMessages.LastOrDefault();
+
+ lastPolled.ShouldNotBeNull();
+ _context.LastSendMessage.ShouldNotBeNull();
+
+ lastPolled.Header.Id.ShouldBe(_context.LastSendMessage.Value.Header.Id);
+ lastPolled.Payload.ShouldBe(_context.LastSendMessage.Value.Payload);
+ }
+}
+
+// Test context for sharing data between steps
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.sln b/foreign/csharp/Iggy_SDK.sln
index 536284bf..bd04f22c 100644
--- a/foreign/csharp/Iggy_SDK.sln
+++ b/foreign/csharp/Iggy_SDK.sln
@@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionF
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iggy_SDK.Tests.BDD", "Iggy_SDK.Tests.BDD\Iggy_SDK.Tests.BDD.csproj", "{91BFFAE1-1252-4061-BF40-C7E7296F82BC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -57,5 +59,9 @@ Global
{59661E1B-1D5E-4D58-AE7B-10E8ABB9839A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59661E1B-1D5E-4D58-AE7B-10E8ABB9839A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59661E1B-1D5E-4D58-AE7B-10E8ABB9839A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {91BFFAE1-1252-4061-BF40-C7E7296F82BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {91BFFAE1-1252-4061-BF40-C7E7296F82BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {91BFFAE1-1252-4061-BF40-C7E7296F82BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {91BFFAE1-1252-4061-BF40-C7E7296F82BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs b/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
index 4dc89310..c7bb4d26 100644
--- a/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
+++ b/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
@@ -26,10 +26,11 @@ using Apache.Iggy.Headers;
using Apache.Iggy.Messages;
namespace Apache.Iggy.Mappers;
-//TODO - write unit tests for all the users related mappers
+
internal static class BinaryMapper
{
private const int PROPERTIES_SIZE = 56;
+
internal static RawPersonalAccessToken MapRawPersonalAccessToken(ReadOnlySpan<byte> payload)
{
var tokenLength = payload[0];
diff --git a/foreign/csharp/Iggy_SDK/Messages/Message.cs b/foreign/csharp/Iggy_SDK/Messages/Message.cs
index 0052b21f..42fd332e 100644
--- a/foreign/csharp/Iggy_SDK/Messages/Message.cs
+++ b/foreign/csharp/Iggy_SDK/Messages/Message.cs
@@ -45,6 +45,19 @@ public readonly struct Message
UserHeaders = userHeaders;
}
+ [System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute]
+ public Message(UInt128 id, byte[] payload, Dictionary<HeaderKey, HeaderValue>? userHeaders = null)
+ {
+ Header = new MessageHeader()
+ {
+ PayloadLength = payload.Length,
+ Id = id,
+ Checksum = CalculateChecksum(payload)
+ };
+ Payload = payload;
+ UserHeaders = userHeaders;
+ }
+
public int GetSize()
{
//return 56 + Payload.Length + (UserHeaders?.Count ?? 0);
diff --git a/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs b/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
index 77801907..6a3f7eee 100644
--- a/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
+++ b/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
@@ -49,6 +49,7 @@ public sealed class BinaryMapper
Assert.Equal(name, response[0].Name);
Assert.Equal(assertExpiry, response[0].ExpiryAt);
}
+
[Fact]
public void MapOffsets_ReturnsValidOffsetResponse()
{
diff --git a/scripts/run-bdd-tests.sh b/scripts/run-bdd-tests.sh
index 12cdb68d..fe209726 100755
--- a/scripts/run-bdd-tests.sh
+++ b/scripts/run-bdd-tests.sh
@@ -48,17 +48,24 @@ case $SDK in
docker compose build --no-cache iggy-server node-bdd
docker compose up --abort-on-container-exit node-bdd
;;
+"csharp")
+ echo "🔷 Running csharp BDD tests..."
+ docker compose build --no-cache iggy-server csharp-bdd
+ docker compose up --abort-on-container-exit csharp-bdd
+ ;;
"all")
echo "🚀 Running all SDK BDD tests..."
echo "🦀 Starting with Rust tests..."
- docker compose build --no-cache iggy-server rust-bdd python-bdd go-bdd
+ #docker compose build --no-cache iggy-server rust-bdd python-bdd go-bdd node-bdd csharp-bdd
docker compose up --abort-on-container-exit rust-bdd
echo "🐍 Now running Python tests..."
docker compose up --abort-on-container-exit python-bdd
echo "🐹 Now running Go tests..."
docker compose up --abort-on-container-exit go-bdd
- echo "🐢🚀 Now unning node BDD tests..."
+ echo "🐢🚀 Now running node BDD tests..."
docker compose up --abort-on-container-exit node-bdd
+ echo "🔷 Now running csharp BDD tests..."
+ docker compose up --abort-on-container-exit csharp-bdd
;;
"clean")
echo "🧹 Cleaning up Docker resources..."
@@ -73,6 +80,7 @@ case $SDK in
echo " $0 python # Run Python tests only"
echo " $0 go # Run Go tests only"
echo " $0 node # Run Node.js tests only"
+ echo " $0 csharp # Run csharp tests only"
echo " $0 all # Run all SDK tests"
echo " $0 clean # Clean up Docker resources"
exit 1