fix(templateMatcher): Исправление ошибок в именах шаблонов и рефакторинг синхронизации
- Устранена ошибка, когда при переиспользовании шаблона для шорткодов передавался ЭК "КОМПЛЕКСЫ-[ЗО]" вместо целевого ЭК шаблона. - Проведен небольшой рефакторинг общих процессов синхронизации различных типов групп работ.
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using PARR.Core.Repositories.Interfaces.Job;
|
||||
using PARR.Core.Repositories.Interfaces.Unit;
|
||||
using PARR.Core.Services.Shortcodes;
|
||||
using PARR.Core.Services.UnitFilterService;
|
||||
using PARR.Domain.Settings;
|
||||
|
||||
namespace PARR.DAL.Tests.DomainServices.Implementations
|
||||
{
|
||||
public class ShortcodesServiceTests
|
||||
{
|
||||
private readonly Mock<ILogger<ShortcodesService>> loggerMock = new();
|
||||
private readonly Mock<SettingsFromDb> settingsMock = new();
|
||||
private readonly Mock<IJobRepository> jobServiceMock = new();
|
||||
private readonly Mock<IUnitRepository> unitServiceMock = new();
|
||||
private readonly Mock<IUnitInValueRepository> unitInValueServiceMock = new();
|
||||
private readonly Mock<IUnitFieldRepository> unitFieldServiceMock = new();
|
||||
private readonly Mock<IUnitFilterService> unitFilterServiceMock = new();
|
||||
|
||||
[Fact]
|
||||
public async Task ApplyShortcodesAsync_WithEc_ShouldReplaceEc()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
namespace PARR.DAL.Tests
|
||||
{
|
||||
public class UnitFilterServiceTests //: IDisposable
|
||||
{
|
||||
// private readonly DbContextOptions<DataContext> _options;
|
||||
// private readonly DataContext _context;
|
||||
// private readonly UnitFilterService _service;
|
||||
|
||||
// public UnitFilterServiceTests()
|
||||
// {
|
||||
// _options = new DbContextOptionsBuilder<DataContext>()
|
||||
// .UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}")
|
||||
// .EnableSensitiveDataLogging()
|
||||
// .Options;
|
||||
|
||||
// _context = new DataContext(_options);
|
||||
// _context.Database.EnsureCreated();
|
||||
// TestDataGenerator.Seed(_context);
|
||||
|
||||
// // Логгеры
|
||||
// var logger = Mock.Of<ILogger<UnitFilterService>>();
|
||||
// var jobServiceLogger = Mock.Of<ILogger<JobService>>();
|
||||
// var unitServiceLogger = Mock.Of<ILogger<UnitService>>();
|
||||
// var unitInUnitServiceLogger = Mock.Of<ILogger<UnitInUnitService>>();
|
||||
// var unitInValueServiceLogger = Mock.Of<ILogger<UnitInValueService>>();
|
||||
|
||||
// // Реальные сервисы с правильными конструкторами
|
||||
// var jobService = new JobService(_context, jobServiceLogger);
|
||||
// var unitService = new UnitService(_context, unitServiceLogger);
|
||||
// var unitInUnitService = new UnitInUnitService(_context, unitInUnitServiceLogger);
|
||||
// var unitInValueService = new UnitInValueService(unitInValueServiceLogger, _context); // ← logger first!
|
||||
|
||||
// _service = new UnitFilterService(
|
||||
// logger,
|
||||
// jobService,
|
||||
// unitService,
|
||||
// unitInUnitService,
|
||||
// unitInValueService
|
||||
// );
|
||||
// }
|
||||
|
||||
// public void Dispose() => _context.Dispose();
|
||||
|
||||
// [Fact]
|
||||
// public async Task GetUnitsIdByJobFilterAsync_WithExistingJobId_ShouldReturnExpectedUnits()
|
||||
// {
|
||||
// // Arrange
|
||||
// var jobId = _context.Jobs.First(j => j.Name == "TestJob_1").Id;
|
||||
// var expectedUnitIds = new[] { TestDataGenerator.UnitAId, TestDataGenerator.UnitBId };
|
||||
|
||||
// // Act
|
||||
// var result = await _service.GetUnitsIdByJobFilterAsync(jobId, takeCount: 10);
|
||||
|
||||
// // Assert
|
||||
// result.Should().NotBeNull()
|
||||
// .And.HaveCount(2)
|
||||
// .And.Contain(expectedUnitIds);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task GetRelatedUnitNamesAsync_WithChildRelationshipFilter_ShouldReturnExpectedNames()
|
||||
// {
|
||||
// // Arrange
|
||||
// var jobId = _context.Jobs.First(j => j.Name == "TestJob_2").Id;
|
||||
// var unitId = TestDataGenerator.UnitWId; // Родитель
|
||||
|
||||
// // rf.IsParent = false
|
||||
// // Ищем юниты, у которых ParentUnitId = UnitWId (т.е. это дети UnitWId)
|
||||
// // и у этих юнитов (детей) есть FieldId1 = "коммутатор"
|
||||
// // Это UnitXId и UnitYId -> имена: "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС"
|
||||
|
||||
// // Act
|
||||
// var result = await _service.GetRelatedUnitNamesAsync(jobId, unitId);
|
||||
|
||||
// // Assert
|
||||
// result.Should().Contain(new[] { "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" });
|
||||
// }
|
||||
}
|
||||
}
|
||||
33
PARR.DAL.Tests/PARR.Core.Tests.csproj
Normal file
33
PARR.DAL.Tests/PARR.Core.Tests.csproj
Normal file
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.16" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="MockQueryable.Moq" Version="9.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PARR.Core\PARR.Core.csproj" />
|
||||
<ProjectReference Include="..\PARR.DAL\PARR.DAL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,616 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using PARR.Core.Common.Interfaces;
|
||||
using PARR.Core.Repositories.Interfaces.Job;
|
||||
using PARR.Core.Repositories.Interfaces.Unit;
|
||||
using PARR.DAL.Context;
|
||||
using PARR.Domain.Cache.Models;
|
||||
using PARR.Domain.Entities.Job;
|
||||
using PARR.Domain.Entities.Unit;
|
||||
using PARR.Domain.Enums;
|
||||
|
||||
namespace PARR.Core.Tests.Services.UnitFilterService;
|
||||
|
||||
public class UnitFilterServiceTests
|
||||
{
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт тестовую сущность Unit с заданным значением поля.
|
||||
/// Используется для подготовки данных в тестах фильтрации.
|
||||
/// </summary>
|
||||
/// <param name="id">Уникальный идентификатор юнита</param>
|
||||
/// <param name="name">Имя юнита для отладки</param>
|
||||
/// <param name="fieldId">Идентификатор поля, к которому привязывается значение</param>
|
||||
/// <param name="fieldValue">Значение поля</param>
|
||||
private static Unit BuildTestUnit(Guid id, string name, Guid fieldId, string fieldValue)
|
||||
{
|
||||
var fieldVal = new UnitFieldValue { Id = Guid.NewGuid(), Value = fieldValue };
|
||||
return new Unit
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
UnitValues = new List<UnitInValue>
|
||||
{
|
||||
new UnitInValue
|
||||
{
|
||||
UnitId = id,
|
||||
FieldId = fieldId,
|
||||
ValueId = fieldVal.Id,
|
||||
Value = fieldVal
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт тестовый объект JobGroupType с кодом Umbrella.
|
||||
/// Используется для тестирования логики групповых работ типа "зонтик".
|
||||
/// </summary>
|
||||
private static JobGroupType BuildTestJobGroupType() => new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Code = JobGroupTypesEnum.Group,
|
||||
Name = "Grouped Group",
|
||||
Description = "Test"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт тестовый объект JobGroup с заполненными обязательными полями.
|
||||
/// Используется для подготовки навигационных свойств в тестах.
|
||||
/// </summary>
|
||||
private static JobGroup BuildTestJobGroup() => new()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
GroupName = "Test Group",
|
||||
ShortDescription = "Short",
|
||||
FullDescription = "Full",
|
||||
Solution = "Sol",
|
||||
TemplateDuration = "0:00:00",
|
||||
ReferenceDate = DateTimeOffset.UtcNow,
|
||||
GroupTypeId = Guid.NewGuid(),
|
||||
ScheduleExcludeTypeId = Guid.NewGuid(),
|
||||
GroupType = BuildTestJobGroupType()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт тестовый объект JobAutoControl с отключённым автоконтролем.
|
||||
/// Используется для заполнения обязательного свойства в сущности Job.
|
||||
/// </summary>
|
||||
private static JobAutoControl BuildTestAutoControl(Guid jobId) => new()
|
||||
{
|
||||
JobId = jobId,
|
||||
IsEnable = false
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Контейнер для моков репозиториев.
|
||||
/// Упрощает передачу зависимостей в метод создания сервиса.
|
||||
/// </summary>
|
||||
private class RepositoryMocks
|
||||
{
|
||||
public Mock<IUnitRepository> Unit { get; set; }
|
||||
public Mock<IUnitInUnitRepository> UnitInUnit { get; set; }
|
||||
public Mock<IUnitInValueRepository> UnitInValue { get; set; }
|
||||
public Mock<IUnitFieldRepository> UnitField { get; set; }
|
||||
public Mock<IJobRepository> Job { get; set; }
|
||||
public Mock<IRedisCacheService> Cache { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает базовые моки репозиториев с использованием InMemory-контекста.
|
||||
/// Возвращает объект с подготовленными моками для повторного использования в тестах.
|
||||
/// </summary>
|
||||
/// <param name="context">Экземпляр DataContext с тестовыми данными</param>
|
||||
private static RepositoryMocks ArrangeRepositoryMocks(DataContext context)
|
||||
{
|
||||
var unitRepoMock = new Mock<IUnitRepository>();
|
||||
unitRepoMock.Setup(r => r.Get()).Returns(context.Units.AsQueryable());
|
||||
unitRepoMock.Setup(r => r.GetInitialUnitIds(It.IsAny<string>()))
|
||||
.Returns(context.Units.Select(u => u.Id).AsQueryable());
|
||||
|
||||
var unitInUnitRepoMock = new Mock<IUnitInUnitRepository>();
|
||||
unitInUnitRepoMock.Setup(r => r.Get()).Returns(context.UnitInUnits.AsQueryable());
|
||||
|
||||
var unitInValueRepoMock = new Mock<IUnitInValueRepository>();
|
||||
// Единый подход: используем контекст для поддержки IAsyncQueryProvider
|
||||
// Это гарантирует корректную работу .ToListAsync() внутри сервиса
|
||||
unitInValueRepoMock.Setup(r => r.GetMatchingTargetIds(It.IsAny<Guid>(), It.IsAny<string>()))
|
||||
.Returns((Guid fId, string mask) =>
|
||||
context.UnitInValues
|
||||
.AsNoTracking()
|
||||
.Where(uv => uv.FieldId == fId && uv.Value != null)
|
||||
.Select(uv => uv.UnitId)
|
||||
.Distinct()
|
||||
.AsQueryable());
|
||||
|
||||
var unitFieldRepoMock = new Mock<IUnitFieldRepository>();
|
||||
unitFieldRepoMock.Setup(r => r.Get()).Returns(new List<UnitField>().AsQueryable());
|
||||
|
||||
var jobRepoMock = new Mock<IJobRepository>();
|
||||
jobRepoMock.Setup(r => r.Get()).Returns(context.Jobs.AsQueryable());
|
||||
|
||||
var cacheMock = new Mock<IRedisCacheService>();
|
||||
cacheMock.Setup(r => r.GetCachedDataAsync<UnitFilterIds>(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.ReturnsAsync((UnitFilterIds)null!);
|
||||
|
||||
return new RepositoryMocks
|
||||
{
|
||||
Unit = unitRepoMock,
|
||||
UnitInUnit = unitInUnitRepoMock,
|
||||
UnitInValue = unitInValueRepoMock,
|
||||
UnitField = unitFieldRepoMock,
|
||||
Job = jobRepoMock,
|
||||
Cache = cacheMock
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт экземпляр тестируемого сервиса (SUT — System Under Test).
|
||||
/// Инкапсулирует логику конструктора для упрощения тестов.
|
||||
/// </summary>
|
||||
/// <param name="mocks">Подготовленные моки репозиториев</param>
|
||||
/// <param name="logger">Экземпляр логгера для сервиса</param>
|
||||
private static PARR.Core.Services.UnitFilterService.UnitFilterService CreateSut(
|
||||
RepositoryMocks mocks,
|
||||
ILogger<PARR.Core.Services.UnitFilterService.UnitFilterService> logger)
|
||||
{
|
||||
var serviceOptions = Options.Create(new PARR.Core.Services.UnitFilterService.Models.UnitFilterServiceOptions { LoadBatchSize = 100 });
|
||||
|
||||
return new PARR.Core.Services.UnitFilterService.UnitFilterService(
|
||||
logger,
|
||||
mocks.Job.Object,
|
||||
mocks.Unit.Object,
|
||||
mocks.UnitInUnit.Object,
|
||||
mocks.UnitInValue.Object,
|
||||
mocks.Cache.Object,
|
||||
serviceOptions,
|
||||
mocks.UnitField.Object
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, что юниты, не прошедшие фильтрацию по полю (FieldFilter)
|
||||
/// или по связям (RelationshipFilter), корректно исключаются из результата.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetUnitsByJobFilterAsync_UnitsWithInvalidFieldOrRelationshipValue_AreExcluded()
|
||||
{
|
||||
// === Arrange ===
|
||||
var dbOptions = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
using var context = new DataContext(dbOptions);
|
||||
|
||||
var jobId = Guid.NewGuid();
|
||||
var fieldId = Guid.NewGuid();
|
||||
var validParentId = Guid.NewGuid();
|
||||
var wrongParentId = Guid.NewGuid();
|
||||
|
||||
var unitPassesAll = Guid.NewGuid();
|
||||
var unitFailsFieldFilter = Guid.NewGuid();
|
||||
var unitFailsRelationshipFilter = Guid.NewGuid();
|
||||
|
||||
// Подготовка тестовых данных: юниты
|
||||
context.Units.AddRange(
|
||||
BuildTestUnit(unitPassesAll, "Unit_PassesAll", fieldId, "Accepted Value"),
|
||||
BuildTestUnit(unitFailsFieldFilter, "Unit_FailsField", fieldId, "Rejected Value"),
|
||||
BuildTestUnit(unitFailsRelationshipFilter, "Unit_FailsRel", fieldId, "Accepted Value"),
|
||||
BuildTestUnit(validParentId, "Parent_Valid", fieldId, "Valid Parent"),
|
||||
BuildTestUnit(wrongParentId, "Parent_Wrong", fieldId, "Wrong Parent")
|
||||
);
|
||||
|
||||
// Подготовка тестовых данных: связи между юнитами
|
||||
context.UnitInUnits.AddRange(
|
||||
new UnitInUnit { ParentUnitId = validParentId, ChildUnitId = unitPassesAll, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = wrongParentId, ChildUnitId = unitFailsRelationshipFilter, DateCreated = DateTimeOffset.UtcNow }
|
||||
);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// Формируем тестовый объект Job с фильтрами
|
||||
var job = new Job
|
||||
{
|
||||
Id = jobId,
|
||||
Name = "Test Job",
|
||||
WorkName = "Test Work",
|
||||
TemplateNameMask = "TestMask",
|
||||
WorkGroupMask = "TestGroup",
|
||||
ResponseAreaMask = "TestArea",
|
||||
GroupId = Guid.NewGuid(),
|
||||
TnkId = Guid.NewGuid(),
|
||||
MinValueRelationships = 1,
|
||||
MaxValueRelationships = 10,
|
||||
IsParentRelationships = true,
|
||||
AutoControl = BuildTestAutoControl(jobId),
|
||||
Group = BuildTestJobGroup(),
|
||||
UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
JobId = jobId,
|
||||
UnitFilter = "%",
|
||||
FieldFilters = new List<JobFieldFilter>
|
||||
{
|
||||
new JobFieldFilter { Id = Guid.NewGuid(), FieldId = fieldId, ValueMask = "Accepted%", IsInverse = false }
|
||||
},
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter { UnitFilterId = Guid.NewGuid(), FieldId = fieldId, ValueMask = "%", IsParent = true, IsInverse = false, IsFullMatch = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Настройка моков репозиториев
|
||||
var mocks = ArrangeRepositoryMocks(context);
|
||||
|
||||
// Переопределяем мок фильтрации по полю: в реальном коде используется EF.Functions.ILike,
|
||||
// в тесте заменяем на прямое сравнение строк для предсказуемости
|
||||
mocks.Unit.Setup(r => r.GetUnitByFieldAndValue(It.IsAny<IQueryable<Unit>>(), It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns((IQueryable<Unit> q, Guid fId, string mask, bool inv) =>
|
||||
q.Where(u => u.UnitValues != null && u.UnitValues.Any(v => v.FieldId == fId && v.Value != null && v.Value.Value == "Accepted Value")));
|
||||
|
||||
// ИСПРАВЛЕНО: используем запрос к контексту вместо массива.
|
||||
// Массивный AsQueryable() не поддерживает IAsyncQueryProvider, что вызывает крах при вызове .ToListAsync() внутри сервиса.
|
||||
// Запрос к InMemory DbSet гарантирует корректную асинхронную материализацию.
|
||||
mocks.UnitInValue.Setup(r => r.GetMatchingTargetIds(fieldId, It.IsAny<string>()))
|
||||
.Returns(context.Units.Where(u => u.Id == validParentId).Select(u => u.Id).AsQueryable());
|
||||
|
||||
var logger = NullLoggerFactory.Instance.CreateLogger<PARR.Core.Services.UnitFilterService.UnitFilterService>();
|
||||
var service = CreateSut(mocks, logger);
|
||||
|
||||
// === Act ===
|
||||
var result = await service.GetUnitsByJobFilterAsync(job);
|
||||
|
||||
// === Assert ===
|
||||
var resultIds = result.Select(u => u.Id).ToList();
|
||||
|
||||
Assert.Contains(unitPassesAll, resultIds);
|
||||
Assert.DoesNotContain(unitFailsFieldFilter, resultIds);
|
||||
Assert.DoesNotContain(unitFailsRelationshipFilter, resultIds);
|
||||
|
||||
Assert.Single(result);
|
||||
var dto = result.First();
|
||||
Assert.Equal("Accepted Value", dto.Values.FirstOrDefault()?.Value);
|
||||
Assert.Single(dto.Parents);
|
||||
Assert.Equal(validParentId, dto.Parents.First().UnitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, что юниты исключаются, если их единственный родительский юнит
|
||||
/// содержит запрещённое значение тега (сценарий с IsInverse = true).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetUnitsByJobFilterAsync_ParentWithForbiddenTag_UnitIsExcluded()
|
||||
{
|
||||
// === Arrange ===
|
||||
var dbOptions = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
using var context = new DataContext(dbOptions);
|
||||
|
||||
var jobId = Guid.NewGuid();
|
||||
var tagFieldId = Guid.NewGuid();
|
||||
var validParentId = Guid.NewGuid();
|
||||
var invalidParentId = Guid.NewGuid();
|
||||
var unitWithValidParent = Guid.NewGuid();
|
||||
var unitWithInvalidParent = Guid.NewGuid();
|
||||
|
||||
// Подготовка тестовых данных: юниты
|
||||
context.Units.AddRange(
|
||||
BuildTestUnit(validParentId, "Parent_Valid", tagFieldId, "ОТВ.ЭК"),
|
||||
BuildTestUnit(invalidParentId, "Parent_Invalid", tagFieldId, "ПАРР-РРПТК-ОТВ.ЭК"),
|
||||
BuildTestUnit(unitWithValidParent, "Unit_Valid", Guid.NewGuid(), "Val"),
|
||||
BuildTestUnit(unitWithInvalidParent, "Unit_Invalid", Guid.NewGuid(), "Val")
|
||||
);
|
||||
|
||||
// Подготовка тестовых данных: связи между юнитами
|
||||
context.UnitInUnits.AddRange(
|
||||
new UnitInUnit { ParentUnitId = validParentId, ChildUnitId = unitWithValidParent, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = invalidParentId, ChildUnitId = unitWithInvalidParent, DateCreated = DateTimeOffset.UtcNow }
|
||||
);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// Формируем тестовый объект Job с настройкой IsInverse = true
|
||||
// IsInverse = true означает: исключить родителей, которые СОВПАДАЮТ с маской
|
||||
var job = new Job
|
||||
{
|
||||
Id = jobId,
|
||||
Name = "Test Job Inverse",
|
||||
WorkName = "Test Work",
|
||||
TemplateNameMask = "TestMask",
|
||||
WorkGroupMask = "TestGroup",
|
||||
ResponseAreaMask = "TestArea",
|
||||
GroupId = Guid.NewGuid(),
|
||||
TnkId = Guid.NewGuid(),
|
||||
MinValueRelationships = 1,
|
||||
MaxValueRelationships = 10,
|
||||
IsParentRelationships = true,
|
||||
AutoControl = BuildTestAutoControl(jobId),
|
||||
Group = BuildTestJobGroup(),
|
||||
UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
JobId = jobId,
|
||||
UnitFilter = "%",
|
||||
FieldFilters = new List<JobFieldFilter>(),
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter
|
||||
{
|
||||
UnitFilterId = Guid.NewGuid(),
|
||||
FieldId = tagFieldId,
|
||||
ValueMask = "%ПАРР-РРПТК-ОТВ.ЭК%",
|
||||
IsParent = true,
|
||||
IsInverse = true,
|
||||
IsFullMatch = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Настройка моков репозиториев
|
||||
var mocks = ArrangeRepositoryMocks(context);
|
||||
|
||||
// Переопределяем мок: возвращаем только родителей с запрещённым тегом.
|
||||
// В реальном коде используется EF.Functions.ILike, в тесте — .Contains() для простоты.
|
||||
// IsInverse = true в сервисе инвертирует результат, поэтому эти родители будут исключены.
|
||||
mocks.UnitInValue.Setup(r => r.GetMatchingTargetIds(tagFieldId, It.IsAny<string>()))
|
||||
.Returns((Guid fId, string mask) =>
|
||||
context.UnitInValues
|
||||
.AsNoTracking()
|
||||
.Where(uv => uv.FieldId == fId && uv.Value != null && uv.Value.Value.Contains("ПАРР-РРПТК-ОТВ.ЭК", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(uv => uv.UnitId)
|
||||
.Distinct()
|
||||
.AsQueryable());
|
||||
|
||||
var logger = NullLoggerFactory.Instance.CreateLogger<PARR.Core.Services.UnitFilterService.UnitFilterService>();
|
||||
var service = CreateSut(mocks, logger);
|
||||
|
||||
// === Act ===
|
||||
var result = await service.GetUnitsByJobFilterAsync(job);
|
||||
|
||||
// === Assert ===
|
||||
var resultIds = result.Select(u => u.Id).ToList();
|
||||
|
||||
Assert.Contains(unitWithValidParent, resultIds);
|
||||
Assert.DoesNotContain(unitWithInvalidParent, resultIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет логику IsFullMatch = true: юнит исключается,
|
||||
/// если хотя бы один из его родительских юнитов не соответствует фильтру.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetUnitsByJobFilterAsync_IsFullMatch_UnitWithAnyNonMatchingParent_IsExcluded()
|
||||
{
|
||||
// === Arrange ===
|
||||
var dbOptions = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
using var context = new DataContext(dbOptions);
|
||||
|
||||
var jobId = Guid.NewGuid();
|
||||
var checkFieldId = Guid.NewGuid();
|
||||
var parentGoodId = Guid.NewGuid();
|
||||
var parentBadId = Guid.NewGuid();
|
||||
var unitMixedId = Guid.NewGuid();
|
||||
var unitCleanId = Guid.NewGuid();
|
||||
|
||||
// Подготовка тестовых данных: юниты
|
||||
context.Units.AddRange(
|
||||
BuildTestUnit(parentGoodId, "Parent_Good", checkFieldId, "Good Parent"),
|
||||
BuildTestUnit(parentBadId, "Parent_Bad", checkFieldId, "Bad Parent"),
|
||||
BuildTestUnit(unitMixedId, "Unit_Mixed", Guid.NewGuid(), "Mixed"),
|
||||
BuildTestUnit(unitCleanId, "Unit_Clean", Guid.NewGuid(), "Clean")
|
||||
);
|
||||
|
||||
// Подготовка тестовых данных: связи между юнитами
|
||||
context.UnitInUnits.AddRange(
|
||||
new UnitInUnit { ParentUnitId = parentGoodId, ChildUnitId = unitMixedId, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = parentBadId, ChildUnitId = unitMixedId, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = parentGoodId, ChildUnitId = unitCleanId, DateCreated = DateTimeOffset.UtcNow }
|
||||
);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// Формируем тестовый объект Job с настройкой IsFullMatch = true
|
||||
// IsFullMatch = true означает: ВСЕ родители должны соответствовать фильтру
|
||||
var job = new Job
|
||||
{
|
||||
Id = jobId,
|
||||
Name = "Test Job FullMatch",
|
||||
WorkName = "Test Work",
|
||||
TemplateNameMask = "TestMask",
|
||||
WorkGroupMask = "TestGroup",
|
||||
ResponseAreaMask = "TestArea",
|
||||
GroupId = Guid.NewGuid(),
|
||||
TnkId = Guid.NewGuid(),
|
||||
MinValueRelationships = 1,
|
||||
MaxValueRelationships = 10,
|
||||
IsParentRelationships = true,
|
||||
AutoControl = BuildTestAutoControl(jobId),
|
||||
Group = BuildTestJobGroup(),
|
||||
UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
JobId = jobId,
|
||||
UnitFilter = "%",
|
||||
FieldFilters = new List<JobFieldFilter>(),
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter
|
||||
{
|
||||
UnitFilterId = Guid.NewGuid(),
|
||||
FieldId = checkFieldId,
|
||||
ValueMask = "%Good%",
|
||||
IsParent = true,
|
||||
IsInverse = false,
|
||||
IsFullMatch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Настройка моков репозиториев
|
||||
var mocks = ArrangeRepositoryMocks(context);
|
||||
|
||||
// Переопределяем мок: возвращаем только родителей, соответствующих маске "Good".
|
||||
// В реальном коде используется EF.Functions.ILike, в тесте — .Contains() для простоты.
|
||||
mocks.UnitInValue.Setup(r => r.GetMatchingTargetIds(checkFieldId, It.IsAny<string>()))
|
||||
.Returns((Guid fId, string mask) =>
|
||||
context.UnitInValues
|
||||
.AsNoTracking()
|
||||
.Where(uv => uv.FieldId == fId && uv.Value != null && uv.Value.Value.Contains("Good", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(uv => uv.UnitId)
|
||||
.Distinct()
|
||||
.AsQueryable());
|
||||
|
||||
var logger = NullLoggerFactory.Instance.CreateLogger<PARR.Core.Services.UnitFilterService.UnitFilterService>();
|
||||
var service = CreateSut(mocks, logger);
|
||||
|
||||
// === Act ===
|
||||
var result = await service.GetUnitsByJobFilterAsync(job);
|
||||
|
||||
// === Assert ===
|
||||
var resultIds = result.Select(u => u.Id).ToList();
|
||||
|
||||
Assert.Contains(unitCleanId, resultIds);
|
||||
Assert.DoesNotContain(unitMixedId, resultIds);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет комбинацию флагов: IsParent=true, IsInverse=true, IsFullMatch=true.
|
||||
/// Логика: юнит проходит, только если НИ ОДИН из его родителей не содержит запрещённое значение.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetUnitsByJobFilterAsync_IsParentAndInverseAndFullMatch_UnitWithAnyForbiddenParent_IsExcluded()
|
||||
{
|
||||
// === Arrange ===
|
||||
var dbOptions = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
using var context = new DataContext(dbOptions);
|
||||
|
||||
var jobId = Guid.NewGuid();
|
||||
var tagFieldId = Guid.NewGuid();
|
||||
var parentForbiddenId = Guid.NewGuid(); // Родитель с запрещённым тегом
|
||||
var parentCleanId = Guid.NewGuid(); // Родитель без запрещённого тега
|
||||
var unitMixedId = Guid.NewGuid(); // Юнит с одним "плохим" и одним "хорошим" родителем
|
||||
var unitAllCleanId = Guid.NewGuid(); // Юнит только с "хорошими" родителями
|
||||
|
||||
// Подготовка тестовых данных: юниты
|
||||
context.Units.AddRange(
|
||||
BuildTestUnit(parentForbiddenId, "Parent_Forbidden", tagFieldId, "FORBIDDEN-TAG"),
|
||||
BuildTestUnit(parentCleanId, "Parent_Clean", tagFieldId, "CLEAN-TAG"),
|
||||
BuildTestUnit(unitMixedId, "Unit_Mixed", Guid.NewGuid(), "Mixed"),
|
||||
BuildTestUnit(unitAllCleanId, "Unit_AllClean", Guid.NewGuid(), "AllClean")
|
||||
);
|
||||
|
||||
// Подготовка тестовых данных: связи
|
||||
// unitMixed имеет обоих родителей — один с запрещённым тегом, один без
|
||||
// unitAllClean имеет только "чистого" родителя
|
||||
context.UnitInUnits.AddRange(
|
||||
new UnitInUnit { ParentUnitId = parentForbiddenId, ChildUnitId = unitMixedId, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = parentCleanId, ChildUnitId = unitMixedId, DateCreated = DateTimeOffset.UtcNow },
|
||||
new UnitInUnit { ParentUnitId = parentCleanId, ChildUnitId = unitAllCleanId, DateCreated = DateTimeOffset.UtcNow }
|
||||
);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// Формируем тестовый объект Job с комбинацией флагов:
|
||||
// IsParent = true: проверяем родительские связи
|
||||
// IsInverse = true: исключаем родителей, которые СОВПАДАЮТ с маской
|
||||
// IsFullMatch = true: ВСЕ родители должны пройти фильтр (ни один не должен совпасть с маской)
|
||||
var job = new Job
|
||||
{
|
||||
Id = jobId,
|
||||
Name = "Test Job Combined Flags",
|
||||
WorkName = "Test Work",
|
||||
TemplateNameMask = "TestMask",
|
||||
WorkGroupMask = "TestGroup",
|
||||
ResponseAreaMask = "TestArea",
|
||||
GroupId = Guid.NewGuid(),
|
||||
TnkId = Guid.NewGuid(),
|
||||
MinValueRelationships = 1,
|
||||
MaxValueRelationships = 10,
|
||||
IsParentRelationships = true,
|
||||
AutoControl = BuildTestAutoControl(jobId),
|
||||
Group = BuildTestJobGroup(),
|
||||
UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
JobId = jobId,
|
||||
UnitFilter = "%",
|
||||
FieldFilters = new List<JobFieldFilter>(),
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter
|
||||
{
|
||||
UnitFilterId = Guid.NewGuid(),
|
||||
FieldId = tagFieldId,
|
||||
ValueMask = "%FORBIDDEN%",
|
||||
IsParent = true,
|
||||
IsInverse = true,
|
||||
IsFullMatch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Настройка моков репозиториев
|
||||
var mocks = ArrangeRepositoryMocks(context);
|
||||
|
||||
// Переопределяем мок: возвращаем только родителей, содержащих "FORBIDDEN".
|
||||
// В реальном коде используется EF.Functions.ILike, в тесте — .Contains() для простоты.
|
||||
// Из-за IsInverse=true эти родители будут исключены из "валидных".
|
||||
// Из-за IsFullMatch=true юнит пройдёт, только если ВСЕ его родители валидны.
|
||||
mocks.UnitInValue.Setup(r => r.GetMatchingTargetIds(tagFieldId, It.IsAny<string>()))
|
||||
.Returns((Guid fId, string mask) =>
|
||||
context.UnitInValues
|
||||
.AsNoTracking()
|
||||
.Where(uv => uv.FieldId == fId && uv.Value != null && uv.Value.Value.Contains("FORBIDDEN", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(uv => uv.UnitId)
|
||||
.Distinct()
|
||||
.AsQueryable());
|
||||
|
||||
var logger = NullLoggerFactory.Instance.CreateLogger<PARR.Core.Services.UnitFilterService.UnitFilterService>();
|
||||
var service = CreateSut(mocks, logger);
|
||||
|
||||
// === Act ===
|
||||
var result = await service.GetUnitsByJobFilterAsync(job);
|
||||
|
||||
// === Assert ===
|
||||
var resultIds = result.Select(u => u.Id).ToList();
|
||||
|
||||
// unitAllCleanId должен остаться: у него один родитель, и он не содержит запрещённый тег
|
||||
Assert.Contains(unitAllCleanId, resultIds);
|
||||
|
||||
// unitMixedId должен быть исключён: у него есть родитель с запрещённым тегом.
|
||||
// Логика:
|
||||
// 1. GetMatchingTargetIds возвращает {parentForbiddenId}
|
||||
// 2. IsInverse=true → валидные родители = все.Кроме({parentForbiddenId}) = {parentCleanId}
|
||||
// 3. IsFullMatch=true → проверяем: все родители {parentForbiddenId, parentCleanId} входят в {parentCleanId}? Нет.
|
||||
// 4. Юнит исключается.
|
||||
Assert.DoesNotContain(unitMixedId, resultIds);
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
using PARR.DAL.Context;
|
||||
using PARR.Domain.Entities.Job;
|
||||
using PARR.Domain.Entities.Unit;
|
||||
using PARR.Domain.Enums;
|
||||
|
||||
namespace PARR.DAL.Tests
|
||||
{
|
||||
public static class TestDataGenerator
|
||||
{
|
||||
// --- Unit IDs ---
|
||||
public static readonly Guid UnitAId = Guid.Parse("11111111-1111-1111-1111-111111111111");
|
||||
public static readonly Guid UnitBId = Guid.Parse("00000000-0000-1111-1111-111111111111");
|
||||
public static readonly Guid UnitCId = Guid.Parse("22222222-2222-2222-2222-222222222222");
|
||||
public static readonly Guid UnitDId = Guid.Parse("33333333-3333-3333-3333-333333333333");
|
||||
public static readonly Guid UnitWId = Guid.Parse("44444444-4444-4444-4444-444444444444");
|
||||
public static readonly Guid UnitXId = Guid.Parse("55555555-5555-5555-5555-555555555555");
|
||||
public static readonly Guid UnitYId = Guid.Parse("66666666-6666-6666-6666-666666666666");
|
||||
public static readonly Guid UnitZId = Guid.Parse("77777777-7777-7777-7777-777777777777");
|
||||
|
||||
// --- Field IDs (для обхода составного ключа UnitFilterId+FieldId) ---
|
||||
public static readonly Guid FieldId1 = Guid.Parse("08b42860-09a7-4a1f-aa4f-0290e86df883");//ТИП_ЭК
|
||||
public static readonly Guid FieldId2 = Guid.Parse("5b2d3f5b-a9f3-4a6b-9142-70ef4357ac37"); //неуникальный
|
||||
public static readonly Guid FieldId3 = Guid.Parse("5f3c7b8b-583d-411b-b9bd-1e045ddc5a8e");//ПОДКАТЕГОРИЯ_ЭК
|
||||
public static readonly Guid FieldId4 = Guid.Parse("3e9ac5aa-531b-4ffc-8f93-694d00aa92a0");//активный
|
||||
|
||||
// --- Field Value IDs ---
|
||||
public static readonly Guid FieldValueId1 = Guid.Parse("fe18edcd-ab6a-4f3e-b7bc-e54aad9ecc0d"); // система хранения данных (схд) field3
|
||||
public static readonly Guid FieldValueId2 = Guid.Parse("f385b22c-42c3-4db9-bd41-b829603cc2d9"); // 1 field2+field4
|
||||
public static readonly Guid FieldValueId3 = Guid.Parse("2badc139-69cb-4d52-830a-1b3c6a9faf9d"); // 0 field2+field4
|
||||
public static readonly Guid FieldValueId4 = Guid.Parse("fde68ef7-a07e-4c10-872c-cd98c3d1c30b"); // коммутатор
|
||||
public static readonly Guid FieldValueId5 = Guid.Parse("9f169862-3f78-49f2-b967-13b5b2f56231"); // серверное оборудование
|
||||
public static readonly Guid FieldValueId6 = Guid.Parse("075b6fad-d24f-40f4-aa0e-83a4cfc46bbf"); // стойка цвк
|
||||
public static readonly Guid FieldValueId7 = Guid.Parse("9679afd8-8004-4ce2-bd6f-9005b037ef96"); // схд
|
||||
|
||||
internal static void Seed(DataContext context)
|
||||
{
|
||||
if (context.Units.Any()) return;
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
// 1. Значения полей (остаются те же)
|
||||
var fieldValues = new[]
|
||||
{
|
||||
new UnitFieldValue { Id = FieldValueId1, Value = "система хранения данных (схд)", DateCreated = now },
|
||||
new UnitFieldValue { Id = FieldValueId2, Value = "1", DateCreated = now },
|
||||
new UnitFieldValue { Id = FieldValueId3, Value = "0", DateCreated = now },
|
||||
new UnitFieldValue { Id = FieldValueId4, Value = "коммутатор", DateCreated = now },
|
||||
};
|
||||
context.UnitFieldValues.AddRange(fieldValues);
|
||||
|
||||
// 2. Unit'ы
|
||||
var units = new[]
|
||||
{
|
||||
new Unit { Id = UnitAId, Name = "СХД-КМТ-DVGD-MDS9148-1-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitBId, Name = "СХД-КМТ-DVGD-MDS9148-2-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitCId, Name = "СРВ-СТОЙКА-СТОЙКА-ХИВЦ-4.0-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitDId, Name = "СХД-VSP-E590-01-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitWId, Name = "СХД-КМТ-SAN1_FAB_1-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitXId, Name = "СХД-КМТ-CISCO-MDS9148-1-ДВС", DateCreated = now },
|
||||
new Unit { Id = UnitYId, Name = "СХД-КМТ-CISCO-MDS9148-3-ДВС", DateCreated = now },
|
||||
};
|
||||
context.Units.AddRange(units);
|
||||
|
||||
// 3. UnitInUnit (связи)
|
||||
var unitInUnits = new[]
|
||||
{
|
||||
new UnitInUnit { ParentUnitId = UnitAId, ChildUnitId = UnitCId, DateCreated = now },
|
||||
new UnitInUnit { ParentUnitId = UnitBId, ChildUnitId = UnitCId, DateCreated = now },
|
||||
new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitAId, DateCreated = now },
|
||||
new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitBId, DateCreated = now },
|
||||
new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitXId, DateCreated = now },
|
||||
new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitYId, DateCreated = now }
|
||||
};
|
||||
context.UnitInUnits.AddRange(unitInUnits);
|
||||
|
||||
// 4. UnitInValue
|
||||
var unitInValues = new List<UnitInValue>
|
||||
{
|
||||
// Основные (FieldId)
|
||||
new UnitInValue { UnitId = UnitAId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitAId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitAId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitAId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitBId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitBId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitBId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitBId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitCId, FieldId = FieldId3, ValueId = FieldValueId5, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitCId, FieldId = FieldId1, ValueId = FieldValueId6, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitCId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitDId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitDId, FieldId = FieldId1, ValueId = FieldValueId7, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitDId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitWId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitWId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitWId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitWId, FieldId = FieldId2, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitXId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitXId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitXId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitYId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitYId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now },
|
||||
new UnitInValue { UnitId = UnitYId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now },
|
||||
|
||||
};
|
||||
context.UnitInValues.AddRange(unitInValues);
|
||||
|
||||
// 5. JobGroupType и Group
|
||||
var groupType = context.JobGroupTypes
|
||||
.FirstOrDefault(t => t.Code == JobGroupTypesEnum.Simple)
|
||||
?? throw new InvalidOperationException("JobGroupType 'Simple' не найден в контексте.");
|
||||
|
||||
var group = new JobGroup
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
GroupTypeId = groupType.Id,
|
||||
GroupType = groupType,
|
||||
GroupName = "Группа",
|
||||
ShortDescription = "%ТНК-КРАТКО% %ЭК%",
|
||||
FullDescription = "Подробно:\n%СВЯЗИ%",
|
||||
Solution = "Решение",
|
||||
TemplateDuration = "540 00:00:00",
|
||||
ReferenceDate = now,
|
||||
IsAutoDistributionEnabled = false,
|
||||
IsAgent = false,
|
||||
DateCreated = now
|
||||
};
|
||||
context.JobGroups.Add(group);
|
||||
|
||||
// 6. Jobs
|
||||
var jobs = new List<Job>();
|
||||
|
||||
// --- TestJob ---
|
||||
var job1 = new Job
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "TestJob_1",
|
||||
WorkName = "Работа",
|
||||
TemplateNameMask = "%ЭК%",
|
||||
WorkGroupMask = "%ГРУППА_РАБОТ%",
|
||||
GroupId = group.Id,
|
||||
TnkId = Guid.NewGuid(),
|
||||
Group = group,
|
||||
DateCreated = now,
|
||||
ResponseAreaMask = "%ЗО_РГ%"
|
||||
};
|
||||
var unitFilter1Id = Guid.NewGuid();
|
||||
job1.UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = unitFilter1Id,
|
||||
UnitFilter = "СХД-%",
|
||||
JobId = job1.Id,
|
||||
Job = job1,
|
||||
DateCreated = now,
|
||||
FieldFilters = new List<JobFieldFilter>
|
||||
{
|
||||
new JobFieldFilter
|
||||
{
|
||||
UnitFilterId = unitFilter1Id,
|
||||
FieldId = FieldId1,
|
||||
ValueMask="коммутатор"
|
||||
},
|
||||
new JobFieldFilter
|
||||
{
|
||||
UnitFilterId = unitFilter1Id,
|
||||
FieldId = FieldId4,
|
||||
ValueMask="1"
|
||||
},
|
||||
new JobFieldFilter
|
||||
{
|
||||
UnitFilterId = unitFilter1Id,
|
||||
FieldId = FieldId2,
|
||||
ValueMask="0"
|
||||
},
|
||||
},
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter
|
||||
{
|
||||
IsParent = true,
|
||||
IsInverse = true,
|
||||
IsFullMatch = true,
|
||||
FieldId = FieldId1,
|
||||
ValueMask = "коммутатор",
|
||||
UnitFilterId = unitFilter1Id
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
jobs.Add(job1);
|
||||
|
||||
// --- TestJob_2 (с IsParent = false для дочерних связей) ---
|
||||
var job2 = new Job
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "TestJob_2",
|
||||
WorkName = "Работа2",
|
||||
TemplateNameMask = "%ЭК%",
|
||||
WorkGroupMask = "%ГРУППА_РАБОТ%",
|
||||
GroupId = group.Id,
|
||||
TnkId = Guid.NewGuid(),
|
||||
Group = group,
|
||||
DateCreated = now,
|
||||
ResponseAreaMask = "%ЗО_РГ%"
|
||||
};
|
||||
var unitFilter2Id = Guid.NewGuid();
|
||||
job2.UnitFilters = new List<JobUnitFilter>
|
||||
{
|
||||
new JobUnitFilter
|
||||
{
|
||||
Id = unitFilter2Id,
|
||||
UnitFilter = "СХД-%",
|
||||
JobId = job2.Id,
|
||||
Job = job2,
|
||||
DateCreated = now,
|
||||
FieldFilters = new List<JobFieldFilter>
|
||||
{
|
||||
new JobFieldFilter
|
||||
{
|
||||
UnitFilterId = unitFilter2Id,
|
||||
FieldId = FieldId1,
|
||||
ValueMask="коммутатор"
|
||||
},
|
||||
new JobFieldFilter
|
||||
{
|
||||
UnitFilterId = unitFilter2Id,
|
||||
FieldId = FieldId4,
|
||||
ValueMask="1"
|
||||
},
|
||||
},
|
||||
RelationshipFilters = new List<JobRelationshipFilter>
|
||||
{
|
||||
new JobRelationshipFilter
|
||||
{
|
||||
IsParent = false, // <-- Новый фильтр: ищем дочерние юниты
|
||||
IsInverse = false, // <-- Простой фильтр
|
||||
IsFullMatch = false, // <-- Простой фильтр
|
||||
FieldId = FieldId1,
|
||||
ValueMask = "коммутатор",
|
||||
UnitFilterId = unitFilter2Id
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
jobs.Add(job2);
|
||||
|
||||
context.Jobs.AddRange(jobs);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user