Updated integration testing for English and Japanese. Fixed minor voice work updater issue. Updated to XUnitV3.

This commit is contained in:
2025-10-28 22:01:34 -04:00
parent 99c397b3bc
commit 6d090390b0
24 changed files with 1018 additions and 354 deletions

View File

@@ -25,4 +25,40 @@ public class CircleSearchProviderFixture : MariaDbFixture
await context.SaveChangesAsync();
}
}
public sealed class CircleSearchProviderFixture2(MariaDbContainerFixture container) : IAsyncLifetime
{
public AppDbContext? DbContext { get; private set; }
public async ValueTask InitializeAsync()
{
DbContext = await MariaTestDb.CreateIsolatedAsync(
container.RootConnectionString,
seed: SeedAsync);
}
private static async Task SeedAsync(AppDbContext context)
{
// Make seeding idempotent (quick existence check)
if (await context.Circles.AnyAsync())
return;
context.Circles.AddRange(
new() { CircleId = 1, Name = "Good Dreams", MakerId = "RG00001" },
new() { CircleId = 2, Name = "Sweet Dreams", Favorite = true, MakerId = "RG00002" },
new() { CircleId = 3, Name = "Nightmare Fuel", Blacklisted = true, MakerId = "RG00003" },
new() { CircleId = 4, Name = "Garbage Studio", Spam = true, MakerId = "RG00004" }
);
await context.SaveChangesAsync();
}
public async ValueTask DisposeAsync()
{
if (DbContext is not null)
{
await DbContext.DisposeAsync();
}
}
}

View File

@@ -23,4 +23,39 @@ public class CreatorSearchProviderFixture : MariaDbFixture
await context.SaveChangesAsync();
}
}
public sealed class CreatorSearchProviderFixture2(MariaDbContainerFixture container) : IAsyncLifetime
{
public AppDbContext? DbContext { get; private set; }
public async ValueTask InitializeAsync()
{
DbContext = await MariaTestDb.CreateIsolatedAsync(
container.RootConnectionString,
seed: SeedAsync);
}
private static async Task SeedAsync(AppDbContext context)
{
if (await context.Tags.AnyAsync())
return;
context.Creators.AddRange(
new() { CreatorId = 1, Name = "John Smith" },
new() { CreatorId = 2, Name = "John Doe", Favorite = true },
new() { CreatorId = 3, Name = "Jane Doe", Blacklisted = true }
);
await context.SaveChangesAsync();
}
public async ValueTask DisposeAsync()
{
if (DbContext is not null)
{
await DbContext.DisposeAsync();
}
}
}

View File

@@ -1,14 +1,57 @@
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using JSMR.Infrastructure.Data;
using JSMR.Tests.Fixtures;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;
using Testcontainers.MariaDb;
using Testcontainers.Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
[assembly: AssemblyFixture(typeof(MariaDbContainerFixture))]
namespace JSMR.Tests.Fixtures;
public sealed class MariaDbContainerFixture : IAsyncLifetime
{
const int MajorVersion = 10;
const int MinorVersion = 11;
const int Build = 6;
private IContainer _container = default!;
public string RootConnectionString { get; private set; } = default!;
public async ValueTask InitializeAsync()
{
//_container = new ContainerBuilder()
// .WithImage("mariadb:11")
// .WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
// .WithPortBinding(3307, 3306)
// .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3306))
// .Build();
_container = new ContainerBuilder()
.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
.WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
.WithPortBinding(3307, 3306)
//.WithPortBinding(3306, assignRandomHostPort: true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(3306))
.Build();
await _container.StartAsync();
// No database specified: well create per-test DBs
//RootConnectionString = "Server=127.0.0.1;Port=3307;User=root;Password=rootpw;SslMode=none;";
//RootConnectionString = _container.GetConnectionString();
var port = _container.GetMappedPublicPort(3306);
RootConnectionString = $"Server=127.0.0.1;Port={port};User=root;Password=rootpw;SslMode=none;";
}
public async ValueTask DisposeAsync() => await _container.DisposeAsync();
}
public class MariaDbFixture : IAsyncLifetime
{
const int MajorVersion = 10;
@@ -19,7 +62,7 @@ public class MariaDbFixture : IAsyncLifetime
public string ConnectionString { get; private set; } = default!;
public async Task InitializeAsync()
public async ValueTask InitializeAsync()
{
MariaDbContainer = new MariaDbBuilder()
.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
@@ -40,13 +83,15 @@ public class MariaDbFixture : IAsyncLifetime
return Task.FromResult(Task.CompletedTask);
}
public async Task DisposeAsync()
public async ValueTask DisposeAsync()
{
if (MariaDbContainer is not null)
{
await MariaDbContainer.StopAsync();
await MariaDbContainer.DisposeAsync();
}
GC.SuppressFinalize(this);
}
public AppDbContext CreateDbContext()
@@ -74,6 +119,9 @@ public class MariaDbFixture : IAsyncLifetime
[CollectionDefinition("db")]
public sealed class MariaDbCollection : ICollectionFixture<MariaDbContainerFixture> { }
//public class MariaDbAssemblyFixtureDefinition : IAssemblyFixture<MariaDbContainerFixture> { }
//[UsedImplicitly]
public sealed class MariaDbContainerFixture2(IMessageSink messageSink)
: ContainerFixture<MariaDbBuilder, MariaDbContainer>(messageSink)
@@ -94,46 +142,6 @@ public sealed class MariaDbContainerFixture2(IMessageSink messageSink)
}
}
public sealed class MariaDbContainerFixture : IAsyncLifetime
{
const int MajorVersion = 10;
const int MinorVersion = 11;
const int Build = 6;
private IContainer _container = default!;
public string RootConnectionString { get; private set; } = default!;
public async Task InitializeAsync()
{
//_container = new ContainerBuilder()
// .WithImage("mariadb:11")
// .WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
// .WithPortBinding(3307, 3306)
// .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(3306))
// .Build();
_container = new ContainerBuilder()
.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
.WithEnvironment("MARIADB_ROOT_PASSWORD", "rootpw")
.WithPortBinding(3307, 3306)
//.WithPortBinding(3306, assignRandomHostPort: true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(3306))
.Build();
await _container.StartAsync();
// No database specified: well create per-test DBs
//RootConnectionString = "Server=127.0.0.1;Port=3307;User=root;Password=rootpw;SslMode=none;";
//RootConnectionString = _container.GetConnectionString();
var port = _container.GetMappedPublicPort(3306);
RootConnectionString = $"Server=127.0.0.1;Port={port};User=root;Password=rootpw;SslMode=none;";
}
public async Task DisposeAsync() => await _container.DisposeAsync();
}
public static class MariaTestDb
{
public static async Task<AppDbContext> CreateIsolatedAsync(string rootConnectionString, Func<AppDbContext, Task>? seed = null)

View File

@@ -38,7 +38,7 @@ public sealed class TagSearchProviderFixture2(MariaDbContainerFixture container)
{
public AppDbContext? DbContext { get; private set; }
public async Task InitializeAsync()
public async ValueTask InitializeAsync()
{
DbContext = await MariaTestDb.CreateIsolatedAsync(
container.RootConnectionString,
@@ -65,7 +65,7 @@ public sealed class TagSearchProviderFixture2(MariaDbContainerFixture container)
await context.SaveChangesAsync();
}
public async Task DisposeAsync()
public async ValueTask DisposeAsync()
{
if (DbContext is not null)
{

View File

@@ -124,4 +124,138 @@ public class VoiceWorkSearchProviderFixture : MariaDbFixture
await context.SaveChangesAsync();
}
}
public sealed class VoiceWorkSearchProviderFixture2(MariaDbContainerFixture container) : IAsyncLifetime
{
public AppDbContext? DbContext { get; private set; }
public async ValueTask InitializeAsync()
{
DbContext = await MariaTestDb.CreateIsolatedAsync(
container.RootConnectionString,
seed: SeedAsync);
}
private static async Task SeedAsync(AppDbContext context)
{
if (await context.VoiceWorks.AnyAsync())
return;
context.Circles.AddRange(
new() { CircleId = 1, Name = "Good Dreams", MakerId = "RG00001" },
new() { CircleId = 2, Name = "Sweet Dreams", Favorite = true, MakerId = "RG00002" },
new() { CircleId = 3, Name = "Nightmare Fuel", Blacklisted = true, MakerId = "RG00003" }
);
context.VoiceWorks.AddRange(
new() { VoiceWorkId = 1, CircleId = 1, ProductId = "RJ0000001", ProductName = "Today Sounds", Description = "An average product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 1), Downloads = 500, WishlistCount = 750, StarRating = 35 },
new() { VoiceWorkId = 2, CircleId = 2, ProductId = "RJ0000002", ProductName = "Super Comfy ASMR", Description = "An amazing product!", Status = (byte)VoiceWorkStatus.NewRelease, SalesDate = new(2025, 1, 3), Downloads = 5000, WishlistCount = 12000, StarRating = 50, Favorite = true },
new() { VoiceWorkId = 3, CircleId = 3, ProductId = "RJ0000003", ProductName = "Low Effort", Description = "A bad product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 2), Downloads = 50, WishlistCount = 100, StarRating = 20 },
new() { VoiceWorkId = 4, CircleId = 1, ProductId = "RJ0000004", ProductName = "Tomorrow Sounds", Description = "A average upcoming product.", Status = (byte)VoiceWorkStatus.Upcoming, ExpectedDate = new(2025, 1, 1), WishlistCount = 300 },
new() { VoiceWorkId = 5, CircleId = 2, ProductId = "RJ0000005", ProductName = "Super Comfy ASMR+", Description = "All your favorite sounds, plus more!", Status = (byte)VoiceWorkStatus.NewAndUpcoming, ExpectedDate = new(2025, 1, 11), WishlistCount = 10000 }
);
context.Tags.AddRange(
new() { TagId = 1, Name = "ASMR" },
new() { TagId = 2, Name = "OL" },
new() { TagId = 3, Name = "ほのぼの" },
new() { TagId = 4, Name = "エルフ/妖精" },
new() { TagId = 5, Name = "ツンデレ", Favorite = true },
new() { TagId = 6, Name = "オールハッピー" },
new() { TagId = 7, Name = "ギャル" },
new() { TagId = 8, Name = "メイド" },
new() { TagId = 9, Name = "ノンフィクション/体験談", Blacklisted = true }
);
context.EnglishTags.AddRange(
new() { EnglishTagId = 1, TagId = 1, Name = "ASMR" },
new() { EnglishTagId = 2, TagId = 2, Name = "Office Lady" },
new() { EnglishTagId = 3, TagId = 3, Name = "Heartwarming" },
new() { EnglishTagId = 4, TagId = 4, Name = "Elf / Fairy" },
new() { EnglishTagId = 5, TagId = 5, Name = "Tsundere" },
new() { EnglishTagId = 6, TagId = 6, Name = "All Happy" },
new() { EnglishTagId = 7, TagId = 7, Name = "Gal" },
new() { EnglishTagId = 8, TagId = 8, Name = "Maid" },
new() { EnglishTagId = 9, TagId = 9, Name = "Non-Fiction / Narrative" }
);
context.VoiceWorkTags.AddRange(
new() { VoiceWorkId = 1, TagId = 1 }, // ASMR
new() { VoiceWorkId = 1, TagId = 2 }, // Office Lady
new() { VoiceWorkId = 2, TagId = 1 }, // ASMR
new() { VoiceWorkId = 2, TagId = 3 }, // Heartwarming
new() { VoiceWorkId = 2, TagId = 4 }, // Elf / Fairy
new() { VoiceWorkId = 2, TagId = 5 }, // Tsundere
new() { VoiceWorkId = 2, TagId = 6 }, // All Happy
new() { VoiceWorkId = 2, TagId = 7 }, // Gal
new() { VoiceWorkId = 2, TagId = 8 }, // Maid
new() { VoiceWorkId = 3, TagId = 5 }, // Tsundere
new() { VoiceWorkId = 3, TagId = 9 } // Non-Fiction / Narrative
//new() { VoiceWorkId = 3, TagId = 1 },
//new() { VoiceWorkId = 3, TagId = 1 },
//new() { VoiceWorkId = 3, TagId = 1 },
//new() { VoiceWorkId = 3, TagId = 1 },
//new() { VoiceWorkId = 3, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 4, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 5 } // Tsundere
//new() { VoiceWorkId = 5, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 1 },
//new() { VoiceWorkId = 5, TagId = 1 }
);
context.Creators.AddRange(
new() { CreatorId = 1, Name = "陽向葵ゅか", Favorite = true },
new() { CreatorId = 2, Name = "秋野かえで" },
new() { CreatorId = 3, Name = "柚木つばめ" },
new() { CreatorId = 4, Name = "逢坂成美" },
new() { CreatorId = 5, Name = "山田じぇみ子", Blacklisted = true }
);
context.VoiceWorkCreators.AddRange(
new() { VoiceWorkId = 1, CreatorId = 2 }, // 秋野かえで
new() { VoiceWorkId = 2, CreatorId = 1 }, // 陽向葵ゅか
new() { VoiceWorkId = 3, CreatorId = 5 }, // 山田じぇみ子
new() { VoiceWorkId = 3, CreatorId = 1 }, // 陽向葵ゅか
new() { VoiceWorkId = 4, CreatorId = 3 }, // 柚木つばめ
new() { VoiceWorkId = 5, CreatorId = 1 }, // 陽向葵ゅか
new() { VoiceWorkId = 5, CreatorId = 4 } // 逢坂成美
);
// <Product Id> <Maker Id> <Circle Name> <Product Name> <Product Description> <Tags> <Creators>
context.VoiceWorkSearches.AddRange(
new() { VoiceWorkId = 1, SearchText = "RJ0000001 RG00001 Good Dreams Today Sounds An average product. ASMR Office Lady" },
new() { VoiceWorkId = 2, SearchText = "RJ0000002 RG00002 Sweet Dreams Super Comfy ASMR An amazing product! ASMR Heartwarming Elf / Fairy Tsundere All Happy Gal Maid" },
new() { VoiceWorkId = 3, SearchText = "RJ0000003 RG00003 Nightmare Fuel Low Effort A bad product." },
new() { VoiceWorkId = 4, SearchText = "RJ0000004 RG00001 Good Dreams Tomorrow Sounds A average upcoming product." },
new() { VoiceWorkId = 5, SearchText = "RJ0000005 RG00002 Sweet Dreams Super Comfy ASMR+ All your favorite sounds, plus more!" }
);
await context.SaveChangesAsync();
}
public async ValueTask DisposeAsync()
{
if (DbContext is not null)
{
await DbContext.DisposeAsync();
}
}
}