using System; using System.Collections.Generic; using System.Data; namespace Kit.Helpers.SqlHelperAbstract { /// /// Строка подключения для unit-тестов: единственный публичный вход в тестовое API (конвенция 1:1). /// Фейк-транспорт (SqlHelperTest) — internal: недостижим из внешнего и прод-кода, /// в том числе через new; тестирование репозиториев идёт только через этот интерфейс. /// public interface IConnectionStringTest : IConnectionString { /// Падает, если открытая заготовка не израсходована вызовом транспорта. void VerifyAll(); /// Снапшоты всех запросов к транспорту, в порядке вызовов (ассерты снаружи). IReadOnlyList Requests { get; } } /// /// Строка подключения для unit-тестов: вместо соединения с БД возвращает внутренний фейк-транспорт. /// Один экземпляр на тест-метод, конвенция 1:1 (одна заготовка = один вызов транспорта = один метод репозитория): /// var expArgs = new ConnectionString().PrepareExecute("req.requirement_select") /// .AsStoredProcedure() /// .AddParameter("p_doc_code", "TEST-DOC") /// .AddParameter("p_rid", DBNull.Value) /// .BuildParams(); /// var cs = new ConnectionStringTest(expArgs, _fxRequirementTable); // один DataTable = один курсор /// var rows = new RequirementRepository(cs).SelectRequirements(filter); /// cs.VerifyAll(); /// // дополнительные ассерты: cs.Requests — журнал вызовов транспорта. /// Скалярный ответ (ExecuteScalar/NonQuery) — фабрика ConnectionStringTest.Scalar(expArgs, 1). /// Заготовкаввввake_case, типизированные DataColumn — значения в CLR-типах, /// которые вернул бы Npgsql (integer → int, bigint → long, boolean → bool, text → string, /// timestamptz → DateTime); null-ячейка — DBNull.Value (NULL-строка LEFT JOIN). /// Беззначная заявка параметра (только наличие, значение не сверяется) — маркер ParamExpect.Any. /// public class ConnectionStringTest : ConnectionString, IConnectionStringTest { private readonly SqlHelperTest _sqlHelper; /// /// Заготовка выборки: таблицы DataTable — по одной на курсор (курсор ↔ конвертер), /// порядок таблиц = порядок курсоров функции БД; пустой результат — таблица с колонками без строк. /// public ConnectionStringTest(RequestParams requestParams, params DataTable[] resultTables) : this(requestParams, resultTables, scalarResult: null) { } /// /// Заготовка скалярного ответа (ExecuteScalar; для ExecuteNonQuery результат не читается, /// но заготовка обязана быть описана — конвенция «ответ обязан быть описан»). /// public static ConnectionStringTest Scalar(RequestParams requestParams, object scalarResult) => new(requestParams, Array.Empty(), scalarResult); private ConnectionStringTest(RequestParams requestParams, DataTable[] resultTables, object? scalarResult) { _sqlHelper = new SqlHelperTest(requestParams, resultTables, scalarResult); } public override ISqlHelper GetSqlHelper() => _sqlHelper; public void VerifyAll() => _sqlHelper.VerifyAll(); public IReadOnlyList Requests => _sqlHelper.Requests; } /// Маркеры ожиданий параметров заготовки ConnectionStringTest. public static class ParamExpect { /// Беззначная заявка: параметр обязан уйти в запрос, значение не сверяется. public static readonly object Any = new(); } }