using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptList;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptList;
/// R04 P8: the /scripts list is a projection without source; source loads per dialog.
[Trait("Category", "Unit")]
public sealed class ScriptListQueriesTests
{
private static OtOpcUaConfigDbContext NewDb(string name) =>
new(new DbContextOptionsBuilder().UseInMemoryDatabase(name).Options);
private static async Task SeedAsync()
{
var name = Guid.NewGuid().ToString("N");
await using var db = NewDb(name);
db.Scripts.AddRange(
new Script { ScriptId = "s-b", Name = "Beta", SourceCode = "return 2;", SourceHash = "hb" },
new Script { ScriptId = "s-a", Name = "Alpha", SourceCode = "return 1;", SourceHash = "ha", Language = "CSharp" });
await db.SaveChangesAsync();
return name;
}
[Fact]
public async Task LoadRowsAsync_returns_name_ordered_rows_without_source()
{
var name = await SeedAsync();
await using var db = NewDb(name);
var rows = await ScriptListQueries.LoadRowsAsync(db);
rows.Select(r => r.ScriptId).ShouldBe(["s-a", "s-b"]);
rows[0].ShouldBe(new ScriptListRow("s-a", "Alpha", "CSharp", "ha"));
// The row type has no source member at all — the list cannot load it by accident.
typeof(ScriptListRow).GetProperty("SourceCode").ShouldBeNull();
}
[Fact]
public async Task LoadSourceAsync_returns_one_scripts_source_or_null_when_gone()
{
var name = await SeedAsync();
await using var db = NewDb(name);
(await ScriptListQueries.LoadSourceAsync(db, "s-b")).ShouldBe("return 2;");
(await ScriptListQueries.LoadSourceAsync(db, "missing")).ShouldBeNull();
}
[Fact]
public void Scripts_page_uses_the_projection()
{
var src = AdminUiSource.Read("Components", "Pages", "Scripts.razor");
src.ShouldContain("ScriptListQueries.LoadRowsAsync(");
src.ShouldNotContain("List