Skip to content

Commit

Permalink
Redis Support (#9)
Browse files Browse the repository at this point in the history
* still wip

* maybe it works

* redis support stuff

* tests for redis 6 and 7

* guarantee one time disposal + ephemerals will try to dispose their underlying value + centralize TryDispose and TryDisposeAsync

* rearrange FixedObjectPool stuff

* reorganize some stuff

* explicitly do not support CleanupAllAsync for redis

* fix naming

* ci changes

* use ubuntu runner

* run cosmos tests in a separate job

* typo

* try something else

* use separate comments

* change redis container wait strategy

* combine results into one comment

* multiline pwsh backticks + rename test result artifacts

* download all artifacts
  • Loading branch information
Confusingboat authored Mar 17, 2024
1 parent 14a5950 commit 700504d
Show file tree
Hide file tree
Showing 34 changed files with 2,324 additions and 30 deletions.
116 changes: 107 additions & 9 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- '*'

env:
TIMEZONE: 'America/Chicago'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_NOLOGO: true
ArtifactDirectory: ${{ github.workspace}}/artifacts
Expand All @@ -22,9 +23,65 @@ defaults:
shell: pwsh

jobs:
build_and_test:
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v2

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8
dotnet-quality: ga

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration:Release

- name: Test Core
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' }}
run: |
dotnet test 'tests\Ephemerally.Tests\' `
--no-build `
--configuration:Release `
--logger:trx `
--results-directory ${{ env.ArtifactDirectory }}
- name: Test Redis
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' }}
run: |
dotnet test 'tests\Ephemerally.Redis.Tests\' `
--no-build `
--configuration:Release `
--logger:trx `
--results-directory ${{ env.ArtifactDirectory }}
- name: Upload Test Results
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' }}
uses: actions/upload-artifact@v4
with:
name: test-results
path: ${{ env.ArtifactDirectory }}/**/*.trx

# - name: Test Results Comment
# if: always()
# uses: im-open/[email protected]
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# timezone: ${{ env.TIMEZONE }}
# create-status-check: false
# create-pr-comment: true
# comment-identifier: 'TestResults'

# Cosmos DB Emulator is broken on Ubuntu
# https://github.com/Azure/azure-cosmos-db-emulator-docker/issues/56
build_and_test:

build_and_test_cosmos:
runs-on: windows-2022
permissions:
checks: write
Expand All @@ -50,14 +107,14 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6
dotnet-version: 8
dotnet-quality: ga

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore
run: dotnet build --no-restore --configuration:Release

# Reenable when Ubuntu is fixed
# - name: Wait for Cosmos Emulator
Expand All @@ -68,16 +125,56 @@ jobs:
# timeout: 5000
# interval: 500

- name: Test
- name: Test Cosmos
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' }}
run: dotnet test --no-build --verbosity normal --logger:trx --results-directory ${{ env.ArtifactDirectory }}
run: |
dotnet test 'tests\Ephemerally.Azure.Cosmos.Tests\' `
--no-build `
--configuration:Release `
--logger:trx `
--results-directory ${{ env.ArtifactDirectory }}
- name: Upload Test Results
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' }}
uses: actions/upload-artifact@v4
with:
name: cosmos-test-results
path: ${{ env.ArtifactDirectory }}/**/*.trx

# - name: Test Results Comment
# if: always()
# uses: im-open/[email protected]
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# timezone: ${{ env.TIMEZONE }}
# create-status-check: false
# create-pr-comment: true
# comment-identifier: 'CosmosTestResults'

test_results_comment:
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
needs:
- build_and_test
- build_and_test_cosmos

steps:
- name: Download Test Results
uses: actions/download-artifact@v4
with:
path: ${{ env.ArtifactDirectory }}

- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action/composite@v2
- name: Test Results Comment
if: always()
uses: im-open/[email protected]
with:
files: |
${{ env.ArtifactDirectory }}/**/*.trx
github-token: ${{ secrets.GITHUB_TOKEN }}
timezone: ${{ env.TIMEZONE }}
create-status-check: false
create-pr-comment: true
comment-identifier: 'TestResults'

tag:
if: ${{ github.event_name != 'pull_request' }}
Expand All @@ -87,6 +184,7 @@ jobs:
needs:
- tag
- build_and_test
- build_and_test_cosmos
if: ${{ github.event_name != 'pull_request' && needs.tag.outputs.prerelease-depth < 2 }}
uses: ./.github/workflows/release.yml
with:
Expand Down
28 changes: 28 additions & 0 deletions Ephemerally.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ephemerally.Azure.Cosmos.Te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ephemerally.Azure", "src\Ephemerally.Azure\Ephemerally.Azure.csproj", "{B99D5934-DD84-4709-95F8-90F795D66747}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ephemerally.Redis", "src\Ephemerally.Redis\Ephemerally.Redis.csproj", "{7E763132-0532-4689-A738-7CB71635AC63}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ephemerally.Redis.Tests", "tests\Ephemerally.Redis.Tests\Ephemerally.Redis.Tests.csproj", "{E186436A-12C9-4140-A279-19227CFF8533}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ephemerally.Redis.Xunit", "src\Ephemerally.Redis.Xunit\Ephemerally.Redis.Xunit.csproj", "{5A30E9EA-AB80-4E08-A81F-99CE329B34B8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{EBD69ABD-040A-447C-8633-D929206F2BBC}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build_and_test.yml = .github\workflows\build_and_test.yml
.github\workflows\release.yml = .github\workflows\release.yml
.github\workflows\tag.yml = .github\workflows\tag.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -49,6 +62,18 @@ Global
{B99D5934-DD84-4709-95F8-90F795D66747}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B99D5934-DD84-4709-95F8-90F795D66747}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B99D5934-DD84-4709-95F8-90F795D66747}.Release|Any CPU.Build.0 = Release|Any CPU
{7E763132-0532-4689-A738-7CB71635AC63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E763132-0532-4689-A738-7CB71635AC63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E763132-0532-4689-A738-7CB71635AC63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E763132-0532-4689-A738-7CB71635AC63}.Release|Any CPU.Build.0 = Release|Any CPU
{E186436A-12C9-4140-A279-19227CFF8533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E186436A-12C9-4140-A279-19227CFF8533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E186436A-12C9-4140-A279-19227CFF8533}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E186436A-12C9-4140-A279-19227CFF8533}.Release|Any CPU.Build.0 = Release|Any CPU
{5A30E9EA-AB80-4E08-A81F-99CE329B34B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A30E9EA-AB80-4E08-A81F-99CE329B34B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A30E9EA-AB80-4E08-A81F-99CE329B34B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A30E9EA-AB80-4E08-A81F-99CE329B34B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,6 +85,9 @@ Global
{751D596B-8729-40DB-A172-EF9C55CBDA95} = {AF30F58C-5E9B-4406-9E13-114C46F4E410}
{DB56A0B0-0EAC-4DE1-8E4D-B62AC8109ACF} = {AF30F58C-5E9B-4406-9E13-114C46F4E410}
{B99D5934-DD84-4709-95F8-90F795D66747} = {F9102409-3748-4DA6-9D7B-784339508244}
{7E763132-0532-4689-A738-7CB71635AC63} = {F9102409-3748-4DA6-9D7B-784339508244}
{E186436A-12C9-4140-A279-19227CFF8533} = {AF30F58C-5E9B-4406-9E13-114C46F4E410}
{5A30E9EA-AB80-4E08-A81F-99CE329B34B8} = {F9102409-3748-4DA6-9D7B-784339508244}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5690E595-5302-46D7-AA86-89D55117B068}
Expand Down
3 changes: 0 additions & 3 deletions src/Ephemerally.Azure.Cosmos/InternalExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ namespace Ephemerally.Azure.Cosmos;

internal static class InternalExtensions
{
internal static T OrDefault<T>(this T options) where T : EphemeralOptions, new() =>
options ?? new T();

internal static async Task<bool> ExistsAsync(this Database database) =>
await database.Client.DatabaseExistsAsync(database.Id).ConfigureAwait(false);

Expand Down
13 changes: 13 additions & 0 deletions src/Ephemerally.Redis.Xunit/DefaultLocalRedisInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using StackExchange.Redis;

namespace Ephemerally.Redis.Xunit;

public class DefaultLocalRedisInstance
{
public static ConnectionMultiplexer GetMultiplexer() => ConnectionMultiplexer.Connect(ConnectionString);


public const string ConnectionString = "localhost:6379,allowAdmin=true";

public static ushort Port => 6379;
}
23 changes: 23 additions & 0 deletions src/Ephemerally.Redis.Xunit/EphemeralRedisDatabaseFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Ephemerally.Redis.Xunit;

internal class EphemeralRedisDatabaseFixture : EphemeralRedisDatabasePoolFixture
{
private readonly Lazy<Task<IEphemeralRedisDatabase>> _database;

public IEphemeralRedisDatabase Database => _database.Value.Result;

public EphemeralRedisDatabaseFixture()
{
_database = new(CreateDatabaseAsync);
}

protected virtual async Task<IEphemeralRedisDatabase> CreateDatabaseAsync()
{
var multiplexer = await GetMultiplexer();
return multiplexer.GetDatabase() as IEphemeralRedisDatabase;
}
}
16 changes: 16 additions & 0 deletions src/Ephemerally.Redis.Xunit/EphemeralRedisDatabasePoolFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using StackExchange.Redis;

namespace Ephemerally.Redis.Xunit;

public class EphemeralRedisDatabasePoolFixture : RedisMultiplexerFixture
{
public EphemeralRedisDatabasePoolFixture() { }
protected EphemeralRedisDatabasePoolFixture(IRedisTestContainerFixture containerFixture)
: base(containerFixture) { }

protected override async Task<IConnectionMultiplexer> CreateMultiplexerAsync()
{
var implementation = await base.CreateMultiplexerAsync();
return new EphemeralConnectionMultiplexer(implementation);
}
}
34 changes: 34 additions & 0 deletions src/Ephemerally.Redis.Xunit/Ephemerally.Redis.Xunit.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>latest</LangVersion>
<IsTestProject>false</IsTestProject>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Ephemerally.Redis.Tests" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Ephemerally.Redis\Ephemerally.Redis.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="minver" Version="4.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StackExchange.Redis" Version="2.7.20" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="Xunit" Version="2.4.2" />
</ItemGroup>

</Project>
44 changes: 44 additions & 0 deletions src/Ephemerally.Redis.Xunit/RedisMultiplexerFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using StackExchange.Redis;
using Xunit;

namespace Ephemerally.Redis.Xunit;

public class RedisMultiplexerFixture<TRedisTestContainerFixture>()
: RedisMultiplexerFixture(new TRedisTestContainerFixture())
where TRedisTestContainerFixture : IRedisTestContainerFixture, new();

public class RedisMultiplexerFixture : IAsyncLifetime
{
private readonly IRedisTestContainerFixture _containerFixture;
private readonly Lazy<Task<IConnectionMultiplexer>> _multiplexer;

public IConnectionMultiplexer Multiplexer => _multiplexer.Value.Result;

protected Task<IConnectionMultiplexer> GetMultiplexer() => _multiplexer.Value;

public RedisMultiplexerFixture() : this(UnmanagedTestContainerFixture.Instance) { }

protected RedisMultiplexerFixture(IRedisTestContainerFixture containerFixture)
{
_containerFixture = containerFixture;
_multiplexer = new Lazy<Task<IConnectionMultiplexer>>(CreateMultiplexerAsync);
}

protected virtual async Task<IConnectionMultiplexer> CreateMultiplexerAsync() =>
await ConnectionMultiplexer.ConnectAsync(_containerFixture.ConnectionString);

public virtual async Task InitializeAsync()
{
await _containerFixture.InitializeAsync();
await _multiplexer.Value;
}

public virtual async Task DisposeAsync()
{
if (!_multiplexer.IsValueCreated) return;

var multiplexer = await GetMultiplexer();
await multiplexer.DisposeAsync();
await _containerFixture.DisposeAsync();
}
}
24 changes: 24 additions & 0 deletions src/Ephemerally.Redis.Xunit/RedisTestContainerFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Xunit;

namespace Ephemerally.Redis.Xunit;

public interface IRedisTestContainerFixture : IAsyncLifetime
{
ushort PublicPort { get; }
string ConnectionString { get; }
}

public sealed class UnmanagedTestContainerFixture : IRedisTestContainerFixture
{
private static readonly Lazy<UnmanagedTestContainerFixture> _instance = new(() => new UnmanagedTestContainerFixture());

public static UnmanagedTestContainerFixture Instance => _instance.Value;

private UnmanagedTestContainerFixture() { }

public ushort PublicPort => DefaultLocalRedisInstance.Port;
public string ConnectionString => DefaultLocalRedisInstance.ConnectionString;
public Task InitializeAsync() => Task.CompletedTask;

public Task DisposeAsync() => Task.CompletedTask;
}
Loading

0 comments on commit 700504d

Please sign in to comment.