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/06/26 06:29:40 UTC

(iggy) branch master updated: chore(csharp): update integration tests (#1913)

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 99a21a18 chore(csharp): update integration tests (#1913)
99a21a18 is described below

commit 99a21a18738badeb9cd897897a15454404fa40a7
Author: Łukasz Zborek <lu...@gmail.com>
AuthorDate: Thu Jun 26 08:29:36 2025 +0200

    chore(csharp): update integration tests (#1913)
    
    - change integration tests to TUnit for better visibility of tests and a
    better debugging experience (mostly for "DependsOn" attribute)
    - change package references to central package management
---
 .gitignore                                         |   1 +
 foreign/csharp/.editorconfig                       | 153 ++++++++++++
 foreign/csharp/Benchmarks/Benchmarks.csproj        |   3 +-
 foreign/csharp/Benchmarks/Program.cs               |  14 +-
 foreign/csharp/DEPENDENCIES.md                     |  16 +-
 foreign/csharp/Directory.Packages.props            |  22 ++
 .../Attributes/SkipHttpAttribute.cs                |  36 +++
 .../Attributes/SkipTcpAttribute.cs                 |  36 +++
 .../ConsumerGroupTests.cs                          | 225 +++++++++++++++++
 .../FetchMessagesTests.cs                          | 185 ++++++++++++++
 .../Fixtures/ConsumerGroupFixture.cs               |  38 +++
 .../Fixtures/FetchMessagesFixture.cs               | 148 ++++++++++++
 .../Fixtures/FlushMessageFixture.cs                |  54 +++++
 .../Fixtures/IggyServerFixture.cs                  | 145 +++++++++++
 .../Fixtures/OffsetFixtures.cs                     |  54 +++++
 .../Fixtures/PartitionsFixture.cs                  |  38 +++
 .../Fixtures/PollMessagesFixture.cs                |  70 ++++++
 .../Fixtures/SendMessageFixture.cs                 |  38 +++
 .../Fixtures/SystemFixture.cs                      |  89 +++++++
 .../FlushMessagesTests.cs                          |  69 ++++++
 .../Helpers/StreamFactory.cs                       |  32 +++
 .../Helpers/TopicFactory.cs                        |  34 +++
 .../Iggy_SDK.Tests.Integration.csproj              |  29 +++
 .../Models/DummyMessage.cs                         |  45 ++++
 .../Iggy_SDK.Tests.Integration/OffsetTests.cs      | 104 ++++++++
 .../Iggy_SDK.Tests.Integration/PartitionsTests.cs  |  88 +++++++
 .../PersonalAccessTokenTests.cs                    |  98 ++++++++
 .../PollMessagesTests.cs                           |  60 +++++
 .../SendMessagesTests.cs                           | 118 +++++++++
 .../Iggy_SDK.Tests.Integration/StreamsTests.cs     | 209 ++++++++++++++++
 .../Iggy_SDK.Tests.Integration/SystemTests.cs      | 195 +++++++++++++++
 .../Iggy_SDK.Tests.Integration/TopicsTests.cs      | 243 +++++++++++++++++++
 .../Iggy_SDK.Tests.Integration/UsersTests.cs       | 266 +++++++++++++++++++++
 foreign/csharp/Iggy_SDK.sln                        |  15 ++
 foreign/csharp/Iggy_SDK.sln.DotSettings            |  23 +-
 .../Iggy_SDK/Configuration/TlsConfiguration.cs     |   4 +-
 .../Iggy_SDK/Contracts/Http/Auth/AuthResponse.cs   |   2 +-
 .../csharp/Iggy_SDK/Contracts/Http/ClientInfo.cs   |   2 +-
 .../Iggy_SDK/Contracts/Http/ConsumerGroupInfo.cs   |   2 +-
 .../Contracts/Http/ConsumerGroupMembers.cs         |   2 +-
 .../Iggy_SDK/Contracts/Http/MessageResponse.cs     |   2 +-
 foreign/csharp/Iggy_SDK/Contracts/Http/Stats.cs    |   2 +-
 .../Iggy_SDK/Contracts/Http/StatsResponse.cs       |   4 +-
 .../Iggy_SDK/Contracts/Http/StreamResponse.cs      |   2 +-
 .../csharp/Iggy_SDK/Contracts/Http/TopicRequest.cs |  42 +++-
 .../csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs  |  12 -
 .../Exceptions/InvalidResponseException.cs         |   2 +-
 foreign/csharp/Iggy_SDK/IggyClient/IIggyOffset.cs  |   2 +-
 .../Implementations/HttpMessageStream.cs           |   4 +-
 .../IggyClient/Implementations/TcpMessageStream.cs |  16 +-
 foreign/csharp/Iggy_SDK/Iggy_SDK.csproj            |  13 +-
 .../JsonConfiguration/AuthResponseConverter.cs     |   6 +-
 .../JsonConfiguration/MessagesConverter.cs         |   3 +-
 .../JsonConfiguration/StatsResponseConverter.cs    |  62 ++---
 .../JsonConfiguration/StreamResponseConverter.cs   |  27 ++-
 .../JsonConfiguration/TopicResponseConverter.cs    |  73 +++---
 .../JsonConfiguration/UserResponseConverter.cs     |  35 ---
 foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs    |  23 +-
 .../Iggy_SDK_Tests/ContractTests/TcpContract.cs    |   7 +-
 .../Iggy_SDK_Tests/E2ETests/ConsumerGroupE2E.cs    | 199 ---------------
 .../Iggy_SDK_Tests/E2ETests/FetchMessagesE2E.cs    | 158 ------------
 .../Bootstraps/ConsumerGroupFixtureBootstrap.cs    |  38 ---
 .../Bootstraps/FetchMessagesFixtureBootstrap.cs    |  66 -----
 .../Bootstraps/FlushMessagesFixtureBootstrap.cs    |  59 -----
 .../Fixtures/Bootstraps/GeneralFixtureBootstrap.cs |  28 ---
 .../Fixtures/Bootstraps/OffsetFixtureBootstrap.cs  |  37 ---
 .../Fixtures/Bootstraps/PATFixtureBootstrap.cs     |  34 ---
 .../Bootstraps/PartitionsFixtureBootstrap.cs       |  37 ---
 .../Bootstraps/PollMessagesFixtureBootstrap.cs     |  70 ------
 .../Bootstraps/SendMessagesFixtureBootstrap.cs     |  47 ----
 .../Fixtures/Bootstraps/StreamsFixtureBootstrap.cs |  32 ---
 .../Fixtures/Bootstraps/SystemFixtureBootstrap.cs  |  72 ------
 .../Fixtures/Bootstraps/TopicsFixtureBootstrap.cs  |  38 ---
 .../Fixtures/Bootstraps/UsersFixtureBootstrap.cs   |  34 ---
 .../Fixtures/Configs/IggyFixtureClientConfigs.cs   |  60 -----
 .../E2ETests/Fixtures/IIggyBootstrap.cs            |  25 --
 .../E2ETests/Fixtures/IggyBaseFixture.cs           | 122 ----------
 .../E2ETests/Fixtures/IggyConsumerGroupFixture.cs  |  29 ---
 .../E2ETests/Fixtures/IggyFetchMessagesFixture.cs  |  29 ---
 .../E2ETests/Fixtures/IggyFlushMessagesFixture.cs  |  29 ---
 .../E2ETests/Fixtures/IggyGeneralFixture.cs        |  30 ---
 .../E2ETests/Fixtures/IggyOffsetFixture.cs         |  30 ---
 .../E2ETests/Fixtures/IggyPATFixture.cs            |  30 ---
 .../E2ETests/Fixtures/IggyPartitionFixture.cs      |  31 ---
 .../E2ETests/Fixtures/IggyPollMessagesFixture.cs   |  30 ---
 .../E2ETests/Fixtures/IggySendMessagesFixture.cs   |  30 ---
 .../E2ETests/Fixtures/IggyStreamFixture.cs         |  30 ---
 .../E2ETests/Fixtures/IggySystemFixture.cs         |  29 ---
 .../E2ETests/Fixtures/IggyTcpUsersFixture.cs       |  30 ---
 .../E2ETests/Fixtures/IggyTopicFixture.cs          |  30 ---
 .../E2ETests/Fixtures/Models/IggyClientModel.cs    |  28 ---
 .../Iggy_SDK_Tests/E2ETests/FlushMessagesE2E.cs    | 106 --------
 .../csharp/Iggy_SDK_Tests/E2ETests/OffsetE2E.cs    |  76 ------
 foreign/csharp/Iggy_SDK_Tests/E2ETests/PATE2E.cs   | 122 ----------
 .../Iggy_SDK_Tests/E2ETests/PartitionsE2E.cs       | 103 --------
 .../Iggy_SDK_Tests/E2ETests/PollMessagesE2E.cs     |  64 -----
 .../Iggy_SDK_Tests/E2ETests/SendMessagesE2E.cs     | 114 ---------
 .../csharp/Iggy_SDK_Tests/E2ETests/StreamsE2E.cs   | 153 ------------
 .../csharp/Iggy_SDK_Tests/E2ETests/SystemE2E.cs    | 119 ---------
 .../csharp/Iggy_SDK_Tests/E2ETests/TopicsE2E.cs    | 209 ----------------
 foreign/csharp/Iggy_SDK_Tests/E2ETests/UsersE2E.cs | 226 -----------------
 .../csharp/Iggy_SDK_Tests/Iggy_SDK_Tests.csproj    |  30 +--
 .../Iggy_SDK_Tests/MapperTests/BinaryMapper.cs     |   3 +-
 .../csharp/Iggy_SDK_Tests/Utils/PriorityOrderer.cs |  13 +-
 .../Iggy_SDK_Tests/Utils/Topics/TopicFactory.cs    |  28 +--
 .../Iggy_Sample_Consumer.csproj                    |   2 +-
 foreign/csharp/Iggy_Sample_Consumer/Program.cs     |  16 +-
 .../Iggy_Sample_Producer.csproj                    |   6 +-
 foreign/csharp/Iggy_Sample_Producer/Program.cs     |  14 +-
 foreign/csharp/README.md                           |  16 +-
 110 files changed, 3194 insertions(+), 3139 deletions(-)

diff --git a/.gitignore b/.gitignore
index d4da1bae..0e722dca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 target*
 .idea/
 .vscode/
+.vs/
 .zed/
 .DS_Store
 .gradle
diff --git a/foreign/csharp/.editorconfig b/foreign/csharp/.editorconfig
new file mode 100644
index 00000000..96d0b569
--- /dev/null
+++ b/foreign/csharp/.editorconfig
@@ -0,0 +1,153 @@
+[*]
+charset = utf-8-bom
+end_of_line = crlf
+trim_trailing_whitespace = false
+insert_final_newline = false
+indent_style = space
+indent_size = 4
+
+# Microsoft .NET properties
+csharp_new_line_before_members_in_object_initializers = false
+csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_style_var_for_built_in_types = true:suggestion
+dotnet_naming_rule.interfaces_rule.import_to_resharper = True
+dotnet_naming_rule.interfaces_rule.resharper_description = Interfaces
+dotnet_naming_rule.interfaces_rule.resharper_guid = a7a3339e-4e89-4319-9735-a9dc4cb74cc7
+dotnet_naming_rule.interfaces_rule.severity = warning
+dotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style
+dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols
+dotnet_naming_rule.private_constants_rule.import_to_resharper = True
+dotnet_naming_rule.private_constants_rule.resharper_description = Constant fields (private)
+dotnet_naming_rule.private_constants_rule.resharper_guid = 236f7aa5-7b06-43ca-bf2a-9b31bfcff09a
+dotnet_naming_rule.private_constants_rule.severity = warning
+dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style
+dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols
+dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = True
+dotnet_naming_rule.private_instance_fields_rule.resharper_description = Instance fields (private)
+dotnet_naming_rule.private_instance_fields_rule.resharper_guid = 4a98fdf6-7d98-4f5a-afeb-ea44ad98c70c
+dotnet_naming_rule.private_instance_fields_rule.severity = warning
+dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style
+dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols
+dotnet_naming_rule.private_static_fields_rule.import_to_resharper = True
+dotnet_naming_rule.private_static_fields_rule.resharper_description = Static fields (private)
+dotnet_naming_rule.private_static_fields_rule.resharper_exclusive_prefixes_suffixes = true
+dotnet_naming_rule.private_static_fields_rule.resharper_guid = f9fce829-e6f4-4cb2-80f1-5497c44f51df
+dotnet_naming_rule.private_static_fields_rule.severity = warning
+dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style
+dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols
+dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = True
+dotnet_naming_rule.private_static_readonly_rule.resharper_description = Static readonly fields (private)
+dotnet_naming_rule.private_static_readonly_rule.resharper_guid = 15b5b1f1-457c-4ca6-b278-5615aedc07d3
+dotnet_naming_rule.private_static_readonly_rule.severity = warning
+dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style
+dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols
+dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
+dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
+dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
+dotnet_naming_rule.unity_serialized_field_rule.severity = warning
+dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style_1
+dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
+dotnet_naming_style.i_upper_camel_case_style.capitalization = pascal_case
+dotnet_naming_style.i_upper_camel_case_style.required_prefix = I
+dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
+dotnet_naming_style.lower_camel_case_style.required_prefix = _
+dotnet_naming_style.lower_camel_case_style_1.capitalization = camel_case
+dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
+dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = *
+dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface
+dotnet_naming_symbols.interfaces_symbols.resharper_applicable_kinds = interface
+dotnet_naming_symbols.interfaces_symbols.resharper_required_modifiers = any
+dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private
+dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field
+dotnet_naming_symbols.private_constants_symbols.required_modifiers = const
+dotnet_naming_symbols.private_constants_symbols.resharper_applicable_kinds = constant_field
+dotnet_naming_symbols.private_constants_symbols.resharper_required_modifiers = any
+dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private
+dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field
+dotnet_naming_symbols.private_instance_fields_symbols.resharper_applicable_kinds = field, readonly_field
+dotnet_naming_symbols.private_instance_fields_symbols.resharper_required_modifiers = instance
+dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private
+dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
+dotnet_naming_symbols.private_static_fields_symbols.resharper_applicable_kinds = field
+dotnet_naming_symbols.private_static_fields_symbols.resharper_required_modifiers = static
+dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
+dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = readonly, static
+dotnet_naming_symbols.private_static_readonly_symbols.resharper_applicable_kinds = readonly_field
+dotnet_naming_symbols.private_static_readonly_symbols.resharper_required_modifiers = static
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+file_header_template = // Licensed to the Apache Software Foundation (ASF) under one\n// or more contributor license agreements.See the NOTICE file\n// distributed with this work for additional information\n// regarding copyright ownership.The ASF licenses this file\n// to you under the Apache License, Version 2.0 (the\n// "License") ; you may not use this file except in compliance\n// with the License.  You may obtain a copy of the License at\n//\n//   http://www.apache.org/licenses/LIC [...]
+
+# ReSharper properties
+resharper_autodetect_indent_settings = true
+resharper_braces_for_for = required
+resharper_braces_for_foreach = required
+resharper_braces_for_ifelse = required
+resharper_braces_for_while = required
+resharper_csharp_stick_comment = false
+resharper_csharp_wrap_lines = false
+resharper_formatter_off_tag = @formatter:off
+resharper_formatter_on_tag = @formatter:on
+resharper_formatter_tags_enabled = true
+resharper_for_other_types = use_var_when_evident
+resharper_use_indent_from_vs = false
+
+# ReSharper inspection severities
+resharper_arrange_redundant_parentheses_highlighting = hint
+resharper_arrange_this_qualifier_highlighting = hint
+resharper_arrange_type_member_modifiers_highlighting = hint
+resharper_arrange_type_modifiers_highlighting = hint
+resharper_built_in_type_reference_style_for_member_access_highlighting = hint
+resharper_built_in_type_reference_style_highlighting = hint
+resharper_check_namespace_highlighting = error
+resharper_mvc_action_not_resolved_highlighting = warning
+resharper_mvc_area_not_resolved_highlighting = warning
+resharper_mvc_controller_not_resolved_highlighting = warning
+resharper_mvc_masterpage_not_resolved_highlighting = warning
+resharper_mvc_partial_view_not_resolved_highlighting = warning
+resharper_mvc_template_not_resolved_highlighting = warning
+resharper_mvc_view_component_not_resolved_highlighting = warning
+resharper_mvc_view_component_view_not_resolved_highlighting = warning
+resharper_mvc_view_not_resolved_highlighting = warning
+resharper_razor_assembly_not_resolved_highlighting = warning
+resharper_redundant_base_qualifier_highlighting = warning
+resharper_suggest_var_or_type_simple_types_highlighting = suggestion
+resharper_web_config_module_not_resolved_highlighting = warning
+resharper_web_config_type_not_resolved_highlighting = warning
+resharper_web_config_wrong_module_highlighting = warning
+
+[{*.yaml,*.yml}]
+indent_style = space
+indent_size = 2
+
+[{*.bash,*.sh,*.zsh}]
+indent_style = space
+indent_size = 2
+
+[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,*.sonarlint/*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,connectedMode.json,jest.config}]
+indent_style = space
+indent_size = 2
+
+[{tsconfig.app.json,tsconfig.base.json,tsconfig.e2e.json,tsconfig.editor.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json,tsconfig.test.json}]
+indent_style = space
+indent_size = 2
+
+[*.{appxmanifest,asax,ascx,aspx,axaml,blockshader,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,h++,hh,hlsl,hlsli,hlslinc,hp,hpp,hxx,icc,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,shaderFoundry,skin,tcc,tpp,urtshader,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}]
+indent_style = space
+indent_size = 4
+tab_width = 4
diff --git a/foreign/csharp/Benchmarks/Benchmarks.csproj b/foreign/csharp/Benchmarks/Benchmarks.csproj
index 9cec0126..7754912b 100644
--- a/foreign/csharp/Benchmarks/Benchmarks.csproj
+++ b/foreign/csharp/Benchmarks/Benchmarks.csproj
@@ -14,7 +14,7 @@
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
+      <PackageReference Include="Microsoft.Extensions.Logging.Console" />
     </ItemGroup>
     <PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
         <DefineConstants>OS_WINDOWS</DefineConstants>
@@ -29,5 +29,4 @@
         <DefineConstants>OS_MAC</DefineConstants>
     </PropertyGroup>
 
-
 </Project>
diff --git a/foreign/csharp/Benchmarks/Program.cs b/foreign/csharp/Benchmarks/Program.cs
index bbf33aa4..54c2f785 100644
--- a/foreign/csharp/Benchmarks/Program.cs
+++ b/foreign/csharp/Benchmarks/Program.cs
@@ -81,13 +81,13 @@ try
         });
         
         await clients[0].CreateTopicAsync(Identifier.Numeric(startingStreamId + i), new TopicRequest(
-            TopicId: topicId,
-            Name: $"Test bench topic_{i}",
-            CompressionAlgorithm: CompressionAlgorithm.None,
-            MessageExpiry: 0,
-            MaxTopicSize: 2_000_000_000,
-            ReplicationFactor: 3,
-            PartitionsCount: 1));
+            topicId: topicId,
+            name: $"Test bench topic_{i}",
+            compressionAlgorithm: CompressionAlgorithm.None,
+            messageExpiry: 0,
+            maxTopicSize: 2_000_000_000,
+            replicationFactor: 3,
+            partitionsCount: 1));
     }
 }
 catch
diff --git a/foreign/csharp/DEPENDENCIES.md b/foreign/csharp/DEPENDENCIES.md
index bf37d349..c311316d 100644
--- a/foreign/csharp/DEPENDENCIES.md
+++ b/foreign/csharp/DEPENDENCIES.md
@@ -1,12 +1,16 @@
 # Dependencies
 
+coverlet.collector: "6.0.4", "MIT",
 FluentAssertions: "6.12.0", "Apache-2.0",
+Microsoft.Extensions.Logging: "8.0.1", "MIT",
+Microsoft.Extensions.Logging.Console: "8.0.1", "MIT",
 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",
-Testcontainers: "4.5.0", "MIT",
+Shouldly: "4.3.0", "BSD-3-Clause",
+System.IO.Hashing: "8.0.0", "MIT",
+Testcontainers: "4.6.0", "MIT",
+TUnit: "0.25.21", "MIT",
 xunit: "2.9.3", "Apache-2.0",
-xunit.runner.visualstudio: "3.1.0", "Apache-2.0",
-coverlet.collector: "6.0.4", "MIT" ,
-Microsoft.Extensions.Logging: "8.0.0", "MIT",
-Microsoft.Extensions.Logging.Console:"7.0.0", "MIT"
-System.IO.Hashing: "9.0.5", "MIT",
+xunit.runner.visualstudio: "3.1.1", "Apache-2.0",
diff --git a/foreign/csharp/Directory.Packages.props b/foreign/csharp/Directory.Packages.props
new file mode 100644
index 00000000..02c84e79
--- /dev/null
+++ b/foreign/csharp/Directory.Packages.props
@@ -0,0 +1,22 @@
+<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>
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipHttpAttribute.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipHttpAttribute.cs
new file mode 100644
index 00000000..dc5c5654
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipHttpAttribute.cs
@@ -0,0 +1,36 @@
+// // 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.Enums;
+
+namespace Apache.Iggy.Tests.Integrations.Attributes;
+
+internal class SkipHttpAttribute() : SkipAttribute("This test is skipped for HTTP protocol")
+{
+    public override Task<bool> ShouldSkip(BeforeTestContext context)
+    {
+        foreach (var argument in context.TestDetails.TestClassArguments)
+        {
+            if (argument is Protocol.Http)
+            {
+                return Task.FromResult(true);
+            }
+        }
+
+        return Task.FromResult(false);
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipTcpAttribute.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipTcpAttribute.cs
new file mode 100644
index 00000000..bb2f4d36
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Attributes/SkipTcpAttribute.cs
@@ -0,0 +1,36 @@
+// // 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.Enums;
+
+namespace Apache.Iggy.Tests.Integrations.Attributes;
+
+internal class SkipTcpAttribute() : SkipAttribute("This test is skipped for TCP protocol")
+{
+    public override Task<bool> ShouldSkip(BeforeTestContext context)
+    {
+        foreach (var argument in context.TestDetails.TestClassArguments)
+        {
+            if (argument is Protocol.Tcp)
+            {
+                return Task.FromResult(true);
+            }
+        }
+
+        return Task.FromResult(false);
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/ConsumerGroupTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/ConsumerGroupTests.cs
new file mode 100644
index 00000000..7619b175
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/ConsumerGroupTests.cs
@@ -0,0 +1,225 @@
+// // 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.Contracts.Http.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.IggyClient;
+using Apache.Iggy.Tests.Integrations.Attributes;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class ConsumerGroupTests(Protocol protocol)
+{
+    private static readonly int GroupId = 1;
+
+    private readonly CreateConsumerGroupRequest _createConsumerGroupRequest = new()
+    {
+        StreamId = Identifier.Numeric(1),
+        TopicId = Identifier.Numeric(1),
+        ConsumerGroupId = GroupId,
+        Name = "test_consumer_group"
+    };
+
+    [ClassDataSource<ConsumerGroupFixture>(Shared = SharedType.PerClass)]
+    public required ConsumerGroupFixture Fixture { get; init; }
+
+    [Test]
+    public async Task CreateConsumerGroup_HappyPath_Should_CreateConsumerGroup_Successfully()
+    {
+        var consumerGroup = await Fixture.Clients[protocol].CreateConsumerGroupAsync(_createConsumerGroupRequest);
+
+        consumerGroup.ShouldNotBeNull();
+        consumerGroup.Id.ShouldBe(_createConsumerGroupRequest.ConsumerGroupId);
+        consumerGroup.PartitionsCount.ShouldBe(Fixture.TopicRequest.PartitionsCount);
+        consumerGroup.MembersCount.ShouldBe(0);
+        consumerGroup.Name.ShouldBe(_createConsumerGroupRequest.Name);
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateConsumerGroup_HappyPath_Should_CreateConsumerGroup_Successfully))]
+    public async Task CreateConsumerGroup_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].CreateConsumerGroupAsync(_createConsumerGroupRequest));
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateConsumerGroup_Should_Throw_InvalidResponse))]
+    public async Task GetConsumerGroupById_Should_Return_ValidResponse()
+    {
+        var response = await Fixture.Clients[protocol].GetConsumerGroupByIdAsync(_createConsumerGroupRequest.StreamId,
+            _createConsumerGroupRequest.TopicId, Identifier.Numeric(1));
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(GroupId);
+        response.Name.ShouldBe(_createConsumerGroupRequest.Name);
+        response.PartitionsCount.ShouldBe(Fixture.TopicRequest.PartitionsCount);
+        response.MembersCount.ShouldBe(0);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetConsumerGroupById_Should_Return_ValidResponse))]
+    public async Task GetConsumerGroups_Should_Return_ValidResponse()
+    {
+        IReadOnlyList<ConsumerGroupResponse> response = await Fixture.Clients[protocol].GetConsumerGroupsAsync(
+            _createConsumerGroupRequest.StreamId, _createConsumerGroupRequest.TopicId);
+
+        response.ShouldNotBeNull();
+        response.Count.ShouldBe(1);
+
+        var group = response.FirstOrDefault();
+        group.ShouldNotBeNull();
+        group.Id.ShouldBe(GroupId);
+        group.Name.ShouldBe(_createConsumerGroupRequest.Name);
+        group.PartitionsCount.ShouldBe(Fixture.TopicRequest.PartitionsCount);
+        group.MembersCount.ShouldBe(0);
+    }
+
+    [Test]
+    [SkipHttp]
+    [DependsOn(nameof(GetConsumerGroups_Should_Return_ValidResponse))]
+    public async Task JoinConsumerGroup_Tcp_Should_JoinConsumerGroup_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].JoinConsumerGroupAsync(new JoinConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+    [Test]
+    [SkipTcp]
+    [DependsOn(nameof(GetConsumerGroups_Should_Return_ValidResponse))]
+    public async Task JoinConsumerGroup_Http_Should_Throw_FeatureUnavailable()
+    {
+        await Should.ThrowAsync<FeatureUnavailableException>(() => Fixture.Clients[protocol].JoinConsumerGroupAsync(new JoinConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+    [Test]
+    [SkipHttp]
+    [DependsOn(nameof(JoinConsumerGroup_Tcp_Should_JoinConsumerGroup_Successfully))]
+    public async Task LeaveConsumerGroup_Tcp_Should_LeaveConsumerGroup_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].LeaveConsumerGroupAsync(new LeaveConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+    [Test]
+    [SkipTcp]
+    [DependsOn(nameof(JoinConsumerGroup_Http_Should_Throw_FeatureUnavailable))]
+    public async Task LeaveConsumerGroup_Http_Should_Throw_FeatureUnavailable()
+    {
+        await Should.ThrowAsync<FeatureUnavailableException>(() => Fixture.Clients[protocol].LeaveConsumerGroupAsync(new LeaveConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(LeaveConsumerGroup_Http_Should_Throw_FeatureUnavailable))]
+    [DependsOn(nameof(LeaveConsumerGroup_Tcp_Should_LeaveConsumerGroup_Successfully))]
+    public async Task GetConsumerGroupById_WithMembers_Should_Return_ValidResponse()
+    {
+        var clientIds = new List<uint>();
+        var clients = new List<IIggyClient>();
+        for (var i = 0; i < 2; i++)
+        {
+            var client = Fixture.CreateClient(Protocol.Tcp, protocol);
+            clients.Add(client);
+            await client.LoginUser(new LoginUserRequest
+            {
+                Username = "iggy",
+                Password = "iggy"
+            });
+            var me = await client.GetMeAsync();
+            clientIds.Add(me!.ClientId);
+            await client.JoinConsumerGroupAsync(new JoinConsumerGroupRequest
+            {
+                ConsumerGroupId = Identifier.Numeric(_createConsumerGroupRequest.ConsumerGroupId),
+                StreamId = _createConsumerGroupRequest.StreamId,
+                TopicId = _createConsumerGroupRequest.TopicId
+            });
+        }
+
+        var response = await Fixture.Clients[protocol].GetConsumerGroupByIdAsync(_createConsumerGroupRequest.StreamId,
+            _createConsumerGroupRequest.TopicId, Identifier.Numeric(1));
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(GroupId);
+        response.PartitionsCount.ShouldBe(Fixture.TopicRequest.PartitionsCount);
+        response.MembersCount.ShouldBe(2);
+        response.Members.ShouldNotBeNull();
+        response.Members.Count.ShouldBe(2);
+        response.Members.ShouldAllBe(m => clientIds.Contains(m.Id));
+        response.Members.ShouldAllBe(x => x.PartitionsCount == 5);
+        response.Members.ShouldAllBe(x => x.Partitions.Count == 5);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetConsumerGroupById_WithMembers_Should_Return_ValidResponse))]
+    public async Task DeleteConsumerGroup_Should_DeleteConsumerGroup_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].DeleteConsumerGroupAsync(new DeleteConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+    [Test]
+    [SkipHttp]
+    [DependsOn(nameof(DeleteConsumerGroup_Should_DeleteConsumerGroup_Successfully))]
+    public async Task JoinConsumerGroup_Tcp_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].JoinConsumerGroupAsync(new JoinConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+
+
+    [Test]
+    [DependsOn(nameof(JoinConsumerGroup_Tcp_Should_Throw_InvalidResponse))]
+    public async Task DeleteConsumerGroup_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].DeleteConsumerGroupAsync(new DeleteConsumerGroupRequest
+        {
+            TopicId = _createConsumerGroupRequest.TopicId,
+            StreamId = _createConsumerGroupRequest.StreamId,
+            ConsumerGroupId = Identifier.Numeric(GroupId)
+        }));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/FetchMessagesTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/FetchMessagesTests.cs
new file mode 100644
index 00000000..b13032b3
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/FetchMessagesTests.cs
@@ -0,0 +1,185 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Headers;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Apache.Iggy.Tests.Integrations.Models;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class FetchMessagesTests(Protocol protocol)
+{
+    [ClassDataSource<FetchMessagesFixture>(Shared = SharedType.PerClass)]
+    public required FetchMessagesFixture Fixture { get; init; }
+
+
+    [Test]
+    public async Task PollMessagesTMessage_WithNoHeaders_Should_PollMessages_Successfully()
+    {
+        PolledMessages<DummyMessage> response = await Fixture.Clients[protocol].FetchMessagesAsync(new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicDummyRequest.TopicId!.Value)
+        }, DummyMessage.DeserializeDummyMessage);
+
+        response.Messages.Count.ShouldBe(10);
+        response.PartitionId.ShouldBe(1);
+        response.CurrentOffset.ShouldBe(19u);
+        uint offset = 0;
+        foreach (MessageResponse<DummyMessage> responseMessage in response.Messages)
+        {
+            responseMessage.UserHeaders.ShouldBeNull();
+            responseMessage.Message.Text.ShouldNotBeNullOrEmpty();
+            responseMessage.Message.Text.ShouldContain("Dummy message");
+            responseMessage.Header.Checksum.ShouldNotBe(0u);
+            responseMessage.Header.Id.ShouldNotBe(0u);
+            responseMessage.Header.Offset.ShouldBe(offset++);
+            responseMessage.Header.PayloadLength.ShouldNotBe(0);
+            responseMessage.Header.UserHeadersLength.ShouldBe(0);
+        }
+    }
+
+    [Test]
+    [DependsOn(nameof(PollMessagesTMessage_WithNoHeaders_Should_PollMessages_Successfully))]
+    public async Task PollMessagesTMessage_Should_Throw_InvalidResponse()
+    {
+        var invalidFetchRequest = new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(55)
+        };
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].FetchMessagesAsync(invalidFetchRequest, DummyMessage.DeserializeDummyMessage));
+    }
+
+    [Test]
+    [DependsOn(nameof(PollMessagesTMessage_Should_Throw_InvalidResponse))]
+    public async Task PollMessages_WithNoHeaders_Should_PollMessages_Successfully()
+    {
+        var response = await Fixture.Clients[protocol].FetchMessagesAsync(new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        });
+
+        response.Messages.Count.ShouldBe(10);
+        response.PartitionId.ShouldBe(1);
+        response.CurrentOffset.ShouldBe(19u);
+
+        foreach (var responseMessage in response.Messages)
+        {
+            responseMessage.UserHeaders.ShouldBeNull();
+            responseMessage.Payload.ShouldNotBeNull();
+            responseMessage.Payload.Length.ShouldBeGreaterThan(0);
+        }
+    }
+
+    [Test]
+    [DependsOn(nameof(PollMessages_WithNoHeaders_Should_PollMessages_Successfully))]
+    public async Task PollMessages_Should_Throw_InvalidResponse()
+    {
+        var invalidFetchRequest = new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(55)
+        };
+
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].FetchMessagesAsync(invalidFetchRequest));
+    }
+
+    [Test]
+    [DependsOn(nameof(PollMessages_Should_Throw_InvalidResponse))]
+    public async Task PollMessages_WithHeaders_Should_PollMessages_Successfully()
+    {
+        var headersMessageFetchRequest = new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.HeadersTopicRequest.TopicId!.Value)
+        };
+
+
+        var response = await Fixture.Clients[protocol].FetchMessagesAsync(headersMessageFetchRequest);
+        response.Messages.Count.ShouldBe(10);
+        response.PartitionId.ShouldBe(1);
+        response.CurrentOffset.ShouldBe(19u);
+        foreach (var responseMessage in response.Messages)
+        {
+            responseMessage.UserHeaders.ShouldNotBeNull();
+            responseMessage.UserHeaders.Count.ShouldBe(2);
+            responseMessage.UserHeaders[HeaderKey.New("header1")].ToString().ShouldBe("value1");
+            responseMessage.UserHeaders[HeaderKey.New("header2")].ToInt32().ShouldBeGreaterThan(0);
+        }
+    }
+
+    [Test]
+    [DependsOn(nameof(PollMessages_WithHeaders_Should_PollMessages_Successfully))]
+    public async Task PollMessagesTMessage_WithHeaders_Should_PollMessages_Successfully()
+    {
+        var headersMessageFetchRequest = new MessageFetchRequest
+        {
+            Count = 10,
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.Next(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicDummyHeaderRequest.TopicId!.Value)
+        };
+
+        PolledMessages<DummyMessage> response = await Fixture.Clients[protocol].FetchMessagesAsync(headersMessageFetchRequest, DummyMessage.DeserializeDummyMessage);
+        response.Messages.Count.ShouldBe(10);
+        response.PartitionId.ShouldBe(1);
+        response.CurrentOffset.ShouldBe(19u);
+        foreach (MessageResponse<DummyMessage> responseMessage in response.Messages)
+        {
+            responseMessage.UserHeaders.ShouldNotBeNull();
+            responseMessage.UserHeaders.Count.ShouldBe(2);
+            responseMessage.UserHeaders[HeaderKey.New("header1")].ToString().ShouldBe("value1");
+            responseMessage.UserHeaders[HeaderKey.New("header2")].ToInt32().ShouldBeGreaterThan(0);
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/ConsumerGroupFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/ConsumerGroupFixture.cs
new file mode 100644
index 00000000..1aba2447
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/ConsumerGroupFixture.cs
@@ -0,0 +1,38 @@
+// // 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.Tests.Integrations.Helpers;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class ConsumerGroupFixture : IggyServerFixture
+{
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic(partitionsCount: 10);
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), TopicRequest);
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FetchMessagesFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FetchMessagesFixture.cs
new file mode 100644
index 00000000..a67d70c6
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FetchMessagesFixture.cs
@@ -0,0 +1,148 @@
+// // 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.Headers;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Helpers;
+using Apache.Iggy.Tests.Integrations.Models;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class FetchMessagesFixture : IggyServerFixture
+{
+    public readonly TopicRequest HeadersTopicRequest = TopicFactory.CreateTopic(2);
+    public readonly int MessageCount = 20;
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicDummyHeaderRequest = TopicFactory.CreateTopic(4);
+    public readonly TopicRequest TopicDummyRequest = TopicFactory.CreateTopic(3);
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        var request = new MessageSendRequest
+        {
+            StreamId = Identifier.Numeric(StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(TopicRequest.TopicId!.Value),
+            Partitioning = Partitioning.None(),
+            Messages = CreateMessagesWithoutHeader(MessageCount)
+        };
+
+        var requestWithHeaders = new MessageSendRequest
+        {
+            StreamId = Identifier.Numeric(StreamRequest.StreamId.Value),
+            TopicId = Identifier.Numeric(HeadersTopicRequest.TopicId!.Value),
+            Partitioning = Partitioning.None(),
+            Messages = CreateMessagesWithHeader(MessageCount)
+        };
+
+        var requestDummyMessage = new MessageSendRequest<DummyMessage>
+        {
+            StreamId = Identifier.Numeric(StreamRequest.StreamId.Value),
+            TopicId = Identifier.Numeric(TopicDummyRequest.TopicId!.Value),
+            Partitioning = Partitioning.None(),
+            Messages = CreateDummyMessagesWithoutHeader(MessageCount)
+        };
+
+        var requestDummyMessageWithHeaders = new MessageSendRequest<DummyMessage>
+        {
+            StreamId = Identifier.Numeric(StreamRequest.StreamId.Value),
+            TopicId = Identifier.Numeric(TopicDummyHeaderRequest.TopicId!.Value),
+            Partitioning = Partitioning.None(),
+            Messages = CreateDummyMessagesWithoutHeader(MessageCount)
+        };
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId.Value), TopicRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId.Value), TopicDummyRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId.Value), HeadersTopicRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId.Value), TopicDummyHeaderRequest);
+
+            await client.SendMessagesAsync(request);
+            await client.SendMessagesAsync(requestDummyMessage, message => message.SerializeDummyMessage());
+            await client.SendMessagesAsync(requestWithHeaders);
+            await client.SendMessagesAsync(requestDummyMessageWithHeaders, message => message.SerializeDummyMessage(), headers: new Dictionary<HeaderKey, HeaderValue>
+            {
+                { HeaderKey.New("header1"), HeaderValue.FromString("value1") },
+                { HeaderKey.New("header2"), HeaderValue.FromInt32(14) }
+            });
+        }
+    }
+
+    private static List<Message> CreateMessagesWithoutHeader(int count)
+    {
+        var messages = new List<Message>();
+        for (var i = 0; i < count; i++)
+        {
+            var dummyJson = $$"""
+                              {
+                                "userId": {{i + 1}},
+                                "id": {{i + 1}},
+                                "title": "delete",
+                                "completed": false
+                              }
+                              """;
+            messages.Add(new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson)));
+        }
+
+        return messages;
+    }
+
+    private static List<DummyMessage> CreateDummyMessagesWithoutHeader(int count)
+    {
+        var messages = new List<DummyMessage>();
+        for (var i = 0; i < count; i++)
+        {
+            messages.Add(new DummyMessage
+            {
+                Text = $"Dummy message {i}",
+                Id = i
+            });
+        }
+
+        return messages;
+    }
+
+    private static List<Message> CreateMessagesWithHeader(int count)
+    {
+        var messages = new List<Message>();
+        for (var i = 0; i < count; i++)
+        {
+            var dummyJson = $$"""
+                              {
+                                "userId": {{i + 1}},
+                                "id": {{i + 1}},
+                                "title": "delete",
+                                "completed": false
+                              }
+                              """;
+            messages.Add(new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson), new Dictionary<HeaderKey, HeaderValue>
+            {
+                { HeaderKey.New("header1"), HeaderValue.FromString("value1") },
+                { HeaderKey.New("header2"), HeaderValue.FromInt32(14 + i) }
+            }));
+        }
+
+        return messages;
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FlushMessageFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FlushMessageFixture.cs
new file mode 100644
index 00000000..36e3aa1c
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FlushMessageFixture.cs
@@ -0,0 +1,54 @@
+// // 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.Kinds;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Helpers;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class FlushMessageFixture : IggyServerFixture
+{
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), TopicRequest);
+
+            await client.SendMessagesAsync(new MessageSendRequest
+            {
+                Partitioning = Partitioning.None(),
+                StreamId = Identifier.Numeric(StreamRequest.StreamId!.Value),
+                TopicId = Identifier.Numeric(TopicRequest.TopicId!.Value),
+                Messages = new List<Message>
+                {
+                    new(Guid.NewGuid(), "Test message 1"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 2"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 3"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 4"u8.ToArray())
+                }
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/IggyServerFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/IggyServerFixture.cs
new file mode 100644
index 00000000..562660ff
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/IggyServerFixture.cs
@@ -0,0 +1,145 @@
+// // 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.Configuration;
+using Apache.Iggy.Contracts.Http.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Factory;
+using Apache.Iggy.IggyClient;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Containers;
+using Microsoft.Extensions.Logging.Abstractions;
+using TUnit.Core.Interfaces;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class IggyServerFixture : IAsyncInitializer, IAsyncDisposable
+{
+    private readonly IContainer _httpContainer = new ContainerBuilder().WithImage("apache/iggy:edge")
+        // Container name is just to be used locally for debbuging effects
+        //.WithName($"SutIggyContainerHTTP")
+        .WithPortBinding(3000, true)
+        .WithPortBinding(8090, true)
+        .WithOutputConsumer(Consume.RedirectStdoutAndStderrToConsole())
+        .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3000))
+        .WithName($"HTTP_{Guid.NewGuid()}")
+        //.WithEnvironment("IGGY_SYSTEM_LOGGING_LEVEL", "trace")
+        //.WithEnvironment("RUST_LOG", "trace")
+        .WithCleanUp(true)
+        .Build();
+
+    private readonly IContainer _tcpContainer = new ContainerBuilder().WithImage("apache/iggy:edge")
+        // Container name is just to be used locally for debbuging effects
+        //.WithName($"SutIggyContainerTCP")
+        .WithPortBinding(3000, true)
+        .WithPortBinding(8090, true)
+        .WithOutputConsumer(Consume.RedirectStdoutAndStderrToConsole())
+        .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(8090))
+        .WithName($"TCP_{Guid.NewGuid()}")
+        //.WithEnvironment("IGGY_SYSTEM_LOGGING_LEVEL", "trace")
+        //.WithEnvironment("RUST_LOG", "trace")
+        .WithCleanUp(true)
+        .Build();
+
+    public Dictionary<Protocol, IIggyClient> Clients { get; } = new();
+
+    private Action<MessageBatchingSettings> BatchingSettings { get; } = options =>
+    {
+        options.Enabled = false;
+        options.Interval = TimeSpan.FromMilliseconds(100);
+        options.MaxMessagesPerBatch = 1000;
+        options.MaxRequests = 4096;
+    };
+
+    private Action<MessagePollingSettings> PollingSettings { get; } = options =>
+    {
+        options.Interval = TimeSpan.FromMilliseconds(100);
+        options.StoreOffsetStrategy = StoreOffset.WhenMessagesAreReceived;
+    };
+
+    public async ValueTask DisposeAsync()
+    {
+        await Task.WhenAll(_tcpContainer.StopAsync(), _httpContainer.StopAsync());
+    }
+
+    public virtual async Task InitializeAsync()
+    {
+        await Task.WhenAll(_tcpContainer.StartAsync(), _httpContainer.StartAsync());
+
+        await CreateTcpClient();
+        await CreateHttpClient();
+    }
+
+    public async Task CreateTcpClient()
+    {
+        var tcpClient = CreateClient(Protocol.Tcp);
+
+        await tcpClient.LoginUser(new LoginUserRequest
+        {
+            Password = "iggy",
+            Username = "iggy"
+        });
+
+        Clients[Protocol.Tcp] = tcpClient;
+    }
+
+    public async Task CreateHttpClient()
+    {
+        var client = CreateClient(Protocol.Http);
+
+        await client.LoginUser(new LoginUserRequest
+        {
+            Password = "iggy",
+            Username = "iggy"
+        });
+
+        Clients[Protocol.Http] = client;
+    }
+
+    public IIggyClient CreateClient(Protocol protocol, Protocol? targetContainer = null)
+    {
+        var port = protocol == Protocol.Tcp
+            ? _tcpContainer.GetMappedPublicPort(8090)
+            : _httpContainer.GetMappedPublicPort(3000);
+
+        if (targetContainer != null
+            && targetContainer != protocol)
+        {
+            port = targetContainer == Protocol.Tcp
+                ? _tcpContainer.GetMappedPublicPort(3000)
+                : _httpContainer.GetMappedPublicPort(8090);
+        }
+
+        var address = protocol == Protocol.Tcp
+            ? $"127.0.0.1:{port}"
+            : $"http://127.0.0.1:{port}";
+
+        return MessageStreamFactory.CreateMessageStream(options =>
+        {
+            options.BaseAdress = address;
+            options.Protocol = protocol;
+            options.MessageBatchingSettings = BatchingSettings;
+            options.MessagePollingSettings = PollingSettings;
+        }, NullLoggerFactory.Instance);
+    }
+
+    public static IEnumerable<Func<Protocol>> ProtocolData()
+    {
+        yield return () => Protocol.Http;
+        yield return () => Protocol.Tcp;
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/OffsetFixtures.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/OffsetFixtures.cs
new file mode 100644
index 00000000..a1e4d866
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/OffsetFixtures.cs
@@ -0,0 +1,54 @@
+// // 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.Kinds;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Helpers;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class OffsetFixtures : IggyServerFixture
+{
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), TopicRequest);
+
+            await client.SendMessagesAsync(new MessageSendRequest
+            {
+                Partitioning = Partitioning.None(),
+                StreamId = Identifier.Numeric(StreamRequest.StreamId!.Value),
+                TopicId = Identifier.Numeric(TopicRequest.TopicId!.Value),
+                Messages = new List<Message>
+                {
+                    new(Guid.NewGuid(), "Test message 1"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 2"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 3"u8.ToArray()),
+                    new(Guid.NewGuid(), "Test message 4"u8.ToArray())
+                }
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PartitionsFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PartitionsFixture.cs
new file mode 100644
index 00000000..85031a21
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PartitionsFixture.cs
@@ -0,0 +1,38 @@
+// // 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.Tests.Integrations.Helpers;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class PartitionsFixture : IggyServerFixture
+{
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), TopicRequest);
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PollMessagesFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PollMessagesFixture.cs
new file mode 100644
index 00000000..31fb08c9
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/PollMessagesFixture.cs
@@ -0,0 +1,70 @@
+// // 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.Headers;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Tests.Integrations.Helpers;
+using Apache.Iggy.Tests.Integrations.Models;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class PollMessagesFixture : IggyServerFixture
+{
+    public readonly int MessageCount = 10;
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
+            await client.SendMessagesAsync(new MessageSendRequest<DummyMessage>
+                {
+                    Messages = CreateDummyMessagesWithoutHeader(MessageCount),
+                    Partitioning = Partitioning.None(),
+                    StreamId = Identifier.Numeric(StreamRequest.StreamId.Value),
+                    TopicId = Identifier.Numeric(TopicRequest.TopicId!.Value)
+                },
+                message => message.SerializeDummyMessage(),
+                headers: new Dictionary<HeaderKey, HeaderValue>
+                {
+                    { HeaderKey.New("header1"), HeaderValue.FromString("value1") },
+                    { HeaderKey.New("header2"), HeaderValue.FromInt32(14) }
+                });
+        }
+    }
+
+    private static List<DummyMessage> CreateDummyMessagesWithoutHeader(int count)
+    {
+        var messages = new List<DummyMessage>();
+        for (var i = 0; i < count; i++)
+        {
+            messages.Add(new DummyMessage
+            {
+                Text = $"Dummy message {i}",
+                Id = i
+            });
+        }
+
+        return messages;
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SendMessageFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SendMessageFixture.cs
new file mode 100644
index 00000000..3c1e70f4
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SendMessageFixture.cs
@@ -0,0 +1,38 @@
+// // 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.Tests.Integrations.Helpers;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class SendMessageFixture : IggyServerFixture
+{
+    public readonly StreamRequest StreamRequest = StreamFactory.CreateStream();
+    public readonly TopicRequest TopicRequest = TopicFactory.CreateTopic();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        foreach (var client in Clients.Values)
+        {
+            await client.CreateStreamAsync(StreamRequest);
+            await client.CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), TopicRequest);
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SystemFixture.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SystemFixture.cs
new file mode 100644
index 00000000..6bc96573
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/SystemFixture.cs
@@ -0,0 +1,89 @@
+// // 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.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.IggyClient;
+
+namespace Apache.Iggy.Tests.Integrations.Fixtures;
+
+public class SystemFixture : IggyServerFixture
+{
+    public readonly int TotalClientsCount = 10;
+
+    private List<IIggyClient> AdditionalClients { get; } = new();
+
+    public override async Task InitializeAsync()
+    {
+        await base.InitializeAsync();
+
+        await CreateClientsAsync();
+    }
+
+    private async Task CreateClientsAsync()
+    {
+        for (var i = 0; i < TotalClientsCount; i++)
+        {
+            await Clients[Protocol.Http].CreateUser(new CreateUserRequest
+            {
+                Username = $"iggy{i}",
+                Password = "iggy",
+                Status = UserStatus.Active
+            });
+
+            var client = CreateClient(Protocol.Tcp, Protocol.Http);
+            AdditionalClients.Add(client);
+            var login = await client.LoginUser(new LoginUserRequest
+            {
+                Password = "iggy",
+                Username = $"iggy{i}"
+            });
+
+            if (login!.UserId == 0)
+            {
+                throw new Exception("Failed to login user 'iggy'.");
+            }
+
+            await client.PingAsync();
+        }
+
+        // One client less for tcp due to a default client
+        for (var i = 0; i < TotalClientsCount - 1; i++)
+        {
+            await Clients[Protocol.Tcp].CreateUser(new CreateUserRequest
+            {
+                Username = $"iggy{i}",
+                Password = "iggy",
+                Status = UserStatus.Active
+            });
+
+            var client = CreateClient(Protocol.Tcp, Protocol.Tcp);
+            AdditionalClients.Add(client);
+            var login = await client.LoginUser(new LoginUserRequest
+            {
+                Password = "iggy",
+                Username = $"iggy{i}"
+            });
+            if (login!.UserId == 0)
+            {
+                throw new Exception("Failed to login user 'iggy'.");
+            }
+
+            await client.PingAsync();
+        }
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/FlushMessagesTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/FlushMessagesTests.cs
new file mode 100644
index 00000000..e5d33800
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/FlushMessagesTests.cs
@@ -0,0 +1,69 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class FlushMessagesTests(Protocol protocol)
+{
+    [ClassDataSource<FlushMessageFixture>(Shared = SharedType.PerClass)]
+    public required FlushMessageFixture Fixture { get; init; }
+
+    [Test]
+    public async Task FlushUnsavedBuffer_WithFsync_Should_Flush_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].FlushUnsavedBufferAsync(new FlushUnsavedBufferRequest
+        {
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value),
+            PartitionId = 1,
+            Fsync = true
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(FlushUnsavedBuffer_WithFsync_Should_Flush_Successfully))]
+    public async Task FlushUnsavedBuffer_WithOutFsync_Should_Flush_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].FlushUnsavedBufferAsync(new FlushUnsavedBufferRequest
+        {
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value),
+            PartitionId = 1,
+            Fsync = false
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(FlushUnsavedBuffer_WithOutFsync_Should_Flush_Successfully))]
+    public async Task FlushUnsavedBuffer_Should_Throw_WhenStream_DoesNotExist()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].FlushUnsavedBufferAsync(new FlushUnsavedBufferRequest
+        {
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(55),
+            PartitionId = 1,
+            Fsync = true
+        }));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/StreamFactory.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/StreamFactory.cs
new file mode 100644
index 00000000..a89edda1
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/StreamFactory.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;
+
+namespace Apache.Iggy.Tests.Integrations.Helpers;
+
+public static class StreamFactory
+{
+    public static StreamRequest CreateStream(int streamId = 1)
+    {
+        return new StreamRequest
+        {
+            StreamId = streamId,
+            Name = "TestStream"
+        };
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/TopicFactory.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/TopicFactory.cs
new file mode 100644
index 00000000..3e4f0777
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Helpers/TopicFactory.cs
@@ -0,0 +1,34 @@
+// // 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;
+
+namespace Apache.Iggy.Tests.Integrations.Helpers;
+
+public static class TopicFactory
+{
+    public static TopicRequest CreateTopic(int topicId = 1, int partitionsCount = 1, ulong messageExpiry = 0)
+    {
+        return new TopicRequest
+        {
+            TopicId = topicId,
+            Name = $"TestTopic {topicId}",
+            PartitionsCount = partitionsCount,
+            MessageExpiry = messageExpiry
+        };
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Iggy_SDK.Tests.Integration.csproj b/foreign/csharp/Iggy_SDK.Tests.Integration/Iggy_SDK.Tests.Integration.csproj
new file mode 100644
index 00000000..1fc29287
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Iggy_SDK.Tests.Integration.csproj
@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>net8.0</TargetFramework>
+        <AssemblyName>Apache.Iggy.Tests.Integrations</AssemblyName>
+        <RootNamespace>Apache.Iggy.Tests.Integrations</RootNamespace>
+        <IsTestingPlatformApplication>true</IsTestingPlatformApplication>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="coverlet.collector">
+            <PrivateAssets>all</PrivateAssets>
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+        </PackageReference>
+        <PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
+        <PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
+        <PackageReference Include="Shouldly" />
+        <PackageReference Include="Testcontainers" />
+        <PackageReference Include="TUnit" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Iggy_SDK\Iggy_SDK.csproj" />
+    </ItemGroup>
+
+</Project>
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/Models/DummyMessage.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/Models/DummyMessage.cs
new file mode 100644
index 00000000..4db4bb4f
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/Models/DummyMessage.cs
@@ -0,0 +1,45 @@
+// // 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.Buffers.Binary;
+using System.Text;
+
+namespace Apache.Iggy.Tests.Integrations.Models;
+
+public sealed class DummyMessage
+{
+    public int Id { get; set; }
+    public required string Text { get; set; }
+
+    internal static Func<byte[], DummyMessage> DeserializeDummyMessage
+        => bytes =>
+        {
+            var id = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan()[..4]);
+            var textLength = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan()[4..8]);
+            var text = Encoding.UTF8.GetString(bytes.AsSpan()[8..(8 + textLength)]);
+            return new DummyMessage { Id = id, Text = text };
+        };
+
+    internal byte[] SerializeDummyMessage()
+    {
+        var bytes = new byte[4 + 4 + Text.Length];
+        BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan()[..4], Id);
+        BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan()[4..8], Text.Length);
+        Encoding.UTF8.GetBytes(Text).CopyTo(bytes.AsSpan()[8..]);
+        return bytes;
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/OffsetTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/OffsetTests.cs
new file mode 100644
index 00000000..614efcfd
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/OffsetTests.cs
@@ -0,0 +1,104 @@
+// // 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.Enums;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class OffsetTests(Protocol protocol)
+{
+    private const ulong SetOffset = 2;
+
+    [ClassDataSource<OffsetFixtures>(Shared = SharedType.PerClass)]
+    public required OffsetFixtures Fixture { get; init; }
+
+    [Test]
+    public async Task StoreOffset_IndividualConsumer_Should_StoreOffset_Successfully()
+    {
+        await Fixture.Clients[protocol].StoreOffsetAsync(new StoreOffsetRequest
+        {
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            PartitionId = 1,
+            Offset = SetOffset,
+            Consumer = Consumer.New(1)
+        });
+    }
+
+    [Test]
+    [DependsOn(nameof(StoreOffset_IndividualConsumer_Should_StoreOffset_Successfully))]
+    public async Task GetOffset_IndividualConsumer_Should_GetOffset_Successfully()
+    {
+        var offset = await Fixture.Clients[protocol].GetOffsetAsync(new OffsetRequest
+        {
+            Consumer = Consumer.New(1),
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            PartitionId = 1
+        });
+
+        offset.ShouldNotBeNull();
+        offset.StoredOffset.ShouldBe(SetOffset);
+        offset.PartitionId.ShouldBe(1);
+        offset.CurrentOffset.ShouldBe(3u);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetOffset_IndividualConsumer_Should_GetOffset_Successfully))]
+    public async Task StoreOffset_ConsumerGroup_Should_StoreOffset_Successfully()
+    {
+        await Fixture.Clients[protocol].CreateConsumerGroupAsync(new CreateConsumerGroupRequest
+        {
+            Name = "test_consumer_group",
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            ConsumerGroupId = 1
+        });
+
+        await Fixture.Clients[protocol].StoreOffsetAsync(new StoreOffsetRequest
+        {
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            PartitionId = 1,
+            Offset = SetOffset,
+            Consumer = Consumer.Group(1)
+        });
+    }
+
+    [Test]
+    [DependsOn(nameof(StoreOffset_ConsumerGroup_Should_StoreOffset_Successfully))]
+    public async Task GetOffset_ConsumerGroup_Should_GetOffset_Successfully()
+    {
+        var offset = await Fixture.Clients[protocol].GetOffsetAsync(new OffsetRequest
+        {
+            Consumer = Consumer.Group(1),
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            PartitionId = 1
+        });
+
+        offset.ShouldNotBeNull();
+        offset.StoredOffset.ShouldBe(SetOffset);
+        offset.PartitionId.ShouldBe(1);
+        offset.CurrentOffset.ShouldBe(3u);
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/PartitionsTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/PartitionsTests.cs
new file mode 100644
index 00000000..e5b04ec9
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/PartitionsTests.cs
@@ -0,0 +1,88 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class PartitionsTests(Protocol protocol)
+{
+    [ClassDataSource<PartitionsFixture>(Shared = SharedType.PerClass)]
+    public required PartitionsFixture Fixture { get; init; }
+
+    [Test]
+    public async Task CreatePartition_HappyPath_Should_CreatePartition_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].CreatePartitionsAsync(new CreatePartitionsRequest
+        {
+            PartitionsCount = 3,
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+
+        var response = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(1));
+        response.ShouldNotBeNull();
+        response.PartitionsCount.ShouldBe(4);
+    }
+
+    [Test]
+    [DependsOn(nameof(CreatePartition_HappyPath_Should_CreatePartition_Successfully))]
+    public async Task DeletePartition_Should_DeletePartition_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].DeletePartitionsAsync(new DeletePartitionsRequest
+        {
+            PartitionsCount = 1,
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+
+        var response = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(1));
+        response.ShouldNotBeNull();
+        response.PartitionsCount.ShouldBe(3);
+    }
+
+    [Test]
+    [DependsOn(nameof(DeletePartition_Should_DeletePartition_Successfully))]
+    public async Task DeletePartition_Should_Throw_WhenTopic_DoesNotExist()
+    {
+        await Fixture.Clients[protocol].DeleteTopicAsync(Identifier.Numeric(1), Identifier.Numeric(1));
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].DeletePartitionsAsync(new DeletePartitionsRequest
+        {
+            PartitionsCount = 1,
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(DeletePartition_Should_Throw_WhenTopic_DoesNotExist))]
+    public async Task DeletePartition_Should_Throw_WhenStream_DoesNotExist()
+    {
+        await Fixture.Clients[protocol].DeleteStreamAsync(Identifier.Numeric(1));
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].DeletePartitionsAsync(new DeletePartitionsRequest
+        {
+            PartitionsCount = 1,
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/PersonalAccessTokenTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/PersonalAccessTokenTests.cs
new file mode 100644
index 00000000..eb14ea2d
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/PersonalAccessTokenTests.cs
@@ -0,0 +1,98 @@
+// // 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.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class PersonalAccessTokenTests(Protocol protocol)
+{
+    private static readonly CreatePersonalAccessTokenRequest CreatePersonalAccessTokenRequest = new()
+    {
+        Name = "test-pat",
+        Expiry = 100_000_000
+    };
+
+    [ClassDataSource<IggyServerFixture>(Shared = SharedType.PerClass)]
+    public required IggyServerFixture Fixture { get; init; }
+
+
+    [Test]
+    public async Task CreatePersonalAccessToken_HappyPath_Should_CreatePersonalAccessToken_Successfully()
+    {
+        var result = await Fixture.Clients[protocol].CreatePersonalAccessTokenAsync(CreatePersonalAccessTokenRequest);
+
+        result.ShouldNotBeNull();
+        result.Token.ShouldNotBeNullOrEmpty();
+    }
+
+    [Test]
+    [DependsOn(nameof(CreatePersonalAccessToken_HappyPath_Should_CreatePersonalAccessToken_Successfully))]
+    public async Task CreatePersonalAccessToken_Duplicate_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].CreatePersonalAccessTokenAsync(CreatePersonalAccessTokenRequest));
+    }
+
+    [Test]
+    [DependsOn(nameof(CreatePersonalAccessToken_Duplicate_Should_Throw_InvalidResponse))]
+    public async Task GetPersonalAccessTokens_Should_ReturnValidResponse()
+    {
+        IReadOnlyList<PersonalAccessTokenResponse> response = await Fixture.Clients[protocol].GetPersonalAccessTokensAsync();
+
+        response.ShouldNotBeNull();
+        response.Count.ShouldBe(1);
+        response[0].Name.ShouldBe(CreatePersonalAccessTokenRequest.Name);
+        var tokenExpiryDateTimeOffset = DateTimeOffset.UtcNow.AddMicroseconds((double)CreatePersonalAccessTokenRequest.Expiry!);
+        response[0].ExpiryAt!.Value.ToUniversalTime().ShouldBe(tokenExpiryDateTimeOffset, TimeSpan.FromMinutes(1));
+    }
+
+    [Test]
+    [DependsOn(nameof(GetPersonalAccessTokens_Should_ReturnValidResponse))]
+    public async Task LoginWithPersonalAccessToken_Should_Be_Successfully()
+    {
+        var response = await Fixture.Clients[protocol].CreatePersonalAccessTokenAsync(new CreatePersonalAccessTokenRequest
+        {
+            Name = "test-pat-login",
+            Expiry = 100_000_000
+        });
+
+        var client = Fixture.CreateClient(protocol);
+
+        var authResponse = await client.LoginWithPersonalAccessToken(new LoginWithPersonalAccessToken
+        {
+            Token = response!.Token
+        });
+
+        authResponse.ShouldNotBeNull();
+        authResponse.UserId.ShouldBe(1);
+    }
+
+    [Test]
+    [DependsOn(nameof(LoginWithPersonalAccessToken_Should_Be_Successfully))]
+    public async Task DeletePersonalAccessToken_Should_DeletePersonalAccessToken_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].DeletePersonalAccessTokenAsync(new DeletePersonalAccessTokenRequest
+        {
+            Name = CreatePersonalAccessTokenRequest.Name
+        }));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/PollMessagesTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/PollMessagesTests.cs
new file mode 100644
index 00000000..91064a41
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/PollMessagesTests.cs
@@ -0,0 +1,60 @@
+// // 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.Enums;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Apache.Iggy.Tests.Integrations.Models;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class PollMessagesTests(Protocol protocol)
+{
+    [ClassDataSource<PollMessagesFixture>(Shared = SharedType.PerClass)]
+    public required PollMessagesFixture Fixture { get; init; }
+
+    [Test]
+    [Timeout(60_000)]
+    public async Task PollMessagesTMessage_Should_PollMessages_Successfully(CancellationToken token)
+    {
+        var messageCount = 0;
+        await foreach (MessageResponse<DummyMessage> msgResponse in Fixture.Clients[protocol].PollMessagesAsync(new PollMessagesRequest
+                       {
+                           Consumer = Consumer.New(1),
+                           Count = 10,
+                           PartitionId = 1,
+                           PollingStrategy = PollingStrategy.Next(),
+                           StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+                           TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+                       }, DummyMessage.DeserializeDummyMessage, token: token))
+        {
+            msgResponse.UserHeaders.ShouldNotBeNull();
+            msgResponse.UserHeaders.Count.ShouldBe(2);
+            msgResponse.Message.Text.ShouldContain("Dummy message");
+            messageCount++;
+            if (messageCount == Fixture.MessageCount)
+            {
+                break;
+            }
+        }
+
+        messageCount.ShouldBe(Fixture.MessageCount);
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/SendMessagesTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/SendMessagesTests.cs
new file mode 100644
index 00000000..3b4ee9e0
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/SendMessagesTests.cs
@@ -0,0 +1,118 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Headers;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+using Partitioning = Apache.Iggy.Kinds.Partitioning;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class SendMessagesTests(Protocol protocol)
+{
+    private static Message[] _messagesWithoutHeaders = [];
+    private static Message[] _messagesWithHeaders = [];
+
+    [ClassDataSource<SendMessageFixture>(Shared = SharedType.PerClass)]
+    public required SendMessageFixture Fixture { get; set; }
+
+    [Before(Class)]
+    public static Task Before()
+    {
+        var dummyJson = """
+                        {
+                          "userId": 1,
+                          "id": 1,
+                          "title": "delete",
+                          "completed": false
+                        }
+                        """;
+        _messagesWithoutHeaders = [new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson)), new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson))];
+        _messagesWithHeaders =
+        [
+            new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson), new Dictionary<HeaderKey, HeaderValue>
+            {
+                { HeaderKey.New("header1"), HeaderValue.FromString("value1") },
+                { HeaderKey.New("header2"), HeaderValue.FromInt32(444) }
+            }),
+            new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson), new Dictionary<HeaderKey, HeaderValue>
+            {
+                { HeaderKey.New("header1"), HeaderValue.FromString("value1") },
+                { HeaderKey.New("header2"), HeaderValue.FromInt32(444) }
+            })
+        ];
+
+        return Task.CompletedTask;
+    }
+
+    [Test]
+    public async Task SendMessages_NoHeaders_Should_SendMessages_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Messages = _messagesWithoutHeaders,
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(SendMessages_NoHeaders_Should_SendMessages_Successfully))]
+    public async Task SendMessages_NoHeaders_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Messages = _messagesWithoutHeaders,
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(69)
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(SendMessages_NoHeaders_Should_Throw_InvalidResponse))]
+    public async Task SendMessages_WithHeaders_Should_SendMessages_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Messages = _messagesWithHeaders,
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(Fixture.TopicRequest.TopicId!.Value)
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(SendMessages_WithHeaders_Should_SendMessages_Successfully))]
+    public async Task SendMessages_WithHeaders_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Messages = _messagesWithHeaders,
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(Fixture.StreamRequest.StreamId!.Value),
+            TopicId = Identifier.Numeric(69)
+        }));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/StreamsTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/StreamsTests.cs
new file mode 100644
index 00000000..a3f7e643
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/StreamsTests.cs
@@ -0,0 +1,209 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Apache.Iggy.Tests.Integrations.Helpers;
+using Shouldly;
+using Partitioning = Apache.Iggy.Kinds.Partitioning;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class StreamsTests(Protocol protocol)
+{
+    private static readonly StreamRequest StreamRequest = new()
+    {
+        Name = "test-stream",
+        StreamId = 1
+    };
+
+    private static readonly UpdateStreamRequest UpdateStreamRequest = new()
+    {
+        Name = "updated-test-stream"
+    };
+
+    [ClassDataSource<IggyServerFixture>(Shared = SharedType.PerClass)]
+    public required IggyServerFixture Fixture { get; init; }
+
+    [Test]
+    public async Task CreateStream_HappyPath_Should_CreateStream_Successfully()
+    {
+        var response = await Fixture.Clients[protocol].CreateStreamAsync(StreamRequest);
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(StreamRequest.StreamId!.Value);
+        response.Name.ShouldBe(StreamRequest.Name);
+        response.Size.ShouldBe(0u);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(20));
+        response.MessagesCount.ShouldBe(0u);
+        response.TopicsCount.ShouldBe(0);
+        response.Topics.ShouldBeEmpty();
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateStream_HappyPath_Should_CreateStream_Successfully))]
+    public async Task CreateStream_Duplicate_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].CreateStreamAsync(StreamRequest));
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateStream_Duplicate_Should_Throw_InvalidResponse))]
+    public async Task GetStreams_Should_ReturnValidResponse()
+    {
+        var secondStreamRequest = StreamFactory.CreateStream(2);
+        await Fixture.Clients[protocol].CreateStreamAsync(secondStreamRequest);
+
+        IReadOnlyList<StreamResponse> response = await Fixture.Clients[protocol].GetStreamsAsync();
+
+        response.ShouldNotBeNull();
+        response.Count.ShouldBe(2);
+        response.ShouldContain(x => x.Id == StreamRequest.StreamId!.Value);
+        response.ShouldContain(x => x.Id == secondStreamRequest.StreamId!.Value);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetStreams_Should_ReturnValidResponse))]
+    public async Task GetStreamById_Should_ReturnValidResponse()
+    {
+        var response = await Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value));
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(StreamRequest.StreamId.Value);
+        response.Name.ShouldBe(StreamRequest.Name);
+        response.Size.ShouldBe(0u);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(20));
+        response.MessagesCount.ShouldBe(0u);
+        response.TopicsCount.ShouldBe(0);
+        response.Topics.ShouldBeEmpty();
+    }
+
+    [Test]
+    [DependsOn(nameof(GetStreamById_Should_ReturnValidResponse))]
+    public async Task GetStreamById_WithTopics_Should_ReturnValidResponse()
+    {
+        var topicRequest1 = TopicFactory.CreateTopic(messageExpiry: 100_000);
+        var topicRequest2 = TopicFactory.CreateTopic(2, messageExpiry: 100_000);
+
+        await Fixture.Clients[protocol].CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), topicRequest1);
+        await Fixture.Clients[protocol].CreateTopicAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), topicRequest2);
+
+
+        await Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(topicRequest1.TopicId!.Value),
+            Messages = new List<Message>
+            {
+                new(Guid.NewGuid(), "Test message 1"u8.ToArray()),
+                new(Guid.NewGuid(), "Test message 2"u8.ToArray()),
+                new(Guid.NewGuid(), "Test message 3"u8.ToArray())
+            }
+        });
+
+        await Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            Partitioning = Partitioning.None(),
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(2),
+            Messages = new List<Message>
+            {
+                new(Guid.NewGuid(), "Test message 4"u8.ToArray()),
+                new(Guid.NewGuid(), "Test message 5"u8.ToArray()),
+                new(Guid.NewGuid(), "Test message 6"u8.ToArray()),
+                new(Guid.NewGuid(), "Test message 7"u8.ToArray())
+            }
+        });
+
+        var response = await Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value));
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(StreamRequest.StreamId.Value);
+        response.Name.ShouldBe(StreamRequest.Name);
+        response.Size.ShouldBe(490u);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(20));
+        response.MessagesCount.ShouldBe(7u);
+        response.TopicsCount.ShouldBe(2);
+        response.Topics.Count().ShouldBe(2);
+
+        var topic = response.Topics.First(x => x.Id == topicRequest1.TopicId!.Value);
+        topic.Id.ShouldBe(topicRequest1.TopicId!.Value);
+        topic.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        topic.Name.ShouldBe(topicRequest1.Name);
+        topic.CompressionAlgorithm.ShouldBe(topicRequest1.CompressionAlgorithm);
+        topic.Partitions.ShouldBeNull();
+        topic.MessageExpiry.ShouldBe(topicRequest1.MessageExpiry);
+        topic.Size.ShouldBe(210u);
+        topic.PartitionsCount.ShouldBe(topicRequest1.PartitionsCount);
+        topic.ReplicationFactor.ShouldBe(topicRequest1.ReplicationFactor);
+        topic.MaxTopicSize.ShouldBeGreaterThan(0u);
+        topic.MessagesCount.ShouldBe(3u);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetStreamById_WithTopics_Should_ReturnValidResponse))]
+    public async Task UpdateStream_Should_UpdateStream_Successfully()
+    {
+        await Fixture.Clients[protocol].UpdateStreamAsync(Identifier.Numeric(StreamRequest.StreamId!.Value), UpdateStreamRequest);
+
+        var result = await Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value));
+        result.ShouldNotBeNull();
+        result.Name.ShouldBe(UpdateStreamRequest.Name);
+    }
+
+    [Test]
+    [DependsOn(nameof(UpdateStream_Should_UpdateStream_Successfully))]
+    public async Task PurgeStream_Should_PurgeStream_Successfully()
+    {
+        // Ensure the stream has messages (created in previous steps) before purging
+        var stream = await Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value));
+        stream.ShouldNotBeNull();
+        stream.MessagesCount.ShouldBe(7u);
+        stream.TopicsCount.ShouldBe(2);
+
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].PurgeStreamAsync(Identifier.Numeric(StreamRequest.StreamId!.Value)));
+
+        stream = await Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value));
+        stream.ShouldNotBeNull();
+        stream.MessagesCount.ShouldBe(0u);
+        stream.TopicsCount.ShouldBe(2);
+    }
+
+    [Test]
+    [DependsOn(nameof(PurgeStream_Should_PurgeStream_Successfully))]
+    public async Task DeleteStream_Should_DeleteStream_Successfully()
+    {
+        await Should.NotThrowAsync(() => Fixture.Clients[protocol].DeleteStreamAsync(Identifier.Numeric(StreamRequest.StreamId!.Value)));
+    }
+
+    [Test]
+    [DependsOn(nameof(DeleteStream_Should_DeleteStream_Successfully))]
+    public async Task DeleteStream_NotExists_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].DeleteStreamAsync(Identifier.Numeric(StreamRequest.StreamId!.Value)));
+    }
+
+    [Test]
+    [DependsOn(nameof(DeleteStream_NotExists_Should_Throw_InvalidResponse))]
+    public async Task GetStreamById_AfterDelete_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(() => Fixture.Clients[protocol].GetStreamByIdAsync(Identifier.Numeric(StreamRequest.StreamId!.Value)));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/SystemTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/SystemTests.cs
new file mode 100644
index 00000000..576b72d3
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/SystemTests.cs
@@ -0,0 +1,195 @@
+// // 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.Contracts.Http.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Kinds;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Attributes;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Apache.Iggy.Tests.Integrations.Helpers;
+using Shouldly;
+using Partitioning = Apache.Iggy.Kinds.Partitioning;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class SystemTests(Protocol protocol)
+{
+    [ClassDataSource<SystemFixture>(Shared = SharedType.PerClass)]
+    public required SystemFixture Fixture { get; init; }
+
+    [Test]
+    public async Task GetClients_Should_Return_CorrectClientsCount()
+    {
+        IReadOnlyList<ClientResponse> clients = await Fixture.Clients[protocol].GetClientsAsync();
+
+        clients.Count.ShouldBe(Fixture.TotalClientsCount);
+        foreach (var client in clients)
+        {
+            client.ClientId.ShouldNotBe(0u);
+            client.UserId.ShouldBeGreaterThan(0u);
+            client.Address.ShouldNotBeNullOrEmpty();
+            client.Transport.ShouldBe("TCP");
+        }
+    }
+
+    [Test]
+    [DependsOn(nameof(GetClients_Should_Return_CorrectClientsCount))]
+    public async Task GetClient_Should_Return_CorrectClient()
+    {
+        IReadOnlyList<ClientResponse> clients = await Fixture.Clients[protocol].GetClientsAsync();
+
+        clients.Count.ShouldBe(Fixture.TotalClientsCount);
+        var id = clients[0].ClientId;
+        var response = await Fixture.Clients[protocol].GetClientByIdAsync(id);
+        response.ShouldNotBeNull();
+        response.ClientId.ShouldBe(id);
+        response.UserId.ShouldBeGreaterThan(0u);
+        response.Address.ShouldNotBeNullOrEmpty();
+        response.Transport.ShouldBe("TCP");
+        response.ConsumerGroupsCount.ShouldBe(0);
+        response.ConsumerGroups.ShouldBeEmpty();
+    }
+
+    [Test]
+    [SkipHttp]
+    [DependsOn(nameof(GetClient_Should_Return_CorrectClient))]
+    public async Task GetMe_Tcp_Should_Return_MyClient()
+    {
+        var me = await Fixture.Clients[protocol].GetMeAsync();
+        me.ShouldNotBeNull();
+        me.ClientId.ShouldNotBe(0u);
+        me.UserId.ShouldBe(1u);
+        me.Address.ShouldNotBeNullOrEmpty();
+        me.Transport.ShouldBe("TCP");
+    }
+
+    [Test]
+    [SkipTcp]
+    [DependsOn(nameof(GetClient_Should_Return_CorrectClient))]
+    public async Task GetMe_HTTP_Should_Throw_FeatureUnavailableException()
+    {
+        await Should.ThrowAsync<FeatureUnavailableException>(() => Fixture.Clients[protocol].GetMeAsync());
+    }
+
+    [Test]
+    [DependsOn(nameof(GetMe_HTTP_Should_Throw_FeatureUnavailableException))]
+    [DependsOn(nameof(GetMe_Tcp_Should_Return_MyClient))]
+    public async Task GetClient_WithConsumerGroup_Should_Return_CorrectClient()
+    {
+        var client = Fixture.CreateClient(Protocol.Tcp, protocol);
+        await client.LoginUser(new LoginUserRequest
+        {
+            Password = "iggy",
+            Username = "iggy"
+        });
+        await client.CreateStreamAsync(StreamFactory.CreateStream());
+        await client.CreateTopicAsync(Identifier.Numeric(1), new TopicRequest
+        {
+            Name = "test_topic",
+            PartitionsCount = 2
+        });
+        var consumerGroup = await client.CreateConsumerGroupAsync(new CreateConsumerGroupRequest
+        {
+            Name = "test_consumer_group",
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            ConsumerGroupId = 1
+        });
+        await client.JoinConsumerGroupAsync(new JoinConsumerGroupRequest
+        {
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            ConsumerGroupId = Identifier.Numeric(consumerGroup!.Id)
+        });
+        var me = await client.GetMeAsync();
+
+
+        var response = await Fixture.Clients[protocol].GetClientByIdAsync(me!.ClientId);
+        response.ShouldNotBeNull();
+        response.UserId.ShouldBe(1u);
+        response.Address.ShouldNotBeNullOrEmpty();
+        response.Transport.ShouldBe("TCP");
+        response.ConsumerGroupsCount.ShouldBe(1);
+        response.ConsumerGroups.ShouldNotBeEmpty();
+        response.ConsumerGroups.ShouldContain(x => x.GroupId == consumerGroup.Id);
+        response.ConsumerGroups.ShouldContain(x => x.TopicId == 1);
+        response.ConsumerGroups.ShouldContain(x => x.StreamId == 1);
+    }
+
+
+    [Test]
+    [DependsOn(nameof(GetClient_WithConsumerGroup_Should_Return_CorrectClient))]
+    public async Task GetStats_Should_ReturnValidResponse()
+    {
+        await Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+        {
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            Partitioning = Partitioning.None(),
+            Messages = [new Message(Guid.NewGuid(), "Test message"u8.ToArray())]
+        });
+
+        await Fixture.Clients[protocol].FetchMessagesAsync(new MessageFetchRequest
+        {
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1),
+            AutoCommit = true,
+            Consumer = Consumer.New(1),
+            Count = 1,
+            PartitionId = 1,
+            PollingStrategy = PollingStrategy.First()
+        });
+
+        var response = await Fixture.Clients[protocol].GetStatsAsync();
+        response.ShouldNotBeNull();
+        response.ProcessId.ShouldBeGreaterThanOrEqualTo(0);
+        response.CpuUsage.ShouldBeGreaterThanOrEqualTo(0);
+        response.TotalCpuUsage.ShouldBeGreaterThanOrEqualTo(0);
+        response.MemoryUsage.ShouldBeGreaterThanOrEqualTo(0u);
+        response.TotalMemory.ShouldBeGreaterThanOrEqualTo(0u);
+        response.AvailableMemory.ShouldNotBe(0u);
+        response.RunTime.ShouldBeGreaterThanOrEqualTo(0u);
+        response.StartTime.ShouldBe(DateTimeOffset.UtcNow, TimeSpan.FromMinutes(5));
+        response.ReadBytes.ShouldBeGreaterThanOrEqualTo(0u);
+        response.WrittenBytes.ShouldBeGreaterThanOrEqualTo(0u);
+        response.MessagesSizeBytes.ShouldBeGreaterThanOrEqualTo(0u);
+        response.StreamsCount.ShouldNotBe(0);
+        response.TopicsCount.ShouldNotBe(0);
+        response.PartitionsCount.ShouldNotBe(0);
+        response.SegmentsCount.ShouldNotBe(0);
+        response.MessagesCount.ShouldNotBe(0u);
+        response.ClientsCount.ShouldNotBe(0);
+        response.ConsumerGroupsCount.ShouldNotBe(0);
+        response.Hostname.ShouldNotBeNullOrEmpty();
+        response.OsName.ShouldNotBeNullOrEmpty();
+        response.OsVersion.ShouldNotBeNullOrEmpty();
+        response.KernelVersion.ShouldNotBeNullOrEmpty();
+        response.IggyServerVersion.ShouldNotBeNullOrEmpty();
+        response.IggyServerSemver.ShouldNotBe(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetStats_Should_ReturnValidResponse))]
+    public async Task Ping_Should_Pong()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].PingAsync());
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/TopicsTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/TopicsTests.cs
new file mode 100644
index 00000000..30b926ba
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/TopicsTests.cs
@@ -0,0 +1,243 @@
+// // 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.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Messages;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Apache.Iggy.Tests.Integrations.Helpers;
+using Shouldly;
+using Partitioning = Apache.Iggy.Kinds.Partitioning;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class TopicsTests(Protocol protocol)
+{
+    private static readonly TopicRequest TopicRequest = new(1, "Test Topic", CompressionAlgorithm.Gzip, 1000, 1, 2, 2_000_000_000);
+    private static readonly TopicRequest TopicRequestSecond = new(2, "Test Topic 2", CompressionAlgorithm.Gzip, 1000, 1, 2, 2_000_000_000);
+    private static readonly UpdateTopicRequest UpdateTopicRequest = new("Updated Topic", CompressionAlgorithm.Gzip, 3_000_000_000, 2000, 3);
+
+    [ClassDataSource<IggyServerFixture>(Shared = SharedType.PerClass)]
+    public required IggyServerFixture Fixture { get; init; }
+
+    [Test]
+    public async Task Create_NewTopic_Should_Return_Successfully()
+    {
+        await Fixture.Clients[protocol].CreateStreamAsync(StreamFactory.CreateStream());
+
+        var response = await Fixture.Clients[protocol].CreateTopicAsync(Identifier.Numeric(1), TopicRequest);
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(TopicRequest.TopicId!.Value);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        response.Name.ShouldBe(TopicRequest.Name);
+        response.CompressionAlgorithm.ShouldBe(TopicRequest.CompressionAlgorithm);
+        response.Partitions!.Count().ShouldBe(TopicRequest.PartitionsCount);
+        response.MessageExpiry.ShouldBe(TopicRequest.MessageExpiry);
+        response.Size.ShouldBe(0u);
+        response.PartitionsCount.ShouldBe(TopicRequest.PartitionsCount);
+        response.ReplicationFactor.ShouldBe(TopicRequest.ReplicationFactor);
+        response.MaxTopicSize.ShouldBe(TopicRequest.MaxTopicSize);
+        response.MessagesCount.ShouldBe(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(Create_NewTopic_Should_Return_Successfully))]
+    public async Task Create_DuplicateTopic_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].CreateTopicAsync(Identifier.Numeric(1), TopicRequest));
+    }
+
+    [Test]
+    [DependsOn(nameof(Create_DuplicateTopic_Should_Throw_InvalidResponse))]
+    public async Task Get_ExistingTopic_Should_ReturnValidResponse()
+    {
+        var response = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(1));
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(TopicRequest.TopicId!.Value);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        response.Name.ShouldBe(TopicRequest.Name);
+        response.CompressionAlgorithm.ShouldBe(TopicRequest.CompressionAlgorithm);
+        response.Partitions!.Count().ShouldBe(TopicRequest.PartitionsCount);
+        response.MessageExpiry.ShouldBe(TopicRequest.MessageExpiry);
+        response.Size.ShouldBe(0u);
+        response.PartitionsCount.ShouldBe(TopicRequest.PartitionsCount);
+        response.ReplicationFactor.ShouldBe(TopicRequest.ReplicationFactor);
+        response.MaxTopicSize.ShouldBe(TopicRequest.MaxTopicSize);
+        response.MessagesCount.ShouldBe(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(Get_ExistingTopic_Should_ReturnValidResponse))]
+    public async Task Get_ExistingTopics_Should_ReturnValidResponse()
+    {
+        await Fixture.Clients[protocol].CreateTopicAsync(Identifier.Numeric(1), TopicRequestSecond);
+
+        IReadOnlyList<TopicResponse> response = await Fixture.Clients[protocol].GetTopicsAsync(Identifier.Numeric(1));
+
+        response.ShouldNotBeNull();
+        response.Count().ShouldBe(2);
+        response.Select(x => x.Id).ShouldContain(TopicRequest.TopicId!.Value);
+        response.Select(x => x.Id).ShouldContain(TopicRequestSecond.TopicId!.Value);
+
+        var firstTopic = response.First(x => x.Id == TopicRequest.TopicId!.Value);
+        firstTopic.Id.ShouldBe(TopicRequest.TopicId!.Value);
+        firstTopic.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        firstTopic.Name.ShouldBe(TopicRequest.Name);
+        firstTopic.CompressionAlgorithm.ShouldBe(TopicRequest.CompressionAlgorithm);
+        firstTopic.Partitions.ShouldBeNull();
+        firstTopic.MessageExpiry.ShouldBe(TopicRequest.MessageExpiry);
+        firstTopic.Size.ShouldBe(0u);
+        firstTopic.PartitionsCount.ShouldBe(TopicRequest.PartitionsCount);
+        firstTopic.ReplicationFactor.ShouldBe(TopicRequest.ReplicationFactor);
+        firstTopic.MaxTopicSize.ShouldBe(TopicRequest.MaxTopicSize);
+        firstTopic.MessagesCount.ShouldBe(0u);
+
+        var secondTopic = response.First(x => x.Id == TopicRequestSecond.TopicId!.Value);
+        secondTopic.Id.ShouldBe(TopicRequestSecond.TopicId!.Value);
+        secondTopic.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        secondTopic.Name.ShouldBe(TopicRequestSecond.Name);
+        secondTopic.CompressionAlgorithm.ShouldBe(TopicRequestSecond.CompressionAlgorithm);
+        secondTopic.Partitions.ShouldBeNull();
+        secondTopic.MessageExpiry.ShouldBe(TopicRequestSecond.MessageExpiry);
+        secondTopic.Size.ShouldBe(0u);
+        secondTopic.PartitionsCount.ShouldBe(TopicRequestSecond.PartitionsCount);
+        secondTopic.ReplicationFactor.ShouldBe(TopicRequestSecond.ReplicationFactor);
+        secondTopic.MaxTopicSize.ShouldBe(TopicRequestSecond.MaxTopicSize);
+        secondTopic.MessagesCount.ShouldBe(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(Get_ExistingTopics_Should_ReturnValidResponse))]
+    public async Task Get_Topic_WithPartitions_Should_ReturnValidResponse()
+    {
+        await Fixture.Clients[protocol].CreatePartitionsAsync(new CreatePartitionsRequest
+        {
+            PartitionsCount = 2,
+            StreamId = Identifier.Numeric(1),
+            TopicId = Identifier.Numeric(1)
+        });
+
+        for (var i = 0; i < 3; i++)
+        {
+            await Fixture.Clients[protocol].SendMessagesAsync(new MessageSendRequest
+            {
+                StreamId = Identifier.Numeric(1),
+                TopicId = Identifier.Numeric(1),
+                Partitioning = Partitioning.None(),
+                Messages = GetMessages(i + 2)
+            });
+        }
+
+        var response = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(1));
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe(TopicRequest.TopicId!.Value);
+        response.CreatedAt.UtcDateTime.ShouldBe(DateTimeOffset.UtcNow.UtcDateTime, TimeSpan.FromSeconds(10));
+        response.Name.ShouldBe(TopicRequest.Name);
+        response.CompressionAlgorithm.ShouldBe(TopicRequest.CompressionAlgorithm);
+        response.Partitions!.Count().ShouldBe(3);
+        response.MessageExpiry.ShouldBe(TopicRequest.MessageExpiry);
+        response.Size.ShouldBe(630u);
+        response.PartitionsCount.ShouldBe(3);
+        response.ReplicationFactor.ShouldBe(TopicRequest.ReplicationFactor);
+        response.MaxTopicSize.ShouldBe(TopicRequest.MaxTopicSize);
+        response.MessagesCount.ShouldBe(9u);
+        response.Partitions.ShouldNotBeNull();
+        response.Partitions!.ShouldAllBe(x => x.MessagesCount > 0);
+        response.Partitions.ShouldAllBe(x => x.CreatedAt > DateTimeOffset.UtcNow.AddMinutes(-5));
+        response.Partitions.ShouldAllBe(x => x.SegmentsCount > 0);
+        response.Partitions.ShouldAllBe(x => x.CurrentOffset > 0);
+        response.Partitions.ShouldAllBe(x => x.Size > 0);
+        response.Partitions.ShouldAllBe(x => x.Id > 0);
+    }
+
+    private static List<Message> GetMessages(int count)
+    {
+        var messages = new List<Message>();
+        for (var i = 0; i < count; i++)
+        {
+            messages.Add(new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes($"Test message {i + 1}")));
+        }
+
+        return messages;
+    }
+
+    [Test]
+    [DependsOn(nameof(Get_Topic_WithPartitions_Should_ReturnValidResponse))]
+    public async Task Update_ExistingTopic_Should_UpdateTopic_Successfully()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].UpdateTopicAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value), UpdateTopicRequest));
+
+        var result = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value));
+        result.ShouldNotBeNull();
+        result!.Name.ShouldBe(UpdateTopicRequest.Name);
+        result.MessageExpiry.ShouldBe(UpdateTopicRequest.MessageExpiry);
+        result.CompressionAlgorithm.ShouldBe(UpdateTopicRequest.CompressionAlgorithm);
+        result.MaxTopicSize.ShouldBe(UpdateTopicRequest.MaxTopicSize);
+        result.ReplicationFactor.ShouldBe(UpdateTopicRequest.ReplicationFactor);
+    }
+
+    [Test]
+    [DependsOn(nameof(Update_ExistingTopic_Should_UpdateTopic_Successfully))]
+    public async Task Purge_ExistingTopic_Should_PurgeTopic_Successfully()
+    {
+        var beforePurge = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(TopicRequest.TopicId!.Value));
+
+        beforePurge.ShouldNotBeNull();
+        beforePurge.MessagesCount.ShouldBe(9u);
+        beforePurge.Size.ShouldBeGreaterThan(0u);
+
+        await Should.NotThrowAsync(Fixture.Clients[protocol].PurgeTopicAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value)));
+
+        var afterPurge = await Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1), Identifier.Numeric(TopicRequest.TopicId!.Value));
+        afterPurge.ShouldNotBeNull();
+        afterPurge!.MessagesCount.ShouldBe(0u);
+        afterPurge.Size.ShouldBe(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(Purge_ExistingTopic_Should_PurgeTopic_Successfully))]
+    public async Task Delete_ExistingTopic_Should_DeleteTopic_Successfully()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].DeleteTopicAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value)));
+    }
+
+    [Test]
+    [DependsOn(nameof(Delete_ExistingTopic_Should_DeleteTopic_Successfully))]
+    public async Task Delete_NonExistingTopic_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].DeleteTopicAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value)));
+    }
+
+    [Test]
+    [DependsOn(nameof(Delete_NonExistingTopic_Should_Throw_InvalidResponse))]
+    public async Task Get_NonExistingTopic_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].GetTopicByIdAsync(Identifier.Numeric(1),
+            Identifier.Numeric(TopicRequest.TopicId!.Value)));
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.Tests.Integration/UsersTests.cs b/foreign/csharp/Iggy_SDK.Tests.Integration/UsersTests.cs
new file mode 100644
index 00000000..abdd39f8
--- /dev/null
+++ b/foreign/csharp/Iggy_SDK.Tests.Integration/UsersTests.cs
@@ -0,0 +1,266 @@
+// // 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.Contracts.Http.Auth;
+using Apache.Iggy.Enums;
+using Apache.Iggy.Exceptions;
+using Apache.Iggy.Tests.Integrations.Fixtures;
+using Shouldly;
+
+namespace Apache.Iggy.Tests.Integrations;
+
+[MethodDataSource<IggyServerFixture>(nameof(IggyServerFixture.ProtocolData))]
+public class UsersTests(Protocol protocol)
+{
+    private const string Username = "test_user_1";
+    private const string NewUsername = "new_user_name";
+
+    [ClassDataSource<IggyServerFixture>(Shared = SharedType.PerClass)]
+    public required IggyServerFixture Fixture { get; init; }
+
+    [Test]
+    public async Task CreateUser_Should_CreateUser_Successfully()
+    {
+        var request = new CreateUserRequest
+        {
+            Username = Username,
+            Password = "test_password_1",
+            Status = UserStatus.Active
+        };
+
+        var result = await Fixture.Clients[protocol].CreateUser(request);
+        result.ShouldNotBeNull();
+        result.Username.ShouldBe(request.Username);
+        result.Status.ShouldBe(request.Status);
+        result.Id.ShouldBeGreaterThan(0u);
+        result.CreatedAt.ShouldBeGreaterThan(0u);
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateUser_Should_CreateUser_Successfully))]
+    public async Task CreateUser_Duplicate_Should_Throw_InvalidResponse()
+    {
+        var request = new CreateUserRequest
+        {
+            Username = Username,
+            Password = "test1",
+            Status = UserStatus.Active
+        };
+
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].CreateUser(request));
+    }
+
+    [Test]
+    [DependsOn(nameof(CreateUser_Duplicate_Should_Throw_InvalidResponse))]
+    public async Task GetUser_WithoutPermissions_Should_ReturnValidResponse()
+    {
+        var response = await Fixture.Clients[protocol].GetUser(Identifier.Numeric(2));
+
+        response.ShouldNotBeNull();
+        response.Id.ShouldBe((uint)2);
+        response.Username.ShouldBe(Username);
+        response.Status.ShouldBe(UserStatus.Active);
+        response.CreatedAt.ShouldBeGreaterThan(0u);
+        response.Permissions.ShouldBeNull();
+    }
+
+    [Test]
+    [DependsOn(nameof(GetUser_WithoutPermissions_Should_ReturnValidResponse))]
+    public async Task GetUsers_Should_ReturnValidResponse()
+    {
+        IReadOnlyList<UserResponse> response = await Fixture.Clients[protocol].GetUsers();
+
+        response.ShouldNotBeNull();
+        response.ShouldNotBeEmpty();
+        response.ShouldContain(user => user.Id == 2 && user.Username == Username && user.Status == UserStatus.Active);
+        response.ShouldAllBe(user => user.CreatedAt > 0u);
+        response.ShouldAllBe(user => user.Permissions == null);
+    }
+
+    [Test]
+    [DependsOn(nameof(GetUsers_Should_ReturnValidResponse))]
+    public async Task UpdateUser_Should_UpdateUser_Successfully()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].UpdateUser(new UpdateUserRequest
+        {
+            UserId = Identifier.Numeric(2), Username = "new_user_name", UserStatus = UserStatus.Active
+        }));
+
+        var user = await Fixture.Clients[protocol].GetUser(Identifier.Numeric(2));
+
+        user.ShouldNotBeNull();
+        user.Id.ShouldBe(2u);
+        user.Username.ShouldBe("new_user_name");
+        user.Status.ShouldBe(UserStatus.Active);
+        user.CreatedAt.ShouldBeGreaterThan(0u);
+        user.Permissions.ShouldBeNull();
+    }
+
+    [Test]
+    [DependsOn(nameof(UpdateUser_Should_UpdateUser_Successfully))]
+    public async Task UpdatePermissions_Should_UpdatePermissions_Successfully()
+    {
+        var permissions = CreatePermissions();
+        await Should.NotThrowAsync(Fixture.Clients[protocol].UpdatePermissions(new UpdateUserPermissionsRequest
+        {
+            UserId = Identifier.Numeric(2),
+            Permissions = permissions
+        }));
+
+        var user = await Fixture.Clients[protocol].GetUser(Identifier.Numeric(2));
+
+        user.ShouldNotBeNull();
+        user.Id.ShouldBe(2u);
+        user.Permissions.ShouldNotBeNull();
+        user.Permissions!.Global.ShouldNotBeNull();
+        user.Permissions.Global.ManageServers.ShouldBeTrue();
+        user.Permissions.Global.ManageUsers.ShouldBeTrue();
+        user.Permissions.Global.ManageStreams.ShouldBeTrue();
+        user.Permissions.Global.ManageTopics.ShouldBeTrue();
+        user.Permissions.Global.PollMessages.ShouldBeTrue();
+        user.Permissions.Global.ReadServers.ShouldBeTrue();
+        user.Permissions.Global.ReadStreams.ShouldBeTrue();
+        user.Permissions.Global.ReadTopics.ShouldBeTrue();
+        user.Permissions.Global.ReadUsers.ShouldBeTrue();
+        user.Permissions.Global.SendMessages.ShouldBeTrue();
+        user.Permissions.Streams.ShouldNotBeNull();
+        user.Permissions.Streams.ShouldContainKey(1);
+        user.Permissions.Streams[1].ManageStream.ShouldBeTrue();
+        user.Permissions.Streams[1].ManageTopics.ShouldBeTrue();
+        user.Permissions.Streams[1].ReadStream.ShouldBeTrue();
+        user.Permissions.Streams[1].SendMessages.ShouldBeTrue();
+        user.Permissions.Streams[1].ReadTopics.ShouldBeTrue();
+        user.Permissions.Streams[1].PollMessages.ShouldBeTrue();
+        user.Permissions.Streams[1].Topics.ShouldNotBeNull();
+        user.Permissions.Streams[1].Topics!.ShouldContainKey(1);
+        user.Permissions.Streams[1].Topics![1].ManageTopic.ShouldBeTrue();
+        user.Permissions.Streams[1].Topics![1].PollMessages.ShouldBeTrue();
+        user.Permissions.Streams[1].Topics![1].ReadTopic.ShouldBeTrue();
+        user.Permissions.Streams[1].Topics![1].SendMessages.ShouldBeTrue();
+    }
+
+    [Test]
+    [DependsOn(nameof(UpdatePermissions_Should_UpdatePermissions_Successfully))]
+    public async Task ChangePassword_Should_ChangePassword_Successfully()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].ChangePassword(new ChangePasswordRequest
+        {
+            UserId = Identifier.Numeric(2),
+            CurrentPassword = "test_password_1",
+            NewPassword = "user2"
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(ChangePassword_Should_ChangePassword_Successfully))]
+    public async Task ChangePassword_WrongCurrentPassword_Should_Throw_InvalidResponse()
+    {
+        await Should.ThrowAsync<InvalidResponseException>(Fixture.Clients[protocol].ChangePassword(new ChangePasswordRequest
+        {
+            UserId = Identifier.Numeric(2),
+            CurrentPassword = "test_password_1",
+            NewPassword = "user2"
+        }));
+    }
+
+    [Test]
+    [DependsOn(nameof(ChangePassword_WrongCurrentPassword_Should_Throw_InvalidResponse))]
+    public async Task LoginUser_Should_LoginUser_Successfully()
+    {
+        var client = Fixture.CreateClient(protocol);
+
+        var response = await client.LoginUser(new LoginUserRequest
+        {
+            Username = NewUsername,
+            Password = "user2"
+        });
+
+        response.ShouldNotBeNull();
+        response.UserId.ShouldBe(2);
+        switch (protocol)
+        {
+            case Protocol.Tcp:
+                response.AccessToken.ShouldBeNull();
+                break;
+            case Protocol.Http:
+                response.AccessToken.ShouldNotBeNull();
+                break;
+        }
+    }
+
+    [Test]
+    [DependsOn(nameof(LoginUser_Should_LoginUser_Successfully))]
+    public async Task DeleteUser_Should_DeleteUser_Successfully()
+    {
+        await Should.NotThrowAsync(Fixture.Clients[protocol].DeleteUser(Identifier.Numeric(2)));
+    }
+
+    [Test]
+    [DependsOn(nameof(DeleteUser_Should_DeleteUser_Successfully))]
+    public async Task LogoutUser_Should_LogoutUser_Successfully()
+    {
+        // act & assert
+        await Should.NotThrowAsync(Fixture.Clients[protocol].LogoutUser());
+    }
+
+    private static Permissions CreatePermissions()
+    {
+        return new Permissions
+        {
+            Global = new GlobalPermissions
+            {
+                ManageServers = true,
+                ManageUsers = true,
+                ManageStreams = true,
+                ManageTopics = true,
+                PollMessages = true,
+                ReadServers = true,
+                ReadStreams = true,
+                ReadTopics = true,
+                ReadUsers = true,
+                SendMessages = true
+            },
+            Streams = new Dictionary<int, StreamPermissions>
+            {
+                {
+                    1, new StreamPermissions
+                    {
+                        ManageStream = true,
+                        ManageTopics = true,
+                        ReadStream = true,
+                        SendMessages = true,
+                        ReadTopics = true,
+                        PollMessages = true,
+                        Topics = new Dictionary<int, TopicPermissions>
+                        {
+                            {
+                                1, new TopicPermissions
+                                {
+                                    ManageTopic = true,
+                                    PollMessages = true,
+                                    ReadTopic = true,
+                                    SendMessages = true
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK.sln b/foreign/csharp/Iggy_SDK.sln
index cffbef77..536284bf 100644
--- a/foreign/csharp/Iggy_SDK.sln
+++ b/foreign/csharp/Iggy_SDK.sln
@@ -12,6 +12,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csp
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{466C54AD-A5FA-401C-9300-8FE21D3C355A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iggy_SDK.Tests.Integration", "Iggy_SDK.Tests.Integration\Iggy_SDK.Tests.Integration.csproj", "{59661E1B-1D5E-4D58-AE7B-10E8ABB9839A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{3A9B7D3B-EFA9-4652-A647-C72F84616114}"
+	ProjectSection(SolutionItems) = preProject
+		DEPENDENCIES.md = DEPENDENCIES.md
+		Directory.Packages.props = Directory.Packages.props
+		LICENSE = LICENSE
+		NOTICE = NOTICE
+		README.md = README.md
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -42,5 +53,9 @@ Global
 		{466C54AD-A5FA-401C-9300-8FE21D3C355A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{466C54AD-A5FA-401C-9300-8FE21D3C355A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{466C54AD-A5FA-401C-9300-8FE21D3C355A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{59661E1B-1D5E-4D58-AE7B-10E8ABB9839A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{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
 	EndGlobalSection
 EndGlobal
diff --git a/foreign/csharp/Iggy_SDK.sln.DotSettings b/foreign/csharp/Iggy_SDK.sln.DotSettings
index 7479ae10..9709c09a 100644
--- a/foreign/csharp/Iggy_SDK.sln.DotSettings
+++ b/foreign/csharp/Iggy_SDK.sln.DotSettings
@@ -1,13 +1,21 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexedValue"></s:String>
+	<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexRemoved">True</s:Boolean>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceUsingStatementBraces/@EntryIndexedValue"></s:String>
+	<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceUsingStatementBraces/@EntryIndexRemoved">True</s:Boolean>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">SUGGESTION</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">SUGGESTION</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">SUGGESTION</s:String>
-	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">NotRequired</s:String>
-	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_USING/@EntryValue">NotRequired</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_USING/@EntryValue">Required</s:String>
+	<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
+	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean>
-	<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVarWhenEvident</s:String>
+	<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVar</s:String>
 	<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseVarWhenEvident</s:String>
-	<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVarWhenEvident</s:String>
+	<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVar</s:String>
 	<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">// 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
@@ -24,8 +32,13 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.</s:String>
+	<s:String x:Key="/Default/Environment/Hierarchy/Build/SolBuilderDuo/CustomGlobalProperties/=PreferredUILang/@EntryIndexedValue">en-US</s:String>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EUnitTestFramework_002EMigrations_002EEnableDisabledProvidersMigration/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=Testing_0020Platform/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=VsTest/@EntryIndexedValue">True</s:Boolean>
+	</wpf:ResourceDictionary>
diff --git a/foreign/csharp/Iggy_SDK/Configuration/TlsConfiguration.cs b/foreign/csharp/Iggy_SDK/Configuration/TlsConfiguration.cs
index ad7ce9a0..7e2f4735 100644
--- a/foreign/csharp/Iggy_SDK/Configuration/TlsConfiguration.cs
+++ b/foreign/csharp/Iggy_SDK/Configuration/TlsConfiguration.cs
@@ -19,7 +19,7 @@ namespace Apache.Iggy.Configuration;
 
 public class TlsSettings
 {
-    public bool Enabled { get; set; } 
-    public string Hostname { get; set; }
+    public bool Enabled { get; set; }
+    public string Hostname { get; set; } = string.Empty;
     public bool Authenticate { get; set; } = false;
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/Auth/AuthResponse.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/Auth/AuthResponse.cs
index 8f581e9c..0595a2e2 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/Auth/AuthResponse.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/Auth/AuthResponse.cs
@@ -17,4 +17,4 @@
 
 namespace Apache.Iggy.Contracts.Http.Auth;
 
-public record AuthResponse(int UserId, TokenInfo AccessToken);
+public record AuthResponse(int UserId, TokenInfo? AccessToken);
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/ClientInfo.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/ClientInfo.cs
index 084f89d4..4b7ecb6f 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/ClientInfo.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/ClientInfo.cs
@@ -24,5 +24,5 @@ public sealed class ClientResponse
     public required uint UserId { get; init; }
     public required string Transport { get; init; }
     public required int ConsumerGroupsCount { get; init; }
-    public IEnumerable<ConsumerGroupInfo>? ConsumerGroups { get; init; }
+    public IEnumerable<ConsumerGroupInfo> ConsumerGroups { get; init; } = [];
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupInfo.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupInfo.cs
index 37497d72..3dddb9d0 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupInfo.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupInfo.cs
@@ -21,5 +21,5 @@ public sealed class ConsumerGroupInfo
 {
     public required int StreamId { get; init; }
     public required int TopicId { get; init; }
-    public required int ConsumerGroupId { get; init; }
+    public required int GroupId { get; init; }
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupMembers.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupMembers.cs
index 8b4478e7..83e38319 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupMembers.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/ConsumerGroupMembers.cs
@@ -19,7 +19,7 @@ namespace Apache.Iggy.Contracts.Http;
 
 public sealed class ConsumerGroupMember
 {
-    public required int Id { get; init; }
+    public required uint Id { get; init; }
     public required int PartitionsCount { get; init; }
     public required List<int> Partitions { get; init; }
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/MessageResponse.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/MessageResponse.cs
index ba2e06ca..11fcc432 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/MessageResponse.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/MessageResponse.cs
@@ -23,6 +23,6 @@ namespace Apache.Iggy.Contracts.Http;
 public sealed class MessageResponse
 {
     public MessageHeader Header { get; set; }
-    public byte[] Payload { get; set; }
+    public byte[] Payload { get; set; } = [];
     public Dictionary<HeaderKey, HeaderValue>? UserHeaders { get; init; }
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/Stats.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/Stats.cs
index de2cc185..f51419cd 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/Stats.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/Stats.cs
@@ -43,5 +43,5 @@ public sealed class Stats
     public required string KernelVersion { get; init; }
     public required string IggyServerVersion { get; init; }
     public uint IggyServerSemver { get; init; }
-    public List<CacheMetrics> CacheMetrics { get; set; }
+    public List<CacheMetrics> CacheMetrics { get; set; } = [];
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/StatsResponse.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/StatsResponse.cs
index f9e0fb44..8addbc2c 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/StatsResponse.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/StatsResponse.cs
@@ -45,7 +45,8 @@ public sealed class StatsResponse
     public required string OsVersion { get; init; }
     public required string KernelVersion { get; init; }
     public required string IggyVersion { get; init; }
-    public List<CacheMetrics> CacheMetrics { get; init; }
+    public uint IggyServerSemver { get; init; }
+    public List<CacheMetrics> CacheMetrics { get; init; } = [];
 
     internal Stats ToStats()
     {
@@ -74,6 +75,7 @@ public sealed class StatsResponse
             OsVersion = OsVersion,
             KernelVersion = KernelVersion,
             IggyServerVersion = IggyVersion,
+            IggyServerSemver = IggyServerSemver,
             CacheMetrics = CacheMetrics
         };
     }
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/StreamResponse.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/StreamResponse.cs
index c92319ec..ffac3a96 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/StreamResponse.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/StreamResponse.cs
@@ -26,5 +26,5 @@ public sealed class StreamResponse
     public required DateTimeOffset CreatedAt { get; init; }
     public required ulong MessagesCount { get; init; }
     public required int TopicsCount { get; init; }
-    public IEnumerable<TopicResponse>? Topics { get; init; }
+    public IEnumerable<TopicResponse> Topics { get; init; } = [];
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Http/TopicRequest.cs b/foreign/csharp/Iggy_SDK/Contracts/Http/TopicRequest.cs
index 74cd21c1..cba72ad0 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Http/TopicRequest.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Http/TopicRequest.cs
@@ -15,15 +15,41 @@
 // specific language governing permissions and limitations
 // under the License.
 
+using System.Diagnostics.CodeAnalysis;
 using Apache.Iggy.Enums;
 
 namespace Apache.Iggy.Contracts.Http;
 
-public record TopicRequest(
-    int? TopicId, // If not provided, the Iggy server will generate one automatically
-    string Name,
-    CompressionAlgorithm CompressionAlgorithm,
-    ulong MessageExpiry,
-    int PartitionsCount,
-    byte ReplicationFactor,
-    ulong MaxTopicSize);
\ No newline at end of file
+public sealed class TopicRequest
+{
+    public int? TopicId { get; set; }
+    public required string Name { get; set; }
+    public CompressionAlgorithm CompressionAlgorithm { get; set; } = CompressionAlgorithm.None;
+    public ulong MessageExpiry { get; set; } = 0;
+    public int PartitionsCount { get; set; } = 1;
+    public byte ReplicationFactor { get; set; } = 1;
+    public ulong MaxTopicSize { get; set; } = 0;
+
+    public TopicRequest()
+    {
+        
+    }
+
+    [SetsRequiredMembers]
+    public TopicRequest(int? topicId,
+        string name,
+        CompressionAlgorithm compressionAlgorithm,
+        ulong messageExpiry,
+        int partitionsCount,
+        byte replicationFactor,
+        ulong maxTopicSize)
+    {
+        TopicId = topicId;
+        Name = name;
+        CompressionAlgorithm = compressionAlgorithm;
+        MessageExpiry = messageExpiry;
+        PartitionsCount = partitionsCount;
+        ReplicationFactor = replicationFactor;
+        MaxTopicSize = maxTopicSize;
+    }
+}
diff --git a/foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs b/foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs
index 182aa71a..540af6c4 100644
--- a/foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs
+++ b/foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs
@@ -766,18 +766,6 @@ internal static class TcpContracts
         };
     }
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    private static byte GetPartitioningKindByte(Partitioning kind)
-    {
-        return kind switch
-        {
-            Partitioning.Balanced => 1,
-            Partitioning.PartitionId => 2,
-            Partitioning.MessageKey => 3,
-            _ => throw new ArgumentOutOfRangeException()
-        };
-    }
-
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     private static byte GetPollingStrategyByte(MessagePolling pollingStrategy)
     {
diff --git a/foreign/csharp/Iggy_SDK/Exceptions/InvalidResponseException.cs b/foreign/csharp/Iggy_SDK/Exceptions/InvalidResponseException.cs
index 76ceb135..6b3febda 100644
--- a/foreign/csharp/Iggy_SDK/Exceptions/InvalidResponseException.cs
+++ b/foreign/csharp/Iggy_SDK/Exceptions/InvalidResponseException.cs
@@ -17,7 +17,7 @@
 
 namespace Apache.Iggy.Exceptions;
 
-internal sealed class InvalidResponseException : Exception
+public sealed class InvalidResponseException : Exception
 {
     internal InvalidResponseException(string message) : base(message)
     {
diff --git a/foreign/csharp/Iggy_SDK/IggyClient/IIggyOffset.cs b/foreign/csharp/Iggy_SDK/IggyClient/IIggyOffset.cs
index c1194db2..ffc4c3cf 100644
--- a/foreign/csharp/Iggy_SDK/IggyClient/IIggyOffset.cs
+++ b/foreign/csharp/Iggy_SDK/IggyClient/IIggyOffset.cs
@@ -21,6 +21,6 @@ namespace Apache.Iggy.IggyClient;
 
 public interface IIggyOffset
 {
-    Task StoreOffsetAsync(StoreOffsetRequest contract, CancellationToken token = default);
+    Task  StoreOffsetAsync(StoreOffsetRequest contract, CancellationToken token = default);
     Task<OffsetResponse?> GetOffsetAsync(OffsetRequest request, CancellationToken token = default);
 }
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/IggyClient/Implementations/HttpMessageStream.cs b/foreign/csharp/Iggy_SDK/IggyClient/Implementations/HttpMessageStream.cs
index 178e07c1..c2de8630 100644
--- a/foreign/csharp/Iggy_SDK/IggyClient/Implementations/HttpMessageStream.cs
+++ b/foreign/csharp/Iggy_SDK/IggyClient/Implementations/HttpMessageStream.cs
@@ -399,7 +399,7 @@ public class HttpMessageStream : IIggyClient
             }
             catch(Exception e)
             {
-                _logger.LogError("Error encountered while polling messages - Stream ID: {streamId}, Topic ID: {topicId}, Partition ID: {partitionId}",
+                _logger.LogError(e, "Error encountered while polling messages - Stream ID: {StreamId}, Topic ID: {TopicId}, Partition ID: {PartitionId}",
                     request.StreamId, request.TopicId, request.PartitionId);
             }
             
@@ -481,7 +481,7 @@ public class HttpMessageStream : IIggyClient
 
     public Task<ClientResponse?> GetMeAsync(CancellationToken token = default)
     {
-        throw new NotImplementedException();
+        throw new FeatureUnavailableException();
     }
 
     public async Task<Stats?> GetStatsAsync(CancellationToken token = default)
diff --git a/foreign/csharp/Iggy_SDK/IggyClient/Implementations/TcpMessageStream.cs b/foreign/csharp/Iggy_SDK/IggyClient/Implementations/TcpMessageStream.cs
index da67c342..62f3a40f 100644
--- a/foreign/csharp/Iggy_SDK/IggyClient/Implementations/TcpMessageStream.cs
+++ b/foreign/csharp/Iggy_SDK/IggyClient/Implementations/TcpMessageStream.cs
@@ -270,6 +270,7 @@ public sealed class TcpMessageStream : IIggyClient, IDisposable
         await _channel!.Writer.WriteAsync(request, token);
     }
     
+    // TODO: Change TMessage implementation
     public async Task SendMessagesAsync<TMessage>(MessageSendRequest<TMessage> request,
         Func<TMessage, byte[]> serializer,
         Func<byte[], byte[]>? encryptor = null, 
@@ -543,16 +544,7 @@ public sealed class TcpMessageStream : IIggyClient, IDisposable
         await _stream.SendAsync(payload, token);
         await _stream.FlushAsync(token);
 
-        var buffer = new byte[BufferSizes.ExpectedResponseSize];
-        await _stream.ReadAsync(buffer, token);
-
-        var response = TcpMessageStreamHelpers.GetResponseLengthAndStatus(buffer);
-        if (response.Status != 0)
-        {
-            var errorBuffer = new byte[response.Length];
-            await _stream.ReadAsync(errorBuffer, token);
-            throw new InvalidResponseException(Encoding.UTF8.GetString(errorBuffer));
-        }
+        await CheckResponseAsync(token);
     }
 
     public async Task<OffsetResponse?> GetOffsetAsync(OffsetRequest request, CancellationToken token = default)
@@ -614,7 +606,7 @@ public sealed class TcpMessageStream : IIggyClient, IDisposable
         return BinaryMapper.MapConsumerGroup(responseBuffer);
     }
 
-    public async Task<ConsumerGroupResponse> CreateConsumerGroupAsync(CreateConsumerGroupRequest request, CancellationToken token = default)
+    public async Task<ConsumerGroupResponse?> CreateConsumerGroupAsync(CreateConsumerGroupRequest request, CancellationToken token = default)
     {
         var message = TcpContracts.CreateGroup(request);
         var payload = new byte[4 + BufferSizes.InitialBytesLength + message.Length];
@@ -906,7 +898,7 @@ public sealed class TcpMessageStream : IIggyClient, IDisposable
         var userId = BinaryPrimitives.ReadInt32LittleEndian(responseBuffer.AsSpan()[..(responseBuffer.Length)]);
 
         //TODO: Figure out how to solve this workaround about default of TokenInfo
-        return new AuthResponse(userId, default);
+        return new AuthResponse(userId, null);
     }
     
     public async Task LogoutUser(CancellationToken token = default)
diff --git a/foreign/csharp/Iggy_SDK/Iggy_SDK.csproj b/foreign/csharp/Iggy_SDK/Iggy_SDK.csproj
index bd8f0624..daa94fde 100644
--- a/foreign/csharp/Iggy_SDK/Iggy_SDK.csproj
+++ b/foreign/csharp/Iggy_SDK/Iggy_SDK.csproj
@@ -18,6 +18,14 @@
         <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
         <PackageReadmeFile>README.md</PackageReadmeFile>
     </PropertyGroup>
+    
+    <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+      <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    </PropertyGroup>
+    
+    <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+      <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    </PropertyGroup>
 
     <ItemGroup>
         <None Include="../README.md" Pack="true" PackagePath="">
@@ -29,8 +37,8 @@
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
-      <PackageReference Include="System.IO.Hashing" Version="9.0.5" />
+      <PackageReference Include="Microsoft.Extensions.Logging" />
+      <PackageReference Include="System.IO.Hashing" />
     </ItemGroup>
 
     <ItemGroup>
@@ -38,4 +46,5 @@
         <Link>Properties\DEPENDENCIES.md</Link>
       </Content>
     </ItemGroup>
+    
 </Project>
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/AuthResponseConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/AuthResponseConverter.cs
index 6e498f8f..eb1806cb 100644
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/AuthResponseConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConfiguration/AuthResponseConverter.cs
@@ -24,7 +24,6 @@ namespace Apache.Iggy.JsonConfiguration;
 
 public sealed class AuthResponseConverter : JsonConverter<AuthResponse>
 {
-
     public override AuthResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
     {
         using var doc = JsonDocument.ParseValue(ref reader);
@@ -35,6 +34,11 @@ public sealed class AuthResponseConverter : JsonConverter<AuthResponse>
         var token = accessToken.GetProperty(nameof(TokenInfo.Token).ToSnakeCase()).GetString();
         var accessTokenExpiry = accessToken.GetProperty(nameof(TokenInfo.Expiry).ToSnakeCase()).GetInt64();
 
+        if(token is null)
+        {
+            throw new JsonException("Access token is null");
+        }
+        
         return new AuthResponse(
             userId,
             new TokenInfo(token, DateTimeOffset.FromUnixTimeSeconds(accessTokenExpiry).LocalDateTime)
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/MessagesConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/MessagesConverter.cs
index 75d67bba..a8123553 100644
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/MessagesConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConfiguration/MessagesConverter.cs
@@ -35,13 +35,12 @@ internal sealed class MessagesConverter : JsonConverter<MessageSendRequest>
     {
         if (value.Messages.Any())
         {
-            
             writer.WriteStartObject();
             writer.WriteStartObject("partitioning");
 
             writer.WriteString(nameof(MessageSendRequest.Partitioning.Kind).ToSnakeCase(), value: value.Partitioning.Kind switch
             {
-                Partitioning.Balanced => "none",
+                Partitioning.Balanced => "balanced",
                 Partitioning.MessageKey => "entity_id",
                 Partitioning.PartitionId => "partition_id",
                 _ => throw new InvalidEnumArgumentException()
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/StatsResponseConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/StatsResponseConverter.cs
index 7a1c92a7..c265e175 100644
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/StatsResponseConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConfiguration/StatsResponseConverter.cs
@@ -35,35 +35,15 @@ public class StatsResponseConverter : JsonConverter<StatsResponse>
         float cpuUsage = root.GetProperty(nameof(Stats.CpuUsage).ToSnakeCase()).GetSingle();
         float totalCpuUsage = root.GetProperty(nameof(Stats.TotalCpuUsage).ToSnakeCase()).GetSingle();
         
-        string? memoryUsageString = root.GetProperty(nameof(Stats.MemoryUsage).ToSnakeCase()).GetString();
-        string[] memoryUsageStringSplit = memoryUsageString.Split(' ');
-        (float memoryUsageBytesVal, string memoryUnit) = (ParseFloat(memoryUsageStringSplit[0]), memoryUsageStringSplit[1]);
-        ulong memoryUsage = ConvertStringBytesToUlong(memoryUnit, memoryUsageBytesVal);
-        
-        string? totalMemoryString = root.GetProperty(nameof(Stats.TotalMemory).ToSnakeCase()).GetString();
-        string[] totalMemoryStringSplit = totalMemoryString.Split(' ');
-        (float totalMemoryUsageBytesVal, string totalMemoryUnit) = (ParseFloat(totalMemoryStringSplit[0]), totalMemoryStringSplit[1]);
-        ulong totalMemoryUsage = ConvertStringBytesToUlong(totalMemoryUnit, totalMemoryUsageBytesVal);
-        
-        string? availableMemoryString = root.GetProperty(nameof(Stats.AvailableMemory).ToSnakeCase()).GetString();
-        string[] availableMemoryStringSplit = availableMemoryString.Split(' ');
-        (float availableMemoryBytesVal, string availableMemoryUnit) = (ParseFloat(availableMemoryStringSplit[0]), availableMemoryStringSplit[1]);
-        ulong availableMemory = ConvertStringBytesToUlong(availableMemoryUnit, availableMemoryBytesVal);
+        ulong memoryUsage = ConvertStringToUlong(root.GetProperty(nameof(Stats.MemoryUsage).ToSnakeCase()).GetString());
+        ulong totalMemoryUsage = ConvertStringToUlong(root.GetProperty(nameof(Stats.TotalMemory).ToSnakeCase()).GetString());
+        ulong availableMemory = ConvertStringToUlong(root.GetProperty(nameof(Stats.AvailableMemory).ToSnakeCase()).GetString());
         
         var runtime = root.GetProperty(nameof(Stats.RunTime).ToSnakeCase()).GetUInt64();
         ulong startTime = root.GetProperty(nameof(Stats.StartTime).ToSnakeCase()).GetUInt64();
-        string? readBytesString = root.GetProperty(nameof(Stats.ReadBytes).ToSnakeCase()).GetString();
-        string[] readBytesStringSplit = readBytesString.Split(' ');
-        (float readBytesVal, string readBytesUnit) = (ParseFloat(readBytesStringSplit[0]), readBytesStringSplit[1]);
-        ulong readBytes = ConvertStringBytesToUlong(readBytesUnit, readBytesVal);
-        string? writtenBytesString = root.GetProperty(nameof(Stats.WrittenBytes).ToSnakeCase()).GetString();
-        string[] writtenBytesStringSplit = writtenBytesString.Split(' ');
-        (float writtenBytesVal, string writtenBytesUnit) = (ParseFloat(writtenBytesStringSplit[0]), writtenBytesStringSplit[1]);
-        ulong writtenBytes = ConvertStringBytesToUlong(writtenBytesUnit, writtenBytesVal);
-        string? messageWrittenSizeBytesString = root.GetProperty(nameof(Stats.MessagesSizeBytes).ToSnakeCase()).GetString();
-        string[] messageWrittenSizeBytesStringSplit = messageWrittenSizeBytesString.Split(' ');
-        (float messageWrittenSizeBytesVal, string messageWrittenSizeBytesUnit) = (ParseFloat(messageWrittenSizeBytesStringSplit[0]), messageWrittenSizeBytesStringSplit[1]);
-        ulong messageWrittenSizeBytes = ConvertStringBytesToUlong(messageWrittenSizeBytesUnit, messageWrittenSizeBytesVal);
+        ulong readBytes = ConvertStringToUlong(root.GetProperty(nameof(Stats.ReadBytes).ToSnakeCase()).GetString());
+        ulong writtenBytes = ConvertStringToUlong(root.GetProperty(nameof(Stats.WrittenBytes).ToSnakeCase()).GetString());
+        ulong messageWrittenSizeBytes = ConvertStringToUlong(root.GetProperty(nameof(Stats.MessagesSizeBytes).ToSnakeCase()).GetString());
         int streamsCount = root.GetProperty(nameof(Stats.StreamsCount).ToSnakeCase()).GetInt32();
         int topicsCount = root.GetProperty(nameof(Stats.TopicsCount).ToSnakeCase()).GetInt32();
         int partitionsCount = root.GetProperty(nameof(Stats.PartitionsCount).ToSnakeCase()).GetInt32();
@@ -76,6 +56,8 @@ public class StatsResponseConverter : JsonConverter<StatsResponse>
         string? osVersion = root.GetProperty(nameof(Stats.OsVersion).ToSnakeCase()).GetString();
         string? kernelVersion = root.GetProperty(nameof(Stats.KernelVersion).ToSnakeCase()).GetString();
         string? iggyVersion = root.GetProperty(nameof(Stats.IggyServerVersion).ToSnakeCase()).GetString();
+        uint iggyServerSemver = root.GetProperty(nameof(Stats.IggyServerSemver).ToSnakeCase()).GetUInt32();
+        
         
         return new StatsResponse
         {
@@ -83,13 +65,13 @@ public class StatsResponseConverter : JsonConverter<StatsResponse>
             ClientsCount = clientsCount,
             ConsumerGroupsCount = consumerGroupsCount,
             CpuUsage = cpuUsage,
-            Hostname = hostname,
-            KernelVersion = kernelVersion,
+            Hostname = hostname ?? string.Empty,
+            KernelVersion = kernelVersion ?? string.Empty,
             MemoryUsage = memoryUsage,
             MessagesCount = messagesCount,
             MessagesSizeBytes = messageWrittenSizeBytes,
-            OsName = osName,
-            OsVersion = osVersion,
+            OsName = osName ?? string.Empty,
+            OsVersion = osVersion ?? string.Empty,
             PartitionsCount = partitionsCount,
             ProcessId = processId,
             ReadBytes = readBytes,
@@ -101,11 +83,29 @@ public class StatsResponseConverter : JsonConverter<StatsResponse>
             TotalCpuUsage = totalCpuUsage,
             TotalMemory = totalMemoryUsage,
             WrittenBytes = writtenBytes,
-            IggyVersion = iggyVersion
+            IggyVersion = iggyVersion ?? string.Empty,
+            IggyServerSemver = iggyServerSemver
         };
 
     }
 
+    private ulong ConvertStringToUlong(string? usage)
+    {
+        if(usage is null)
+        {
+            return 0;
+        }
+        
+        var usageStringSplit = usage.Split(' ');
+        if (usageStringSplit.Length != 2)
+        {
+            throw new InvalidEnumArgumentException($"Error Wrong format when deserializing MemoryUsage: {usage}");
+        }
+        
+        var (memoryUsageBytesVal, memoryUnit) = (ParseFloat(usageStringSplit[0]), usageStringSplit[1]);
+        return ConvertStringBytesToUlong(memoryUnit, memoryUsageBytesVal);
+    }
+    
     private static ulong ConvertStringBytesToUlong(string memoryUnit, float memoryUsageBytesVal)
     {
         float memoryUsage = memoryUnit switch
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/StreamResponseConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/StreamResponseConverter.cs
index 3c7dd200..894b6466 100644
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/StreamResponseConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConfiguration/StreamResponseConverter.cs
@@ -33,17 +33,22 @@ public sealed class StreamResponseConverter : JsonConverter<StreamResponse>
         var createdAt = root.GetProperty(nameof(StreamResponse.CreatedAt).ToSnakeCase()).GetUInt64();
         var name = root.GetProperty(nameof(StreamResponse.Name).ToSnakeCase()).GetString();
         var sizeBytesString = root.GetProperty(nameof(StreamResponse.Size).ToSnakeCase()).GetString();
-        var sizeBytesStringSplit = sizeBytesString.Split(' ');
-        var (sizeBytesVal, Unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
-        var sizeBytes = Unit switch
+        ulong sizeBytes = 0;
+        if (sizeBytesString is not null)
         {
-            "B" => sizeBytesVal,
-            "KB" => sizeBytesVal * (ulong)1e03,
-            "MB" => sizeBytesVal * (ulong)1e06,
-            "GB" => sizeBytesVal * (ulong)1e09,
-            "TB" => sizeBytesVal * (ulong)1e12,
-            _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
-        };
+            var sizeBytesStringSplit = sizeBytesString.Split(' ');
+            var (sizeBytesVal, unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
+            sizeBytes = unit switch
+            {
+                "B" => sizeBytesVal,
+                "KB" => sizeBytesVal * (ulong)1e03,
+                "MB" => sizeBytesVal * (ulong)1e06,
+                "GB" => sizeBytesVal * (ulong)1e09,
+                "TB" => sizeBytesVal * (ulong)1e12,
+                _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
+            };
+        }
+
         var messagesCount = root.GetProperty(nameof(StreamResponse.MessagesCount).ToSnakeCase()).GetUInt64();
         var topicsCount = root.GetProperty(nameof(StreamResponse.TopicsCount).ToSnakeCase()).GetInt32();
         root.TryGetProperty(nameof(StreamResponse.Topics).ToSnakeCase(), out var topicsProperty);
@@ -63,7 +68,7 @@ public sealed class StreamResponseConverter : JsonConverter<StreamResponse>
             CreatedAt = DateTimeOffsetUtils.FromUnixTimeMicroSeconds(createdAt).LocalDateTime,
             MessagesCount = messagesCount,
             TopicsCount = topicsCount,
-            Topics = topics
+            Topics = topics ?? []
         };
     }
 
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/TopicResponseConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/TopicResponseConverter.cs
index 9a52dcd1..b77a0d40 100644
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/TopicResponseConverter.cs
+++ b/foreign/csharp/Iggy_SDK/JsonConfiguration/TopicResponseConverter.cs
@@ -26,13 +26,6 @@ namespace Apache.Iggy.JsonConfiguration;
 
 internal sealed class TopicResponseConverter : JsonConverter<TopicResponse>
 {
-    private readonly JsonSerializerOptions _options;
-    public TopicResponseConverter()
-    {
-        _options = new JsonSerializerOptions();
-        _options.PropertyNamingPolicy = new ToSnakeCaseNamingPolicy();
-    }
-
     public override TopicResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
     {
         using var doc = JsonDocument.ParseValue(ref reader);
@@ -42,35 +35,24 @@ internal sealed class TopicResponseConverter : JsonConverter<TopicResponse>
         var name = root.GetProperty(nameof(TopicResponse.Name).ToSnakeCase()).GetString();
         var compressionAlgorithm = Enum.Parse<CompressionAlgorithm>(root.GetProperty(nameof(TopicResponse.CompressionAlgorithm).ToSnakeCase()).GetString()!, true);
         var sizeBytesString = root.GetProperty(nameof(TopicResponse.Size).ToSnakeCase()).GetString();
-        var sizeBytesStringSplit = sizeBytesString.Split(' ');
-        var (sizeBytesVal, Unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
-        var sizeBytes = Unit switch
+        ulong sizeBytes = 0;
+        if (sizeBytesString is not null)
         {
-            "B" => sizeBytesVal,
-            "KB" => sizeBytesVal * (ulong)1e03,
-            "MB" => sizeBytesVal * (ulong)1e06,
-            "GB" => sizeBytesVal * (ulong)1e09,
-            "TB" => sizeBytesVal * (ulong)1e12,
-            _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
-        };
+            var sizeBytesStringSplit = sizeBytesString.Split(' ');
+            var (sizeBytesVal, unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
+            sizeBytes = unit switch
+            {
+                "B" => sizeBytesVal,
+                "KB" => sizeBytesVal * (ulong)1e03,
+                "MB" => sizeBytesVal * (ulong)1e06,
+                "GB" => sizeBytesVal * (ulong)1e09,
+                "TB" => sizeBytesVal * (ulong)1e12,
+                _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
+            };
+        }
+
         var replicationFactor = root.GetProperty(nameof(TopicResponse.ReplicationFactor).ToSnakeCase()).GetUInt16();
         var maxTopicSize = root.GetProperty(nameof(TopicResponse.MaxTopicSize).ToSnakeCase()).GetUInt64();
-        // var maxTopicSizeString = root.GetProperty(nameof(TopicResponse.MaxTopicSize).ToSnakeCase()).GetUInt64();
-        // ulong maxTopicSize = 0;
-        // if (maxTopicSizeString is not null)
-        // {
-        //     var maxTopicSizeStringSplit = maxTopicSizeString.Split(' ');
-        //     (ulong maxTopicSizeVal, string maxTopicUnit) = (ulong.Parse(maxTopicSizeStringSplit[0]), maxTopicSizeStringSplit[1]);
-        //     maxTopicSize = Unit switch
-        //     {
-        //         "B" => maxTopicSizeVal,
-        //         "KB" => maxTopicSizeVal * (ulong)1e03,
-        //         "MB" => maxTopicSizeVal * (ulong)1e06,
-        //         "GB" => maxTopicSizeVal * (ulong)1e09,
-        //         "TB" => maxTopicSizeVal * (ulong)1e12,
-        //         _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
-        //     };
-        // }
 
         var messageExpiryProperty = root.GetProperty(nameof(TopicResponse.MessageExpiry).ToSnakeCase());
         var messageExpiry = messageExpiryProperty.ValueKind switch
@@ -118,17 +100,22 @@ internal sealed class TopicResponseConverter : JsonConverter<TopicResponse>
             var currentOffset = partition.GetProperty(nameof(PartitionContract.CurrentOffset).ToSnakeCase())
                 .GetUInt64();
             var sizeBytesString = partition.GetProperty(nameof(PartitionContract.Size).ToSnakeCase()).GetString();
-            var sizeBytesStringSplit = sizeBytesString.Split(' ');
-            var (sizeBytesVal, Unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
-            ulong sizeBytes = Unit switch
+            ulong sizeBytes = 0;
+            if (sizeBytesString is not null)
             {
-                "B" => sizeBytesVal,
-                "KB" => sizeBytesVal * (ulong)1e03,
-                "MB" => sizeBytesVal * (ulong)1e06,
-                "GB" => sizeBytesVal * (ulong)1e09,
-                "TB" => sizeBytesVal * (ulong)1e12,
-                _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
-            };
+                var sizeBytesStringSplit = sizeBytesString.Split(' ');
+                var (sizeBytesVal, unit) = (ulong.Parse(sizeBytesStringSplit[0]), sizeBytesStringSplit[1]);
+                sizeBytes = unit switch
+                {
+                    "B" => sizeBytesVal,
+                    "KB" => sizeBytesVal * (ulong)1e03,
+                    "MB" => sizeBytesVal * (ulong)1e06,
+                    "GB" => sizeBytesVal * (ulong)1e09,
+                    "TB" => sizeBytesVal * (ulong)1e12,
+                    _ => throw new InvalidEnumArgumentException("Error Wrong Unit when deserializing SizeBytes")
+                };
+            }
+
             var messagesCount = partition.GetProperty(nameof(PartitionContract.MessagesCount).ToSnakeCase())
                 .GetUInt64();
             partitions.Add(new PartitionContract
diff --git a/foreign/csharp/Iggy_SDK/JsonConfiguration/UserResponseConverter.cs b/foreign/csharp/Iggy_SDK/JsonConfiguration/UserResponseConverter.cs
deleted file mode 100644
index 7ea50ca9..00000000
--- a/foreign/csharp/Iggy_SDK/JsonConfiguration/UserResponseConverter.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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.Json;
-using System.Text.Json.Serialization;
-using Apache.Iggy.Contracts.Http.Auth;
-
-namespace Apache.Iggy.JsonConfiguration;
-
-public sealed class UserResponseConverter : JsonConverter<UserResponse>
-{
-
-    public override UserResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-    {
-        throw new NotImplementedException();
-    }
-    public override void Write(Utf8JsonWriter writer, UserResponse value, JsonSerializerOptions options)
-    {
-        throw new NotImplementedException();
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs b/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
index f4a1a5ad..4dc89310 100644
--- a/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
+++ b/foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs
@@ -238,7 +238,7 @@ internal static class BinaryMapper
                 {
                     StreamId = streamId,
                     TopicId = topicId,
-                    ConsumerGroupId = consumerGroupId
+                    GroupId = consumerGroupId
                 };
                 consumerGroups.Add(consumerGroup);
                 position += 12;
@@ -258,7 +258,7 @@ internal static class BinaryMapper
     {
         if (payload.Length == 0)
         {
-            return Array.Empty<ClientResponse>();
+            return [];
         }
 
         var response = new List<ClientResponse>();
@@ -316,19 +316,6 @@ internal static class BinaryMapper
         };
     }
     
-    private static MessageState MapMessageState(ReadOnlySpan<byte> payload, int position)
-    {
-        var state = payload[position + 8] switch
-        {
-            1 => MessageState.Available,
-            10 => MessageState.Unavailable,
-            20 => MessageState.Poisoned,
-            30 => MessageState.MarkedForDeletion,
-            _ => throw new ArgumentOutOfRangeException()
-        };
-        return state;
-    }
-    
     internal static PolledMessages MapMessages(ReadOnlySpan<byte> payload,
         Func<byte[], byte[]>? decryptor = null)
     {
@@ -669,7 +656,7 @@ internal static class BinaryMapper
         ulong createdAt = BinaryPrimitives.ReadUInt64LittleEndian(payload[(position + 4)..(position + 12)]);
         int partitionsCount = BinaryPrimitives.ReadInt32LittleEndian(payload[(position + 12)..(position + 16)]);
         ulong messageExpiry = BinaryPrimitives.ReadUInt64LittleEndian(payload[(position + 16)..(position + 24)]);
-        byte complessionAlgoritm = payload[position + 24];
+        byte compressionAlgorithm = payload[position + 24];
         ulong maxTopicSize = BinaryPrimitives.ReadUInt64LittleEndian(payload[(position + 25)..(position + 33)]);
         byte replicationFactor = payload[position + 33];
         ulong sizeBytes = BinaryPrimitives.ReadUInt64LittleEndian(payload[(position + 34)..(position + 42)]);
@@ -684,7 +671,7 @@ internal static class BinaryMapper
                 Id = id,
                 PartitionsCount = partitionsCount,
                 Name = name,
-                CompressionAlgorithm = (CompressionAlgorithm)complessionAlgoritm,
+                CompressionAlgorithm = (CompressionAlgorithm)compressionAlgorithm,
                 MessagesCount = messagesCount,
                 Size = sizeBytes,
                 CreatedAt = DateTimeOffsetUtils.FromUnixTimeMicroSeconds(createdAt).LocalDateTime,
@@ -842,7 +829,7 @@ internal static class BinaryMapper
     
     private static (ConsumerGroupMember, int readBytes) MapToMember(ReadOnlySpan<byte> payload, int position)
     {
-        var id = BinaryPrimitives.ReadInt32LittleEndian(payload[position..(position + 4)]);
+        var id = BinaryPrimitives.ReadUInt32LittleEndian(payload[position..(position + 4)]);
         var partitionsCount = BinaryPrimitives.ReadInt32LittleEndian(payload[(position + 4)..(position + 8)]);
         var partitions = new List<int>();
         for (int i = 0; i < partitionsCount; i++)
diff --git a/foreign/csharp/Iggy_SDK_Tests/ContractTests/TcpContract.cs b/foreign/csharp/Iggy_SDK_Tests/ContractTests/TcpContract.cs
index cbc03c24..a5bb9759 100644
--- a/foreign/csharp/Iggy_SDK_Tests/ContractTests/TcpContract.cs
+++ b/foreign/csharp/Iggy_SDK_Tests/ContractTests/TcpContract.cs
@@ -180,7 +180,7 @@ public sealed class TcpContract
         {
             UserId = Identifier.Numeric(1),
             Username = "newusername",
-            UserStatus = UserStatus.Active
+            UserStatus = UserStatus.Inactive
         };
 
         // Act
@@ -214,10 +214,11 @@ public sealed class TcpContract
         position += 1;
         if (statusFlag == 1)
         {
-            var userStatus = statusFlag switch
+            var userStatus = result[position] switch
             {
                 1 => UserStatus.Active,
-                2 => UserStatus.Inactive
+                2 => UserStatus.Inactive,
+                _ => throw new ArgumentOutOfRangeException()
             };
             Assert.Equal(request.UserStatus, userStatus);
         }
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/ConsumerGroupE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/ConsumerGroupE2E.cs
deleted file mode 100644
index 4f6bb240..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/ConsumerGroupE2E.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Groups;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class ConsumerGroupE2E : IClassFixture<IggyConsumerGroupFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-
-    private readonly IggyConsumerGroupFixture _fixture;
-
-    private const int GROUP_ID = 1;
-
-    private static readonly CreateConsumerGroupRequest _createConsumerGroupRequest =
-        ConsumerGroupFactory.CreateRequest(
-            (int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId!,
-            (int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId!, GROUP_ID);
-
-    private static readonly JoinConsumerGroupRequest _joinConsumerGroupRequest =
-        ConsumerGroupFactory.CreateJoinGroupRequest(
-            (int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId,
-            (int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId, GROUP_ID);
-
-    private static readonly LeaveConsumerGroupRequest _leaveConsumerGroupRequest =
-        ConsumerGroupFactory.CreateLeaveGroupRequest(
-            (int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId,
-            (int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId, GROUP_ID);
-
-    private static readonly DeleteConsumerGroupRequest _deleteConsumerGroupRequest =
-        ConsumerGroupFactory.CreateDeleteGroupRequest(
-            (int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId,
-            (int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId, GROUP_ID);
-
-    private Identifier ConsumerGroupId = Identifier.Numeric(GROUP_ID);
-
-    public ConsumerGroupE2E(IggyConsumerGroupFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task CreateConsumerGroup_HappyPath_Should_CreateConsumerGroup_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var consumerGroup = await sut.Client.CreateConsumerGroupAsync(_createConsumerGroupRequest);
-            consumerGroup.Should().NotBeNull();
-            consumerGroup!.Id.Should().Be(GROUP_ID);
-            consumerGroup.PartitionsCount.Should().Be(ConsumerGroupFixtureBootstrap.TopicRequest.PartitionsCount);
-            consumerGroup.MembersCount.Should().Be(0);
-            consumerGroup.Name.Should().Be(_createConsumerGroupRequest.Name);
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task CreateConsumerGroup_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.CreateConsumerGroupAsync(_createConsumerGroupRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task GetConsumerGroupById_Should_Return_ValidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetConsumerGroupByIdAsync(
-                Identifier.Numeric((int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId!), Identifier.Numeric((int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId!),
-                ConsumerGroupId);
-
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(GROUP_ID);
-            response.PartitionsCount.Should().Be(ConsumerGroupFixtureBootstrap.TopicRequest.PartitionsCount);
-            response.MembersCount.Should().Be(0);
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task GetConsumerGroups_Should_Return_ValidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetConsumerGroupsAsync(
-                Identifier.Numeric((int)ConsumerGroupFixtureBootstrap.StreamRequest.StreamId!), Identifier.Numeric((int)ConsumerGroupFixtureBootstrap.TopicRequest.TopicId!));
-
-            response.Should().NotBeNull();
-            response.Count.Should().Be(1);
-            var group = response.FirstOrDefault();
-            group!.Id.Should().Be(GROUP_ID);
-            group.PartitionsCount.Should().Be(ConsumerGroupFixtureBootstrap.TopicRequest.PartitionsCount);
-            group.MembersCount.Should().Be(0);
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(5)]
-    public async Task JoinConsumerGroup_Should_JoinConsumerGroup_Successfully()
-    {
-        // act & assert
-        await _fixture.HttpClient.Client.Invoking(y =>
-                y.JoinConsumerGroupAsync(_joinConsumerGroupRequest)
-            ).Should()
-            .ThrowExactlyAsync<FeatureUnavailableException>();
-
-        await _fixture.TcpClient.Client.Invoking(x => x.JoinConsumerGroupAsync(_joinConsumerGroupRequest))
-            .Should()
-            .NotThrowAsync();
-    }
-
-    [Fact, TestPriority(6)]
-    public async Task LeaveConsumerGroup_Should_LeaveConsumerGroup_Successfully()
-    {
-        // act & assert
-        await _fixture.HttpClient.Client.Invoking(x => x.LeaveConsumerGroupAsync(_leaveConsumerGroupRequest))
-            .Should()
-            .ThrowAsync<FeatureUnavailableException>();
-
-        await _fixture.TcpClient.Client.Invoking(x => x.LeaveConsumerGroupAsync(_leaveConsumerGroupRequest))
-            .Should()
-            .NotThrowAsync();
-    }
-
-    [Fact, TestPriority(7)]
-    public async Task DeleteConsumerGroup_Should_DeleteConsumerGroup_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.DeleteConsumerGroupAsync(_deleteConsumerGroupRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(8)]
-    public async Task JoinConsumerGroup_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        await _fixture.HttpClient.Client.Invoking(x => x.LeaveConsumerGroupAsync(_leaveConsumerGroupRequest))
-            .Should()
-            .ThrowAsync<FeatureUnavailableException>();
-
-        await _fixture.TcpClient.Client.Invoking(x => x.JoinConsumerGroupAsync(_joinConsumerGroupRequest))
-            .Should()
-            .ThrowExactlyAsync<InvalidResponseException>();
-    }
-
-    [Fact, TestPriority(9)]
-    public async Task DeleteConsumerGroup_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.DeleteConsumerGroupAsync(_deleteConsumerGroupRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/FetchMessagesE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/FetchMessagesE2E.cs
deleted file mode 100644
index 5061b775..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/FetchMessagesE2E.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Messages;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class FetchMessagesE2E : IClassFixture<IggyFetchMessagesFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-    private readonly IggyFetchMessagesFixture _fixture;
-
-    private static readonly MessageFetchRequest _messageFetchRequest =
-        MessageFactory.CreateMessageFetchRequestConsumer(10, FetchMessagesFixtureBootstrap.StreamId,
-            FetchMessagesFixtureBootstrap.TopicId, FetchMessagesFixtureBootstrap.PartitionId);
-
-    private static readonly MessageFetchRequest _headersMessageFetchRequest =
-        MessageFactory.CreateMessageFetchRequestConsumer(10, FetchMessagesFixtureBootstrap.StreamId,
-            FetchMessagesFixtureBootstrap.HeadersTopicId, FetchMessagesFixtureBootstrap.PartitionId);
-
-    private static readonly MessageFetchRequest _invalidFetchRequest =
-        MessageFactory.CreateMessageFetchRequestConsumer(10, FetchMessagesFixtureBootstrap.InvalidStreamId,
-            FetchMessagesFixtureBootstrap.InvalidTopicId, FetchMessagesFixtureBootstrap.PartitionId);
-    public FetchMessagesE2E(IggyFetchMessagesFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task PollMessagesTMessage_WithNoHeaders_Should_PollMessages_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.FetchMessagesAsync(_messageFetchRequest, MessageFactory.DeserializeDummyMessage);
-            response.Messages.Count.Should().Be(10);
-            response.PartitionId.Should().Be(FetchMessagesFixtureBootstrap.PartitionId);
-            response.CurrentOffset.Should().Be(19);
-            foreach (var responseMessage in response.Messages)
-            {
-                responseMessage.UserHeaders.Should().BeNull();
-                responseMessage.Message.Text.Should().NotBeNullOrEmpty();
-            }
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task PollMessagesTMessage_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.FetchMessagesAsync(_invalidFetchRequest, MessageFactory.DeserializeDummyMessage))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task PollMessages_WithNoHeaders_Should_PollMessages_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.FetchMessagesAsync(_messageFetchRequest);
-            response.Messages.Count.Should().Be(10);
-            response.PartitionId.Should().Be(FetchMessagesFixtureBootstrap.PartitionId);
-            response.CurrentOffset.Should().Be(19);
-            foreach (var responseMessage in response.Messages)
-            {
-                responseMessage.UserHeaders.Should().BeNull();
-                responseMessage.Payload.Should().NotBeNull();
-                responseMessage.Payload.Length.Should().BeGreaterThan(0);
-            }
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task PollMessages_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.FetchMessagesAsync(_invalidFetchRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(5)]
-    public async Task PollMessages_WithHeaders_Should_PollMessages_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.FetchMessagesAsync(_headersMessageFetchRequest);
-            response.Messages.Count.Should().Be(10);
-            response.PartitionId.Should().Be(FetchMessagesFixtureBootstrap.PartitionId);
-            response.CurrentOffset.Should().Be(19);
-            foreach (var responseMessage in response.Messages)
-            {
-                responseMessage.UserHeaders.Should().NotBeNull();
-                responseMessage.UserHeaders!.Count.Should().Be(3);
-            }
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(6)]
-    public async Task PollMessagesTMessage_WithHeaders_Should_PollMessages_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.FetchMessagesAsync(_headersMessageFetchRequest, MessageFactory.DeserializeDummyMessage);
-            response.Messages.Count.Should().Be(10);
-            response.PartitionId.Should().Be(FetchMessagesFixtureBootstrap.PartitionId);
-            response.CurrentOffset.Should().Be(19);
-            foreach (var responseMessage in response.Messages)
-            {
-                responseMessage.UserHeaders.Should().NotBeNull();
-                responseMessage.UserHeaders!.Count.Should().Be(3);
-            }
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/ConsumerGroupFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/ConsumerGroupFixtureBootstrap.cs
deleted file mode 100644
index a663bb80..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/ConsumerGroupFixtureBootstrap.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class ConsumerGroupFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    public static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FetchMessagesFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FetchMessagesFixtureBootstrap.cs
deleted file mode 100644
index 589f58eb..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FetchMessagesFixtureBootstrap.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Messages;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class FetchMessagesFixtureBootstrap : IIggyBootstrap
-{
-    private static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly StreamRequest NonExistingStreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly TopicRequest NonExistingTopicRequest = TopicFactory.CreateTopicRequest(3000);
-    private static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    private static readonly TopicRequest HeadersTopicRequest = TopicFactory.CreateTopicRequest();
-
-    public static readonly int StreamId = (int)StreamRequest.StreamId!;
-    public static readonly int TopicId = (int)TopicRequest.TopicId!;
-    public static readonly int HeadersTopicId = (int)HeadersTopicRequest.TopicId!;
-
-    public static readonly int InvalidStreamId = (int)NonExistingStreamRequest.StreamId!;
-    public static readonly int InvalidTopicId = (int)NonExistingTopicRequest.TopicId!;
-    public const int PartitionId = 1;
-
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId), HeadersTopicRequest);
-
-        var request = MessageFactory.CreateMessageSendRequest(
-            (int)StreamRequest.StreamId, (int)TopicRequest.TopicId!, PartitionId,
-            MessageFactory.GenerateMessages(20));
-        var requestWithHeaders = MessageFactory.CreateMessageSendRequest(
-            (int)StreamRequest.StreamId, (int)HeadersTopicRequest.TopicId!, PartitionId,
-            MessageFactory.GenerateMessages(20, MessageFactory.GenerateMessageHeaders(3)));
-        await tcpClient.Client.SendMessagesAsync(request);
-        await tcpClient.Client.SendMessagesAsync(requestWithHeaders);
-        await Task.Delay(1000);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId), HeadersTopicRequest);
-
-        await httpClient.Client.SendMessagesAsync(request);
-        await httpClient.Client.SendMessagesAsync(requestWithHeaders);
-        await Task.Delay(1000);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FlushMessagesFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FlushMessagesFixtureBootstrap.cs
deleted file mode 100644
index bf4f8b74..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/FlushMessagesFixtureBootstrap.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Messages;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class FlushMessagesFixtureBootstrap : IIggyBootstrap
-{
-    private static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly StreamRequest NonExistingStreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly TopicRequest NonExistingTopicRequest = TopicFactory.CreateTopicRequest(3000);
-    private static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    private static readonly TopicRequest HeadersTopicRequest = TopicFactory.CreateTopicRequest();
-
-    public static readonly int StreamId = (int)StreamRequest.StreamId!;
-    public static readonly int TopicId = (int)TopicRequest.TopicId!;
-    public static readonly int HeadersTopicId = (int)HeadersTopicRequest.TopicId!;
-
-    public static readonly int InvalidStreamId = (int)NonExistingStreamRequest.StreamId!;
-    public static readonly int InvalidTopicId = (int)NonExistingTopicRequest.TopicId!;
-    public const int PartitionId = 1;
-
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-
-        var request = MessageFactory.CreateMessageSendRequest(
-            (int)StreamRequest.StreamId, (int)TopicRequest.TopicId!, PartitionId,
-            MessageFactory.GenerateMessages(20));
-        await tcpClient.Client.SendMessagesAsync(request);
-        await Task.Delay(1000);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-
-        await httpClient.Client.SendMessagesAsync(request);
-        await Task.Delay(1000);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/GeneralFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/GeneralFixtureBootstrap.cs
deleted file mode 100644
index ad00699b..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/GeneralFixtureBootstrap.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class GeneralFixtureBootstrap : IIggyBootstrap
-{
-    public Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/OffsetFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/OffsetFixtureBootstrap.cs
deleted file mode 100644
index 98f58366..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/OffsetFixtureBootstrap.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class OffsetFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    public static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PATFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PATFixtureBootstrap.cs
deleted file mode 100644
index 6d17e6f3..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PATFixtureBootstrap.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.Auth;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class PATFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly CreatePersonalAccessTokenRequest CreatePersonalAccessTokenRequest = new()
-    {
-        Name = "test",
-        Expiry = 694200000
-    };
-    public Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PartitionsFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PartitionsFixtureBootstrap.cs
deleted file mode 100644
index 593f82fa..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PartitionsFixtureBootstrap.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class PartitionsFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    public static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PollMessagesFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PollMessagesFixtureBootstrap.cs
deleted file mode 100644
index e6f286ea..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/PollMessagesFixtureBootstrap.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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.Kinds;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.DummyObj;
-using Apache.Iggy.Tests.Utils.Messages;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class PollMessagesFixtureBootstrap : IIggyBootstrap
-{
-    private static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    private static readonly TopicRequest HeadersTopicRequest = TopicFactory.CreateTopicRequest();
-    public const int MessageCount = 100;
-
-    public static readonly int StreamId = (int)StreamRequest.StreamId!;
-    public static readonly int TopicId = (int)TopicRequest.TopicId!;
-    public static readonly int HeadersTopicId = (int)HeadersTopicRequest.TopicId!;
-
-    public const int PartitionId = 1;
-    public const int HeadersCount = 3;
-
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId), HeadersTopicRequest);
-        await tcpClient.Client.SendMessagesAsync(new MessageSendRequest<DummyMessage>
-            {
-                Messages = MessageFactory.GenerateDummyMessages(MessageCount),
-                Partitioning = Partitioning.PartitionId(PartitionId),
-                StreamId = Identifier.Numeric(StreamId),
-                TopicId = Identifier.Numeric(TopicId)
-            },
-            MessageFactory.Serializer,
-            headers: MessageFactory.GenerateMessageHeaders(HeadersCount));
-
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId), HeadersTopicRequest);
-        await httpClient.Client.SendMessagesAsync(new MessageSendRequest<DummyMessage>
-            {
-                Messages = MessageFactory.GenerateDummyMessages(MessageCount),
-                Partitioning = Partitioning.PartitionId(PartitionId),
-                StreamId = Identifier.Numeric(StreamId),
-                TopicId = Identifier.Numeric(TopicId)
-            },
-            MessageFactory.Serializer,
-            headers: MessageFactory.GenerateMessageHeaders(HeadersCount));
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SendMessagesFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SendMessagesFixtureBootstrap.cs
deleted file mode 100644
index 2cb50886..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SendMessagesFixtureBootstrap.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class SendMessagesFixtureBootstrap : IIggyBootstrap
-{
-    private static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly StreamRequest NonExistingStreamRequest = StreamFactory.CreateStreamRequest();
-    private static readonly TopicRequest NonExistingTopicRequest = TopicFactory.CreateTopicRequest();
-    private static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-
-    public static readonly int StreamId = (int)StreamRequest.StreamId!;
-    public static readonly int TopicId = (int)TopicRequest.TopicId!;
-
-    public static readonly int InvalidStreamId = (int)NonExistingStreamRequest.StreamId!;
-    public static readonly int InvalidTopicId = (int)NonExistingTopicRequest.TopicId!;
-
-    public const int PartitionId = 1;
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await tcpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-        
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateTopicAsync(Identifier.Numeric((int)StreamRequest.StreamId!), TopicRequest);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/StreamsFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/StreamsFixtureBootstrap.cs
deleted file mode 100644
index 056ff939..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/StreamsFixtureBootstrap.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class StreamsFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    public static readonly UpdateStreamRequest UpdateStreamRequest = StreamFactory.CreateUpdateStreamRequest();
-    public Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SystemFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SystemFixtureBootstrap.cs
deleted file mode 100644
index af0d3993..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/SystemFixtureBootstrap.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.Auth;
-using Apache.Iggy.Enums;
-using Apache.Iggy.Factory;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class SystemFixtureBootstrap : IIggyBootstrap
-{
-    private const int FRESH_CLIENTS_COUNT = 6;
-    
-    public const int TotalClientsCount = FRESH_CLIENTS_COUNT + 1;
-    
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        for (int i = 0; i < FRESH_CLIENTS_COUNT; i++)
-        {
-            var client = MessageStreamFactory.CreateMessageStream(options =>
-            {
-                options.BaseAdress = $"127.0.0.1:{tcpClient.TcpPort}";
-                options.Protocol = Protocol.Tcp;
-                options.MessageBatchingSettings = x =>
-                {
-                    x.MaxMessagesPerBatch = 1000;
-                    x.Interval = TimeSpan.FromMilliseconds(100);
-                };
-            }, NullLoggerFactory.Instance);
-            await client.LoginUser(new LoginUserRequest
-            {
-                Password = "iggy",
-                Username = "iggy"
-            });
-        }
-
-        for (int i = 0; i < TotalClientsCount; i++)
-        {
-            var client = MessageStreamFactory.CreateMessageStream(options =>
-            {
-                options.BaseAdress = $"127.0.0.1:{httpClient.TcpPort}";
-                options.Protocol = Protocol.Tcp;
-                options.MessageBatchingSettings = x =>
-                {
-                    x.MaxMessagesPerBatch = 1000;
-                    x.Interval = TimeSpan.FromMilliseconds(100);
-                };
-            }, NullLoggerFactory.Instance);
-            await client.LoginUser(new LoginUserRequest
-            {
-                Password = "iggy",
-                Username = "iggy"
-            });
-        }
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/TopicsFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/TopicsFixtureBootstrap.cs
deleted file mode 100644
index 6547f0df..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/TopicsFixtureBootstrap.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Streams;
-using Apache.Iggy.Tests.Utils.Topics;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class TopicsFixtureBootstrap : IIggyBootstrap
-{
-    public static readonly StreamRequest StreamRequest = StreamFactory.CreateStreamRequest();
-    public static readonly TopicRequest TopicRequest = TopicFactory.CreateTopicRequest();
-    public static readonly TopicRequest TopicRequestSecond = TopicFactory.CreateTopicRequest();
-    public static readonly UpdateTopicRequest UpdateTopicRequest = TopicFactory.CreateUpdateTopicRequest();
-    
-    
-    public async Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        await tcpClient.Client.CreateStreamAsync(StreamRequest);
-        await httpClient.Client.CreateStreamAsync(StreamRequest);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/UsersFixtureBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/UsersFixtureBootstrap.cs
deleted file mode 100644
index 58219c65..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Bootstraps/UsersFixtureBootstrap.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.Auth;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-using Apache.Iggy.Tests.Utils.Users;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-
-public class UsersFixtureBootstrap : IIggyBootstrap
-{
-    public static CreateUserRequest UserRequest = UsersFactory.CreateUserRequest("user1", "user1");
-    public static Permissions UpdatePermissionsRequest = UsersFactory.CreatePermissions();
-    public const string NewUsername = "new_username";
-    
-    public Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient)
-    {
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Configs/IggyFixtureClientConfigs.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Configs/IggyFixtureClientConfigs.cs
deleted file mode 100644
index bc9109bc..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Configs/IggyFixtureClientConfigs.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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.Configuration;
-using Apache.Iggy.Enums;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-public static class IggyFixtureClientMessagingSettings
-{
-    public static Action<MessagePollingSettings> PollingSettings { get; set; } = options =>
-    {
-        options.Interval = TimeSpan.FromMilliseconds(100);
-        options.StoreOffsetStrategy = StoreOffset.WhenMessagesAreReceived;
-    };
-    public static Action<MessageBatchingSettings> BatchingSettings { get; set; } = options =>
-    {
-        options.Interval = TimeSpan.FromMilliseconds(100);
-        options.MaxMessagesPerBatch = 1000;
-        options.MaxRequests = 4096;
-    };
-    
-    public static Action<MessageBatchingSettings> BatchingSettingsSendFixture { get; set; } = options =>
-    {
-        options.Enabled = false;
-        options.Interval = TimeSpan.FromMilliseconds(100);
-        options.MaxMessagesPerBatch = 1000;
-        options.MaxRequests = 4096;
-    };
-
-    public static Action<MessageBatchingSettings> BatchingSettingsFetchFixture { get; set; } = options =>
-    {
-        options.Enabled = false;
-        options.Interval = TimeSpan.FromMilliseconds(100);
-        options.MaxMessagesPerBatch = 1000;
-        options.MaxRequests = 8912;
-    };
-
-    public static Action<MessageBatchingSettings> BatchingSettingsPollMessagesFixture { get; set; } = options =>
-    {
-        options.Enabled = true;
-        options.Interval = TimeSpan.FromMilliseconds(99);
-        options.MaxMessagesPerBatch = 1000;
-        options.MaxRequests = 8912;
-    };
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IIggyBootstrap.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IIggyBootstrap.cs
deleted file mode 100644
index 23986cf1..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IIggyBootstrap.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Models;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public interface IIggyBootstrap
-{
-    public Task BootstrapResourcesAsync(IggyClientModel httpClient, IggyClientModel tcpClient);
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyBaseFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyBaseFixture.cs
deleted file mode 100644
index 840fe486..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyBaseFixture.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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.Configuration;
-using Apache.Iggy.Contracts.Http.Auth;
-using Apache.Iggy.Enums;
-using Apache.Iggy.Factory;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-using DotNet.Testcontainers.Builders;
-using DotNet.Testcontainers.Containers;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public abstract class IggyBaseFixture : IAsyncLifetime
-{
-    private readonly IIggyBootstrap _bootstraper;
-    private readonly Action<MessagePollingSettings> _pollingSettings;
-    private readonly Action<MessageBatchingSettings> _batchingSettings;
-
-    private readonly IContainer _tcpContainer = new ContainerBuilder().WithImage("apache/iggy:edge")
-        // Container name is just to be used locally for debbuging effects
-        //.WithName($"SutIggyContainerTCP")
-        .WithPortBinding(3000, true)
-        .WithPortBinding(8090, true)
-        .WithOutputConsumer(Consume.RedirectStdoutAndStderrToConsole())
-        .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(8090))
-        //.WithEnvironment("IGGY_SYSTEM_LOGGING_LEVEL", "debug")
-        //.WithEnvironment("RUST_LOG", "trace")
-        .WithCleanUp(true)
-        .Build();
-    
-    private readonly IContainer _httpContainer = new ContainerBuilder().WithImage("apache/iggy:edge")
-        // Container name is just to be used locally for debbuging effects
-        //.WithName($"SutIggyContainerHTTP")
-        .WithPortBinding(3000, true)
-        .WithPortBinding(8090, true)
-        .WithOutputConsumer(Consume.RedirectStdoutAndStderrToConsole())
-        .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3000))
-        //.WithEnvironment("IGGY_SYSTEM_LOGGING_LEVEL", "debug")
-        //.WithEnvironment("RUST_LOG", "trace")
-        .WithCleanUp(true)
-        .Build();
-    
-    public IggyClientModel[] SubjectsUnderTest { get; } = new IggyClientModel[2];
-    public IggyClientModel TcpClient => SubjectsUnderTest[0];
-    public IggyClientModel HttpClient => SubjectsUnderTest[1];
-    
-    protected IggyBaseFixture(IIggyBootstrap bootstraper, Action<MessagePollingSettings> pollingSettings,
-        Action<MessageBatchingSettings> batchingSettings)
-    {
-        _bootstraper = bootstraper;
-        _pollingSettings = pollingSettings;
-        _batchingSettings = batchingSettings;
-    }
-
-    public async Task InitializeAsync()
-    {
-        await _tcpContainer.StartAsync();
-        await _httpContainer.StartAsync();
-        var tcpClient = new IggyClientModel { Name = "Tcp" };
-        var httpClient = new IggyClientModel { Name = "Http" };
-        
-        tcpClient.TcpPort = _tcpContainer.GetMappedPublicPort(8090);
-        tcpClient.HttpPort = _tcpContainer.GetMappedPublicPort(3000);
-        
-        httpClient.TcpPort = _httpContainer.GetMappedPublicPort(8090);
-        httpClient.HttpPort = _httpContainer.GetMappedPublicPort(3000);
-        
-        tcpClient.Client = MessageStreamFactory.CreateMessageStream(options =>
-        {
-            options.BaseAdress = $"127.0.0.1:{tcpClient.TcpPort}";
-            options.Protocol = Protocol.Tcp;
-            options.MessageBatchingSettings = _batchingSettings;
-            options.MessagePollingSettings = _pollingSettings;
-        }, NullLoggerFactory.Instance);
-        
-        await tcpClient.Client.LoginUser(new LoginUserRequest
-        {
-            Password = "iggy",
-            Username = "iggy"
-        });
-        
-        httpClient.Client = MessageStreamFactory.CreateMessageStream(options =>
-        {
-            options.BaseAdress = $"http://127.0.0.1:{httpClient.HttpPort}";
-            options.Protocol = Protocol.Http;
-            options.MessageBatchingSettings = _batchingSettings;
-            options.MessagePollingSettings = _pollingSettings;
-        }, NullLoggerFactory.Instance);
-        await httpClient.Client.LoginUser(new LoginUserRequest
-        {
-            Password = "iggy",
-            Username = "iggy"
-        });
-        
-        SubjectsUnderTest[0] = tcpClient;
-        SubjectsUnderTest[1] = httpClient;
-        
-        await _bootstraper.BootstrapResourcesAsync(httpClient, tcpClient);
-    }
-
-    public async Task DisposeAsync()
-    {
-        await _tcpContainer.StopAsync();
-        await _httpContainer.StopAsync();
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyConsumerGroupFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyConsumerGroupFixture.cs
deleted file mode 100644
index af2e1c4e..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyConsumerGroupFixture.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyConsumerGroupFixture : IggyBaseFixture
-{
-    public IggyConsumerGroupFixture() : base(new ConsumerGroupFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings, IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFetchMessagesFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFetchMessagesFixture.cs
deleted file mode 100644
index 4dd15e54..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFetchMessagesFixture.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyFetchMessagesFixture : IggyBaseFixture
-{
-    public IggyFetchMessagesFixture() : base(new FetchMessagesFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings, IggyFixtureClientMessagingSettings.BatchingSettingsFetchFixture)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFlushMessagesFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFlushMessagesFixture.cs
deleted file mode 100644
index df7d05f5..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyFlushMessagesFixture.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyFlushMessagesFixture : IggyBaseFixture
-{
-    public IggyFlushMessagesFixture() : base(new FlushMessagesFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings, IggyFixtureClientMessagingSettings.BatchingSettingsFetchFixture)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyGeneralFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyGeneralFixture.cs
deleted file mode 100644
index 42f06ff5..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyGeneralFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyGeneralFixture : IggyBaseFixture
-{
-    public IggyGeneralFixture() : base(new GeneralFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyOffsetFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyOffsetFixture.cs
deleted file mode 100644
index 0b5636ec..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyOffsetFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyOffsetFixture : IggyBaseFixture
-{
-    public IggyOffsetFixture() : base(new OffsetFixtureBootstrap(), 
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPATFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPATFixture.cs
deleted file mode 100644
index 8b7a3334..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPATFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyPATFixture : IggyBaseFixture
-{
-    public IggyPATFixture() : base(new PATFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPartitionFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPartitionFixture.cs
deleted file mode 100644
index cb312ddf..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPartitionFixture.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyPartitionFixture : IggyBaseFixture
-{
-    public IggyPartitionFixture() : base(new PartitionsFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings
-    )
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPollMessagesFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPollMessagesFixture.cs
deleted file mode 100644
index 26a22141..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyPollMessagesFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyPollMessagesFixture : IggyBaseFixture
-{
-    public IggyPollMessagesFixture() : base(new PollMessagesFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettingsPollMessagesFixture)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySendMessagesFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySendMessagesFixture.cs
deleted file mode 100644
index 5416c247..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySendMessagesFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggySendMessagesFixture : IggyBaseFixture
-{
-    public IggySendMessagesFixture() : base(new SendMessagesFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettingsSendFixture)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyStreamFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyStreamFixture.cs
deleted file mode 100644
index ef47c351..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyStreamFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyStreamFixture : IggyBaseFixture
-{
-    public IggyStreamFixture() : base(new StreamsFixtureBootstrap(), 
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySystemFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySystemFixture.cs
deleted file mode 100644
index 54cc39ce..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggySystemFixture.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggySystemFixture : IggyBaseFixture 
-{
-    public IggySystemFixture() : base(new SystemFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings, IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTcpUsersFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTcpUsersFixture.cs
deleted file mode 100644
index 818480ee..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTcpUsersFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyTcpUsersFixture : IggyBaseFixture
-{
-    public IggyTcpUsersFixture() : base(new UsersFixtureBootstrap(),
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTopicFixture.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTopicFixture.cs
deleted file mode 100644
index c33993ca..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/IggyTopicFixture.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Configs;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures;
-
-public sealed class IggyTopicFixture : IggyBaseFixture
-{
-    public IggyTopicFixture() : base(new TopicsFixtureBootstrap(),  
-        IggyFixtureClientMessagingSettings.PollingSettings,
-        IggyFixtureClientMessagingSettings.BatchingSettings)
-    {
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Models/IggyClientModel.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Models/IggyClientModel.cs
deleted file mode 100644
index 426e8706..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/Fixtures/Models/IggyClientModel.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.IggyClient;
-
-namespace Apache.Iggy.Tests.E2ETests.Fixtures.Models;
-
-public class IggyClientModel
-{
-    public string Name { get; set; }
-    public IIggyClient Client { get; set; }
-    public int HttpPort { get; set; }
-    public int TcpPort { get; set; }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/FlushMessagesE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/FlushMessagesE2E.cs
deleted file mode 100644
index daafe1a2..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/FlushMessagesE2E.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class FlushMessagesE2E : IClassFixture<IggyFlushMessagesFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-    private readonly IggyFlushMessagesFixture _fixture;
-
-    private static readonly FlushUnsavedBufferRequest _flushRequestFsync = new()
-    {
-        StreamId = Identifier.Numeric(FlushMessagesFixtureBootstrap.StreamId),
-        TopicId = Identifier.Numeric(FlushMessagesFixtureBootstrap.TopicId),
-        PartitionId = FlushMessagesFixtureBootstrap.PartitionId,
-        Fsync = true
-    };
-
-    private static readonly FlushUnsavedBufferRequest _flushRequest = new()
-    {
-        StreamId = Identifier.Numeric(FlushMessagesFixtureBootstrap.StreamId),
-        TopicId = Identifier.Numeric(FlushMessagesFixtureBootstrap.TopicId),
-        PartitionId = FlushMessagesFixtureBootstrap.PartitionId,
-        Fsync = false
-    };
-
-    private static readonly FlushUnsavedBufferRequest _flushInvalidStreamRequest = new()
-    {
-        StreamId = Identifier.Numeric(FlushMessagesFixtureBootstrap.InvalidStreamId),
-        TopicId = Identifier.Numeric(FlushMessagesFixtureBootstrap.TopicId),
-        PartitionId = FlushMessagesFixtureBootstrap.PartitionId,
-        Fsync = false
-    };
-
-
-    public FlushMessagesE2E(IggyFlushMessagesFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task FlushUnsavedBuffer_WithFsync_Should_Flush_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.FlushUnsavedBufferAsync(_flushRequestFsync))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task FlushUnsavedBuffer_WithOutFsync_Should_Flush_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.FlushUnsavedBufferAsync(_flushRequest))
-                .Should()
-                .NotThrowAsync();
-
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task FlushUnsavedBuffer_Should_Throw_WhenStream_DoesNotExist()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.FlushUnsavedBufferAsync(_flushInvalidStreamRequest))
-                .Should()
-                .ThrowAsync<InvalidResponseException>();
-
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/OffsetE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/OffsetE2E.cs
deleted file mode 100644
index d8b8abd6..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/OffsetE2E.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Offsets;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class OffsetE2E : IClassFixture<IggyOffsetFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-    private readonly IggyOffsetFixture _fixture;
-    private readonly StoreOffsetRequest _storeOffsetIndividualConsumer;
-    private readonly OffsetRequest _offsetIndividualConsumer;
-
-    private const int GET_INDIVIDUAL_CONSUMER_ID = 1;
-    private const int GET_PARTITION_ID = 1;
-    private const ulong GET_OFFSET = 0;
-
-    public OffsetE2E(IggyOffsetFixture fixture)
-    {
-        _fixture = fixture;
-        _storeOffsetIndividualConsumer = OffsetFactory.CreateOffsetContract(
-            (int)OffsetFixtureBootstrap.StreamRequest.StreamId!, (int)OffsetFixtureBootstrap.TopicRequest.TopicId!, GET_INDIVIDUAL_CONSUMER_ID, GET_OFFSET,
-            GET_PARTITION_ID);
-        _offsetIndividualConsumer = OffsetFactory.CreateOffsetRequest((int)OffsetFixtureBootstrap.StreamRequest.StreamId,
-            (int)OffsetFixtureBootstrap.TopicRequest.TopicId, GET_PARTITION_ID, GET_INDIVIDUAL_CONSUMER_ID);
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task StoreOffset_IndividualConsumer_Should_StoreOffset_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.StoreOffsetAsync(_storeOffsetIndividualConsumer))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task GetOffset_IndividualConsumer_Should_GetOffset_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            var offset = await sut.Client.GetOffsetAsync(_offsetIndividualConsumer);
-            offset.Should().NotBeNull();
-            offset!.StoredOffset.Should().Be(_storeOffsetIndividualConsumer.Offset);
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PATE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/PATE2E.cs
deleted file mode 100644
index fe8520a0..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PATE2E.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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.Auth;
-using Apache.Iggy.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-using FluentAssertions.Common;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class PATE2E : IClassFixture<IggyPATFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-    private readonly IggyPATFixture _fixture;
-
-    public PATE2E(IggyPATFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task CreatePersonalAccessToken_HappyPath_Should_CreatePersonalAccessToken_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.CreatePersonalAccessTokenAsync(PATFixtureBootstrap.CreatePersonalAccessTokenRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task CreatePersonalAccessToken_Duplicate_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.CreatePersonalAccessTokenAsync(PATFixtureBootstrap.CreatePersonalAccessTokenRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task GetPersonalAccessTokens_Should_ReturnValidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetPersonalAccessTokensAsync();
-            response.Should().NotBeNull();
-            response.Count.Should().Be(1);
-            response[0].Name.Should().Be(PATFixtureBootstrap.CreatePersonalAccessTokenRequest.Name);
-            var tokenExpiryDateTimeOffset = DateTime.UtcNow.AddMicroseconds((double)PATFixtureBootstrap.CreatePersonalAccessTokenRequest.Expiry!).ToDateTimeOffset();
-            response[0].ExpiryAt!.Value.Date.Should().Be(tokenExpiryDateTimeOffset.LocalDateTime.Date);
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task LoginWithPersonalAccessToken_Should_Be_Successfull()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.CreatePersonalAccessTokenAsync(new CreatePersonalAccessTokenRequest
-            {
-                Name = "test-login",
-                Expiry = 69420
-            });
-            await sut.Client.LogoutUser();
-            await sut.Invoking(x => x.Client.LoginWithPersonalAccessToken(new LoginWithPersonalAccessToken
-            {
-                Token = response!.Token
-            })).Should().NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(5)]
-    public async Task DeletePersonalAccessToken_Should_DeletePersonalAccessToken_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.DeletePersonalAccessTokenAsync(new DeletePersonalAccessTokenRequest
-            {
-                Name = PATFixtureBootstrap.CreatePersonalAccessTokenRequest.Name
-            }))
-            .Should()
-            .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PartitionsE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/PartitionsE2E.cs
deleted file mode 100644
index 05d904e1..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PartitionsE2E.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Partitions;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class PartitionsE2E : IClassFixture<IggyPartitionFixture>
-{
-    private readonly IggyPartitionFixture _fixture;
-    private readonly CreatePartitionsRequest _partitionsRequest;
-    private readonly DeletePartitionsRequest _deletePartitionsRequest;
-    public PartitionsE2E(IggyPartitionFixture fixture)
-    {
-        _fixture = fixture;
-        _partitionsRequest =
-            PartitionFactory.CreatePartitionsRequest((int)PartitionsFixtureBootstrap.StreamRequest.StreamId!, (int)PartitionsFixtureBootstrap.TopicRequest.TopicId!);
-        _deletePartitionsRequest = PartitionFactory.CreateDeletePartitionsRequest((int)PartitionsFixtureBootstrap.StreamRequest.StreamId,
-            (int)PartitionsFixtureBootstrap.TopicRequest.TopicId, _partitionsRequest.PartitionsCount);
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task CreatePartition_HappyPath_Should_CreatePartition_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.CreatePartitionsAsync(_partitionsRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task DeletePartition_Should_DeletePartition_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.DeletePartitionsAsync(_deletePartitionsRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task DeletePartition_Should_Throw_WhenTopic_DoesNotExist()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            await sut.Client.CreatePartitionsAsync(_partitionsRequest);
-            await sut.Client.DeleteTopicAsync(Identifier.Numeric((int)PartitionsFixtureBootstrap.StreamRequest.StreamId!),
-                Identifier.Numeric((int)PartitionsFixtureBootstrap.TopicRequest.TopicId!));
-            await sut.Invoking(x => x.Client.DeletePartitionsAsync(_deletePartitionsRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task DeletePartition_Should_Throw_WhenStream_DoesNotExist()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select( sut => Task.Run(async () =>
-        {
-            await sut.Client.CreateTopicAsync(Identifier.Numeric((int)PartitionsFixtureBootstrap.StreamRequest.StreamId!), PartitionsFixtureBootstrap.TopicRequest);
-            await sut.Client.DeleteStreamAsync(Identifier.Numeric((int)PartitionsFixtureBootstrap.StreamRequest.StreamId));
-            await sut.Client.Invoking(x => x.DeletePartitionsAsync(_deletePartitionsRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PollMessagesE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/PollMessagesE2E.cs
deleted file mode 100644
index a1e67102..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/PollMessagesE2E.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.Kinds;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Messages;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class PollMessagesE2E(IggyPollMessagesFixture fixture) : IClassFixture<IggyPollMessagesFixture>
-{
-
-    [Fact, TestPriority(1)]
-    public async Task PollMessagesTMessage_Should_PollMessages_Successfully()
-    {
-        
-        var tasks = fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var i = 0;
-            await foreach (var msgResponse in sut.Client.PollMessagesAsync(new PollMessagesRequest
-                           {
-                               Consumer = Consumer.New(1),
-                               Count = 10,
-                               PartitionId = PollMessagesFixtureBootstrap.PartitionId,
-                               PollingStrategy = PollingStrategy.Next(),
-                               StreamId = Identifier.Numeric(PollMessagesFixtureBootstrap.StreamId),
-                               TopicId = Identifier.Numeric(PollMessagesFixtureBootstrap.TopicId)
-                           }, MessageFactory.DeserializeDummyMessage))
-            {
-                msgResponse.UserHeaders.Should().NotBeNull();
-                msgResponse.UserHeaders.Should().HaveCount(PollMessagesFixtureBootstrap.HeadersCount);
-                i++;
-                if (i == PollMessagesFixtureBootstrap.MessageCount)
-                {
-                    break;
-                }
-            }
-        
-            i.Should().Be(PollMessagesFixtureBootstrap.MessageCount);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/SendMessagesE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/SendMessagesE2E.cs
deleted file mode 100644
index 3ecfe3e0..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/SendMessagesE2E.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using Apache.Iggy.Tests.Utils.Messages;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class SendMessagesE2E : IClassFixture<IggySendMessagesFixture>
-{
-    private const string SkipMessage = "TCP implementation needs to be aligned with Iggy core changes";
-    private readonly IggySendMessagesFixture _fixture;
-
-    private readonly MessageSendRequest _messageNoHeadersSendRequest;
-    private readonly MessageSendRequest _messageWithHeadersSendRequest;
-    private readonly MessageSendRequest _invalidMessageNoHeadersSendRequest;
-    private readonly MessageSendRequest _invalidMessageWithHeadersSendRequest;
-
-    public SendMessagesE2E(IggySendMessagesFixture fixture)
-    {
-        _fixture = fixture;
-
-        var messageWithHeaders = MessageFactory.GenerateDummyMessages(
-            Random.Shared.Next(20, 50),
-            Random.Shared.Next(69, 420),
-            MessageFactory.GenerateMessageHeaders(2));
-
-        _messageNoHeadersSendRequest = MessageFactory.CreateMessageSendRequest(SendMessagesFixtureBootstrap.StreamId,
-            SendMessagesFixtureBootstrap.TopicId, SendMessagesFixtureBootstrap.PartitionId);
-        _invalidMessageNoHeadersSendRequest = MessageFactory.CreateMessageSendRequest(SendMessagesFixtureBootstrap.InvalidStreamId,
-            SendMessagesFixtureBootstrap.InvalidTopicId, SendMessagesFixtureBootstrap.PartitionId);
-
-        _messageWithHeadersSendRequest = MessageFactory.CreateMessageSendRequest(SendMessagesFixtureBootstrap.StreamId,
-            SendMessagesFixtureBootstrap.TopicId, SendMessagesFixtureBootstrap.PartitionId, messageWithHeaders);
-        _invalidMessageWithHeadersSendRequest = MessageFactory.CreateMessageSendRequest(SendMessagesFixtureBootstrap.InvalidStreamId,
-            SendMessagesFixtureBootstrap.InvalidTopicId, SendMessagesFixtureBootstrap.PartitionId, messageWithHeaders);
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task SendMessages_NoHeaders_Should_SendMessages_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.SendMessagesAsync(_messageNoHeadersSendRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task SendMessages_NoHeaders_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.SendMessagesAsync(_invalidMessageNoHeadersSendRequest))
-                .Should()
-                .ThrowAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(3)]
-    public async Task SendMessages_WithHeaders_Should_SendMessages_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.SendMessagesAsync(_messageWithHeadersSendRequest))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task SendMessages_WithHeaders_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(x => x.Client.SendMessagesAsync(_invalidMessageWithHeadersSendRequest))
-                .Should()
-                .ThrowAsync<InvalidResponseException>();
-        })).ToArray();
-
-        await Task.WhenAll(tasks);
-    }
-}
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/StreamsE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/StreamsE2E.cs
deleted file mode 100644
index 5e7f4d0e..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/StreamsE2E.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class StreamsE2E : IClassFixture<IggyStreamFixture>
-{
-    private readonly IggyStreamFixture _fixture;
-
-    public StreamsE2E(IggyStreamFixture fixture)
-    {
-        _fixture = fixture;
-    }
-    
-    [Fact, TestPriority(1)]
-    public async Task CreateStream_HappyPath_Should_CreateStream_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.CreateStreamAsync(StreamsFixtureBootstrap.StreamRequest);
-            
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(StreamsFixtureBootstrap.StreamRequest.StreamId);
-            response.Name.Should().Be(StreamsFixtureBootstrap.StreamRequest.Name);
-            response.Size.Should().Be(0);
-            response.CreatedAt.UtcDateTime.Should().BeAfter(DateTimeOffset.UtcNow.UtcDateTime.AddSeconds(-20));
-            response.MessagesCount.Should().Be(0);
-            response.TopicsCount.Should().Be(0);
-            response.Topics.Should().BeEmpty();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(2)]
-    public async Task CreateStream_Duplicate_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.CreateStreamAsync(StreamsFixtureBootstrap.StreamRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(3)]
-    public async Task GetStreamById_Should_ReturnValidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetStreamByIdAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!));
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(StreamsFixtureBootstrap.StreamRequest.StreamId);
-            response.Name.Should().Be(StreamsFixtureBootstrap.StreamRequest.Name);
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(4)]
-    public async Task UpdateStream_Should_UpdateStream_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.UpdateStreamAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!), StreamsFixtureBootstrap.UpdateStreamRequest))
-                .Should()
-                .NotThrowAsync();
-            
-            var result = await sut.Client.GetStreamByIdAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!));
-            result.Should().NotBeNull();
-            result!.Name.Should().Be(StreamsFixtureBootstrap.UpdateStreamRequest.Name);
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(5)]
-    public async Task PurgeStream_Should_PurgeStream_Successfully()
-    {
-        // act
-        // TODO: Check if the stream is empty after purging
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.PurgeStreamAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!)))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(6)]
-    public async Task DeleteStream_Should_DeleteStream_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.DeleteStreamAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!)))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(7)]
-    public async Task DeleteStream_Should_Throw_InvalidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.DeleteStreamAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!)))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(8)]
-    public async Task GetStreamById_Should_Throw_InvalidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                await x.Client.GetStreamByIdAsync(Identifier.Numeric((int)StreamsFixtureBootstrap.StreamRequest.StreamId!)))
-            .Should()
-            .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/SystemE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/SystemE2E.cs
deleted file mode 100644
index c5d5e0f4..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/SystemE2E.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class SystemE2E : IClassFixture<IggySystemFixture>
-{
-    private readonly IggySystemFixture _fixture;
-
-    public SystemE2E(IggySystemFixture fixture)
-    {
-        _fixture = fixture;
-    }
-    
-    [Fact, TestPriority(1)]
-    public async Task GetClients_Should_Return_CorrectClientsCount()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var clients = await sut.Client.GetClientsAsync();
-            clients.Count.Should().Be(SystemFixtureBootstrap.TotalClientsCount);
-            foreach (var client in clients)
-            {
-                client.ClientId.Should().NotBe(0);
-                client.UserId.Should().Be(1);
-                client.Address.Should().NotBeNullOrEmpty();
-                client.Transport.Should().Be("TCP");
-            }
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-        
-    [Fact, TestPriority(2)]
-    public async Task GetClient_Should_Return_CorrectClient()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var clients = await sut.Client.GetClientsAsync();
-            clients.Count.Should().Be(SystemFixtureBootstrap.TotalClientsCount);
-            uint id = clients[0].ClientId;
-            var response = await sut.Client.GetClientByIdAsync(id);
-            response!.ClientId.Should().Be(id);
-            response.UserId.Should().Be(1);
-            response.Address.Should().NotBeNullOrEmpty();
-            response.Transport.Should().Be("TCP");
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(3)]
-    public async Task GetMe_Should_Return_MyClient()
-    {
-        await _fixture.Invoking(x=>x.HttpClient.Client.GetMeAsync())
-            .Should()
-            .ThrowAsync<NotImplementedException>();
-
-
-        var me = await _fixture.TcpClient.Client.GetMeAsync();
-        me.Should().NotBeNull();
-        me.ClientId.Should().NotBe(0);
-        me.UserId.Should().Be(1);
-        me.Address.Should().NotBeNullOrEmpty();
-        me.Transport.Should().Be("TCP");
-    }
-
-    [Fact, TestPriority(4)]
-    public async Task GetStats_Should_ReturnValidResponse()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetStatsAsync();
-            response.Should().NotBeNull();
-            response!.MessagesCount.Should().Be(0);
-            response.PartitionsCount.Should().Be(0);
-            response.StreamsCount.Should().Be(0);
-            response.TopicsCount.Should().Be(0);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(5)]
-    public async Task Ping_Should_Pong()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x => await x.Client.PingAsync())
-                .Should()
-                .NotThrowAsync();
-            
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/TopicsE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/TopicsE2E.cs
deleted file mode 100644
index 825d4da4..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/TopicsE2E.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-// 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.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class TopicsE2E : IClassFixture<IggyTopicFixture>
-{
-    private readonly IggyTopicFixture _fixture;
-    
-    public TopicsE2E(IggyTopicFixture fixture)
-    {
-        _fixture = fixture;
-    }
-    
-    [Fact, TestPriority(1)]
-    public async Task Create_NewTopic_Should_Return_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.CreateTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!), TopicsFixtureBootstrap.TopicRequest);
-            
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(TopicsFixtureBootstrap.TopicRequest.TopicId);
-            response.Name.Should().Be(TopicsFixtureBootstrap.TopicRequest.Name);
-            response.CompressionAlgorithm.Should().Be(TopicsFixtureBootstrap.TopicRequest.CompressionAlgorithm);
-            response.Partitions.Should().HaveCount(TopicsFixtureBootstrap.TopicRequest.PartitionsCount);
-            response.MessageExpiry.Should().Be(TopicsFixtureBootstrap.TopicRequest.MessageExpiry);
-            response.Size.Should().Be(0);
-            response.CreatedAt.UtcDateTime.Should().BeAfter(DateTimeOffset.UtcNow.UtcDateTime.AddSeconds(-20));
-        })).ToArray();
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(2)]
-    public async Task Create_DuplicateTopic_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.CreateTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!), TopicsFixtureBootstrap.TopicRequest))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(3)]
-    public async Task Get_ExistingTopic_Should_ReturnValidResponse()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetTopicByIdAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!));
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(TopicsFixtureBootstrap.TopicRequest.TopicId);
-            response.Name.Should().Be(TopicsFixtureBootstrap.TopicRequest.Name);
-            response.CompressionAlgorithm.Should().Be(TopicsFixtureBootstrap.TopicRequest.CompressionAlgorithm);
-            response.Partitions.Should().HaveCount(TopicsFixtureBootstrap.TopicRequest.PartitionsCount);
-            response.MessageExpiry.Should().Be(TopicsFixtureBootstrap.TopicRequest.MessageExpiry);
-            response.Size.Should().Be(0);
-            response.MessagesCount.Should().Be(0);
-            response.MaxTopicSize.Should().Be(TopicsFixtureBootstrap.TopicRequest.MaxTopicSize);
-            response.MessageExpiry.Should().Be(TopicsFixtureBootstrap.TopicRequest.MessageExpiry);
-            response.CreatedAt.UtcDateTime.Year.Should().Be(DateTimeOffset.UtcNow.Year);
-            response.CreatedAt.UtcDateTime.Month.Should().Be(DateTimeOffset.UtcNow.Month);
-            response.CreatedAt.UtcDateTime.Day.Should().Be(DateTimeOffset.UtcNow.Day);
-            response.CreatedAt.UtcDateTime.Minute.Should().Be(DateTimeOffset.UtcNow.Minute);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(4)]
-    public async Task Get_ExistingTopics_Should_ReturnValidResponse()
-    {
-        var createTasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.CreateTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!), TopicsFixtureBootstrap.TopicRequestSecond))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        await Task.WhenAll(createTasks);
-        
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            var response = await sut.Client.GetTopicsAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!));
-            response.Should().NotBeNull();
-            response.Count.Should().Be(2);
-            response.Select(x=>x.Id).Should().ContainSingle(x=> x== TopicsFixtureBootstrap.TopicRequest.TopicId);
-            response.Select(x=>x.Id).Should().ContainSingle(x=> x== TopicsFixtureBootstrap.TopicRequestSecond.TopicId);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(5)]
-    public async Task Update_ExistingTopic_Should_UpdateTopic_Successfully()
-    {
-        // act
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.UpdateTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                        Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!), TopicsFixtureBootstrap.UpdateTopicRequest))
-                .Should()
-                .NotThrowAsync();
-        
-            var result = await sut.Client.GetTopicByIdAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!));
-            result.Should().NotBeNull();
-            result!.Name.Should().Be(TopicsFixtureBootstrap.UpdateTopicRequest.Name);
-            result.MessageExpiry.Should().Be(TopicsFixtureBootstrap.UpdateTopicRequest.MessageExpiry);
-            result.CompressionAlgorithm.Should().Be(TopicsFixtureBootstrap.UpdateTopicRequest.CompressionAlgorithm);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(6)]
-    public async Task Purge_ExistingTopic_Should_PurgeTopic_Successfully()
-    {
-        // act
-        // TODO: Check if the topic is empty after purge
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.PurgeTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                        Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!)))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(7)]
-    public async Task Delete_ExistingTopic_Should_DeleteTopic_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.DeleteTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                        Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!)))
-                .Should()
-                .NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(8)]
-    public async Task Delete_NonExistingTopic_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-       
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.DeleteTopicAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                        Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!)))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(9)]
-    public async Task Get_NonExistingTopic_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Invoking(async x =>
-                    await x.Client.GetTopicByIdAsync(Identifier.Numeric((int)TopicsFixtureBootstrap.StreamRequest.StreamId!),
-                        Identifier.Numeric((int)TopicsFixtureBootstrap.TopicRequest.TopicId!)))
-                .Should()
-                .ThrowExactlyAsync<InvalidResponseException>();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-}
\ No newline at end of file
diff --git a/foreign/csharp/Iggy_SDK_Tests/E2ETests/UsersE2E.cs b/foreign/csharp/Iggy_SDK_Tests/E2ETests/UsersE2E.cs
deleted file mode 100644
index 1da63c37..00000000
--- a/foreign/csharp/Iggy_SDK_Tests/E2ETests/UsersE2E.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-// 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.Auth;
-using Apache.Iggy.Enums;
-using Apache.Iggy.Exceptions;
-using Apache.Iggy.Tests.E2ETests.Fixtures;
-using Apache.Iggy.Tests.E2ETests.Fixtures.Bootstraps;
-using Apache.Iggy.Tests.Utils;
-using FluentAssertions;
-
-namespace Apache.Iggy.Tests.E2ETests;
-
-[TestCaseOrderer("Apache.Iggy.Tests.Utils.PriorityOrderer", "Apache.Iggy.Tests")]
-public sealed class UsersE2E : IClassFixture<IggyTcpUsersFixture>
-{
-    private readonly IggyTcpUsersFixture _fixture;
-    public UsersE2E(IggyTcpUsersFixture fixture)
-    {
-        _fixture = fixture;
-    }
-
-    [Fact, TestPriority(1)]
-    public async Task CreateUser_Should_CreateUser_Successfully()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Client.Invoking(async x =>
-                await x.CreateUser(UsersFixtureBootstrap.UserRequest)
-            )
-            .Should()
-            .NotThrowAsync();
-        }));
-        
-        await Task.WhenAll(tasks);
-    }
-
-    [Fact, TestPriority(2)]
-    public async Task CreateUser_Duplicate_Should_Throw_InvalidResponse()
-    {
-        // act & assert
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            await sut.Client.Invoking(async x =>
-                await x.CreateUser(UsersFixtureBootstrap.UserRequest)
-            )
-            .Should()
-            .ThrowExactlyAsync<InvalidResponseException>();
-        }));
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(3)]
-    public async Task GetUser_Should_ReturnValidResponse()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act
-            var response = await sut.Client.GetUser(Identifier.Numeric(2));
-            
-            // assert
-            response.Should().NotBeNull();
-            response!.Id.Should().Be(2);
-            response.Username.Should().Be(UsersFixtureBootstrap.UserRequest.Username);
-            response.Status.Should().Be(UsersFixtureBootstrap.UserRequest.Status);
-            response.Permissions!.Global.Should().NotBeNull();
-            response.Permissions.Global.Should().BeEquivalentTo(UsersFixtureBootstrap.UserRequest.Permissions!.Global);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(4)]
-    public async Task GetUsers_Should_ReturnValidResponse()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act
-            var response = await sut.Client.GetUsers();
-            
-            // assert
-            response.Should().NotBeEmpty();
-            response.Should().HaveCount(2);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(5)]
-    public async Task UpdateUser_Should_UpdateUser_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            //act
-            await sut.Client.Invoking(async x =>
-                await x.UpdateUser(new UpdateUserRequest
-                {
-                    UserId = Identifier.Numeric(2), Username = UsersFixtureBootstrap.NewUsername, UserStatus = UserStatus.Active
-                })).Should().NotThrowAsync();
-            
-            var user = await sut.Client.GetUser(Identifier.Numeric(2));
-            
-            // assert
-            user!.Username.Should().Be(UsersFixtureBootstrap.NewUsername);
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(6)]
-    public async Task UpdatePermissions_Should_UpdatePermissions_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act
-            await sut.Client.Invoking(async x =>
-                await x.UpdatePermissions(new UpdateUserPermissionsRequest
-                {
-                    UserId = Identifier.Numeric(2), Permissions = UsersFixtureBootstrap.UpdatePermissionsRequest
-                })).Should().NotThrowAsync();
-            
-            var user = await sut.Client.GetUser(Identifier.Numeric(2));
-            
-            // assert
-            user!.Permissions!.Global.Should().NotBeNull();
-            user.Permissions.Should().BeEquivalentTo(UsersFixtureBootstrap.UpdatePermissionsRequest);
-            user.Permissions!.Streams.Should().NotBeNull();
-            user.Permissions.Streams.Should().HaveCount(1);
-            
-            user.Permissions.Global.ManageServers.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ManageServers);
-            user.Permissions.Global.ManageUsers.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ManageUsers);
-            user.Permissions.Global.ManageStreams.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ManageStreams);
-            user.Permissions.Global.ManageTopics.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ManageTopics);
-            user.Permissions.Global.PollMessages.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.PollMessages);
-            user.Permissions.Global.ReadServers.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ReadServers);
-            user.Permissions.Global.ReadStreams.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ReadStreams);
-            user.Permissions.Global.ReadTopics.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ReadTopics);
-            user.Permissions.Global.ReadUsers.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Global!.ReadUsers);
-            
-            var stream = user.Permissions.Streams!.FirstOrDefault();
-            stream.Should().NotBeNull();
-            stream!.Key.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Key);
-            stream.Value.Should().NotBeNull();
-            stream.Value.ManageStream.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.ManageStream);
-            stream.Value.ManageTopics.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.ManageTopics);
-            stream.Value.ReadStream.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.ReadStream);
-            stream.Value.SendMessages.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.SendMessages);
-            stream.Value.ReadTopics.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.ReadTopics);
-            stream.Value.PollMessages.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.PollMessages);
-            stream.Value.Topics.Should().NotBeNull();
-            
-            stream.Value.Topics!.Should().HaveCount(1);
-            stream.Value.Topics!.Should().ContainKey(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Key);
-            stream.Value.Topics!.First().Value.Should().NotBeNull();
-            stream.Value.Topics!.First().Key.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Key);
-            stream.Value.Topics!.First().Value.ManageTopic.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Value.ManageTopic);
-            stream.Value.Topics!.First().Value.PollMessages.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Value.PollMessages);
-            stream.Value.Topics!.First().Value.ReadTopic.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Value.ReadTopic);
-            stream.Value.Topics!.First().Value.SendMessages.Should().Be(UsersFixtureBootstrap.UpdatePermissionsRequest.Streams!.First().Value.Topics!.First().Value.SendMessages);
-            
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(7)]
-    public async Task ChangePassword_Should_ChangePassword_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act
-            await sut.Client.Invoking(async x =>
-                await x.ChangePassword(new ChangePasswordRequest()
-                {
-                    UserId = Identifier.Numeric(2), 
-                    CurrentPassword = "user1",
-                    NewPassword = "user2"
-                })).Should().NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-    
-    [Fact, TestPriority(8)]
-    public async Task DeleteUser_Should_DeleteUser_Successfully()
-    {
-        var task = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act & assert
-            await sut.Client.Invoking(async x =>
-                await x.DeleteUser(Identifier.Numeric(2))).Should().NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(task);
-    }
-    
-    [Fact, TestPriority(8)]
-    public async Task LogoutUser_Should_LogoutUser_Successfully()
-    {
-        var tasks = _fixture.SubjectsUnderTest.Select(sut => Task.Run(async () =>
-        {
-            // act & assert
-            await sut.Client.Invoking(async x =>
-                await x.LogoutUser()).Should().NotThrowAsync();
-        })).ToArray();
-        
-        await Task.WhenAll(tasks);
-    }
-}
-
diff --git a/foreign/csharp/Iggy_SDK_Tests/Iggy_SDK_Tests.csproj b/foreign/csharp/Iggy_SDK_Tests/Iggy_SDK_Tests.csproj
index eb2319b1..6efde3b3 100644
--- a/foreign/csharp/Iggy_SDK_Tests/Iggy_SDK_Tests.csproj
+++ b/foreign/csharp/Iggy_SDK_Tests/Iggy_SDK_Tests.csproj
@@ -12,16 +12,16 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="FluentAssertions" Version="6.12.0" />
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
-        <PackageReference Include="Moq" Version="4.20.72" />
-        <PackageReference Include="Testcontainers" Version="4.5.0" />
-        <PackageReference Include="xunit" Version="2.9.3" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="3.1.0">
+        <PackageReference Include="FluentAssertions" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" />
+        <PackageReference Include="Moq" />
+        <PackageReference Include="Testcontainers" />
+        <PackageReference Include="xunit" />
+        <PackageReference Include="xunit.runner.visualstudio">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="coverlet.collector" Version="6.0.4">
+        <PackageReference Include="coverlet.collector">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
@@ -32,20 +32,4 @@
       <ProjectReference Include="..\Shared\Shared.csproj" />
     </ItemGroup>
 
-
-    <ItemGroup>
-      <Reference Include="Microsoft.Extensions.Logging">
-        <HintPath>..\..\..\.nuget\packages\microsoft.extensions.logging\7.0.0\lib\net7.0\Microsoft.Extensions.Logging.dll</HintPath>
-      </Reference>
-    </ItemGroup>
-
-    <ItemGroup>
-      <Reference Include="Microsoft.Extensions.Logging">
-        <HintPath>..\..\..\.nuget\packages\microsoft.extensions.logging\7.0.0\lib\net7.0\Microsoft.Extensions.Logging.dll</HintPath>
-      </Reference>
-    </ItemGroup>
-
-
-
-
 </Project>
diff --git a/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs b/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
index f6968fc4..77801907 100644
--- a/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
+++ b/foreign/csharp/Iggy_SDK_Tests/MapperTests/BinaryMapper.cs
@@ -340,7 +340,8 @@ public sealed class BinaryMapper
         Assert.Equal(membersCount, response.MembersCount);
         Assert.Equal(partitionsCount, response.PartitionsCount);
         Assert.Equal(memberPartitions.Count, partitionsCount);
-        Assert.Equal(response.Members.Count, 1);
+        Assert.NotNull(response.Members);
+        Assert.Single(response.Members);
     }
 
     [Fact]
diff --git a/foreign/csharp/Iggy_SDK_Tests/Utils/PriorityOrderer.cs b/foreign/csharp/Iggy_SDK_Tests/Utils/PriorityOrderer.cs
index 8a70a9cc..b7fa4193 100644
--- a/foreign/csharp/Iggy_SDK_Tests/Utils/PriorityOrderer.cs
+++ b/foreign/csharp/Iggy_SDK_Tests/Utils/PriorityOrderer.cs
@@ -39,17 +39,22 @@ public class PriorityOrderer : ITestCaseOrderer
         foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
         {
             list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
-            foreach (TTestCase testCase in list) yield return testCase;
-
+            foreach (TTestCase testCase in list)
+            {
+                yield return testCase;
+            }
         }
     }
 
     static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key)
         where TValue : new()
     {
-        TValue result;
+        TValue? result;
 
-        if (dictionary.TryGetValue(key, out result)) return result;
+        if (dictionary.TryGetValue(key, out result))
+        {
+            return result;
+        }
 
         result = new TValue();
         dictionary[key] = result;
diff --git a/foreign/csharp/Iggy_SDK_Tests/Utils/Topics/TopicFactory.cs b/foreign/csharp/Iggy_SDK_Tests/Utils/Topics/TopicFactory.cs
index de74c04d..84eb20ff 100644
--- a/foreign/csharp/Iggy_SDK_Tests/Utils/Topics/TopicFactory.cs
+++ b/foreign/csharp/Iggy_SDK_Tests/Utils/Topics/TopicFactory.cs
@@ -41,13 +41,13 @@ internal static class TopicFactory
     internal static TopicRequest CreateTopicRequest()
     {
         return new TopicRequest(
-            TopicId: Random.Shared.Next(1, 9999),
-            Name: "test_topic" + Random.Shared.Next(1, 69) + Utility.RandomString(12).ToLower(),
-            CompressionAlgorithm: CompressionAlgorithm.Gzip,
-            MessageExpiry: (ulong)Random.Shared.Next(1, 69),
-            MaxTopicSize: (ulong)Random.Shared.NextInt64(2_000_000_000, 10_000_000_000),
-            ReplicationFactor: (byte)Random.Shared.Next(1, 8),
-            PartitionsCount: Random.Shared.Next(5, 25));
+            topicId: Random.Shared.Next(1, 9999),
+            name: "test_topic" + Random.Shared.Next(1, 69) + Utility.RandomString(12).ToLower(),
+            compressionAlgorithm: CompressionAlgorithm.Gzip,
+            messageExpiry: (ulong)Random.Shared.Next(1, 69),
+            maxTopicSize: (ulong)Random.Shared.NextInt64(2_000_000_000, 10_000_000_000),
+            replicationFactor: (byte)Random.Shared.Next(1, 8),
+            partitionsCount: Random.Shared.Next(5, 25));
     }
 
     internal static UpdateTopicRequest CreateUpdateTopicRequest()
@@ -63,13 +63,13 @@ internal static class TopicFactory
     internal static TopicRequest CreateTopicRequest(int topicId)
     {
         return new TopicRequest(
-            TopicId: topicId,
-            Name: "test_topic" + Random.Shared.Next(1, 69) + Utility.RandomString(12).ToLower(),
-            CompressionAlgorithm: CompressionAlgorithm.None,
-            MessageExpiry: (ulong)Random.Shared.Next(1, 69),
-            MaxTopicSize: (ulong)Random.Shared.NextInt64(2_000_000_000, 10_000_000_000),
-            ReplicationFactor: (byte)Random.Shared.Next(1, 8),
-            PartitionsCount: Random.Shared.Next(5, 25));
+            topicId: topicId,
+            name: "test_topic" + Random.Shared.Next(1, 69) + Utility.RandomString(12).ToLower(),
+            compressionAlgorithm: CompressionAlgorithm.None,
+            messageExpiry: (ulong)Random.Shared.Next(1, 69),
+            maxTopicSize: (ulong)Random.Shared.NextInt64(2_000_000_000, 10_000_000_000),
+            replicationFactor: (byte)Random.Shared.Next(1, 8),
+            partitionsCount: Random.Shared.Next(5, 25));
     }
     
     internal static TopicResponse CreateTopicsResponse()
diff --git a/foreign/csharp/Iggy_Sample_Consumer/Iggy_Sample_Consumer.csproj b/foreign/csharp/Iggy_Sample_Consumer/Iggy_Sample_Consumer.csproj
index 4bcfe602..902d2c6d 100644
--- a/foreign/csharp/Iggy_Sample_Consumer/Iggy_Sample_Consumer.csproj
+++ b/foreign/csharp/Iggy_Sample_Consumer/Iggy_Sample_Consumer.csproj
@@ -15,7 +15,7 @@
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
+      <PackageReference Include="Microsoft.Extensions.Logging.Console" />
     </ItemGroup>
 
 </Project>
diff --git a/foreign/csharp/Iggy_Sample_Consumer/Program.cs b/foreign/csharp/Iggy_Sample_Consumer/Program.cs
index e67e2c9d..0619d469 100644
--- a/foreign/csharp/Iggy_Sample_Consumer/Program.cs
+++ b/foreign/csharp/Iggy_Sample_Consumer/Program.cs
@@ -189,7 +189,7 @@ async Task ValidateSystem(Identifier streamId, Identifier topicId, int partition
         
         var result = await bus.GetStreamByIdAsync(streamId);
         
-        Console.WriteLine(result.Name);
+        Console.WriteLine(result!.Name);
         
         Console.WriteLine($"Validating if topic exists.. {topicId}");
         
@@ -214,13 +214,13 @@ async Task ValidateSystem(Identifier streamId, Identifier topicId, int partition
         Console.WriteLine($"Creating topic with {topicId}");
         
         await bus.CreateTopicAsync(streamId, new TopicRequest(
-            TopicId: topicIdVal,
-            Name: "Test Consumer Topic",
-            CompressionAlgorithm: CompressionAlgorithm.None,
-            MessageExpiry: 0,
-            MaxTopicSize: 1_000_000_000,
-            ReplicationFactor: 3,
-            PartitionsCount: 3));
+            topicId: topicIdVal,
+            name: "Test Consumer Topic",
+            compressionAlgorithm: CompressionAlgorithm.None,
+            messageExpiry: 0,
+            maxTopicSize: 1_000_000_000,
+            replicationFactor: 3,
+            partitionsCount: 3));
         
         var topicRes = await bus.GetTopicByIdAsync(streamId, topicId);
         
diff --git a/foreign/csharp/Iggy_Sample_Producer/Iggy_Sample_Producer.csproj b/foreign/csharp/Iggy_Sample_Producer/Iggy_Sample_Producer.csproj
index c492c7d6..dff32fb2 100644
--- a/foreign/csharp/Iggy_Sample_Producer/Iggy_Sample_Producer.csproj
+++ b/foreign/csharp/Iggy_Sample_Producer/Iggy_Sample_Producer.csproj
@@ -15,9 +15,7 @@
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
+      <PackageReference Include="Microsoft.Extensions.Logging.Console" />
     </ItemGroup>
-
-
-
+    
 </Project>
diff --git a/foreign/csharp/Iggy_Sample_Producer/Program.cs b/foreign/csharp/Iggy_Sample_Producer/Program.cs
index c528760b..1198ca9c 100644
--- a/foreign/csharp/Iggy_Sample_Producer/Program.cs
+++ b/foreign/csharp/Iggy_Sample_Producer/Program.cs
@@ -151,13 +151,13 @@ catch
 
     Console.WriteLine($"Creating topic with id:{topicId}");
     await bus.CreateTopicAsync(streamId, new TopicRequest(
-        TopicId: topicIdVal,
-        Name: "producer-topic",
-        CompressionAlgorithm: CompressionAlgorithm.None,
-        MessageExpiry: 0,
-        MaxTopicSize: 0,
-        ReplicationFactor: 3,
-        PartitionsCount: 3));
+        topicId: topicIdVal,
+        name: "producer-topic",
+        compressionAlgorithm: CompressionAlgorithm.None,
+        messageExpiry: 0,
+        maxTopicSize: 0,
+        replicationFactor: 3,
+        partitionsCount: 3));
 }
 
 var actualStream = await bus.GetStreamByIdAsync(streamId);
diff --git a/foreign/csharp/README.md b/foreign/csharp/README.md
index af1dfac1..c072eaa9 100644
--- a/foreign/csharp/README.md
+++ b/foreign/csharp/README.md
@@ -1,4 +1,4 @@
-# C# SDK for [Iggy](https://github.com/spetz/iggy)
+# C# SDK for [Iggy](https://github.com/apache/iggy)
 
 <div align="center">
 
@@ -228,8 +228,20 @@ The `Message` struct has two fields `Id` and `Payload`.
 ```c#
 struct Message
 {
-    public required Guid Id { get; init; }
+    public required MessageHeader Header { get; init; }
     public required byte[] Payload { get; init; }
+    public Dictionary<HeaderKey, HeaderValue>? UserHeaders { get; init; }
+}
+
+public readonly struct MessageHeader
+{
+    public ulong Checksum { get; init; }
+    public UInt128 Id { get; init; }
+    public ulong Offset { get; init; }
+    public DateTimeOffset Timestamp { get; init; }
+    public ulong OriginTimestamp { get; init; }
+    public int UserHeadersLength { get; init; }
+    public int PayloadLength { get; init; }
 }
 ```