fix(configuration-database): resolve ConfigurationDatabase-005,006,008,009,010,011 — bounded gRPC columns, split queries, CSV-parse logging, null guards, coverage
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ScadaLink.Commons.Entities.Sites;
|
||||
using ScadaLink.Commons.Entities.Templates;
|
||||
using ScadaLink.ConfigurationDatabase;
|
||||
using ScadaLink.ConfigurationDatabase.Repositories;
|
||||
|
||||
namespace ScadaLink.ConfigurationDatabase.Tests;
|
||||
|
||||
public class SchemaConfigurationTests : IDisposable
|
||||
{
|
||||
private readonly ScadaLinkDbContext _context;
|
||||
|
||||
public SchemaConfigurationTests()
|
||||
{
|
||||
_context = SqliteTestHelper.CreateInMemoryContext();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Database.CloseConnection();
|
||||
_context.Dispose();
|
||||
}
|
||||
|
||||
// ConfigurationDatabase-006: the gRPC node-address columns must be length-bounded
|
||||
// (HasMaxLength(500)) consistently with the sibling NodeAAddress/NodeBAddress columns,
|
||||
// rather than being left to map to nvarchar(max).
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(Site.GrpcNodeAAddress))]
|
||||
[InlineData(nameof(Site.GrpcNodeBAddress))]
|
||||
public void GrpcNodeAddressColumns_AreLengthBoundedTo500(string propertyName)
|
||||
{
|
||||
var property = _context.Model
|
||||
.FindEntityType(typeof(Site))!
|
||||
.FindProperty(propertyName)!;
|
||||
|
||||
Assert.Equal(500, property.GetMaxLength());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(Site.NodeAAddress))]
|
||||
[InlineData(nameof(Site.NodeBAddress))]
|
||||
public void GrpcNodeAddressColumns_MatchSiblingNodeAddressBounds(string siblingPropertyName)
|
||||
{
|
||||
var entity = _context.Model.FindEntityType(typeof(Site))!;
|
||||
var siblingMaxLength = entity.FindProperty(siblingPropertyName)!.GetMaxLength();
|
||||
|
||||
Assert.Equal(siblingMaxLength, entity.FindProperty(nameof(Site.GrpcNodeAAddress))!.GetMaxLength());
|
||||
Assert.Equal(siblingMaxLength, entity.FindProperty(nameof(Site.GrpcNodeBAddress))!.GetMaxLength());
|
||||
}
|
||||
}
|
||||
|
||||
public class SplitQueryBehaviourTests : IDisposable
|
||||
{
|
||||
private readonly ScadaLinkDbContext _context;
|
||||
private readonly TemplateEngineRepository _repository;
|
||||
|
||||
public SplitQueryBehaviourTests()
|
||||
{
|
||||
_context = SqliteTestHelper.CreateInMemoryContext();
|
||||
_repository = new TemplateEngineRepository(_context);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Database.CloseConnection();
|
||||
_context.Dispose();
|
||||
}
|
||||
|
||||
// ConfigurationDatabase-009: the multi-collection eager-load queries were switched to
|
||||
// AsSplitQuery() to avoid cartesian-product joins. The result set must be unchanged —
|
||||
// every member collection still fully populated, with no row duplication.
|
||||
|
||||
[Fact]
|
||||
public async Task GetAllTemplatesAsync_WithMultipleMembersPerCollection_LoadsAllWithoutDuplication()
|
||||
{
|
||||
var template = new Template("MultiMember");
|
||||
for (int i = 0; i < 3; i++)
|
||||
template.Attributes.Add(new TemplateAttribute($"Attr{i}"));
|
||||
for (int i = 0; i < 2; i++)
|
||||
template.Alarms.Add(new TemplateAlarm($"Alarm{i}"));
|
||||
for (int i = 0; i < 4; i++)
|
||||
template.Scripts.Add(new TemplateScript($"Script{i}", "return 1;"));
|
||||
_context.Templates.Add(template);
|
||||
await _context.SaveChangesAsync();
|
||||
_context.ChangeTracker.Clear();
|
||||
|
||||
var all = await _repository.GetAllTemplatesAsync();
|
||||
|
||||
var loaded = Assert.Single(all);
|
||||
// A cartesian-product single query would yield 3 x 2 x 4 = 24 joined rows; the
|
||||
// collections must still contain exactly the inserted counts.
|
||||
Assert.Equal(3, loaded.Attributes.Count);
|
||||
Assert.Equal(2, loaded.Alarms.Count);
|
||||
Assert.Equal(4, loaded.Scripts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTemplateByIdAsync_WithMultipleMembers_LoadsAllCollections()
|
||||
{
|
||||
var template = new Template("Single");
|
||||
template.Attributes.Add(new TemplateAttribute("A1"));
|
||||
template.Attributes.Add(new TemplateAttribute("A2"));
|
||||
template.Scripts.Add(new TemplateScript("S1", "return 1;"));
|
||||
_context.Templates.Add(template);
|
||||
await _context.SaveChangesAsync();
|
||||
_context.ChangeTracker.Clear();
|
||||
|
||||
var loaded = await _repository.GetTemplateByIdAsync(template.Id);
|
||||
|
||||
Assert.NotNull(loaded);
|
||||
Assert.Equal(2, loaded!.Attributes.Count);
|
||||
Assert.Single(loaded.Scripts);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user