-
Notifications
You must be signed in to change notification settings - Fork 69
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
|
@@ -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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
|
@@ -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 汉字"; | ||
|
@@ -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)); | ||
|
@@ -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 = ""; | ||
|
@@ -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(); | ||
|
@@ -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>(); | ||
|
||
|
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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use internal when possible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nunit requires a public default constructor https://docs.nunit.org/articles/nunit/writing-tests/attributes/setupfixture.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
{ | ||
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]); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why was this removed?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.