feat(m9/T32a): SharedSchema entity + EF config + idempotent migration + repository
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Schemas;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
|
||||
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Round-trip tests for <see cref="SharedSchemaRepository"/> (M9 template-level JSON-Schema
|
||||
/// library, Task T32a). Uses the in-memory SQLite harness (<see cref="SqliteTestHelper"/>)
|
||||
/// — the schema is materialized via <c>EnsureCreated()</c> from the same model the
|
||||
/// migration was generated from, so the tests run unconditionally (no live MSSQL needed)
|
||||
/// while still exercising the real unique-<c>Name</c> index. Mirrors the
|
||||
/// <c>SecurityRepositoryTests</c> in-memory harness shape.
|
||||
/// </summary>
|
||||
public class SharedSchemaRepositoryTests : IDisposable
|
||||
{
|
||||
private readonly ScadaBridgeDbContext _context;
|
||||
private readonly SharedSchemaRepository _repository;
|
||||
|
||||
public SharedSchemaRepositoryTests()
|
||||
{
|
||||
_context = SqliteTestHelper.CreateInMemoryContext();
|
||||
_repository = new SharedSchemaRepository(_context);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Database.CloseConnection();
|
||||
_context.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Add_ThenGetByName_RoundTrips()
|
||||
{
|
||||
var schema = new SharedSchema
|
||||
{
|
||||
Name = "TankReading",
|
||||
Scope = "Plant",
|
||||
SchemaJson = "{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"number\"}}}",
|
||||
};
|
||||
|
||||
var id = await _repository.AddAsync(schema);
|
||||
Assert.True(id > 0, "AddAsync should return the store-generated identity.");
|
||||
|
||||
var byName = await _repository.GetByNameAsync("TankReading");
|
||||
Assert.NotNull(byName);
|
||||
Assert.Equal(id, byName!.Id);
|
||||
Assert.Equal("TankReading", byName.Name);
|
||||
Assert.Equal("Plant", byName.Scope);
|
||||
Assert.Equal(
|
||||
"{\"type\":\"object\",\"properties\":{\"level\":{\"type\":\"number\"}}}",
|
||||
byName.SchemaJson);
|
||||
|
||||
var byId = await _repository.GetByIdAsync(id);
|
||||
Assert.NotNull(byId);
|
||||
Assert.Equal("TankReading", byId!.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByName_Unknown_ReturnsNull()
|
||||
{
|
||||
var missing = await _repository.GetByNameAsync("DoesNotExist");
|
||||
Assert.Null(missing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Add_DuplicateName_Throws()
|
||||
{
|
||||
await _repository.AddAsync(new SharedSchema { Name = "Dup", SchemaJson = "{}" });
|
||||
|
||||
// The UNIQUE index on Name must reject the second insert.
|
||||
await Assert.ThrowsAnyAsync<DbUpdateException>(() =>
|
||||
_repository.AddAsync(new SharedSchema { Name = "Dup", SchemaJson = "{}" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NullableScope_RoundTrips()
|
||||
{
|
||||
var id = await _repository.AddAsync(new SharedSchema { Name = "NoScope", SchemaJson = "{}" });
|
||||
|
||||
var loaded = await _repository.GetByIdAsync(id);
|
||||
Assert.NotNull(loaded);
|
||||
Assert.Null(loaded!.Scope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_ReturnsAll_OrderedByName()
|
||||
{
|
||||
await _repository.AddAsync(new SharedSchema { Name = "Bravo", SchemaJson = "{}" });
|
||||
await _repository.AddAsync(new SharedSchema { Name = "Alpha", SchemaJson = "{}" });
|
||||
|
||||
var all = await _repository.ListAsync();
|
||||
Assert.Equal(2, all.Count);
|
||||
Assert.Equal("Alpha", all[0].Name);
|
||||
Assert.Equal("Bravo", all[1].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Update_PersistsChanges()
|
||||
{
|
||||
var id = await _repository.AddAsync(new SharedSchema { Name = "Edit", SchemaJson = "{}" });
|
||||
|
||||
var loaded = await _repository.GetByIdAsync(id);
|
||||
Assert.NotNull(loaded);
|
||||
loaded!.SchemaJson = "{\"type\":\"string\"}";
|
||||
loaded.Scope = "Updated";
|
||||
await _repository.UpdateAsync(loaded);
|
||||
|
||||
var reloaded = await _repository.GetByIdAsync(id);
|
||||
Assert.NotNull(reloaded);
|
||||
Assert.Equal("{\"type\":\"string\"}", reloaded!.SchemaJson);
|
||||
Assert.Equal("Updated", reloaded.Scope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Delete_RemovesRow()
|
||||
{
|
||||
var id = await _repository.AddAsync(new SharedSchema { Name = "ToDelete", SchemaJson = "{}" });
|
||||
|
||||
await _repository.DeleteAsync(id);
|
||||
|
||||
Assert.Null(await _repository.GetByIdAsync(id));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user