Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: Make IT to manage redis. (#116) #1072

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need that? Logs produce too much noise and aren't helpful.
If you need logs, I'll activate them, but in IntegrationTestBase::SetUp

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use info in tests by default, in order to get decent logs on our tests. they're loud, but it helps for failures in CI.

}
using static tests.Integration.IntegrationTestBase;

public class GetAndSet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this a class? what's the intended separation between test classes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No separation, I just renamed it. I assume we won't put all tests into a single class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, again - what's the intended separation?

{
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))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to create client once for all these tests?

{
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use internal when possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added internal config

{
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]);
}
}
Loading