Skip to content

Commit

Permalink
C#: Make IT to manage redis. (#116) (valkey-io#1072)
Browse files Browse the repository at this point in the history
* C#: Make IT to manage redis. (#116)

Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand authored Mar 6, 2024
1 parent 3448bea commit 94e1186
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 19 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/csharp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ jobs:
working-directory: ./csharp
run: dotnet format --verify-no-changes --verbosity diagnostic

- uses: ./.github/workflows/test-benchmark
with:
language-flag: -csharp

- name: Test dotnet ${{ matrix.dotnet }}
working-directory: ./csharp
run: dotnet test --framework net${{ matrix.dotnet }} "-l:html;LogFileName=TestReport.html" --results-directory . -warnaserror

- uses: ./.github/workflows/test-benchmark
with:
language-flag: -csharp

- name: Upload test reports
if: always()
continue-on-error: true
Expand All @@ -88,6 +88,7 @@ jobs:
path: |
csharp/TestReport.html
benchmarks/results/*
utils/clusters/**
lint-rust:
timeout-minutes: 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@
* Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
*/

namespace tests;
namespace tests.Integration;

using Glide;

// TODO - need to start a new redis server for each test?
public class AsyncClientTests
{
[OneTimeSetUp]
public void Setup()
{
Glide.Logger.SetLoggerConfig(Glide.Level.Info);
}
using static tests.Integration.IntegrationTestBase;

public class GetAndSet
{
private async Task GetAndSetRandomValues(AsyncClient client)
{
var key = Guid.NewGuid().ToString();
Expand All @@ -27,7 +22,7 @@ private async Task GetAndSetRandomValues(AsyncClient client)
[Test]
public async Task GetReturnsLastSet()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
await GetAndSetRandomValues(client);
}
Expand All @@ -36,7 +31,7 @@ public async Task GetReturnsLastSet()
[Test]
public async Task GetAndSetCanHandleNonASCIIUnicode()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
var key = Guid.NewGuid().ToString();
var value = "שלום hello 汉字";
Expand All @@ -49,7 +44,7 @@ public async Task GetAndSetCanHandleNonASCIIUnicode()
[Test]
public async Task GetReturnsNull()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
var result = await client.GetAsync(Guid.NewGuid().ToString());
Assert.That(result, Is.EqualTo(null));
Expand All @@ -59,7 +54,7 @@ public async Task GetReturnsNull()
[Test]
public async Task GetReturnsEmptyString()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
var key = Guid.NewGuid().ToString();
var value = "";
Expand All @@ -72,7 +67,7 @@ public async Task GetReturnsEmptyString()
[Test]
public async Task HandleVeryLargeInput()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
var key = Guid.NewGuid().ToString();
var value = Guid.NewGuid().ToString();
Expand All @@ -92,7 +87,7 @@ public async Task HandleVeryLargeInput()
[Test]
public void ConcurrentOperationsWork()
{
using (var client = new AsyncClient("localhost", 6379, false))
using (var client = new AsyncClient("localhost", TestConfiguration.STANDALONE_PORTS[0], false))
{
var operations = new List<Task>();

Expand Down
134 changes: 134 additions & 0 deletions csharp/tests/Integration/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
*/

using System.Diagnostics;

// Note: All IT should be in the same namespace
namespace tests.Integration;

[SetUpFixture]
public class IntegrationTestBase
{
internal class TestConfiguration
{
public static List<uint> STANDALONE_PORTS { get; internal set; } = new();
public static List<uint> CLUSTER_PORTS { get; internal set; } = new();
public static Version REDIS_VERSION { get; internal set; } = new();
}

[OneTimeSetUp]
public void SetUp()
{
// Stop all if weren't stopped on previous test run
StopRedis(false);

// Delete dirs if stop failed due to https://github.com/aws/glide-for-redis/issues/849
Directory.Delete(Path.Combine(_scriptDir, "clusters"), true);

// Start cluster
TestConfiguration.CLUSTER_PORTS = StartRedis(true);
// Start standalone
TestConfiguration.STANDALONE_PORTS = StartRedis(false);
// Get redis version
TestConfiguration.REDIS_VERSION = GetRedisVersion();

TestContext.Progress.WriteLine($"Cluster ports = {string.Join(',', TestConfiguration.CLUSTER_PORTS)}");
TestContext.Progress.WriteLine($"Standalone ports = {string.Join(',', TestConfiguration.STANDALONE_PORTS)}");
TestContext.Progress.WriteLine($"Redis version = {TestConfiguration.REDIS_VERSION}");
}

[OneTimeTearDown]
public void TearDown()
{
// Stop all
StopRedis(true);
}

private readonly string _scriptDir;

// Nunit requires a public default constructor. These variables would be set in SetUp method.
public IntegrationTestBase()
{
string? projectDir = Directory.GetCurrentDirectory();
while (!(Path.GetFileName(projectDir) == "csharp" || projectDir == null))
projectDir = Path.GetDirectoryName(projectDir);

if (projectDir == null)
throw new FileNotFoundException("Can't detect the project dir. Are you running tests from `csharp` directory?");

_scriptDir = Path.Combine(projectDir, "..", "utils");
}

internal List<uint> StartRedis(bool cluster, bool tls = false, string? name = null)
{
string cmd = $"start {(cluster ? "--cluster-mode" : "-r 0")} {(tls ? " --tls" : "")} {(name != null ? " --prefix " + name : "")}";
return ParsePortsFromOutput(RunClusterManager(cmd, false));
}

/// <summary>
/// Stop <b>all</b> instances on the given <paramref name="name"/>.
/// </summary>
internal void StopRedis(bool keepLogs, string? name = null)
{
string cmd = $"stop --prefix {name ?? "redis-cluster"} {(keepLogs ? "--keep-folder" : "")}";
RunClusterManager(cmd, true);
}

private string RunClusterManager(string cmd, bool ignoreExitCode)
{
ProcessStartInfo info = new()
{
WorkingDirectory = _scriptDir,
FileName = "python3",
Arguments = "cluster_manager.py " + cmd,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
Process? script = Process.Start(info);
script?.WaitForExit();
string? error = script?.StandardError.ReadToEnd();
string? output = script?.StandardOutput.ReadToEnd();
int? exit_code = script?.ExitCode;

TestContext.Progress.WriteLine($"cluster_manager.py stdout\n====\n{output}\n====\ncluster_manager.py stderr\n====\n{error}\n====\n");

if (!ignoreExitCode && exit_code != 0)
throw new ApplicationException($"cluster_manager.py script failed: exit code {exit_code}.");

return output ?? "";
}

private static List<uint> ParsePortsFromOutput(string output)
{
List<uint> ports = new();
foreach (string line in output.Split("\n"))
{
if (!line.StartsWith("CLUSTER_NODES="))
continue;

string[] addresses = line.Split("=")[1].Split(",");
foreach (string address in addresses)
ports.Add(uint.Parse(address.Split(":")[1]));
}
return ports;
}

private static Version GetRedisVersion()
{
ProcessStartInfo info = new()
{
FileName = "redis-server",
Arguments = "-v",
UseShellExecute = false,
RedirectStandardOutput = true,
};
Process? proc = Process.Start(info);
proc?.WaitForExit();
string output = proc?.StandardOutput.ReadToEnd() ?? "";

// Redis server v=7.2.3 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=7504b1fedf883f2
return new Version(output.Split(" ")[2].Split("=")[1]);
}
}

0 comments on commit 94e1186

Please sign in to comment.