diff --git a/packagedef b/packagedef index 8613d52..836c7b7 100644 --- a/packagedef +++ b/packagedef @@ -19,6 +19,7 @@ .ЗависитОт("tempfiles") .ЗависитОт("1connector", "2.3.3") .ЗависитОт("reflector", "0.3.1") + .ЗависитОт("annotations", "1.3.0") .РазработкаЗависитОт("coverage", "0.6.1") .РазработкаЗависитОт("1testrunner", "1.9.2") .РазработкаЗависитОт("1bdd", "1.14.0") @@ -26,4 +27,7 @@ .РазработкаЗависитОт("winow", "0.11.0") .ОпределяетКласс("МенеджерПараметров", "src/Классы/МенеджерПараметров.os") .ОпределяетКласс("КонструкторПараметров", "src/Классы/КонструкторПараметров.os") + .ОпределяетКласс("АннотацияЗначениеПоУмолчанию", "src/Классы/АннотацияЗначениеПоУмолчанию.os") + .ОпределяетКласс("АннотацияСиноним", "src/Классы/АннотацияСиноним.os") + .ОпределяетКласс("АннотацияВложенныйДТО", "src/Классы/АннотацияВложенныйДТО.os") ; diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\320\275\320\270\320\265\320\237\320\276\320\273\321\217\320\224\320\242\320\236.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\320\275\320\270\320\265\320\237\320\276\320\273\321\217\320\224\320\242\320\236.os" new file mode 100644 index 0000000..cdeb01e --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\320\275\320\270\320\265\320\237\320\276\320\273\321\217\320\224\320\242\320\236.os" @@ -0,0 +1,89 @@ +// Описание одного поля DTO-класса, полученное из аннотаций свойства + +Перем Имя Экспорт; // Строка - имя свойства класса +Перем Тип Экспорт; // Тип - тип значения (определяется из ЗначениеПоУмолчанию) +Перем ЗначениеПоУмолчанию Экспорт; // Произвольный - значение по умолчанию +Перем Синоним Экспорт; // Строка - альтернативное имя ключа в конфигурации +Перем Обязательное Экспорт; // Булево - признак обязательности (нет ЗначениеПоУмолчанию) +Перем ВложенныйОбъект Экспорт; // Объект - ссылка на вложенный DTO-объект (Неопределено если примитив) +Перем ОписанияВложенныхПолей Экспорт; // Массив из ОписаниеПоляДТО - описания полей вложенного DTO + +// Конструктор описания поля +// +// Параметры: +// ИмяПоля - Строка - имя свойства класса +// +Процедура ПриСозданииОбъекта(Знач ИмяПоля) + + Имя = ИмяПоля; + Тип = Неопределено; + ЗначениеПоУмолчанию = Неопределено; + Синоним = Неопределено; + Обязательное = Истина; + ВложенныйОбъект = Неопределено; + ОписанияВложенныхПолей = Неопределено; + +КонецПроцедуры + +// Устанавливает значение по умолчанию и определяет тип из него +// +// Параметры: +// Значение - Произвольный - значение по умолчанию +// +Процедура УстановитьЗначениеПоУмолчанию(Знач Значение) Экспорт + + ЗначениеПоУмолчанию = Значение; + Обязательное = Ложь; + +КонецПроцедуры + +// Устанавливает тип значения явно (из аннотации &Тип) +// +// Параметры: +// НовыйТип - Тип - целевой тип значения +// +Процедура УстановитьТип(Знач НовыйТип) Экспорт + Тип = НовыйТип; +КонецПроцедуры + +// Устанавливает синоним поля +// +// Параметры: +// НовыйСиноним - Строка - альтернативное имя ключа в конфигурации +// +Процедура УстановитьСиноним(Знач НовыйСиноним) Экспорт + Синоним = НовыйСиноним; +КонецПроцедуры + +// Устанавливает вложенный DTO-объект и его описания полей +// +// Параметры: +// Объект - Объект - вложенный DTO-объект +// ОписанияПолей - Массив из ОписаниеПоляДТО - описания полей вложенного DTO +// +Процедура УстановитьВложенныйОбъект(Знач Объект, Знач ОписанияПолей) Экспорт + ВложенныйОбъект = Объект; + ОписанияВложенныхПолей = ОписанияПолей; + Обязательное = Ложь; +КонецПроцедуры + +// Возвращает признак того, что поле является вложенным DTO +Функция ЭтоВложенныйДТО() Экспорт + Возврат Не ВложенныйОбъект = Неопределено; +КонецФункции + +// Возвращает ключ для поиска в конфигурации +// Если задан синоним — возвращает его, иначе — имя свойства +// +// Возвращаемое значение: +// Строка - ключ для поиска в конфигурации +// +Функция КлючКонфигурации() Экспорт + + Если Не Синоним = Неопределено Тогда + Возврат Синоним; + КонецЕсли; + + Возврат Имя; + +КонецФункции diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236.os" new file mode 100644 index 0000000..cafa1cf --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236.os" @@ -0,0 +1,98 @@ +#Использовать annotations +#Использовать reflector +#Использовать logos + +Перем КонтейнерАннотаций; +Перем Лог; + +// Описывает класс DTO: читает аннотации экспортных свойств и возвращает массив описаний полей +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса с аннотированными свойствами +// +// Возвращаемое значение: +// Массив из ОписаниеПоляДТО - массив описаний полей DTO +// +Функция ОписатьКласс(Знач КлассОбъект) Экспорт + + РефлекторОбъекта = Новый РефлекторОбъекта(КлассОбъект); + Свойства = РефлекторОбъекта.ПолучитьТаблицуСвойств(Неопределено, Истина); + + ОписанияПолей = Новый Массив; + + Для Каждого Свойство Из Свойства Цикл + + Если Не Свойство.Экспорт Тогда + Продолжить; + КонецЕсли; + + ОписаниеПоля = Новый ОписаниеПоляДТО(Свойство.Имя); + + Для Каждого Аннотация Из Свойство.Аннотации Цикл + + ИмяАннотации = Врег(Аннотация.Имя); + + Если ИмяАннотации = "ТИП" Тогда + ЗначениеТипа = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(Аннотация); + ОписаниеПоля.УстановитьТип(Тип(ЗначениеТипа)); + Продолжить; + КонецЕсли; + + ОпределениеАннотации = КонтейнерАннотаций.ПолучитьОпределениеАннотации(Аннотация.Имя); + + Если ОпределениеАннотации = Неопределено Тогда + Продолжить; + КонецЕсли; + + ОбъектАннотации = ОпределениеАннотации.СоздатьОбъектАннотации(Аннотация); + + Если ИмяАннотации = "ЗНАЧЕНИЕПОУМОЛЧАНИЮ" Тогда + + ОписаниеПоля.УстановитьЗначениеПоУмолчанию(ОбъектАннотации.Значение()); + + ИначеЕсли ИмяАннотации = "СИНОНИМ" Тогда + + ОписаниеПоля.УстановитьСиноним(ОбъектАннотации.Синоним()); + + ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда + + СтандартныйРефлектор = Новый Рефлектор; + ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); + + Если ВложенныйОбъект = Неопределено Тогда + ИмяКлассаДТО = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(Аннотация, , ""); + Если ИмяКлассаДТО <> "" Тогда + ВложенныйОбъект = Новый(ИмяКлассаДТО); + СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); + Иначе + Лог.Предупреждение("Свойство <%1> помечено как вложенный DTO, но не инициализировано в конструкторе", Свойство.Имя); + КонецЕсли; + КонецЕсли; + + Если ВложенныйОбъект <> Неопределено Тогда + ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); + ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); + КонецЕсли; + + КонецЕсли; + + КонецЦикла; + + ОписанияПолей.Добавить(ОписаниеПоля); + + КонецЦикла; + + Возврат ОписанияПолей; + +КонецФункции + +Процедура ПриСозданииОбъекта() + + КонтейнерАннотаций = Новый КонтейнерАннотаций; + КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияЗначениеПоУмолчанию")); + КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); + КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); + + Лог = Логирование.ПолучитьЛог("oscript.lib.configor.dto"); + +КонецПроцедуры diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\271\320\224\320\242\320\236.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\271\320\224\320\242\320\236.os" new file mode 100644 index 0000000..be38540 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\271\320\224\320\242\320\236.os" @@ -0,0 +1,12 @@ +// Возвращает имя аннотации +// +// Возвращаемое значение: +// Строка - имя аннотации +// +Функция ИмяАннотации() Экспорт + Возврат "ВложенныйДТО"; +КонецФункции + +&Аннотация("ВложенныйДТО") +Процедура ПриСозданииОбъекта(Значение = "") Экспорт +КонецПроцедуры diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\227\320\275\320\260\321\207\320\265\320\275\320\270\320\265\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\227\320\275\320\260\321\207\320\265\320\275\320\270\320\265\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" new file mode 100644 index 0000000..11e00fe --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\227\320\275\320\260\321\207\320\265\320\275\320\270\320\265\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" @@ -0,0 +1,24 @@ +Перем _Значение; + +// Возвращает имя аннотации +// +// Возвращаемое значение: +// Строка - имя аннотации +// +Функция ИмяАннотации() Экспорт + Возврат "ЗначениеПоУмолчанию"; +КонецФункции + +// Возвращает значение по умолчанию +// +// Возвращаемое значение: +// Произвольный - значение по умолчанию +// +Функция Значение() Экспорт + Возврат _Значение; +КонецФункции + +&Аннотация("ЗначениеПоУмолчанию") +Процедура ПриСозданииОбъекта(Значение) Экспорт + _Значение = Значение; +КонецПроцедуры diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\241\320\270\320\275\320\276\320\275\320\270\320\274.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\241\320\270\320\275\320\276\320\275\320\270\320\274.os" new file mode 100644 index 0000000..133c89b --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217\320\241\320\270\320\275\320\276\320\275\320\270\320\274.os" @@ -0,0 +1,24 @@ +Перем _Синоним; + +// Возвращает имя аннотации +// +// Возвращаемое значение: +// Строка - имя аннотации +// +Функция ИмяАннотации() Экспорт + Возврат "Синоним"; +КонецФункции + +// Возвращает синоним (альтернативное имя ключа в конфигурации) +// +// Возвращаемое значение: +// Строка - синоним свойства +// +Функция Синоним() Экспорт + Возврат _Синоним; +КонецФункции + +&Аннотация("Синоним") +Процедура ПриСозданииОбъекта(Значение) Экспорт + _Синоним = Значение; +КонецПроцедуры diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262.os" index 273f5a2..d51eb3d 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262.os" @@ -19,6 +19,9 @@ Перем ВнутреннийКонструкторПараметров; // Класс КонструкторПараметров Перем ИспользуетсяКонструкторПараметров; // булево +Перем ВнутреннийОписательДТО; // Класс ОписательДТО +Перем РеестрКлассовПараметров; // Соответствие: КлассОбъект -> Структура(Префикс, ОписанияПолей) + Перем Лог; // Логгер #Область Экспортных_процедур @@ -310,10 +313,127 @@ КонецФункции +// Регистрирует класс DTO-параметров по префиксу +// Считывает аннотации свойств класса и сохраняет описания полей +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса с аннотированными свойствами +// Префикс - Строка - ключ верхнего уровня в конфигурации +// +Процедура ЗарегистрироватьКлассПараметров(Знач КлассОбъект, Знач Префикс) Экспорт + + ОписанияПолей = ВнутреннийОписательДТО.ОписатьКласс(КлассОбъект); + + Лог.Отладка("Зарегистрирован класс параметров с префиксом <%1>, полей: %2", Префикс, ОписанияПолей.Количество()); + + Запись = Новый Структура("Префикс, ОписанияПолей", Префикс, ОписанияПолей); + РеестрКлассовПараметров.Вставить(КлассОбъект, Запись); + +КонецПроцедуры + +// Заполняет свойства объекта DTO из прочитанных параметров +// Использует зарегистрированные описания полей для маппинга +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса для заполнения +// Префикс - Строка - ключ верхнего уровня в конфигурации +// +Процедура ЗаполнитьОбъектПараметров(Знач КлассОбъект, Знач Префикс) Экспорт + + Запись = РеестрКлассовПараметров[КлассОбъект]; + + Если Запись = Неопределено Тогда + ВызватьИсключение "Класс параметров с префиксом """ + Префикс + """ не зарегистрирован"; + КонецЕсли; + + ОписанияПолей = Запись.ОписанияПолей; + + ОбъектРефлектор = Новый Рефлектор; + + Для Каждого ОписаниеПоля Из ОписанияПолей Цикл + + КлючКонфигурации = ОписаниеПоля.КлючКонфигурации(); + ПолныйКлюч = Префикс + "." + КлючКонфигурации; + + Если ОписаниеПоля.ЭтоВложенныйДТО() Тогда + + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ЗаполнитьВложенныйДТО(ОписаниеПоля, ПолныйКлюч); + + Иначе + + ЗначениеИзКонфига = Параметр(ПолныйКлюч); + + Если Не ЗначениеИзКонфига = Неопределено Тогда + + ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ОписаниеПоля); + Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); + + ИначеЕсли Не ОписаниеПоля.Обязательное Тогда + + Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); + + Иначе + + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ОписаниеПоля.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + + КонецЕсли; + + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + #КонецОбласти #Область Вспомогательные_процедуры_и_функции +Процедура ЗаполнитьВложенныйДТО(Знач ОписаниеПоля, Знач Префикс) + + ВложенныйОбъект = ОписаниеПоля.ВложенныйОбъект; + ВложенныеОписания = ОписаниеПоля.ОписанияВложенныхПолей; + ОбъектРефлектор = Новый Рефлектор; + + Для Каждого ВложенноеОписание Из ВложенныеОписания Цикл + + ВложенныйКлюч = ВложенноеОписание.КлючКонфигурации(); + ПолныйКлюч = Префикс + "." + ВложенныйКлюч; + + Если ВложенноеОписание.ЭтоВложенныйДТО() Тогда + + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ВложенноеОписание.Имя, ПолныйКлюч); + ЗаполнитьВложенныйДТО(ВложенноеОписание, ПолныйКлюч); + + Иначе + + ЗначениеИзКонфига = Параметр(ПолныйКлюч); + + Если Не ЗначениеИзКонфига = Неопределено Тогда + + ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ВложенноеОписание); + Лог.Отладка("Установка вложенного свойства <%1> из параметра <%2>", ВложенноеОписание.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ЗначениеИзКонфига); + + ИначеЕсли Не ВложенноеОписание.Обязательное Тогда + + Лог.Отладка("Вложенное свойство <%1> не найдено в конфиге, используется значение по умолчанию", ВложенноеОписание.Имя); + ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ВложенноеОписание.ЗначениеПоУмолчанию); + + Иначе + + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ВложенноеОписание.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + + КонецЕсли; + + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + Функция ПолучитьКонструкторПараметров() Конструктор = Новый КонструкторПараметров(); @@ -322,6 +442,55 @@ КонецФункции +Функция ПривестиЗначениеКТипу(Знач Значение, Знач ОписаниеПоля) + + ЦелевойТип = ОписаниеПоля.Тип; + + Если ЦелевойТип = Неопределено Тогда + Возврат Значение; + КонецЕсли; + + Если ТипЗнч(Значение) = ЦелевойТип Тогда + Возврат Значение; + КонецЕсли; + + Попытка + + Если ЦелевойТип = Тип("Дата") Тогда + // ОписаниеТипов не парсит ISO-формат + Возврат XMLЗначение(Тип("Дата"), Значение); + Иначе + + // Для булево: "1"/"0" не поддерживаются ОписаниеТипов — подменяем + Если ЦелевойТип = Тип("Булево") И ТипЗнч(Значение) = Тип("Строка") Тогда + СтрЗначение = СокрЛП(Значение); + Если СтрЗначение = "1" Тогда + Возврат Истина; + ИначеЕсли СтрЗначение = "0" Тогда + Возврат Ложь; + КонецЕсли; + КонецЕсли; + + ОписаниеТипов = Новый ОписаниеТипов(Строка(ЦелевойТип)); + Результат = ОписаниеТипов.ПривестиЗначение(Значение); + + // ОписаниеТипов.ПривестиЗначение не бросает ошибку для невалидных строк + Если ЦелевойТип = Тип("Число") И ТипЗнч(Значение) = Тип("Строка") Тогда + Если Результат = 0 И СокрЛП(Значение) <> "0" Тогда + ВызватьИсключение "Невалидное числовое значение"; + КонецЕсли; + КонецЕсли; + + Возврат Результат; + КонецЕсли; + + Исключение + ВызватьИсключение "Не удалось привести значение параметра """ + + ОписаниеПоля.Имя + """ (" + Значение + ") к типу " + ЦелевойТип; + КонецПопытки; + +КонецФункции + Процедура ВыполнитьЧтениеПровайдеров() КоллекцияПровайдеров = Новый ПроцессорКоллекций; @@ -471,6 +640,9 @@ ВнутреннийКонструкторПараметров = Неопределено; ИспользуетсяКонструкторПараметров = Ложь; + ВнутреннийОписательДТО = Новый ОписательДТО; + РеестрКлассовПараметров = Новый Соответствие; + КонецПроцедуры Функция ИнтерфейсРеализован(Интерфейс, КлассОбъект, ВыдатьОшибку = Ложь) diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265.os" new file mode 100644 index 0000000..1254f93 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265.os" @@ -0,0 +1,10 @@ +// Вложенный DTO: параметры подключения + +&ЗначениеПоУмолчанию("localhost") +Перем Хост Экспорт; + +&ЗначениеПоУмолчанию(5432) +Перем Порт Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" new file mode 100644 index 0000000..1c79b01 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\320\276\320\265\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" @@ -0,0 +1,13 @@ +// Вложенный DTO с аннотациями типов + +&Тип("Строка") +Перем Хост Экспорт; + +&Тип("Число") +Перем Порт Экспорт; + +&Тип("Булево") +Перем SSL Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236.os" new file mode 100644 index 0000000..524bf55 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236.os" @@ -0,0 +1,10 @@ +// Тестовый DTO с аннотированными свойствами для проверки ОписательДТО и МенеджерПараметров + +&ЗначениеПоУмолчанию("localhost") +Перем АдресСервера Экспорт; + +&ЗначениеПоУмолчанию(8080) +Перем ПортСервера Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205.os" new file mode 100644 index 0000000..32a3650 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205.os" @@ -0,0 +1,11 @@ +// DTO второго уровня: настройки базы данных с вложенным DTO подключения + +&ЗначениеПоУмолчанию("main_db") +Перем ИмяБазы Экспорт; + +&ВложенныйДТО +Перем Подключение Экспорт; + +Процедура ПриСозданииОбъекта() + Подключение = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "ТестовоеПодключение.os")); +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" new file mode 100644 index 0000000..6d7108b --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\220\320\275\320\275\320\276\321\202\320\260\321\206\320\270\321\217.os" @@ -0,0 +1,12 @@ +// DTO второго уровня: то же самое, что ТестовыйДТОБазаДанных, +// но вложенный DTO указан через аннотацию &ВложенныйДТО вместо Новый в конструкторе. + +&ЗначениеПоУмолчанию("main_db") +Перем ИмяБазы Экспорт; + +&ВложенныйДТО +Перем Подключение Экспорт; + +Процедура ПриСозданииОбъекта() + Подключение = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "ТестовоеПодключение.os")); +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" new file mode 100644 index 0000000..4c5bccd --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\221\320\260\320\267\320\260\320\224\320\260\320\275\320\275\321\213\321\205\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" @@ -0,0 +1,11 @@ +// DTO с вложенным DTO, оба используют аннотации типов + +&Тип("Строка") +Перем ИмяБазы Экспорт; + +&ВложенныйДТО +Перем Подключение Экспорт; + +Процедура ПриСозданииОбъекта() + Подключение = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "ТестовоеПодключениеСТипами.os")); +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.os" new file mode 100644 index 0000000..40ceb7d --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.os" @@ -0,0 +1,14 @@ +// DTO верхнего уровня: конфигурация приложения с вложенными DTO + +&ЗначениеПоУмолчанию("MyApp") +Перем Название Экспорт; + +&ЗначениеПоУмолчанию("1.0.0") +Перем Версия Экспорт; + +&ВложенныйДТО +Перем БазаДанных Экспорт; + +Процедура ПриСозданииОбъекта() + БазаДанных = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "ТестовыйДТОБазаДанных.os")); +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\234\320\260\321\201\321\201\320\270\320\262\320\276\320\274.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\234\320\260\321\201\321\201\320\270\320\262\320\276\320\274.os" new file mode 100644 index 0000000..49e9140 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\234\320\260\321\201\321\201\320\270\320\262\320\276\320\274.os" @@ -0,0 +1,10 @@ +// DTO с массивом вложенных объектов + +&ЗначениеПоУмолчанию("cluster") +Перем Имя Экспорт; + +Перем Узлы Экспорт; + +Процедура ПриСозданииОбъекта() + Узлы = Новый Массив; +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\236\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\274\320\237\320\276\320\273\320\265\320\274.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\236\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\274\320\237\320\276\320\273\320\265\320\274.os" new file mode 100644 index 0000000..07564fb --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\236\320\261\321\217\320\267\320\260\321\202\320\265\320\273\321\214\320\275\321\213\320\274\320\237\320\276\320\273\320\265\320\274.os" @@ -0,0 +1,9 @@ +// Тестовый DTO с обязательным полем (без ЗначениеПоУмолчанию) + +&ЗначениеПоУмолчанию("localhost") +Перем АдресСервера Экспорт; + +Перем ОбязательноеПоле Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\241\320\270\320\275\320\276\320\275\320\270\320\274\320\276\320\274.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\241\320\270\320\275\320\276\320\275\320\270\320\274\320\276\320\274.os" new file mode 100644 index 0000000..47a527a --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\241\320\270\320\275\320\276\320\275\320\270\320\274\320\276\320\274.os" @@ -0,0 +1,8 @@ +// Тестовый DTO с синонимом свойства + +&Синоним("port") +&ЗначениеПоУмолчанию(8080) +Перем Порт Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" new file mode 100644 index 0000000..083a9d7 --- /dev/null +++ "b/tests/fixtures/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\224\320\242\320\236\320\241\320\242\320\270\320\277\320\260\320\274\320\270.os" @@ -0,0 +1,16 @@ +// DTO с аннотациями типов для проверки приведения типов + +&Тип("Число") +Перем Порт Экспорт; + +&Тип("Булево") +Перем Включен Экспорт; + +&Тип("Строка") +Перем Название Экспорт; + +&Тип("Дата") +Перем ДатаЗапуска Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры diff --git "a/tests/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236_test.os" "b/tests/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236_test.os" new file mode 100644 index 0000000..0aba529 --- /dev/null +++ "b/tests/\320\236\320\277\320\270\321\201\320\260\321\202\320\265\320\273\321\214\320\224\320\242\320\236_test.os" @@ -0,0 +1,470 @@ +#использовать "../" +#Использовать asserts +#Использовать tempfiles + +Перем юТест; +Перем КаталогФикстур; + +Функция ПолучитьСписокТестов(Знач Тестирование) Экспорт + + юТест = Тестирование; + + КаталогФикстур = ОбъединитьПути(ТекущийСценарий().Каталог, "fixtures"); + + ИменаТестов = Новый Массив; + + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств"); + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля"); + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетСинонимы"); + ИменаТестов.Добавить("ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига"); + ИменаТестов.Добавить("ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля"); + ИменаТестов.Добавить("ТестДолжен_ИспользоватьСинонимПриЗаполнении"); + ИменаТестов.Добавить("ТестДолжен_ЗаполнитьВложенныйДТО"); + ИменаТестов.Добавить("ТестДолжен_ЗаполнитьДвухуровневыйДТО"); + ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа"); + ИменаТестов.Добавить("ТестДолжен_ПривестиТипыВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_НеПриводитьЕслиТипСовпадает"); + + Возврат ИменаТестов; + +КонецФункции + +Функция ЗагрузитьДТО(Знач ИмяФайла) + Возврат ЗагрузитьСценарий(ОбъединитьПути(КаталогФикстур, ИмяФайла)); +КонецФункции + +// КП-1.1: ОписательДТО читает аннотации свойств +Процедура ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {}}"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); + + // Assert + Ожидаем.Что(ДТО.АдресСервера, "Аннотация ЗначениеПоУмолчанию должна быть прочитана для АдресСервера").Равно("localhost"); + Ожидаем.Что(ДТО.ПортСервера, "Аннотация ЗначениеПоУмолчанию должна быть прочитана для ПортСервера").Равно(8080); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.2: ОписательДТО определяет обязательные поля +Процедура ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""АдресСервера"": ""test""}}"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + Менеджер.Прочитать(); + + // Act + Assert + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); + Ожидаем.Что(Ложь, "Ожидалось исключение для обязательного поля").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("ОбязательноеПоле"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.3: Синонимы +Процедура ТестДолжен_ОписатьКлассЧитаетСинонимы() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""port"": 3306}}"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); + + // Assert + Ожидаем.Что(ДТО.Порт, "Синоним port должен быть прочитан из аннотации").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.4: Регистрация и заполнение DTO из конфига +Процедура ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Префикс"": {""АдресСервера"": ""prod-server"", ""ПортСервера"": 3306}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + + Ожидаем.Что(ДТО.АдресСервера, "АдресСервера должен быть из конфига").Равно("prod-server"); + Ожидаем.Что(ДТО.ПортСервера, "ПортСервера должен быть из конфига").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.5: Значения по умолчанию при отсутствии ключа +Процедура ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Префикс"": {""АдресСервера"": ""prod-server""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + + Ожидаем.Что(ДТО.АдресСервера, "АдресСервера должен быть из конфига").Равно("prod-server"); + Ожидаем.Что(ДТО.ПортСервера, "ПортСервера должен быть дефолтным").Равно(8080); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.6: Обязательное поле без значения — исключение +Процедура ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Префикс"": {""АдресСервера"": ""prod-server""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); + Менеджер.Прочитать(); + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + Ожидаем.Что(Ложь, "Ожидалось исключение для обязательного поля").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("ОбязательноеПоле"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-1.7: Синоним при заполнении +Процедура ТестДолжен_ИспользоватьСинонимПриЗаполнении() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Префикс"": {""port"": 3306}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + + Ожидаем.Что(ДТО.Порт, "Порт должен быть заполнен из конфига по синониму port").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-2.1: Вложенный DTO — БазаДанных.Подключение +Процедура ТестДолжен_ЗаполнитьВложенныйДТО() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанных.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""БД"": {""ИмяБазы"": ""test_db"", ""Подключение"": {""Хост"": ""db-server"", ""Порт"": 3306}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + + // Assert + Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("test_db"); + Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть заполнен из конфига").Равно("db-server"); + Ожидаем.Что(ДТО.Подключение.Порт, "Вложенный Порт должен быть заполнен из конфига").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-2.2: Трёхуровневый DTO — Приложение.БазаДанных.Подключение +Процедура ТестДолжен_ЗаполнитьДвухуровневыйДТО() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОПриложение.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Приложение"": {""Название"": ""SuperApp"", ""Версия"": ""2.0.0"", ""БазаДанных"": {""ИмяБазы"": ""prod_db"", ""Подключение"": {""Хост"": ""prod-host"", ""Порт"": 5432}}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Приложение"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Приложение"); + + // Assert + Ожидаем.Что(ДТО.Название, "Название должно быть из конфига").Равно("SuperApp"); + Ожидаем.Что(ДТО.Версия, "Версия должна быть из конфига").Равно("2.0.0"); + Ожидаем.Что(ДТО.БазаДанных.ИмяБазы, "Вложенное ИмяБазы должно быть из конфига").Равно("prod_db"); + Ожидаем.Что(ДТО.БазаДанных.Подключение.Хост, "Глубоко вложенный Хост должен быть из конфига").Равно("prod-host"); + Ожидаем.Что(ДТО.БазаДанных.Подключение.Порт, "Глубоко вложенный Порт должен быть из конфига").Равно(5432); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-2.3: Вложенный DTO — умолчания при отсутствии ключей во вложенной структуре +Процедура ТестДолжен_ПрименитьУмолчанияВоВложенномДТО() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанных.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""БД"": {""ИмяБазы"": ""custom_db""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + + // Assert + Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("custom_db"); + Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть по умолчанию").Равно("localhost"); + Ожидаем.Что(ДТО.Подключение.Порт, "Вложенный Порт должен быть по умолчанию").Равно(5432); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-2.4: DTO с массивом во вложенной структуре +Процедура ТестДолжен_ОбработатьМассивВоВложенномДТО() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОСМассивом.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Кластер"": {""Имя"": ""prod-cluster"", ""Узлы"": [""node1"", ""node2"", ""node3""]}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Кластер"); + + // Act + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Кластер"); + + // Assert + Ожидаем.Что(ДТО.Имя, "Имя кластера должно быть из конфига").Равно("prod-cluster"); + Ожидаем.Что(ДТО.Узлы.Количество(), "Должно быть 3 узла").Равно(3); + Ожидаем.Что(ДТО.Узлы[0], "Первый узел").Равно("node1"); + Ожидаем.Что(ДТО.Узлы[1], "Второй узел").Равно("node2"); + Ожидаем.Что(ДТО.Узлы[2], "Третий узел").Равно("node3"); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-3.1: Приведение строки к числу по &Тип("Число") +Процедура ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + // Имитируем ситуацию, когда из ENV/INI приходят строки + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""8080"", ""Включен"": ""true"", ""Название"": 42, ""ДатаЗапуска"": ""2025-01-15T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.Порт), "Порт должен быть числом").Равно(Тип("Число")); + Ожидаем.Что(ДТО.Порт, "Порт должен быть 8080").Равно(8080); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-3.2: Приведение строки к булево по &Тип("Булево") +Процедура ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""3306"", ""Включен"": ""Истина"", ""Название"": ""test"", ""ДатаЗапуска"": ""2025-06-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); + Ожидаем.Что(ДТО.Включен, "Включен должен быть Истина").Равно(Истина); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-3.3: Ошибка при невозможности приведения типа +Процедура ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""not_a_number"", ""Включен"": ""false"", ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Ожидаем.Что(Ложь, "Ожидалось исключение приведения типа").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("Порт"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-3.4: Приведение типов во вложенном ДТО +Процедура ТестДолжен_ПривестиТипыВоВложенномДТО() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанныхСТипами.os"); + + // Все значения как строки (имитация ENV) + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""db"": {""ИмяБазы"": ""test_db"", ""Подключение"": {""Хост"": ""db-host"", ""Порт"": ""5432"", ""SSL"": ""true""}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "db"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "db"); + + Ожидаем.Что(ТипЗнч(ДТО.ИмяБазы), "ИмяБазы должно быть строкой").Равно(Тип("Строка")); + Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть строкой").Равно("db-host"); + Ожидаем.Что(ТипЗнч(ДТО.Подключение.Порт), "Вложенный Порт должен стать числом").Равно(Тип("Число")); + Ожидаем.Что(ДТО.Подключение.Порт, "Вложенный Порт должен быть 5432").Равно(5432); + Ожидаем.Что(ТипЗнч(ДТО.Подключение.SSL), "Вложенный SSL должен стать булево").Равно(Тип("Булево")); + Ожидаем.Что(ДТО.Подключение.SSL, "Вложенный SSL должен быть Истина").Равно(Истина); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// КП-3.5: Не приводить если тип уже совпадает +Процедура ТестДолжен_НеПриводитьЕслиТипСовпадает() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + // JSON провайдер вернёт число как число — приведение не нужно + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": 8080, ""Включен"": true, ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.Порт), "Порт должен остаться числом").Равно(Тип("Число")); + Ожидаем.Что(ДТО.Порт, "Порт должен быть 8080").Равно(8080); + Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен остаться булево").Равно(Тип("Булево")); + Ожидаем.Что(ДТО.Включен, "Включен должен быть true").Равно(Истина); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +Функция ПолучитьВременныйФайлJSON(Знач СодержимоеJSON) + + ВременныйФайл = ПолучитьИмяВременногоФайла("json"); + ЗаписьТекста = Новый ЗаписьТекста(ВременныйФайл, "UTF-8"); + ЗаписьТекста.Записать(СодержимоеJSON); + ЗаписьТекста.Закрыть(); + Возврат ВременныйФайл; + +КонецФункции