131 lines
4.7 KiB
C#
131 lines
4.7 KiB
C#
using System.Collections;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace ZB.MOM.WW.LocalDb.Tests;
|
|
|
|
public sealed class DependencyInjectionTests : IDisposable
|
|
{
|
|
private readonly string _path = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".db");
|
|
|
|
public void Dispose()
|
|
{
|
|
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
|
if (File.Exists(_path))
|
|
File.Delete(_path);
|
|
}
|
|
|
|
private IConfiguration Config() =>
|
|
new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?> { ["LocalDb:Path"] = _path })
|
|
.Build();
|
|
|
|
[Fact]
|
|
public async Task AddZbLocalDb_ResolvesILocalDb_Singleton()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddZbLocalDb(Config());
|
|
await using var provider = services.BuildServiceProvider();
|
|
|
|
var a = provider.GetRequiredService<ILocalDb>();
|
|
var b = provider.GetRequiredService<ILocalDb>();
|
|
Assert.Same(a, b);
|
|
|
|
await a.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY)");
|
|
await a.ExecuteAsync("INSERT INTO t (id) VALUES (@id)", new { id = 1 });
|
|
var rows = await a.QueryAsync("SELECT COUNT(*) FROM t", r => r.GetInt64(0));
|
|
Assert.Equal(1L, rows[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddZbLocalDb_WithRegistrations_TablesRegisteredOnStart()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddZbLocalDb(Config(), db =>
|
|
{
|
|
using var conn = db.CreateConnection();
|
|
using var cmd = conn.CreateCommand();
|
|
cmd.CommandText = "CREATE TABLE widget (id INTEGER PRIMARY KEY, name TEXT)";
|
|
cmd.ExecuteNonQuery();
|
|
db.RegisterReplicated("widget");
|
|
});
|
|
using var provider = services.BuildServiceProvider();
|
|
|
|
var db = provider.GetRequiredService<ILocalDb>();
|
|
Assert.Contains("widget", db.ReplicatedTables.Keys);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddZbLocalDb_OnReadyThrows_ExceptionPropagates()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddZbLocalDb(Config(), _ => throw new InvalidOperationException("boom"));
|
|
using var provider = services.BuildServiceProvider();
|
|
|
|
var first = Assert.Throws<InvalidOperationException>(() => provider.GetRequiredService<ILocalDb>());
|
|
Assert.Equal("boom", first.Message);
|
|
|
|
// No half-initialized singleton is cached: the next resolution re-runs the factory and fails again.
|
|
var second = Assert.Throws<InvalidOperationException>(() => provider.GetRequiredService<ILocalDb>());
|
|
Assert.Equal("boom", second.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validator_EmptyPath_Fails()
|
|
{
|
|
var result = new LocalDbOptionsValidator().Validate(
|
|
null, new LocalDbOptions { Path = " " });
|
|
|
|
Assert.True(result.Failed);
|
|
Assert.Contains(result.Failures, f => f.Contains("LocalDb:Path", StringComparison.Ordinal));
|
|
}
|
|
|
|
[Fact]
|
|
public void Validator_NonPositiveBusyTimeout_Fails()
|
|
{
|
|
var result = new LocalDbOptionsValidator().Validate(
|
|
null, new LocalDbOptions { Path = "x.db", BusyTimeoutMs = 0 });
|
|
|
|
Assert.True(result.Failed);
|
|
Assert.Contains(result.Failures, f => f.Contains("BusyTimeoutMs", StringComparison.Ordinal));
|
|
}
|
|
|
|
[Fact]
|
|
public void Validator_InvalidSynchronous_Fails()
|
|
{
|
|
var result = new LocalDbOptionsValidator().Validate(
|
|
null, new LocalDbOptions { Path = "x.db", Synchronous = "SOMETIMES" });
|
|
|
|
Assert.True(result.Failed);
|
|
Assert.Contains(result.Failures, f => f.Contains("Synchronous", StringComparison.Ordinal));
|
|
}
|
|
|
|
[Fact]
|
|
public void Validator_ValidOptions_Succeeds()
|
|
{
|
|
var result = new LocalDbOptionsValidator().Validate(
|
|
null, new LocalDbOptions { Path = "x.db", BusyTimeoutMs = 5000, Synchronous = "normal" });
|
|
|
|
Assert.True(result.Succeeded);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Bind_NonObjectDictionary_Throws()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddZbLocalDb(Config());
|
|
await using var provider = services.BuildServiceProvider();
|
|
var db = provider.GetRequiredService<ILocalDb>();
|
|
await db.ExecuteAsync("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)");
|
|
|
|
var stringDict = new Dictionary<string, string> { ["id"] = "1" };
|
|
await Assert.ThrowsAsync<ArgumentException>(() =>
|
|
db.ExecuteAsync("INSERT INTO t (id) VALUES (@id)", stringDict));
|
|
|
|
var hashtable = new Hashtable { ["id"] = 1 };
|
|
await Assert.ThrowsAsync<ArgumentException>(() =>
|
|
db.ExecuteAsync("INSERT INTO t (id) VALUES (@id)", hashtable));
|
|
}
|
|
}
|