From 4a96c9e1b6179c7a859732a1487f7a990a219da7 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Sat, 11 Apr 2026 00:32:07 +0300 Subject: [PATCH 01/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=9E=D0=BF=D0=B8=D1=81=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=94=D0=A2=D0=9E,=20=D0=B0=D0=BD=D0=BD=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=A1=D0=B8=D0=BD=D0=BE=D0=BD=D0=B8?= =?UTF-8?q?=D0=BC=20=D0=B8=20=D0=97=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=D0=9F=D0=BE=D0=A3=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8E,=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 3 + ...20\273\321\217\320\224\320\242\320\236.os" | 63 +++++++ ...20\273\321\214\320\224\320\242\320\236.os" | 70 ++++++++ ...21\207\320\260\320\275\320\270\321\216.os" | 24 +++ ...20\275\320\276\320\275\320\270\320\274.os" | 24 +++ ...20\265\321\202\321\200\320\276\320\262.os" | 71 ++++++++ ...21\213\320\271\320\224\320\242\320\236.os" | 10 ++ ...20\237\320\276\320\273\320\265\320\274.os" | 9 + ...20\275\320\270\320\274\320\276\320\274.os" | 8 + ...3\321\214\320\224\320\242\320\236_test.os" | 164 ++++++++++++++++++ 10 files changed, 446 insertions(+) create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" diff --git a/packagedef b/packagedef index 8613d52..ca63b73 100644 --- a/packagedef +++ b/packagedef @@ -19,6 +19,7 @@ .ЗависитОт("tempfiles") .ЗависитОт("1connector", "2.3.3") .ЗависитОт("reflector", "0.3.1") + .ЗависитОт("annotations") .РазработкаЗависитОт("coverage", "0.6.1") .РазработкаЗависитОт("1testrunner", "1.9.2") .РазработкаЗависитОт("1bdd", "1.14.0") @@ -26,4 +27,6 @@ .РазработкаЗависитОт("winow", "0.11.0") .ОпределяетКласс("МенеджерПараметров", "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..5ab56c9 --- /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,63 @@ +// Описание одного поля 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..52fa3e3 --- /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,70 @@ +#Использовать annotations +#Использовать reflector + +Перем КонтейнерАннотаций; +Перем Лог; + +// Описывает класс DTO: читает аннотации экспортных свойств и возвращает массив описаний полей +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса с аннотированными свойствами +// +// Возвращаемое значение: +// Массив из ОписаниеПоляДТО - массив описаний полей 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\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..1ddb273 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,6 +313,71 @@ КонецФункции +// Регистрирует класс DTO-параметров по префиксу +// Считывает аннотации свойств класса и сохраняет описания полей +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса с аннотированными свойствами +// Префикс - Строка - ключ верхнего уровня в конфигурации +// +Процедура ЗарегистрироватьКлассПараметров(Знач КлассОбъект, Знач Префикс) Экспорт + + Если ВнутреннийОписательДТО = Неопределено Тогда + ВнутреннийОписательДТО = Новый ОписательДТО; + КонецЕсли; + + ОписанияПолей = ВнутреннийОписательДТО.ОписатьКласс(КлассОбъект); + + Лог.Отладка("Зарегистрирован класс параметров с префиксом <%1>, полей: %2", Префикс, ОписанияПолей.Количество()); + + РеестрКлассовПараметров.Вставить(Префикс, ОписанияПолей); + +КонецПроцедуры + +// Заполняет свойства объекта DTO из прочитанных параметров +// Использует зарегистрированные описания полей для маппинга +// +// Параметры: +// КлассОбъект - Объект - экземпляр DTO-класса для заполнения +// Префикс - Строка - ключ верхнего уровня в конфигурации +// +Процедура ЗаполнитьОбъектПараметров(Знач КлассОбъект, Знач Префикс) Экспорт + + ОписанияПолей = РеестрКлассовПараметров[Префикс]; + + Если ОписанияПолей = Неопределено Тогда + ВызватьИсключение "Класс параметров с префиксом """ + Префикс + """ не зарегистрирован"; + КонецЕсли; + + ОбъектРефлектор = Новый Рефлектор; + + Для Каждого ОписаниеПоля Из ОписанияПолей Цикл + + КлючКонфигурации = ОписаниеПоля.КлючКонфигурации(); + ПолныйКлюч = Префикс + "." + КлючКонфигурации; + + ЗначениеИзКонфига = Параметр(ПолныйКлюч); + + Если Не ЗначениеИзКонфига = Неопределено Тогда + + Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); + + ИначеЕсли Не ОписаниеПоля.Обязательное Тогда + + Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); + + Иначе + + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ОписаниеПоля.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + #КонецОбласти #Область Вспомогательные_процедуры_и_функции @@ -471,6 +539,9 @@ ВнутреннийКонструкторПараметров = Неопределено; ИспользуетсяКонструкторПараметров = Ложь; + ВнутреннийОписательДТО = Неопределено; + РеестрКлассовПараметров = Новый Соответствие; + КонецПроцедуры Функция ИнтерфейсРеализован(Интерфейс, КлассОбъект, ВыдатьОшибку = Ложь) 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\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/\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..0380445 --- /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,164 @@ +#использовать "../" +#Использовать asserts +#Использовать tempfiles + +Перем юТест; +Перем КаталогФикстур; + +Функция ПолучитьСписокТестов(Знач Тестирование) Экспорт + + юТест = Тестирование; + + КаталогФикстур = ОбъединитьПути(ТекущийСценарий().Каталог, "fixtures"); + + ИменаТестов = Новый Массив; + + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств"); + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля"); + ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетСинонимы"); + ИменаТестов.Добавить("ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига"); + ИменаТестов.Добавить("ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля"); + ИменаТестов.Добавить("ТестДолжен_ИспользоватьСинонимПриЗаполнении"); + + Возврат ИменаТестов; + +КонецФункции + +Функция ЗагрузитьДТО(Знач ИмяФайла) + Возврат ЗагрузитьСценарий(ОбъединитьПути(КаталогФикстур, ИмяФайла)); +КонецФункции + +// КП-1.1: ОписательДТО читает аннотации свойств +Процедура ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + +КонецПроцедуры + +// КП-1.2: ОписательДТО определяет обязательные поля +Процедура ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + +КонецПроцедуры + +// КП-1.3: Синонимы +Процедура ТестДолжен_ОписатьКлассЧитаетСинонимы() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); + +КонецПроцедуры + +// КП-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); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +Функция ПолучитьВременныйФайлJSON(Знач СодержимоеJSON) + + ВременныйФайл = ПолучитьИмяВременногоФайла("json"); + ЗаписьТекста = Новый ЗаписьТекста(ВременныйФайл, "UTF-8"); + ЗаписьТекста.Записать(СодержимоеJSON); + ЗаписьТекста.Закрыть(); + Возврат ВременныйФайл; + +КонецФункции From dd9f8321c046cd17b97062664f201635f08ee93f Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Sat, 11 Apr 2026 00:56:25 +0300 Subject: [PATCH 02/25] =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...3\321\214\320\224\320\242\320\236_test.os" | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) 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" index 0380445..3e91c73 100644 --- "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" @@ -32,31 +32,73 @@ // КП-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 из конфига From 4cf1f0448af61d18ccfbe845be45f5d291acbb27 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Sat, 11 Apr 2026 01:31:59 +0300 Subject: [PATCH 03/25] =?UTF-8?q?=D0=92=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=94=D0=A2=D0=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\273\321\217\320\224\320\242\320\236.os" | 21 ++++ ...20\273\321\214\320\224\320\242\320\236.os" | 36 ++++++ ...21\213\320\271\320\224\320\242\320\236.os" | 24 ++++ ...20\265\321\202\321\200\320\276\320\262.os" | 69 +++++++++-- ...21\207\320\265\320\275\320\270\320\265.os" | 10 ++ ...20\260\320\275\320\275\321\213\321\205.os" | 10 ++ ...21\202\320\260\321\206\320\270\321\217.os" | 12 ++ ...20\266\320\265\320\275\320\270\320\265.os" | 13 ++ ...21\201\320\270\320\262\320\276\320\274.os" | 10 ++ ...3\321\214\320\224\320\242\320\236_test.os" | 116 ++++++++++++++++++ 10 files changed, 312 insertions(+), 9 deletions(-) create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" 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" index 5ab56c9..313d4d7 100644 --- "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" @@ -5,6 +5,8 @@ Перем ЗначениеПоУмолчанию Экспорт; // Произвольный - значение по умолчанию Перем Синоним Экспорт; // Строка - альтернативное имя ключа в конфигурации Перем Обязательное Экспорт; // Булево - признак обязательности (нет ЗначениеПоУмолчанию) +Перем ВложенныйОбъект Экспорт; // Объект - ссылка на вложенный DTO-объект (Неопределено если примитив) +Перем ОписанияВложенныхПолей Экспорт; // Массив из ОписаниеПоляДТО - описания полей вложенного DTO // Конструктор описания поля // @@ -18,6 +20,8 @@ ЗначениеПоУмолчанию = Неопределено; Синоним = Неопределено; Обязательное = Истина; + ВложенныйОбъект = Неопределено; + ОписанияВложенныхПолей = Неопределено; КонецПроцедуры @@ -46,6 +50,23 @@ Синоним = НовыйСиноним; КонецПроцедуры +// Устанавливает вложенный 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" index 52fa3e3..7b1543a 100644 --- "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" @@ -47,10 +47,45 @@ ОписаниеПоля.УстановитьСиноним(ОбъектАннотации.Синоним()); + ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда + + ВложенныйОбъект = Новый(ОбъектАннотации.ИмяКласса()); + ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); + ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); + + СтандартныйРефлектор = Новый Рефлектор; + СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); + КонецЕсли; КонецЦикла; + // Проверяем, является ли текущее значение свойства вложенным DTO-объектом + // (если аннотация &ВложенныйДТО не была задана явно) + Если Не ОписаниеПоля.ЭтоВложенныйДТО() Тогда + Попытка + СтандартныйРефлектор = Новый Рефлектор; + ЗначениеСвойства = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); + Если Не ЗначениеСвойства = Неопределено Тогда + ВложенныйРефлектор = Новый РефлекторОбъекта(ЗначениеСвойства); + ВложенныеСвойства = ВложенныйРефлектор.ПолучитьТаблицуСвойств(Неопределено, Истина); + ЕстьЭкспортныеСвойства = Ложь; + Для Каждого ВлСвойство Из ВложенныеСвойства Цикл + Если ВлСвойство.Экспорт Тогда + ЕстьЭкспортныеСвойства = Истина; + Прервать; + КонецЕсли; + КонецЦикла; + Если ЕстьЭкспортныеСвойства Тогда + ВложенныеОписания = ОписатьКласс(ЗначениеСвойства); + ОписаниеПоля.УстановитьВложенныйОбъект(ЗначениеСвойства, ВложенныеОписания); + КонецЕсли; + КонецЕсли; + Исключение + // Значение недоступно или не является объектом — пропускаем + КонецПопытки; + КонецЕсли; + ОписанияПолей.Добавить(ОписаниеПоля); КонецЦикла; @@ -64,6 +99,7 @@ КонтейнерАннотаций = Новый КонтейнерАннотаций; КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияЗначениеПоУмолчанию")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); + КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); КонецПроцедуры 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..c8b894f --- /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,24 @@ +Перем _ИмяКласса; + +// Возвращает имя аннотации +// +// Возвращаемое значение: +// Строка - имя аннотации +// +Функция ИмяАннотации() Экспорт + Возврат "ВложенныйДТО"; +КонецФункции + +// Возвращает имя класса вложенного DTO +// +// Возвращаемое значение: +// Строка - имя класса +// +Функция ИмяКласса() Экспорт + Возврат _ИмяКласса; +КонецФункции + +&Аннотация("ВложенныйДТО") +Процедура ПриСозданииОбъекта(ИмяКласса) Экспорт + _ИмяКласса = ИмяКласса; +КонецПроцедуры 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 1ddb273..a86094c 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" @@ -356,21 +356,30 @@ КлючКонфигурации = ОписаниеПоля.КлючКонфигурации(); ПолныйКлюч = Префикс + "." + КлючКонфигурации; - ЗначениеИзКонфига = Параметр(ПолныйКлюч); + Если ОписаниеПоля.ЭтоВложенныйДТО() Тогда - Если Не ЗначениеИзКонфига = Неопределено Тогда + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ЗаполнитьВложенныйДТО(ОписаниеПоля, ПолныйКлюч); - Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); - ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); + Иначе - ИначеЕсли Не ОписаниеПоля.Обязательное Тогда + ЗначениеИзКонфига = Параметр(ПолныйКлюч); - Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); - ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); + Если Не ЗначениеИзКонфига = Неопределено Тогда - Иначе + Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); + + ИначеЕсли Не ОписаниеПоля.Обязательное Тогда + + Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); + ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); - ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ОписаниеПоля.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + Иначе + + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ОписаниеПоля.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + + КонецЕсли; КонецЕсли; @@ -382,6 +391,48 @@ #Область Вспомогательные_процедуры_и_функции +Процедура ЗаполнитьВложенныйДТО(Знач ОписаниеПоля, Знач Префикс) + + ВложенныйОбъект = ОписаниеПоля.ВложенныйОбъект; + ВложенныеОписания = ОписаниеПоля.ОписанияВложенныхПолей; + ОбъектРефлектор = Новый Рефлектор; + + Для Каждого ВложенноеОписание Из ВложенныеОписания Цикл + + ВложенныйКлюч = ВложенноеОписание.КлючКонфигурации(); + ПолныйКлюч = Префикс + "." + ВложенныйКлюч; + + Если ВложенноеОписание.ЭтоВложенныйДТО() Тогда + + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ВложенноеОписание.Имя, ПолныйКлюч); + ЗаполнитьВложенныйДТО(ВложенноеОписание, ПолныйКлюч); + + Иначе + + ЗначениеИзКонфига = Параметр(ПолныйКлюч); + + Если Не ЗначениеИзКонфига = Неопределено Тогда + + Лог.Отладка("Установка вложенного свойства <%1> из параметра <%2>", ВложенноеОписание.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ЗначениеИзКонфига); + + ИначеЕсли Не ВложенноеОписание.Обязательное Тогда + + Лог.Отладка("Вложенное свойство <%1> не найдено в конфиге, используется значение по умолчанию", ВложенноеОписание.Имя); + ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ВложенноеОписание.ЗначениеПоУмолчанию); + + Иначе + + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ВложенноеОписание.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + + КонецЕсли; + + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + Функция ПолучитьКонструкторПараметров() Конструктор = Новый КонструкторПараметров(); 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\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..f4292f6 --- /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,10 @@ +// 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..2c8f717 --- /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") +Перем ИмяБазы Экспорт; + +&ВложенныйДТО("ТестовоеПодключение") +Перем Подключение Экспорт; + +Процедура ПриСозданииОбъекта() + +КонецПроцедуры 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..b66341e --- /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,13 @@ +// 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/\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" index 3e91c73..48105e0 100644 --- "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" @@ -20,6 +20,10 @@ ИменаТестов.Добавить("ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча"); ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля"); ИменаТестов.Добавить("ТестДолжен_ИспользоватьСинонимПриЗаполнении"); + ИменаТестов.Добавить("ТестДолжен_ЗаполнитьВложенныйДТО"); + ИменаТестов.Добавить("ТестДолжен_ЗаполнитьДвухуровневыйДТО"); + ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); Возврат ИменаТестов; @@ -195,6 +199,118 @@ КонецПроцедуры +// КП-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"); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + Функция ПолучитьВременныйФайлJSON(Знач СодержимоеJSON) ВременныйФайл = ПолучитьИмяВременногоФайла("json"); From 4a6d0878f46283e4c9d9ab873a53df360c6f970f Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Sat, 11 Apr 2026 02:21:30 +0300 Subject: [PATCH 04/25] =?UTF-8?q?=D0=92=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D1=81=D1=85=D0=B5=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 1 + ...20\275\320\275\321\213\320\271\320\224\320\242\320\236.os" | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packagedef b/packagedef index ca63b73..1d888ec 100644 --- a/packagedef +++ b/packagedef @@ -29,4 +29,5 @@ .ОпределяетКласс("КонструкторПараметров", "src/Классы/КонструкторПараметров.os") .ОпределяетКласс("АннотацияЗначениеПоУмолчанию", "src/Классы/АннотацияЗначениеПоУмолчанию.os") .ОпределяетКласс("АннотацияСиноним", "src/Классы/АннотацияСиноним.os") + .ОпределяетКласс("АннотацияВложенныйДТО", "src/Классы/АннотацияВложенныйДТО.os") ; 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" index c8b894f..628a16a 100644 --- "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" @@ -19,6 +19,6 @@ КонецФункции &Аннотация("ВложенныйДТО") -Процедура ПриСозданииОбъекта(ИмяКласса) Экспорт - _ИмяКласса = ИмяКласса; +Процедура ПриСозданииОбъекта(Значение) Экспорт + _ИмяКласса = Значение; КонецПроцедуры From 1b258be363efc197cfa2782d4075a1e1286be84f Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 13:54:54 +0300 Subject: [PATCH 05/25] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 2 +- ...20\273\321\214\320\224\320\242\320\236.os" | 44 +++++-------------- ...21\213\320\271\320\224\320\242\320\236.os" | 14 +----- ...20\265\321\202\321\200\320\276\320\262.os" | 16 ++++--- ...20\260\320\275\320\275\321\213\321\205.os" | 1 + ...21\202\320\260\321\206\320\270\321\217.os" | 4 +- ...20\266\320\265\320\275\320\270\320\265.os" | 1 + 7 files changed, 27 insertions(+), 55 deletions(-) diff --git a/packagedef b/packagedef index 1d888ec..8cd5dda 100644 --- a/packagedef +++ b/packagedef @@ -19,7 +19,7 @@ .ЗависитОт("tempfiles") .ЗависитОт("1connector", "2.3.3") .ЗависитОт("reflector", "0.3.1") - .ЗависитОт("annotations") + .ЗависитОт("annotations", "1.3.1") .РазработкаЗависитОт("coverage", "0.6.1") .РазработкаЗависитОт("1testrunner", "1.9.2") .РазработкаЗависитОт("1bdd", "1.14.0") 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" index 7b1543a..6372baa 100644 --- "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" @@ -1,5 +1,6 @@ #Использовать annotations #Использовать reflector +#Использовать logos Перем КонтейнерАннотаций; Перем Лог; @@ -49,43 +50,20 @@ ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда - ВложенныйОбъект = Новый(ОбъектАннотации.ИмяКласса()); - ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); - ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); - СтандартныйРефлектор = Новый Рефлектор; - СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); + ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); + + Если ВложенныйОбъект <> Неопределено Тогда + ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); + ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); + Иначе + Лог.Предупреждение("Свойство <%1> помечено как вложенный DTO, но не инициализировано в конструкторе", Свойство.Имя); + КонецЕсли; КонецЕсли; КонецЦикла; - // Проверяем, является ли текущее значение свойства вложенным DTO-объектом - // (если аннотация &ВложенныйДТО не была задана явно) - Если Не ОписаниеПоля.ЭтоВложенныйДТО() Тогда - Попытка - СтандартныйРефлектор = Новый Рефлектор; - ЗначениеСвойства = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); - Если Не ЗначениеСвойства = Неопределено Тогда - ВложенныйРефлектор = Новый РефлекторОбъекта(ЗначениеСвойства); - ВложенныеСвойства = ВложенныйРефлектор.ПолучитьТаблицуСвойств(Неопределено, Истина); - ЕстьЭкспортныеСвойства = Ложь; - Для Каждого ВлСвойство Из ВложенныеСвойства Цикл - Если ВлСвойство.Экспорт Тогда - ЕстьЭкспортныеСвойства = Истина; - Прервать; - КонецЕсли; - КонецЦикла; - Если ЕстьЭкспортныеСвойства Тогда - ВложенныеОписания = ОписатьКласс(ЗначениеСвойства); - ОписаниеПоля.УстановитьВложенныйОбъект(ЗначениеСвойства, ВложенныеОписания); - КонецЕсли; - КонецЕсли; - Исключение - // Значение недоступно или не является объектом — пропускаем - КонецПопытки; - КонецЕсли; - ОписанияПолей.Добавить(ОписаниеПоля); КонецЦикла; @@ -101,6 +79,6 @@ КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); -КонецПроцедуры + Лог = Логирование.ПолучитьЛог("oscript.lib.configor.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" index 628a16a..54dcb7a 100644 --- "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" @@ -1,5 +1,3 @@ -Перем _ИмяКласса; - // Возвращает имя аннотации // // Возвращаемое значение: @@ -9,16 +7,6 @@ Возврат "ВложенныйДТО"; КонецФункции -// Возвращает имя класса вложенного DTO -// -// Возвращаемое значение: -// Строка - имя класса -// -Функция ИмяКласса() Экспорт - Возврат _ИмяКласса; -КонецФункции - &Аннотация("ВложенныйДТО") -Процедура ПриСозданииОбъекта(Значение) Экспорт - _ИмяКласса = Значение; +Процедура ПриСозданииОбъекта() Экспорт КонецПроцедуры 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 a86094c..8587573 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" @@ -322,15 +322,19 @@ // Процедура ЗарегистрироватьКлассПараметров(Знач КлассОбъект, Знач Префикс) Экспорт - Если ВнутреннийОписательДТО = Неопределено Тогда - ВнутреннийОписательДТО = Новый ОписательДТО; - КонецЕсли; - ОписанияПолей = ВнутреннийОписательДТО.ОписатьКласс(КлассОбъект); Лог.Отладка("Зарегистрирован класс параметров с префиксом <%1>, полей: %2", Префикс, ОписанияПолей.Количество()); - РеестрКлассовПараметров.Вставить(Префикс, ОписанияПолей); + СуществующиеОписания = РеестрКлассовПараметров[Префикс]; + + Если СуществующиеОписания <> Неопределено Тогда + Для Каждого Описание Из ОписанияПолей Цикл + СуществующиеОписания.Добавить(Описание); + КонецЦикла; + Иначе + РеестрКлассовПараметров.Вставить(Префикс, ОписанияПолей); + КонецЕсли; КонецПроцедуры @@ -590,7 +594,7 @@ ВнутреннийКонструкторПараметров = Неопределено; ИспользуетсяКонструкторПараметров = Ложь; - ВнутреннийОписательДТО = Неопределено; + ВнутреннийОписательДТО = Новый ОписательДТО; РеестрКлассовПараметров = Новый Соответствие; КонецПроцедуры 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" index f4292f6..32a3650 100644 --- "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" @@ -3,6 +3,7 @@ &ЗначениеПоУмолчанию("main_db") Перем ИмяБазы Экспорт; +&ВложенныйДТО Перем Подключение Экспорт; Процедура ПриСозданииОбъекта() 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" index 2c8f717..6d7108b 100644 --- "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" @@ -4,9 +4,9 @@ &ЗначениеПоУмолчанию("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\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" index b66341e..40ceb7d 100644 --- "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" @@ -6,6 +6,7 @@ &ЗначениеПоУмолчанию("1.0.0") Перем Версия Экспорт; +&ВложенныйДТО Перем БазаДанных Экспорт; Процедура ПриСозданииОбъекта() From e74efa27162d192a4c5105dc7f6fdf3344b244e9 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 13:55:24 +0300 Subject: [PATCH 06/25] bump annotations version --- packagedef | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packagedef b/packagedef index 8cd5dda..89759cc 100644 --- a/packagedef +++ b/packagedef @@ -19,7 +19,7 @@ .ЗависитОт("tempfiles") .ЗависитОт("1connector", "2.3.3") .ЗависитОт("reflector", "0.3.1") - .ЗависитОт("annotations", "1.3.1") + .ЗависитОт("annotations", "1.4.0") .РазработкаЗависитОт("coverage", "0.6.1") .РазработкаЗависитОт("1testrunner", "1.9.2") .РазработкаЗависитОт("1bdd", "1.14.0") From 173e303d7a8cfaf5f66b99da03bf2faf051fee8a Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 14:42:57 +0300 Subject: [PATCH 07/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BA=D0=B0=D1=81=D1=82=20=D1=82=D0=B8=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 1 + ...20\273\321\217\320\224\320\242\320\236.os" | 11 +- ...20\273\321\214\320\224\320\242\320\236.os" | 5 + ...20\274\320\265\321\202\321\200\320\260.os" | 24 ++++ ...20\265\321\202\321\200\320\276\320\262.os" | 51 +++++++ ...20\270\320\277\320\260\320\274\320\270.os" | 13 ++ ...20\270\320\277\320\260\320\274\320\270.os" | 11 ++ ...20\270\320\277\320\260\320\274\320\270.os" | 16 +++ ...3\321\214\320\224\320\242\320\236_test.os" | 132 ++++++++++++++++++ 9 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 "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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.os" create mode 100644 "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" create mode 100644 "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" create mode 100644 "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" diff --git a/packagedef b/packagedef index 89759cc..deea22b 100644 --- a/packagedef +++ b/packagedef @@ -29,5 +29,6 @@ .ОпределяетКласс("КонструкторПараметров", "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" index 313d4d7..cdeb01e 100644 --- "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" @@ -35,10 +35,15 @@ ЗначениеПоУмолчанию = Значение; Обязательное = Ложь; - Если Не Значение = Неопределено Тогда - Тип = ТипЗнч(Значение); - КонецЕсли; +КонецПроцедуры +// Устанавливает тип значения явно (из аннотации &Тип) +// +// Параметры: +// НовыйТип - Тип - целевой тип значения +// +Процедура УстановитьТип(Знач НовыйТип) Экспорт + Тип = НовыйТип; КонецПроцедуры // Устанавливает синоним поля 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" index 6372baa..eb7fe64 100644 --- "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" @@ -48,6 +48,10 @@ ОписаниеПоля.УстановитьСиноним(ОбъектАннотации.Синоним()); + ИначеЕсли ИмяАннотации = "ТИП" Тогда + + ОписаниеПоля.УстановитьТип(ОбъектАннотации.ТипЗначения()); + ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда СтандартныйРефлектор = Новый Рефлектор; @@ -77,6 +81,7 @@ КонтейнерАннотаций = Новый КонтейнерАннотаций; КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияЗначениеПоУмолчанию")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); + КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияТипПараметра")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); Лог = Логирование.ПолучитьЛог("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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.os" new file mode 100644 index 0000000..b220e65 --- /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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.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 8587573..c1de7c0 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" @@ -371,6 +371,7 @@ Если Не ЗначениеИзКонфига = Неопределено Тогда + ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ОписаниеПоля); Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); @@ -417,6 +418,7 @@ Если Не ЗначениеИзКонфига = Неопределено Тогда + ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ВложенноеОписание); Лог.Отладка("Установка вложенного свойства <%1> из параметра <%2>", ВложенноеОписание.Имя, ПолныйКлюч); ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ЗначениеИзКонфига); @@ -445,6 +447,55 @@ КонецФункции +Функция ПривестиЗначениеКТипу(Знач Значение, Знач ОписаниеПоля) + + ЦелевойТип = ОписаниеПоля.Тип; + + Если ЦелевойТип = Неопределено Тогда + Возврат Значение; + КонецЕсли; + + Если ТипЗнч(Значение) = ЦелевойТип Тогда + Возврат Значение; + КонецЕсли; + + Попытка + + Если ЦелевойТип = Тип("Дата") Тогда + // ОписаниеТипов не парсит ISO-формат + Возврат XMLЗначение(Тип("Дата"), Значение); + Иначе + + // Для булево: "1"/"0" не поддерживаются ОписаниеТипов — подменяем + Если ЦелевойТип = Тип("Булево") И ТипЗнч(Значение) = Тип("Строка") Тогда + СтрЗначение = СокрЛП(Значение); + Если СтрЗначение = "1" Тогда + Возврат Истина; + ИначеЕсли СтрЗначение = "0" Тогда + Возврат Ложь; + КонецЕсли; + КонецЕсли; + + ОписаниеТипов = Новый ОписаниеТипов(Строка(ЦелевойТип)); + Результат = ОписаниеТипов.ПривестиЗначение(Значение); + + // ОписаниеТипов.ПривестиЗначение не бросает ошибку для невалидных строк + Если ЦелевойТип = Тип("Число") И ТипЗнч(Значение) = Тип("Строка") Тогда + Если Результат = 0 И СокрЛП(Значение) <> "0" Тогда + ВызватьИсключение "Невалидное числовое значение"; + КонецЕсли; + КонецЕсли; + + Возврат Результат; + КонецЕсли; + + Исключение + ВызватьИсключение "Не удалось привести значение параметра """ + + ОписаниеПоля.Имя + """ (" + Значение + ") к типу " + ЦелевойТип; + КонецПопытки; + +КонецФункции + Процедура ВыполнитьЧтениеПровайдеров() КоллекцияПровайдеров = Новый ПроцессорКоллекций; 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\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\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" index 48105e0..fe4c9f6 100644 --- "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" @@ -24,6 +24,11 @@ ИменаТестов.Добавить("ТестДолжен_ЗаполнитьДвухуровневыйДТО"); ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа"); + ИменаТестов.Добавить("ТестДолжен_ПривестиТипыВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_НеПриводитьЕслиТипСовпадает"); Возврат ИменаТестов; @@ -311,6 +316,133 @@ КонецПроцедуры +// КП-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"); From d6d549b4cbd07ab2a78a6973a4c84d29dc3fc771 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 15:01:43 +0300 Subject: [PATCH 08/25] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BB=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=80=D0=B5=D0=B5=D1=81=D1=82=D1=80=D0=B0=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\265\321\202\321\200\320\276\320\262.os" | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) 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 c1de7c0..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" @@ -20,7 +20,7 @@ Перем ИспользуетсяКонструкторПараметров; // булево Перем ВнутреннийОписательДТО; // Класс ОписательДТО -Перем РеестрКлассовПараметров; // Соответствие: Префикс -> ОписанияПолей (Массив ОписаниеПоляДТО) +Перем РеестрКлассовПараметров; // Соответствие: КлассОбъект -> Структура(Префикс, ОписанияПолей) Перем Лог; // Логгер @@ -326,15 +326,8 @@ Лог.Отладка("Зарегистрирован класс параметров с префиксом <%1>, полей: %2", Префикс, ОписанияПолей.Количество()); - СуществующиеОписания = РеестрКлассовПараметров[Префикс]; - - Если СуществующиеОписания <> Неопределено Тогда - Для Каждого Описание Из ОписанияПолей Цикл - СуществующиеОписания.Добавить(Описание); - КонецЦикла; - Иначе - РеестрКлассовПараметров.Вставить(Префикс, ОписанияПолей); - КонецЕсли; + Запись = Новый Структура("Префикс, ОписанияПолей", Префикс, ОписанияПолей); + РеестрКлассовПараметров.Вставить(КлассОбъект, Запись); КонецПроцедуры @@ -347,12 +340,14 @@ // Процедура ЗаполнитьОбъектПараметров(Знач КлассОбъект, Знач Префикс) Экспорт - ОписанияПолей = РеестрКлассовПараметров[Префикс]; + Запись = РеестрКлассовПараметров[КлассОбъект]; - Если ОписанияПолей = Неопределено Тогда + Если Запись = Неопределено Тогда ВызватьИсключение "Класс параметров с префиксом """ + Префикс + """ не зарегистрирован"; КонецЕсли; + ОписанияПолей = Запись.ОписанияПолей; + ОбъектРефлектор = Новый Рефлектор; Для Каждого ОписаниеПоля Из ОписанияПолей Цикл From 7006b6f8e2db40aa42c1dbf952169b5e810f8cfc Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 15:34:30 +0300 Subject: [PATCH 09/25] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BE?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D0=BE=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B0=D1=8E=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=BE=20=D0=BF=D0=BE=20=D0=B8=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 1 - ...20\273\321\214\320\224\320\242\320\236.os" | 11 +++++---- ...20\274\320\265\321\202\321\200\320\260.os" | 24 ------------------- 3 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 "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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.os" diff --git a/packagedef b/packagedef index deea22b..89759cc 100644 --- a/packagedef +++ b/packagedef @@ -29,6 +29,5 @@ .ОпределяетКласс("КонструкторПараметров", "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\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" index eb7fe64..8a3d5ef 100644 --- "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" @@ -32,6 +32,12 @@ ИмяАннотации = Врег(Аннотация.Имя); + Если ИмяАннотации = "ТИП" Тогда + ЗначениеТипа = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(Аннотация); + ОписаниеПоля.УстановитьТип(Тип(ЗначениеТипа)); + Продолжить; + КонецЕсли; + ОпределениеАннотации = КонтейнерАннотаций.ПолучитьОпределениеАннотации(Аннотация.Имя); Если ОпределениеАннотации = Неопределено Тогда @@ -48,10 +54,6 @@ ОписаниеПоля.УстановитьСиноним(ОбъектАннотации.Синоним()); - ИначеЕсли ИмяАннотации = "ТИП" Тогда - - ОписаниеПоля.УстановитьТип(ОбъектАннотации.ТипЗначения()); - ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда СтандартныйРефлектор = Новый Рефлектор; @@ -81,7 +83,6 @@ КонтейнерАннотаций = Новый КонтейнерАннотаций; КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияЗначениеПоУмолчанию")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); - КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияТипПараметра")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); Лог = Логирование.ПолучитьЛог("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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.os" deleted file mode 100644 index b220e65..0000000 --- "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\242\320\270\320\277\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\260.os" +++ /dev/null @@ -1,24 +0,0 @@ -Перем _Тип; - -// Возвращает имя аннотации -// -// Возвращаемое значение: -// Строка - имя аннотации -// -Функция ИмяАннотации() Экспорт - Возврат "Тип"; -КонецФункции - -// Возвращает тип значения -// -// Возвращаемое значение: -// Тип - целевой тип значения -// -Функция ТипЗначения() Экспорт - Возврат _Тип; -КонецФункции - -&Аннотация("Тип") -Процедура ПриСозданииОбъекта(Значение) Экспорт - _Тип = Тип(Значение); -КонецПроцедуры From 230c2b2a1dc3a02203070096245b464a9e2090e2 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 15:51:59 +0300 Subject: [PATCH 10/25] fix --- ...\320\275\320\275\321\213\320\271\320\224\320\242\320\236.os" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" index 54dcb7a..be38540 100644 --- "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" @@ -8,5 +8,5 @@ КонецФункции &Аннотация("ВложенныйДТО") -Процедура ПриСозданииОбъекта() Экспорт +Процедура ПриСозданииОбъекта(Значение = "") Экспорт КонецПроцедуры From 6e867f6d2e2f1f370c6c31338d7f48ea477ac669 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 13 Apr 2026 16:04:35 +0300 Subject: [PATCH 11/25] =?UTF-8?q?=D0=98=D0=BD=D0=B8=D1=86=D0=B8=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D0=BB=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B4=D1=82=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\265\320\273\321\214\320\224\320\242\320\236.os" | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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" index 8a3d5ef..cafa1cf 100644 --- "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" @@ -59,11 +59,19 @@ СтандартныйРефлектор = Новый Рефлектор; ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); + Если ВложенныйОбъект = Неопределено Тогда + ИмяКлассаДТО = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(Аннотация, , ""); + Если ИмяКлассаДТО <> "" Тогда + ВложенныйОбъект = Новый(ИмяКлассаДТО); + СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); + Иначе + Лог.Предупреждение("Свойство <%1> помечено как вложенный DTO, но не инициализировано в конструкторе", Свойство.Имя); + КонецЕсли; + КонецЕсли; + Если ВложенныйОбъект <> Неопределено Тогда ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); - Иначе - Лог.Предупреждение("Свойство <%1> помечено как вложенный DTO, но не инициализировано в конструкторе", Свойство.Имя); КонецЕсли; КонецЕсли; From 0957c339b63fd154c611da65c8e3959f4108caf0 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 8 Jun 2026 15:02:29 +0300 Subject: [PATCH 12/25] =?UTF-8?q?=20=D1=84=D0=B8=D0=BA=D1=81=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B1=D0=B5=D0=B9=D0=B7?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 2 +- ...3\321\214\320\224\320\242\320\236_test.os" | 80 +++++++++++-------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/packagedef b/packagedef index 89759cc..836c7b7 100644 --- a/packagedef +++ b/packagedef @@ -19,7 +19,7 @@ .ЗависитОт("tempfiles") .ЗависитОт("1connector", "2.3.3") .ЗависитОт("reflector", "0.3.1") - .ЗависитОт("annotations", "1.4.0") + .ЗависитОт("annotations", "1.3.0") .РазработкаЗависитОт("coverage", "0.6.1") .РазработкаЗависитОт("1testrunner", "1.9.2") .РазработкаЗависитОт("1bdd", "1.14.0") 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" index fe4c9f6..0aba529 100644 --- "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" @@ -46,8 +46,9 @@ ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {}}"); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); // Act @@ -70,8 +71,9 @@ ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""АдресСервера"": ""test""}}"); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); Менеджер.Прочитать(); @@ -95,8 +97,9 @@ ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""port"": 3306}}"); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); // Act @@ -120,8 +123,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); @@ -143,8 +147,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); @@ -166,8 +171,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); @@ -192,8 +198,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); @@ -214,8 +221,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); // Act @@ -241,8 +249,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Приложение"); // Act @@ -270,8 +279,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); // Act @@ -297,8 +307,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Кластер"); // Act @@ -327,8 +338,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); @@ -350,8 +362,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); @@ -373,8 +386,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); @@ -400,8 +414,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "db"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "db"); @@ -428,8 +443,9 @@ ); Менеджер = Новый МенеджерПараметров; - Менеджер.ИспользоватьПровайдерJSON(); - Менеджер.УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); From 5a071acacff597383d241f3b8b39d9cd4bd88d0e Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Thu, 9 Jul 2026 10:53:27 +0300 Subject: [PATCH 13/25] =?UTF-8?q?=D0=B1=D0=B0=D0=BC=D0=BF=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packagedef | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packagedef b/packagedef index 836c7b7..52360ce 100644 --- a/packagedef +++ b/packagedef @@ -4,7 +4,7 @@ // Описание.Имя("configor") - .Версия("0.12.0") + .Версия("0.13.0") .Автор("Khorev Aleksey") .АдресАвтора("Khorevaa@gmail.com") .Описание("Библиотека для работы с конфигурационными файлами в формате json, yaml") @@ -12,7 +12,7 @@ .ВключитьФайл("src") .ВключитьФайл("docs") .ЗависитОт("logos") - .ЗависитОт("oscript-yaml") + .ЗависитОт("oscript-yaml", "0.3.0") .ЗависитОт("asserts") .ЗависитОт("fluent", "0.6.0") .ЗависитОт("notify", "0.2.0") From 00140665fdc9d1f0c546c741ea2c01ddb661d7ab Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Thu, 9 Jul 2026 13:28:58 +0300 Subject: [PATCH 14/25] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B0=20=D0=B4=D0=BB=D1=8F=202.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\320\274\320\265\321\202\321\200\320\276\320\262_test.os" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\247\321\202\320\265\320\275\320\270\321\217\320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262_test.os" "b/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\247\321\202\320\265\320\275\320\270\321\217\320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262_test.os" index 96f5936..f3f1bf6 100644 --- "a/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\247\321\202\320\265\320\275\320\270\321\217\320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262_test.os" +++ "b/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\247\321\202\320\265\320\275\320\270\321\217\320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262_test.os" @@ -63,12 +63,12 @@ СтруктураПараметров = Новый Структура("version, ПараметрСтрока, ПараметрДата, ПараметрЧисло, ПараметрМассив, ПараметрСтруктура, ПараметрСоответствие", "1.0", "ПростоСтрока", - ТекущаяДата(), + Дата(2017, 1, 1, 12, 30, 45), 10, ПолучитьНовыйМассив("Элемент1, Элемент2, Элемент3"), Новый Структура("Строка, Дата, Число, Массив, Структура, Соответствие", "ПростоСтрока", - ТекущаяДата(), + Дата(2017, 1, 1, 12, 30, 45), 10, ПолучитьНовыйМассив("Элемент1, Элемент2, Элемент3"), Новый Структура("Ключ1, Ключ2", "ЗначениеСтруктуры", "ЗначениеСтруктуры2"), From 39ad7de84c61b5df7c887958a739b49f457f98ca Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:16:12 +0200 Subject: [PATCH 15/25] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B4=D0=BB=D0=B8=D0=BD=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=B5=20=D0=BD=D0=B0=20=D0=B4=D0=B5=D1=84?= =?UTF-8?q?=D0=B8=D1=81=D1=8B=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bsl-language-server помечает символ U+2014 как ошибку InvalidCharacterInFile. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...320\276\320\273\321\217\320\224\320\242\320\236.os" | 2 +- ...65\320\273\321\214\320\224\320\242\320\236_test.os" | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) 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" index cdeb01e..bea6f97 100644 --- "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" @@ -73,7 +73,7 @@ КонецФункции // Возвращает ключ для поиска в конфигурации -// Если задан синоним — возвращает его, иначе — имя свойства +// Если задан синоним - возвращает его, иначе - имя свойства // // Возвращаемое значение: // Строка - ключ для поиска в конфигурации 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" index 0aba529..71a9745 100644 --- "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" @@ -161,7 +161,7 @@ КонецПроцедуры -// КП-1.6: Обязательное поле без значения — исключение +// КП-1.6: Обязательное поле без значения - исключение Процедура ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); @@ -211,7 +211,7 @@ КонецПроцедуры -// КП-2.1: Вложенный DTO — БазаДанных.Подключение +// КП-2.1: Вложенный DTO - БазаДанных.Подключение Процедура ТестДолжен_ЗаполнитьВложенныйДТО() Экспорт // Arrange @@ -239,7 +239,7 @@ КонецПроцедуры -// КП-2.2: Трёхуровневый DTO — Приложение.БазаДанных.Подключение +// КП-2.2: Трёхуровневый DTO - Приложение.БазаДанных.Подключение Процедура ТестДолжен_ЗаполнитьДвухуровневыйДТО() Экспорт // Arrange @@ -269,7 +269,7 @@ КонецПроцедуры -// КП-2.3: Вложенный DTO — умолчания при отсутствии ключей во вложенной структуре +// КП-2.3: Вложенный DTO - умолчания при отсутствии ключей во вложенной структуре Процедура ТестДолжен_ПрименитьУмолчанияВоВложенномДТО() Экспорт // Arrange @@ -437,7 +437,7 @@ ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); - // JSON провайдер вернёт число как число — приведение не нужно + // JSON провайдер вернёт число как число - приведение не нужно ВременныйФайл = ПолучитьВременныйФайлJSON( "{""cfg"": {""Порт"": 8080, ""Включен"": true, ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" ); From 4c7c84982d1c0c0cb35d075a03017262be5c8641 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:16:56 +0200 Subject: [PATCH 16/25] =?UTF-8?q?=D0=91=D1=83=D0=BB=D0=B5=D0=B2=D0=BE=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=B2=D0=BE=D0=B4=D0=B8=D1=82=D1=81=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=8F=D0=B2=D0=BD=D0=BE=D0=BC=D1=83=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D1=83=20=D0=B4=D0=BE=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D1=8B=D1=85=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ОписаниеТипов.ПривестиЗначение возвращает Ложь для любой нераспознанной строки, поэтому "yes", "on" и опечатки вроде "treu" молча превращались в Ложь вместо ошибки. Значения перечислены явно (1/true/истина/да/yes/on и 0/false/ложь/нет/no/off, регистронезависимо), остальное вызывает исключение с именем поля. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\265\321\202\321\200\320\276\320\262.os" | 33 +++++-- ...3\321\214\320\224\320\242\320\236_test.os" | 96 +++++++++++++++++++ 2 files changed, 122 insertions(+), 7 deletions(-) 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 d51eb3d..9642a3a 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" @@ -442,6 +442,31 @@ КонецФункции +// Разбирает строковое представление булева значения +// Строка, не входящая в список допустимых значений, вызывает исключение +// +// Параметры: +// Значение - Строка - строковое представление булева значения +// +// Возвращаемое значение: +// Булево - разобранное значение +// +Функция ПривестиСтрокуКБулево(Знач Значение) + + СтрЗначение = ВРег(СокрЛП(Значение)); + + Если Не СтрРазделить("1,TRUE,ИСТИНА,ДА,YES,ON", ",").Найти(СтрЗначение) = Неопределено Тогда + Возврат Истина; + КонецЕсли; + + Если Не СтрРазделить("0,FALSE,ЛОЖЬ,НЕТ,NO,OFF", ",").Найти(СтрЗначение) = Неопределено Тогда + Возврат Ложь; + КонецЕсли; + + ВызватьИсключение "Невалидное булево значение"; + +КонецФункции + Функция ПривестиЗначениеКТипу(Знач Значение, Знач ОписаниеПоля) ЦелевойТип = ОписаниеПоля.Тип; @@ -461,14 +486,8 @@ Возврат XMLЗначение(Тип("Дата"), Значение); Иначе - // Для булево: "1"/"0" не поддерживаются ОписаниеТипов — подменяем Если ЦелевойТип = Тип("Булево") И ТипЗнч(Значение) = Тип("Строка") Тогда - СтрЗначение = СокрЛП(Значение); - Если СтрЗначение = "1" Тогда - Возврат Истина; - ИначеЕсли СтрЗначение = "0" Тогда - Возврат Ложь; - КонецЕсли; + Возврат ПривестиСтрокуКБулево(Значение); КонецЕсли; ОписаниеТипов = Новый ОписаниеТипов(Строка(ЦелевойТип)); 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" index 71a9745..0deb666 100644 --- "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" @@ -26,6 +26,8 @@ ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ПривестиАнглийскиеСтрокиКБулево"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевалидномБулево"); ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа"); ИменаТестов.Добавить("ТестДолжен_ПривестиТипыВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_НеПриводитьЕслиТипСовпадает"); @@ -376,6 +378,100 @@ КонецПроцедуры +// Приведение английских строк true/false к булево +Процедура ТестДолжен_ПривестиАнглийскиеСтрокиКБулево() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""true"", ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); + Ожидаем.Что(ДТО.Включен, "Строка ""true"" должна стать Истина").Равно(Истина); + + УдалитьФайлы(ВременныйФайл); + + // та же проверка для "false" + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""false"", ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); + Ожидаем.Что(ДТО.Включен, "Строка ""false"" должна стать Ложь").Равно(Ложь); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// Нераспознанная строка для булево вызывает исключение +Процедура ТестДолжен_ВыброситьИсключениеПриНевалидномБулево() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""maybe"", ""Название"": ""test"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Ожидаем.Что(Ложь, "Ожидалось исключение для невалидного булева значения").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("Включен"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + + // перечисление нескольких допустимых значений через запятую не является допустимым значением + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""1,TRUE"", ""Название"": ""t"", ""ДатаЗапуска"": ""2025-01-01T00:00:00""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Ожидаем.Что(Ложь, "Ожидалось исключение для значения ""1,TRUE""").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("Включен"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + // КП-3.3: Ошибка при невозможности приведения типа Процедура ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа() Экспорт From 5f7a26dc7fae788185c9c83dcb289bcc4b998f13 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:17:20 +0200 Subject: [PATCH 17/25] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=BB=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D1=91=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D0=BE=D0=B4=D0=B8=D0=BD=20=D1=80=D0=B0=D0=B7=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Экземпляр создавался внутри цикла по свойствам, на каждое поле с аннотацией ВложенныйДТО. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...321\202\320\265\320\273\321\214\320\224\320\242\320\236.os" | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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" index cafa1cf..6407b58 100644 --- "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" @@ -18,6 +18,8 @@ РефлекторОбъекта = Новый РефлекторОбъекта(КлассОбъект); Свойства = РефлекторОбъекта.ПолучитьТаблицуСвойств(Неопределено, Истина); + СтандартныйРефлектор = Новый Рефлектор; + ОписанияПолей = Новый Массив; Для Каждого Свойство Из Свойства Цикл @@ -56,7 +58,6 @@ ИначеЕсли ИмяАннотации = "ВЛОЖЕННЫЙДТО" Тогда - СтандартныйРефлектор = Новый Рефлектор; ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); Если ВложенныйОбъект = Неопределено Тогда From 40593ef85d82cc9ec751b41d02a4d425e295ab2f Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:17:58 +0200 Subject: [PATCH 18/25] =?UTF-8?q?=D0=98=D0=BC=D1=8F=20=D0=BA=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=81=D0=B0=20=D0=B2=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20DTO=20=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BE=D0=B1?= =?UTF-8?q?=D1=8A=D0=B5=D0=BA=D1=82=20=D0=B0=D0=BD=D0=BD=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit АннотацияВложенныйДТО не хранила параметр и не имела аксессора: объект аннотации создавался, но значение бралось мимо него, напрямую через РаботаСАннотациями. Класс приведён к виду АннотацияСиноним и АннотацияЗначениеПоУмолчанию - хранит имя и отдаёт его через ИмяКлассаДТО(). Ветка создания вложенного объекта по имени класса не была покрыта: фикстура ТестовыйДТОБазаДанныхАннотация вопреки своему комментарию инициализировала объект в конструкторе и не использовалась ни одним тестом. Фикстура приведена в соответствие назначению, добавлен тест. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\273\321\214\320\224\320\242\320\236.os" | 2 +- ...21\213\320\271\320\224\320\242\320\236.os" | 14 +++++++ ...21\202\320\260\321\206\320\270\321\217.os" | 7 ++-- ...3\321\214\320\224\320\242\320\236_test.os" | 39 +++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) 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" index 6407b58..ce67ff8 100644 --- "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" @@ -61,7 +61,7 @@ ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); Если ВложенныйОбъект = Неопределено Тогда - ИмяКлассаДТО = РаботаСАннотациями.ПолучитьЗначениеПараметраАннотации(Аннотация, , ""); + ИмяКлассаДТО = ОбъектАннотации.ИмяКлассаДТО(); Если ИмяКлассаДТО <> "" Тогда ВложенныйОбъект = Новый(ИмяКлассаДТО); СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); 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" index be38540..c1b37ca 100644 --- "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" @@ -1,3 +1,5 @@ +Перем _ИмяКлассаДТО; // Строка - имя класса вложенного DTO, пустая строка если не указано + // Возвращает имя аннотации // // Возвращаемое значение: @@ -7,6 +9,18 @@ Возврат "ВложенныйДТО"; КонецФункции +// Возвращает имя класса вложенного DTO +// Пустая строка означает, что класс не указан и объект +// должен быть создан в конструкторе владеющего класса +// +// Возвращаемое значение: +// Строка - имя класса вложенного DTO +// +Функция ИмяКлассаДТО() Экспорт + Возврат _ИмяКлассаДТО; +КонецФункции + &Аннотация("ВложенныйДТО") Процедура ПриСозданииОбъекта(Значение = "") Экспорт + _ИмяКлассаДТО = ?(Значение = Неопределено, "", Значение); КонецПроцедуры 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" index 6d7108b..0580c30 100644 --- "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" @@ -1,12 +1,13 @@ // DTO второго уровня: то же самое, что ТестовыйДТОБазаДанных, -// но вложенный DTO указан через аннотацию &ВложенныйДТО вместо Новый в конструкторе. +// но вложенный DTO НЕ инициализируется в конструкторе - его создаёт +// ОписательДТО по имени класса, указанному параметром аннотации &ВложенныйДТО. +// Имя класса регистрируется тестом через ПодключитьСценарий. &ЗначениеПоУмолчанию("main_db") Перем ИмяБазы Экспорт; -&ВложенныйДТО +&ВложенныйДТО("ТестовоеПодключениеКласс") Перем Подключение Экспорт; Процедура ПриСозданииОбъекта() - Подключение = ЗагрузитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "ТестовоеПодключение.os")); КонецПроцедуры 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" index 0deb666..6673073 100644 --- "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" @@ -23,6 +23,7 @@ ИменаТестов.Добавить("ТестДолжен_ЗаполнитьВложенныйДТО"); ИменаТестов.Добавить("ТестДолжен_ЗаполнитьДвухуровневыйДТО"); ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); + ИменаТестов.Добавить("ТестДолжен_СоздатьВложенныйДТОПоИмениКлассаИзАннотации"); ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); @@ -299,6 +300,44 @@ КонецПроцедуры +// Вложенный DTO создаётся по имени класса из параметра аннотации &ВложенныйДТО, +// когда свойство не инициализировано в конструкторе +Процедура ТестДолжен_СоздатьВложенныйДТОПоИмениКлассаИзАннотации() Экспорт + + // Arrange + // имя класса, на которое ссылается аннотация в фикстуре, должно быть доступно для Новый() + ПодключитьСценарий(ОбъединитьПути(КаталогФикстур, "ТестовоеПодключение.os"), "ТестовоеПодключениеКласс"); + + ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанныхАннотация.os"); + Ожидаем.Что(ДТО.Подключение, "До регистрации вложенный объект не создан").Равно(Неопределено); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""БД"": {""ИмяБазы"": ""test_db"", ""Подключение"": {""Хост"": ""db-server"", ""Порт"": 3306}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + + // Act + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); + + // Assert: объект создан описателем по имени класса из аннотации + Ожидаем.Что(Строка(ТипЗнч(ДТО.Подключение)), "Вложенный объект создан по имени класса из аннотации") + .Равно("ТестовоеПодключениеКласс"); + + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + + Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("test_db"); + Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть заполнен из конфига").Равно("db-server"); + Ожидаем.Что(ДТО.Подключение.Порт, "Вложенный Порт должен быть заполнен из конфига").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + // КП-2.4: DTO с массивом во вложенной структуре Процедура ТестДолжен_ОбработатьМассивВоВложенномДТО() Экспорт From a5f319d596afd3787ef7b37ef2b04a679587533d Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:18:49 +0200 Subject: [PATCH 19/25] =?UTF-8?q?=D0=97=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=B8=20=D0=B2=D0=B5=D1=80=D1=85=D0=BD?= =?UTF-8?q?=D0=B5=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D0=B5=D0=B2=D1=8B=D1=85=20?= =?UTF-8?q?DTO=20=D1=81=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=BE=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D1=83=20=D1=80=D0=B5=D0=BA=D1=83=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ЗаполнитьОбъектПараметров и ЗаполнитьВложенныйДТО отличались только источником объекта и коллекции описаний, остальные ~40 строк совпадали. Тексты логов уже начали расходиться ("Установка свойства" против "Установка вложенного свойства"). Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\265\321\202\321\200\320\276\320\262.os" | 89 ++++++------------- 1 file changed, 29 insertions(+), 60 deletions(-) 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 9642a3a..f71b464 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" @@ -346,44 +346,7 @@ ВызватьИсключение "Класс параметров с префиксом """ + Префикс + """ не зарегистрирован"; КонецЕсли; - ОписанияПолей = Запись.ОписанияПолей; - - ОбъектРефлектор = Новый Рефлектор; - - Для Каждого ОписаниеПоля Из ОписанияПолей Цикл - - КлючКонфигурации = ОписаниеПоля.КлючКонфигурации(); - ПолныйКлюч = Префикс + "." + КлючКонфигурации; - - Если ОписаниеПоля.ЭтоВложенныйДТО() Тогда - - Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ОписаниеПоля.Имя, ПолныйКлюч); - ЗаполнитьВложенныйДТО(ОписаниеПоля, ПолныйКлюч); - - Иначе - - ЗначениеИзКонфига = Параметр(ПолныйКлюч); - - Если Не ЗначениеИзКонфига = Неопределено Тогда - - ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ОписаниеПоля); - Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); - ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ЗначениеИзКонфига); - - ИначеЕсли Не ОписаниеПоля.Обязательное Тогда - - Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); - ОбъектРефлектор.УстановитьСвойство(КлассОбъект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); - - Иначе - - ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ОписаниеПоля.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; - - КонецЕсли; - - КонецЕсли; - - КонецЦикла; + ЗаполнитьПоОписаниям(КлассОбъект, Запись.ОписанияПолей, Префикс); КонецПроцедуры @@ -391,42 +354,48 @@ #Область Вспомогательные_процедуры_и_функции -Процедура ЗаполнитьВложенныйДТО(Знач ОписаниеПоля, Знач Префикс) +// Заполняет свойства объекта по описаниям полей, +// рекурсивно спускаясь во вложенные DTO +// +// Параметры: +// Объект - Объект - заполняемый объект +// ОписанияПолей - Массив из ОписаниеПоляДТО - описания полей этого объекта +// Префикс - Строка - ключ конфигурации, соответствующий текущему уровню вложенности +// +Процедура ЗаполнитьПоОписаниям(Знач Объект, Знач ОписанияПолей, Знач Префикс) - ВложенныйОбъект = ОписаниеПоля.ВложенныйОбъект; - ВложенныеОписания = ОписаниеПоля.ОписанияВложенныхПолей; ОбъектРефлектор = Новый Рефлектор; - Для Каждого ВложенноеОписание Из ВложенныеОписания Цикл - - ВложенныйКлюч = ВложенноеОписание.КлючКонфигурации(); - ПолныйКлюч = Префикс + "." + ВложенныйКлюч; + Для Каждого ОписаниеПоля Из ОписанияПолей Цикл - Если ВложенноеОписание.ЭтоВложенныйДТО() Тогда + ПолныйКлюч = Префикс + "." + ОписаниеПоля.КлючКонфигурации(); - Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ВложенноеОписание.Имя, ПолныйКлюч); - ЗаполнитьВложенныйДТО(ВложенноеОписание, ПолныйКлюч); + Если ОписаниеПоля.ЭтоВложенныйДТО() Тогда - Иначе + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ЗаполнитьПоОписаниям(ОписаниеПоля.ВложенныйОбъект, ОписаниеПоля.ОписанияВложенныхПолей, ПолныйКлюч); + Продолжить; - ЗначениеИзКонфига = Параметр(ПолныйКлюч); + КонецЕсли; - Если Не ЗначениеИзКонфига = Неопределено Тогда + ЗначениеИзКонфига = Параметр(ПолныйКлюч); - ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ВложенноеОписание); - Лог.Отладка("Установка вложенного свойства <%1> из параметра <%2>", ВложенноеОписание.Имя, ПолныйКлюч); - ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ЗначениеИзКонфига); + Если Не ЗначениеИзКонфига = Неопределено Тогда - ИначеЕсли Не ВложенноеОписание.Обязательное Тогда + ЗначениеИзКонфига = ПривестиЗначениеКТипу(ЗначениеИзКонфига, ОписаниеПоля); + Лог.Отладка("Установка свойства <%1> из параметра <%2>", ОписаниеПоля.Имя, ПолныйКлюч); + ОбъектРефлектор.УстановитьСвойство(Объект, ОписаниеПоля.Имя, ЗначениеИзКонфига); - Лог.Отладка("Вложенное свойство <%1> не найдено в конфиге, используется значение по умолчанию", ВложенноеОписание.Имя); - ОбъектРефлектор.УстановитьСвойство(ВложенныйОбъект, ВложенноеОписание.Имя, ВложенноеОписание.ЗначениеПоУмолчанию); + ИначеЕсли Не ОписаниеПоля.Обязательное Тогда - Иначе + Лог.Отладка("Свойство <%1> не найдено в конфиге, используется значение по умолчанию", ОписаниеПоля.Имя); + ОбъектРефлектор.УстановитьСвойство(Объект, ОписаниеПоля.Имя, ОписаниеПоля.ЗначениеПоУмолчанию); - ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + """ (свойство """ + ВложенноеОписание.Имя + """) не задан в конфигурации и не имеет значения по умолчанию"; + Иначе - КонецЕсли; + ВызватьИсключение "Обязательный параметр """ + ПолныйКлюч + + """ (свойство """ + ОписаниеПоля.Имя + + """) не задан в конфигурации и не имеет значения по умолчанию"; КонецЕсли; From 830f5ac73e31a9a582e896568ba4a0f5cde12220 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:19:48 +0200 Subject: [PATCH 20/25] =?UTF-8?q?=D0=92=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=B1=D0=B5=D1=80=D1=91=D1=82=D1=81=D1=8F=20=D1=83=20=D0=B2?= =?UTF-8?q?=D0=BB=D0=B0=D0=B4=D0=B5=D0=BB=D1=8C=D1=86=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=BC=D0=B5=D0=BD=D1=82=20=D0=B7=D0=B0=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ОписаниеПоляДТО хранило ссылку на экземпляр вложенного DTO, захваченную при регистрации, из-за чего описание было привязано к конкретному объекту: если свойство переприсваивали между регистрацией и заполнением, заполнялся прежний экземпляр. Описание сведено к метаданным, признак вложенности определяется по наличию описаний полей, а объект достаётся у владельца рефлектором при заполнении - так же, как верхнеуровневый объект приходит параметром. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\273\321\217\320\224\320\242\320\236.os" | 15 ++++----- ...20\273\321\214\320\224\320\242\320\236.os" | 3 +- ...20\265\321\202\321\200\320\276\320\262.os" | 9 +++++- ...3\321\214\320\224\320\242\320\236_test.os" | 32 +++++++++++++++++++ 4 files changed, 48 insertions(+), 11 deletions(-) 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" index bea6f97..9f825d8 100644 --- "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" @@ -5,7 +5,6 @@ Перем ЗначениеПоУмолчанию Экспорт; // Произвольный - значение по умолчанию Перем Синоним Экспорт; // Строка - альтернативное имя ключа в конфигурации Перем Обязательное Экспорт; // Булево - признак обязательности (нет ЗначениеПоУмолчанию) -Перем ВложенныйОбъект Экспорт; // Объект - ссылка на вложенный DTO-объект (Неопределено если примитив) Перем ОписанияВложенныхПолей Экспорт; // Массив из ОписаниеПоляДТО - описания полей вложенного DTO // Конструктор описания поля @@ -20,7 +19,6 @@ ЗначениеПоУмолчанию = Неопределено; Синоним = Неопределено; Обязательное = Истина; - ВложенныйОбъект = Неопределено; ОписанияВложенныхПолей = Неопределено; КонецПроцедуры @@ -55,21 +53,22 @@ Синоним = НовыйСиноним; КонецПроцедуры -// Устанавливает вложенный 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" index ce67ff8..ff3a0b2 100644 --- "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" @@ -71,8 +71,7 @@ КонецЕсли; Если ВложенныйОбъект <> Неопределено Тогда - ВложенныеОписания = ОписатьКласс(ВложенныйОбъект); - ОписаниеПоля.УстановитьВложенныйОбъект(ВложенныйОбъект, ВложенныеОписания); + ОписаниеПоля.УстановитьВложенныйДТО(ОписатьКласс(ВложенныйОбъект)); КонецЕсли; КонецЕсли; 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 f71b464..c3cb073 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" @@ -372,8 +372,15 @@ Если ОписаниеПоля.ЭтоВложенныйДТО() Тогда + ВложенныйОбъект = ОбъектРефлектор.ПолучитьСвойство(Объект, ОписаниеПоля.Имя); + + Если ВложенныйОбъект = Неопределено Тогда + ВызватьИсключение "Свойство """ + ОписаниеПоля.Имя + + """ описано как вложенный DTO, но не инициализировано на момент заполнения"; + КонецЕсли; + Лог.Отладка("Рекурсивное заполнение вложенного DTO <%1> по ключу <%2>", ОписаниеПоля.Имя, ПолныйКлюч); - ЗаполнитьПоОписаниям(ОписаниеПоля.ВложенныйОбъект, ОписаниеПоля.ОписанияВложенныхПолей, ПолныйКлюч); + ЗаполнитьПоОписаниям(ВложенныйОбъект, ОписаниеПоля.ОписанияВложенныхПолей, ПолныйКлюч); Продолжить; КонецЕсли; 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" index 6673073..d9a0fa9 100644 --- "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" @@ -24,6 +24,7 @@ ИменаТестов.Добавить("ТестДолжен_ЗаполнитьДвухуровневыйДТО"); ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_СоздатьВложенныйДТОПоИмениКлассаИзАннотации"); + ИменаТестов.Добавить("ТестДолжен_ЗаполнитьВложенныйДТОПереприсвоенныйПослеРегистрации"); ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); @@ -338,6 +339,37 @@ КонецПроцедуры +// Заполняется тот экземпляр вложенного DTO, который лежит в свойстве +// на момент заполнения, а не тот, что был там при регистрации +Процедура ТестДолжен_ЗаполнитьВложенныйДТОПереприсвоенныйПослеРегистрации() Экспорт + + // Arrange + ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанных.os"); + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""БД"": {""ИмяБазы"": ""test_db"", ""Подключение"": {""Хост"": ""db-server"", ""Порт"": 3306}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); + + // Act: подменяем вложенный объект уже после регистрации + НовоеПодключение = ЗагрузитьДТО("ТестовоеПодключение.os"); + ДТО.Подключение = НовоеПодключение; + + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + + // Assert: заполнен новый экземпляр, а не захваченный при регистрации + Ожидаем.Что(НовоеПодключение.Хост, "Заполнен должен быть текущий экземпляр вложенного DTO").Равно("db-server"); + Ожидаем.Что(НовоеПодключение.Порт, "Заполнен должен быть текущий экземпляр вложенного DTO").Равно(3306); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + // КП-2.4: DTO с массивом во вложенной структуре Процедура ТестДолжен_ОбработатьМассивВоВложенномДТО() Экспорт From cb5ff085f984601371ea0a4a765698d1e0cebf85 Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:20:53 +0200 Subject: [PATCH 21/25] =?UTF-8?q?=D0=92=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20DTO=20=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D0=BD=20=D0=BA=20=D0=B8=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B2?= =?UTF-8?q?=20=D1=80=D0=BE=D0=B4=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Свойство с аннотацией ВложенныйДТО, у которого нет ни объекта, ни имени класса, давало предупреждение в лог и продолжало описание. Поле деградировало до обычного скаляра, и при заполнении в него попадала сырая ветка конфига - Соответствие вместо типизированного DTO. Теперь такое объявление вызывает исключение при описании класса. Логгер в ОписательДТО остался без использования и убран вместе с зависимостью от logos. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\273\321\214\320\224\320\242\320\236.os" | 24 ++++++++--------- ...20\265\320\275\320\275\321\213\320\274.os" | 11 ++++++++ ...3\321\214\320\224\320\242\320\236_test.os" | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 "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\235\320\265\320\270\320\275\320\270\321\206\320\270\320\260\320\273\320\270\320\267\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\274\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\274.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\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" index ff3a0b2..72bd020 100644 --- "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" @@ -1,9 +1,7 @@ #Использовать annotations #Использовать reflector -#Использовать logos -Перем КонтейнерАннотаций; -Перем Лог; +Перем КонтейнерАннотаций; // КонтейнерАннотаций - определения поддерживаемых аннотаций // Описывает класс DTO: читает аннотации экспортных свойств и возвращает массив описаний полей // @@ -61,19 +59,21 @@ ВложенныйОбъект = СтандартныйРефлектор.ПолучитьСвойство(КлассОбъект, Свойство.Имя); Если ВложенныйОбъект = Неопределено Тогда + ИмяКлассаДТО = ОбъектАннотации.ИмяКлассаДТО(); - Если ИмяКлассаДТО <> "" Тогда - ВложенныйОбъект = Новый(ИмяКлассаДТО); - СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); - Иначе - Лог.Предупреждение("Свойство <%1> помечено как вложенный DTO, но не инициализировано в конструкторе", Свойство.Имя); + + Если ИмяКлассаДТО = "" Тогда + ВызватьИсключение "Свойство """ + Свойство.Имя + """ помечено как вложенный DTO," + + " но не инициализировано в конструкторе и класс не указан в аннотации"; КонецЕсли; - КонецЕсли; - Если ВложенныйОбъект <> Неопределено Тогда - ОписаниеПоля.УстановитьВложенныйДТО(ОписатьКласс(ВложенныйОбъект)); + ВложенныйОбъект = Новый(ИмяКлассаДТО); + СтандартныйРефлектор.УстановитьСвойство(КлассОбъект, Свойство.Имя, ВложенныйОбъект); + КонецЕсли; + ОписаниеПоля.УстановитьВложенныйДТО(ОписатьКласс(ВложенныйОбъект)); + КонецЕсли; КонецЦикла; @@ -93,6 +93,4 @@ КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияСиноним")); КонтейнерАннотаций.ДобавитьАннотацию(Тип("АннотацияВложенныйДТО")); - Лог = Логирование.ПолучитьЛог("oscript.lib.configor.dto"); - КонецПроцедуры 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\235\320\265\320\270\320\275\320\270\321\206\320\270\320\260\320\273\320\270\320\267\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\274\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\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\235\320\265\320\270\320\275\320\270\321\206\320\270\320\260\320\273\320\270\320\267\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\274\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\274.os" new file mode 100644 index 0000000..a345b89 --- /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\235\320\265\320\270\320\275\320\270\321\206\320\270\320\260\320\273\320\270\320\267\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\213\320\274\320\222\320\273\320\276\320\266\320\265\320\275\320\275\321\213\320\274.os" @@ -0,0 +1,11 @@ +// DTO с ошибкой в объявлении: свойство помечено как вложенный DTO, +// но ни инициализации в конструкторе, ни имени класса в аннотации нет + +&ЗначениеПоУмолчанию("main_db") +Перем ИмяБазы Экспорт; + +&ВложенныйДТО +Перем Подключение Экспорт; + +Процедура ПриСозданииОбъекта() +КонецПроцедуры 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" index d9a0fa9..2532772 100644 --- "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" @@ -25,6 +25,7 @@ ИменаТестов.Добавить("ТестДолжен_ПрименитьУмолчанияВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_СоздатьВложенныйДТОПоИмениКлассаИзАннотации"); ИменаТестов.Добавить("ТестДолжен_ЗаполнитьВложенныйДТОПереприсвоенныйПослеРегистрации"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеЕслиВложенныйДТОНеИнициализирован"); ИменаТестов.Добавить("ТестДолжен_ОбработатьМассивВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип"); ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); @@ -370,6 +371,32 @@ КонецПроцедуры +// Регистрация падает, если поле помечено как вложенный DTO, +// но объекта нет и класс в аннотации не указан +Процедура ТестДолжен_ВыброситьИсключениеЕслиВложенныйДТОНеИнициализирован() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСНеинициализированнымВложенным.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""БД"": {""ИмяБазы"": ""test_db"", ""Подключение"": {""Хост"": ""h"", ""Порт"": 1}}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + + Попытка + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "БД"); + Ожидаем.Что(Ложь, "Ожидалось исключение при описании неинициализированного вложенного DTO").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("Подключение"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + // КП-2.4: DTO с массивом во вложенной структуре Процедура ТестДолжен_ОбработатьМассивВоВложенномДТО() Экспорт From c404536a151df53525f4699e9e0fb4b88bfecf4e Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:21:29 +0200 Subject: [PATCH 22/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA?= =?UTF-8?q?=20=D1=82=D0=B8=D0=BF=D1=83=20=D0=94=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ветка с XMLЗначение не была покрыта ни одним тестом, хотя поле ДатаЗапуска присутствовало во всех фикстурах с типами. Проверяется разбор строки вместе со временем и исключение на нераспознанном значении. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...3\321\214\320\224\320\242\320\236_test.os" | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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" index 2532772..f26e6fa 100644 --- "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" @@ -31,6 +31,8 @@ ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип"); ИменаТестов.Добавить("ТестДолжен_ПривестиАнглийскиеСтрокиКБулево"); ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевалидномБулево"); + ИменаТестов.Добавить("ТестДолжен_ПривестиСтрокуКДатеПоАннотацииТип"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевалиднойДате"); ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа"); ИменаТестов.Добавить("ТестДолжен_ПривестиТипыВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_НеПриводитьЕслиТипСовпадает"); @@ -570,6 +572,58 @@ КонецПроцедуры +// Приведение строки в формате ISO к дате по &Тип("Дата") +Процедура ТестДолжен_ПривестиСтрокуКДатеПоАннотацииТип() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""true"", ""Название"": ""t"", ""ДатаЗапуска"": ""2025-01-15T13:45:30""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + + Ожидаем.Что(ТипЗнч(ДТО.ДатаЗапуска), "ДатаЗапуска должна быть датой").Равно(Тип("Дата")); + Ожидаем.Что(ДТО.ДатаЗапуска, "Строка должна разобраться целиком, вместе со временем") + .Равно(Дата(2025, 1, 15, 13, 45, 30)); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// Нераспознанная дата вызывает исключение +Процедура ТестДолжен_ВыброситьИсключениеПриНевалиднойДате() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""cfg"": {""Порт"": ""1"", ""Включен"": ""true"", ""Название"": ""t"", ""ДатаЗапуска"": ""вчера""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); + Менеджер.Прочитать(); + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Ожидаем.Что(Ложь, "Ожидалось исключение для невалидной даты").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("ДатаЗапуска"); + КонецПопытки; + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + // КП-3.3: Ошибка при невозможности приведения типа Процедура ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа() Экспорт From 68cdc2d40342e5082baeebd20f6c61f165e75cee Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:22:02 +0200 Subject: [PATCH 23/25] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80=D1=83=D1=8E=D1=89?= =?UTF-8?q?=D0=B8=D0=B5=D1=81=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit КП-1.2 и КП-1.6 проверяли одно и то же исключение на обязательном поле, КП-1.3 и КП-1.7 - один и тот же синоним. Пары отличались только префиксом в конфиге. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...3\321\214\320\224\320\242\320\236_test.os" | 52 ------------------- 1 file changed, 52 deletions(-) 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" index f26e6fa..cc2a0cf 100644 --- "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" @@ -14,8 +14,6 @@ ИменаТестов = Новый Массив; ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств"); - ИменаТестов.Добавить("ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля"); - ИменаТестов.Добавить("ТестДолжен_ОписатьКлассЧитаетСинонимы"); ИменаТестов.Добавить("ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига"); ИменаТестов.Добавить("ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча"); ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля"); @@ -70,56 +68,6 @@ КонецПроцедуры -// КП-1.2: ОписательДТО определяет обязательные поля -Процедура ТестДолжен_ОписатьКлассОпределяетОбязательныеПоля() Экспорт - - // Arrange - ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); - ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""АдресСервера"": ""test""}}"); - - Менеджер = Новый МенеджерПараметров; - Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) - .Настройки() - .УстановитьФайлПараметров(ВременныйФайл); - Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); - Менеджер.Прочитать(); - - // Act + Assert - Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); - Ожидаем.Что(Ложь, "Ожидалось исключение для обязательного поля").Равно(Истина); - Исключение - Ожидаем.Что(ОписаниеОшибки()).Содержит("ОбязательноеПоле"); - КонецПопытки; - - УдалитьФайлы(ВременныйФайл); - -КонецПроцедуры - -// КП-1.3: Синонимы -Процедура ТестДолжен_ОписатьКлассЧитаетСинонимы() Экспорт - - // Arrange - ДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); - ВременныйФайл = ПолучитьВременныйФайлJSON("{""Тест"": {""port"": 3306}}"); - - Менеджер = Новый МенеджерПараметров; - Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) - .Настройки() - .УстановитьФайлПараметров(ВременныйФайл); - Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Тест"); - - // Act - Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); - - // Assert - Ожидаем.Что(ДТО.Порт, "Синоним port должен быть прочитан из аннотации").Равно(3306); - - УдалитьФайлы(ВременныйФайл); - -КонецПроцедуры - // КП-1.4: Регистрация и заполнение DTO из конфига Процедура ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига() Экспорт From 116b19f398b008ee6ba0eb283cd5cadd65f2bbeb Mon Sep 17 00:00:00 2001 From: Nikita Fedkin Date: Tue, 21 Jul 2026 18:22:43 +0200 Subject: [PATCH 24/25] =?UTF-8?q?=D0=98=D0=B7=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B5=D0=B2=20=D1=83=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D1=8B=20=D0=BD=D0=B5=D0=B2=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B8=20=D0=B2=D0=BD=D0=B5=D1=88=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=81=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Комментарий к переменной Тип и к УстановитьЗначениеПоУмолчанию утверждали, что тип выводится из значения по умолчанию, хотя он берётся из аннотации Тип. Комментарии в ПривестиЗначениеКТипу описывали поведение ОписаниеТипов, а не то, что делает сам метод. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...20\237\320\276\320\273\321\217\320\224\320\242\320\236.os" | 4 ++-- ...20\260\320\274\320\265\321\202\321\200\320\276\320\262.os" | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) 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" index 9f825d8..42fd9cd 100644 --- "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" @@ -1,7 +1,7 @@ // Описание одного поля DTO-класса, полученное из аннотаций свойства Перем Имя Экспорт; // Строка - имя свойства класса -Перем Тип Экспорт; // Тип - тип значения (определяется из ЗначениеПоУмолчанию) +Перем Тип Экспорт; // Тип - целевой тип значения из аннотации Тип Перем ЗначениеПоУмолчанию Экспорт; // Произвольный - значение по умолчанию Перем Синоним Экспорт; // Строка - альтернативное имя ключа в конфигурации Перем Обязательное Экспорт; // Булево - признак обязательности (нет ЗначениеПоУмолчанию) @@ -23,7 +23,7 @@ КонецПроцедуры -// Устанавливает значение по умолчанию и определяет тип из него +// Устанавливает значение по умолчанию, делая поле необязательным // // Параметры: // Значение - Произвольный - значение по умолчанию 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 c3cb073..c73a9c2 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" @@ -458,7 +458,6 @@ Попытка Если ЦелевойТип = Тип("Дата") Тогда - // ОписаниеТипов не парсит ISO-формат Возврат XMLЗначение(Тип("Дата"), Значение); Иначе @@ -469,7 +468,6 @@ ОписаниеТипов = Новый ОписаниеТипов(Строка(ЦелевойТип)); Результат = ОписаниеТипов.ПривестиЗначение(Значение); - // ОписаниеТипов.ПривестиЗначение не бросает ошибку для невалидных строк Если ЦелевойТип = Тип("Число") И ТипЗнч(Значение) = Тип("Строка") Тогда Если Результат = 0 И СокрЛП(Значение) <> "0" Тогда ВызватьИсключение "Невалидное числовое значение"; From 7fcf16f5958ba1fa6aff9286766f5b2222df9800 Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Mon, 27 Jul 2026 13:57:05 +0300 Subject: [PATCH 25/25] =?UTF-8?q?=D0=A0=D0=B5=D0=B3=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D1=8B=D0=B9=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B8=D0=BA=D1=81=20=D0=BF=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=20=D0=B8=D1=81=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC.=20=D0=A4=D0=B8=D0=BA=D1=81=20?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD?= =?UTF-8?q?=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\265\321\202\321\200\320\276\320\262.os" | 40 ++++- ...3\321\214\320\224\320\242\320\236_test.os" | 156 ++++++++++++++---- 2 files changed, 154 insertions(+), 42 deletions(-) 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 c73a9c2..53bb7ad 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" @@ -322,6 +322,8 @@ // Процедура ЗарегистрироватьКлассПараметров(Знач КлассОбъект, Знач Префикс) Экспорт + ПроверитьСвободенЛиПрефикс(КлассОбъект, Префикс); + ОписанияПолей = ВнутреннийОписательДТО.ОписатьКласс(КлассОбъект); Лог.Отладка("Зарегистрирован класс параметров с префиксом <%1>, полей: %2", Префикс, ОписанияПолей.Количество()); @@ -332,21 +334,22 @@ КонецПроцедуры // Заполняет свойства объекта DTO из прочитанных параметров -// Использует зарегистрированные описания полей для маппинга +// Использует зарегистрированные описания полей и префикс для маппинга // // Параметры: -// КлассОбъект - Объект - экземпляр DTO-класса для заполнения -// Префикс - Строка - ключ верхнего уровня в конфигурации +// КлассОбъект - Объект - экземпляр DTO-класса для заполнения, +// ранее переданный в ЗарегистрироватьКлассПараметров // -Процедура ЗаполнитьОбъектПараметров(Знач КлассОбъект, Знач Префикс) Экспорт +Процедура ЗаполнитьОбъектПараметров(Знач КлассОбъект) Экспорт Запись = РеестрКлассовПараметров[КлассОбъект]; Если Запись = Неопределено Тогда - ВызватьИсключение "Класс параметров с префиксом """ + Префикс + """ не зарегистрирован"; + ВызватьИсключение "Класс параметров не зарегистрирован," + + " перед заполнением вызовите ЗарегистрироватьКлассПараметров"; КонецЕсли; - ЗаполнитьПоОписаниям(КлассОбъект, Запись.ОписанияПолей, Префикс); + ЗаполнитьПоОписаниям(КлассОбъект, Запись.ОписанияПолей, Запись.Префикс); КонецПроцедуры @@ -354,6 +357,31 @@ #Область Вспомогательные_процедуры_и_функции +// Проверяет, что префикс не занят другим зарегистрированным классом параметров +// Повторная регистрация того же объекта разрешена и заменяет прежнюю запись +// Префиксы сравниваются без учета регистра, так как индекс параметров регистронезависим +// +// Параметры: +// КлассОбъект - Объект - регистрируемый экземпляр DTO-класса +// Префикс - Строка - ключ верхнего уровня в конфигурации +// +Процедура ПроверитьСвободенЛиПрефикс(Знач КлассОбъект, Знач Префикс) + + Для Каждого ЭлементРеестра Из РеестрКлассовПараметров Цикл + + Если ЭлементРеестра.Ключ = КлассОбъект Тогда + Продолжить; + КонецЕсли; + + Если НРег(ЭлементРеестра.Значение.Префикс) = НРег(Префикс) Тогда + ВызватьИсключение "Префикс """ + Префикс + + """ уже занят другим зарегистрированным классом параметров"; + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + // Заполняет свойства объекта по описаниям полей, // рекурсивно спускаясь во вложенные 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" index cc2a0cf..e42f870 100644 --- "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" @@ -34,6 +34,10 @@ ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа"); ИменаТестов.Добавить("ТестДолжен_ПривестиТипыВоВложенномДТО"); ИменаТестов.Добавить("ТестДолжен_НеПриводитьЕслиТипСовпадает"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриЗанятомПрефиксе"); + ИменаТестов.Добавить("ТестДолжен_СчитатьПрефиксыЗанятымиБезУчетаРегистра"); + ИменаТестов.Добавить("ТестДолжен_ЗаменитьПрефиксПриПовторнойРегистрацииТогоЖеОбъекта"); + ИменаТестов.Добавить("ТестДолжен_ВыброситьИсключениеПриЗаполненииНезарегистрированногоКласса"); Возврат ИменаТестов; @@ -43,7 +47,7 @@ Возврат ЗагрузитьСценарий(ОбъединитьПути(КаталогФикстур, ИмяФайла)); КонецФункции -// КП-1.1: ОписательДТО читает аннотации свойств +// ОписательДТО читает аннотации свойств Процедура ТестДолжен_ОписатьКлассЧитаетАннотацииСвойств() Экспорт // Arrange @@ -58,7 +62,7 @@ // Act Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Тест"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert Ожидаем.Что(ДТО.АдресСервера, "Аннотация ЗначениеПоУмолчанию должна быть прочитана для АдресСервера").Равно("localhost"); @@ -68,7 +72,7 @@ КонецПроцедуры -// КП-1.4: Регистрация и заполнение DTO из конфига +// Регистрация и заполнение DTO из конфига Процедура ТестДолжен_ЗарегистрироватьИЗаполнитьДТОИзКонфига() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); @@ -83,7 +87,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ДТО.АдресСервера, "АдресСервера должен быть из конфига").Равно("prod-server"); Ожидаем.Что(ДТО.ПортСервера, "ПортСервера должен быть из конфига").Равно(3306); @@ -92,7 +96,7 @@ КонецПроцедуры -// КП-1.5: Значения по умолчанию при отсутствии ключа +// Значения по умолчанию при отсутствии ключа Процедура ТестДолжен_ПрименитьЗначенияПоУмолчаниюПриОтсутствииКлюча() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); @@ -107,7 +111,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ДТО.АдресСервера, "АдресСервера должен быть из конфига").Равно("prod-server"); Ожидаем.Что(ДТО.ПортСервера, "ПортСервера должен быть дефолтным").Равно(8080); @@ -116,7 +120,7 @@ КонецПроцедуры -// КП-1.6: Обязательное поле без значения - исключение +// Обязательное поле без значения - исключение Процедура ТестДолжен_ВыброситьИсключениеПриОтсутствииОбязательногоПоля() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСОбязательнымПолем.os"); @@ -133,7 +137,7 @@ Менеджер.Прочитать(); Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(Ложь, "Ожидалось исключение для обязательного поля").Равно(Истина); Исключение Ожидаем.Что(ОписаниеОшибки()).Содержит("ОбязательноеПоле"); @@ -143,7 +147,7 @@ КонецПроцедуры -// КП-1.7: Синоним при заполнении +// Синоним при заполнении Процедура ТестДолжен_ИспользоватьСинонимПриЗаполнении() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); @@ -158,7 +162,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Префикс"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Префикс"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ДТО.Порт, "Порт должен быть заполнен из конфига по синониму port").Равно(3306); @@ -166,7 +170,7 @@ КонецПроцедуры -// КП-2.1: Вложенный DTO - БазаДанных.Подключение +// Вложенный DTO - БазаДанных.Подключение Процедура ТестДолжен_ЗаполнитьВложенныйДТО() Экспорт // Arrange @@ -183,7 +187,7 @@ // Act Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("test_db"); @@ -194,7 +198,7 @@ КонецПроцедуры -// КП-2.2: Трёхуровневый DTO - Приложение.БазаДанных.Подключение +// Трёхуровневый DTO - Приложение.БазаДанных.Подключение Процедура ТестДолжен_ЗаполнитьДвухуровневыйДТО() Экспорт // Arrange @@ -211,7 +215,7 @@ // Act Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Приложение"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert Ожидаем.Что(ДТО.Название, "Название должно быть из конфига").Равно("SuperApp"); @@ -224,7 +228,7 @@ КонецПроцедуры -// КП-2.3: Вложенный DTO - умолчания при отсутствии ключей во вложенной структуре +// Вложенный DTO - умолчания при отсутствии ключей во вложенной структуре Процедура ТестДолжен_ПрименитьУмолчанияВоВложенномДТО() Экспорт // Arrange @@ -241,7 +245,7 @@ // Act Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("custom_db"); @@ -280,7 +284,7 @@ .Равно("ТестовоеПодключениеКласс"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ДТО.ИмяБазы, "ИмяБазы должно быть из конфига").Равно("test_db"); Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть заполнен из конфига").Равно("db-server"); @@ -311,7 +315,7 @@ ДТО.Подключение = НовоеПодключение; Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "БД"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert: заполнен новый экземпляр, а не захваченный при регистрации Ожидаем.Что(НовоеПодключение.Хост, "Заполнен должен быть текущий экземпляр вложенного DTO").Равно("db-server"); @@ -347,7 +351,7 @@ КонецПроцедуры -// КП-2.4: DTO с массивом во вложенной структуре +// DTO с массивом во вложенной структуре Процедура ТестДолжен_ОбработатьМассивВоВложенномДТО() Экспорт // Arrange @@ -364,7 +368,7 @@ // Act Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "Кластер"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); // Assert Ожидаем.Что(ДТО.Имя, "Имя кластера должно быть из конфига").Равно("prod-cluster"); @@ -377,7 +381,7 @@ КонецПроцедуры -// КП-3.1: Приведение строки к числу по &Тип("Число") +// Приведение строки к числу по &Тип("Число") Процедура ТестДолжен_ПривестиСтрокуКЧислуПоАннотацииТип() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); @@ -393,7 +397,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.Порт), "Порт должен быть числом").Равно(Тип("Число")); Ожидаем.Что(ДТО.Порт, "Порт должен быть 8080").Равно(8080); @@ -402,7 +406,7 @@ КонецПроцедуры -// КП-3.2: Приведение строки к булево по &Тип("Булево") +// Приведение строки к булево по &Тип("Булево") Процедура ТестДолжен_ПривестиСтрокуКБулевоПоАннотацииТип() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); @@ -417,7 +421,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); Ожидаем.Что(ДТО.Включен, "Включен должен быть Истина").Равно(Истина); @@ -441,7 +445,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); Ожидаем.Что(ДТО.Включен, "Строка ""true"" должна стать Истина").Равно(Истина); @@ -461,7 +465,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.Включен), "Включен должен быть булево").Равно(Тип("Булево")); Ожидаем.Что(ДТО.Включен, "Строка ""false"" должна стать Ложь").Равно(Ложь); @@ -487,7 +491,7 @@ Менеджер.Прочитать(); Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(Ложь, "Ожидалось исключение для невалидного булева значения").Равно(Истина); Исключение Ожидаем.Что(ОписаниеОшибки()).Содержит("Включен"); @@ -510,7 +514,7 @@ Менеджер.Прочитать(); Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(Ложь, "Ожидалось исключение для значения ""1,TRUE""").Равно(Истина); Исключение Ожидаем.Что(ОписаниеОшибки()).Содержит("Включен"); @@ -535,7 +539,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.ДатаЗапуска), "ДатаЗапуска должна быть датой").Равно(Тип("Дата")); Ожидаем.Что(ДТО.ДатаЗапуска, "Строка должна разобраться целиком, вместе со временем") @@ -562,7 +566,7 @@ Менеджер.Прочитать(); Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(Ложь, "Ожидалось исключение для невалидной даты").Равно(Истина); Исключение Ожидаем.Что(ОписаниеОшибки()).Содержит("ДатаЗапуска"); @@ -572,7 +576,7 @@ КонецПроцедуры -// КП-3.3: Ошибка при невозможности приведения типа +// Ошибка при невозможности приведения типа Процедура ТестДолжен_ВыброситьИсключениеПриНевозможностиПриведенияТипа() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); @@ -589,7 +593,7 @@ Менеджер.Прочитать(); Попытка - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(Ложь, "Ожидалось исключение приведения типа").Равно(Истина); Исключение Ожидаем.Что(ОписаниеОшибки()).Содержит("Порт"); @@ -599,7 +603,7 @@ КонецПроцедуры -// КП-3.4: Приведение типов во вложенном ДТО +// Приведение типов во вложенном ДТО Процедура ТестДолжен_ПривестиТипыВоВложенномДТО() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОБазаДанныхСТипами.os"); @@ -615,7 +619,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "db"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "db"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.ИмяБазы), "ИмяБазы должно быть строкой").Равно(Тип("Строка")); Ожидаем.Что(ДТО.Подключение.Хост, "Вложенный Хост должен быть строкой").Равно("db-host"); @@ -628,7 +632,7 @@ КонецПроцедуры -// КП-3.5: Не приводить если тип уже совпадает +// Не приводить если тип уже совпадает Процедура ТестДолжен_НеПриводитьЕслиТипСовпадает() Экспорт ДТО = ЗагрузитьДТО("ТестовыйДТОСТипами.os"); @@ -644,7 +648,7 @@ .УстановитьФайлПараметров(ВременныйФайл); Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "cfg"); Менеджер.Прочитать(); - Менеджер.ЗаполнитьОбъектПараметров(ДТО, "cfg"); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); Ожидаем.Что(ТипЗнч(ДТО.Порт), "Порт должен остаться числом").Равно(Тип("Число")); Ожидаем.Что(ДТО.Порт, "Порт должен быть 8080").Равно(8080); @@ -655,6 +659,86 @@ КонецПроцедуры +// Два разных класса не могут занять один префикс: +// какой из них выиграл бы при заполнении, определить нельзя +Процедура ТестДолжен_ВыброситьИсключениеПриЗанятомПрефиксе() Экспорт + + ПервыйДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + ВторойДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ЗарегистрироватьКлассПараметров(ПервыйДТО, "Префикс"); + + Попытка + Менеджер.ЗарегистрироватьКлассПараметров(ВторойДТО, "Префикс"); + Ожидаем.Что(Ложь, "Ожидалось исключение при регистрации на занятый префикс").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("Префикс"); + КонецПопытки; + +КонецПроцедуры + +// Индекс параметров регистронезависим, поэтому префиксы, +// отличающиеся только регистром, считаются одним и тем же +Процедура ТестДолжен_СчитатьПрефиксыЗанятымиБезУчетаРегистра() Экспорт + + ПервыйДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + ВторойДТО = ЗагрузитьДТО("ТестовыйДТОССинонимом.os"); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ЗарегистрироватьКлассПараметров(ПервыйДТО, "Префикс"); + + Попытка + Менеджер.ЗарегистрироватьКлассПараметров(ВторойДТО, "ПРЕФИКС"); + Ожидаем.Что(Ложь, "Ожидалось исключение для префикса, отличающегося регистром").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("ПРЕФИКС"); + КонецПопытки; + +КонецПроцедуры + +// Повторная регистрация того же объекта заменяет прежнюю запись, +// заполнение идёт по префиксу из последней регистрации +Процедура ТестДолжен_ЗаменитьПрефиксПриПовторнойРегистрацииТогоЖеОбъекта() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + + ВременныйФайл = ПолучитьВременныйФайлJSON( + "{""Старый"": {""АдресСервера"": ""old-server""}, ""Новый"": {""АдресСервера"": ""new-server""}}" + ); + + Менеджер = Новый МенеджерПараметров; + Менеджер.ДобавитьПровайдерПараметров(Новый ПровайдерПараметровJSON()) + .Настройки() + .УстановитьФайлПараметров(ВременныйФайл); + + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Старый"); + Менеджер.ЗарегистрироватьКлассПараметров(ДТО, "Новый"); + Менеджер.Прочитать(); + Менеджер.ЗаполнитьОбъектПараметров(ДТО); + + Ожидаем.Что(ДТО.АдресСервера, "Заполнение должно идти по префиксу последней регистрации").Равно("new-server"); + + УдалитьФайлы(ВременныйФайл); + +КонецПроцедуры + +// Заполнение возможно только для зарегистрированного класса +Процедура ТестДолжен_ВыброситьИсключениеПриЗаполненииНезарегистрированногоКласса() Экспорт + + ДТО = ЗагрузитьДТО("ТестовыйДТО.os"); + + Менеджер = Новый МенеджерПараметров; + + Попытка + Менеджер.ЗаполнитьОбъектПараметров(ДТО); + Ожидаем.Что(Ложь, "Ожидалось исключение для незарегистрированного класса").Равно(Истина); + Исключение + Ожидаем.Что(ОписаниеОшибки()).Содержит("ЗарегистрироватьКлассПараметров"); + КонецПопытки; + +КонецПроцедуры + Функция ПолучитьВременныйФайлJSON(Знач СодержимоеJSON) ВременныйФайл = ПолучитьИмяВременногоФайла("json");