fix(tests): починка юнит-харнесса, вскрытая компиляцией UnitTests
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions

Два предсуществующих бага, незаметных пока UnitTests не компилировался (с pass 1
конструктор StockService требовал новый параметр):

- MoneyConfigurations: CHECK ck_money_link_single_target использовал Postgres-only
  `::int` — SQLite (EnsureCreated в юнит-тестах) падал 'unrecognized token ":"'.
  Переписан на портируемый CASE WHEN (семантика та же, что в миграции Phase33a).
- ApiReferenceDocsJob: regex прав требовал `[RequiresPermission(...)]` в отдельных
  скобках и пропускал комбинированную форму `[HttpGet, RequiresPermission("X")]`
  (её используют в т.ч. контроллеры negative-stock). Убраны обязательные скобки.

Юнит-сьют снова зелёный: 297/297.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nns 2026-10-09 03:23:35 +05:00
parent c69d40a366
commit 7e4cdf4c4f
2 changed files with 9 additions and 3 deletions

View file

@ -147,7 +147,10 @@ private static IEnumerable<EndpointInfo> ScanDir(string dir)
.Where(s => !string.IsNullOrEmpty(s))).Trim('/'); .Where(s => !string.IsNullOrEmpty(s))).Trim('/');
} }
var permMatch = Regex.Match(attrs, @"\[RequiresPermission\(""([^""]+)""\)\]"); // Без обязательных внешних скобок — атрибут часто комбинируется:
// `[HttpGet, RequiresPermission("StocksView")]`. С `\[...\]` такие
// пропускались (Permission пустел).
var permMatch = Regex.Match(attrs, @"RequiresPermission\(""([^""]+)""\)");
var perm = permMatch.Success ? permMatch.Groups[1].Value : ""; var perm = permMatch.Success ? permMatch.Groups[1].Value : "";
var docRaw = m.Groups["doc"].Value; var docRaw = m.Groups["doc"].Value;

View file

@ -85,10 +85,13 @@ public static void ConfigureMoney(this ModelBuilder b)
e.HasOne(x => x.Supply).WithMany().HasForeignKey(x => x.SupplyId).OnDelete(DeleteBehavior.Restrict); e.HasOne(x => x.Supply).WithMany().HasForeignKey(x => x.SupplyId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(x => x.SupplierReturn).WithMany().HasForeignKey(x => x.SupplierReturnId).OnDelete(DeleteBehavior.Restrict); e.HasOne(x => x.SupplierReturn).WithMany().HasForeignKey(x => x.SupplierReturnId).OnDelete(DeleteBehavior.Restrict);
// CHECK: ровно одна целевая ссылка должна быть заполнена. // CHECK: ровно одна целевая ссылка должна быть заполнена. Портируемое
// выражение (CASE WHEN вместо Postgres-only `::int`) — чтобы схему можно
// было построить и на SQLite (юнит-тесты через EnsureCreated), и на
// Postgres. Семантика идентична `::int`-варианту из миграции Phase33a.
e.ToTable(t => t.HasCheckConstraint( e.ToTable(t => t.HasCheckConstraint(
"ck_money_link_single_target", "ck_money_link_single_target",
"((\"SupplyId\" IS NOT NULL)::int + (\"SupplierReturnId\" IS NOT NULL)::int) = 1")); "((CASE WHEN \"SupplyId\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"SupplierReturnId\" IS NOT NULL THEN 1 ELSE 0 END)) = 1"));
e.HasIndex(x => new { x.OrganizationId, x.SupplyId }) e.HasIndex(x => new { x.OrganizationId, x.SupplyId })
.HasFilter("\"SupplyId\" IS NOT NULL"); .HasFilter("\"SupplyId\" IS NOT NULL");