test(verify-snapshots): убрать маскирующий hostEdit — верификатор не правит проверяемый объект

Верификатор для плана счетов с субконто без extDimensionTypes сам выдумывал стаб-ПВХ И патчил
план через meta-edit (modify-property ExtDimensionTypes=…). Это маскировка: в 1С грузилась
изменённая версия, а не та, что выдал meta-compile — байт-снэпшот (пустой <ExtDimensionTypes/>)
расходился с загружаемым артефактом. К тому же для главного объекта правка не работала вовсе
(hostEdit в Step 3.5 бил по объекту до его компиляции в Step 4 → «object not found»).

Убран весь механизм hostEdits (единственный источник — ветка ChartOfAccounts в getStructuralDeps).
Теперь верификатор создаёт только стабы ЗАВИСИМОСТЕЙ (объектов, на которые вход РЕАЛЬНО ссылается,
через extractTypeRefs); postEdit правит стабы-зависимости (легитимно), но не проверяемый объект.

Следствие: план с ext-dim обязан сам нести extDimensionTypes. Кейсы form-compile-from-object
(chartofaccounts-item/list) переведены на самодостаточный паттерн — реальный ПВХ ВидыСубконто
в preRun (типизированные предопределённые) + явный extDimensionTypes. Фикстура ПС теперь несёт
<ExtDimensionTypes>ChartOfCharacteristicTypes.ВидыСубконто (консистентно с загрузкой); Form.xml
не изменился. Байт 12/12, 1С-cert обоих ✓. Прочие ссылки на ПС (accounting-register) не затронуты —
makeStubDSL даёт maxExtDimensionCount:0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-05 21:06:55 +03:00
parent 2c50030789
commit faec6e6f19
13 changed files with 258 additions and 48 deletions
@@ -1,6 +1,16 @@
{ {
"name": "ChartOfAccounts Item — план счетов с флагами и субконто", "name": "ChartOfAccounts Item — план счетов с флагами и субконто",
"preRun": [ "preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": {
"type": "ChartOfCharacteristicTypes",
"name": "ВидыСубконто",
"valueType": "String(100)",
"predefined": ["Номенклатура: String(100)"]
},
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{ {
"script": "meta-compile/scripts/meta-compile", "script": "meta-compile/scripts/meta-compile",
"input": { "input": {
@@ -8,6 +18,7 @@
"name": "Хозрасчетный", "name": "Хозрасчетный",
"codeLength": 9, "codeLength": 9,
"descriptionLength": 100, "descriptionLength": 100,
"extDimensionTypes": "ChartOfCharacteristicTypes.ВидыСубконто",
"maxExtDimensionCount": 3, "maxExtDimensionCount": 3,
"accountingFlags": [ "accountingFlags": [
{ "name": "Валютный" }, { "name": "Валютный" },
@@ -1,6 +1,16 @@
{ {
"name": "ChartOfAccounts List — план счетов, форма списка", "name": "ChartOfAccounts List — план счетов, форма списка",
"preRun": [ "preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": {
"type": "ChartOfCharacteristicTypes",
"name": "ВидыСубконто",
"valueType": "String(100)",
"predefined": ["Номенклатура: String(100)"]
},
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{ {
"script": "meta-compile/scripts/meta-compile", "script": "meta-compile/scripts/meta-compile",
"input": { "input": {
@@ -8,6 +18,7 @@
"name": "Хозрасчетный", "name": "Хозрасчетный",
"codeLength": 9, "codeLength": 9,
"descriptionLength": 100, "descriptionLength": 100,
"extDimensionTypes": "ChartOfCharacteristicTypes.ВидыСубконто",
"maxExtDimensionCount": 3, "maxExtDimensionCount": 3,
"accountingFlags": [ "accountingFlags": [
{ "name": "Валютный" }, { "name": "Валютный" },
@@ -43,7 +43,7 @@
<UseStandardCommands>true</UseStandardCommands> <UseStandardCommands>true</UseStandardCommands>
<IncludeHelpInContents>false</IncludeHelpInContents> <IncludeHelpInContents>false</IncludeHelpInContents>
<BasedOn /> <BasedOn />
<ExtDimensionTypes /> <ExtDimensionTypes>ChartOfCharacteristicTypes.ВидыСубконто</ExtDimensionTypes>
<MaxExtDimensionCount>3</MaxExtDimensionCount> <MaxExtDimensionCount>3</MaxExtDimensionCount>
<CodeMask /> <CodeMask />
<CodeLength>9</CodeLength> <CodeLength>9</CodeLength>
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<ChartOfCharacteristicTypes uuid="UUID-001">
<InternalInfo>
<xr:GeneratedType name="ChartOfCharacteristicTypesObject.ВидыСубконто" category="Object">
<xr:TypeId>UUID-002</xr:TypeId>
<xr:ValueId>UUID-003</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesRef.ВидыСубконто" category="Ref">
<xr:TypeId>UUID-004</xr:TypeId>
<xr:ValueId>UUID-005</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesSelection.ВидыСубконто" category="Selection">
<xr:TypeId>UUID-006</xr:TypeId>
<xr:ValueId>UUID-007</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesList.ВидыСубконто" category="List">
<xr:TypeId>UUID-008</xr:TypeId>
<xr:ValueId>UUID-009</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="Characteristic.ВидыСубконто" category="Characteristic">
<xr:TypeId>UUID-010</xr:TypeId>
<xr:ValueId>UUID-011</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesManager.ВидыСубконто" category="Manager">
<xr:TypeId>UUID-012</xr:TypeId>
<xr:ValueId>UUID-013</xr:ValueId>
</xr:GeneratedType>
</InternalInfo>
<Properties>
<Name>ВидыСубконто</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Виды субконто</v8:content>
</v8:item>
</Synonym>
<Comment/>
<UseStandardCommands>true</UseStandardCommands>
<IncludeHelpInContents>false</IncludeHelpInContents>
<CharacteristicExtValues/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>100</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<Hierarchical>false</Hierarchical>
<FoldersOnTop>true</FoldersOnTop>
<CodeLength>9</CodeLength>
<CodeAllowedLength>Variable</CodeAllowedLength>
<DescriptionLength>100</DescriptionLength>
<CodeSeries>WholeCharacteristicKind</CodeSeries>
<CheckUnique>true</CheckUnique>
<Autonumbering>true</Autonumbering>
<DefaultPresentation>AsDescription</DefaultPresentation>
<Characteristics/>
<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
<EditType>InDialog</EditType>
<QuickChoice>false</QuickChoice>
<ChoiceMode>BothWays</ChoiceMode>
<InputByString>
<xr:Field>ChartOfCharacteristicTypes.ВидыСубконто.StandardAttribute.Description</xr:Field>
<xr:Field>ChartOfCharacteristicTypes.ВидыСубконто.StandardAttribute.Code</xr:Field>
</InputByString>
<CreateOnInput>DontUse</CreateOnInput>
<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<DefaultObjectForm/>
<DefaultFolderForm/>
<DefaultListForm/>
<DefaultChoiceForm/>
<DefaultFolderChoiceForm/>
<AuxiliaryObjectForm/>
<AuxiliaryFolderForm/>
<AuxiliaryListForm/>
<AuxiliaryChoiceForm/>
<AuxiliaryFolderChoiceForm/>
<BasedOn/>
<DataLockFields/>
<DataLockControlMode>Managed</DataLockControlMode>
<FullTextSearch>Use</FullTextSearch>
<ObjectPresentation/>
<ExtendedObjectPresentation/>
<ListPresentation/>
<ExtendedListPresentation/>
<Explanation/>
<DataHistory>DontUse</DataHistory>
<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
</Properties>
<ChildObjects/>
</ChartOfCharacteristicTypes>
</MetaDataObject>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<PredefinedData xmlns="http://v8.1c.ru/8.3/xcf/predef" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="PlanOfCharacteristicKindPredefinedItems" version="2.17">
<Item id="UUID-001">
<Name>Номенклатура</Name>
<Code/>
<Description>Номенклатура</Description>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>100</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<IsFolder>false</IsFolder>
</Item>
</PredefinedData>
@@ -246,6 +246,7 @@
</Properties> </Properties>
<ChildObjects> <ChildObjects>
<Language>Русский</Language> <Language>Русский</Language>
<ChartOfCharacteristicTypes>ВидыСубконто</ChartOfCharacteristicTypes>
<ChartOfAccounts>Хозрасчетный</ChartOfAccounts> <ChartOfAccounts>Хозрасчетный</ChartOfAccounts>
</ChildObjects> </ChildObjects>
</Configuration> </Configuration>
@@ -43,7 +43,7 @@
<UseStandardCommands>true</UseStandardCommands> <UseStandardCommands>true</UseStandardCommands>
<IncludeHelpInContents>false</IncludeHelpInContents> <IncludeHelpInContents>false</IncludeHelpInContents>
<BasedOn /> <BasedOn />
<ExtDimensionTypes /> <ExtDimensionTypes>ChartOfCharacteristicTypes.ВидыСубконто</ExtDimensionTypes>
<MaxExtDimensionCount>3</MaxExtDimensionCount> <MaxExtDimensionCount>3</MaxExtDimensionCount>
<CodeMask /> <CodeMask />
<CodeLength>9</CodeLength> <CodeLength>9</CodeLength>
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<ChartOfCharacteristicTypes uuid="UUID-001">
<InternalInfo>
<xr:GeneratedType name="ChartOfCharacteristicTypesObject.ВидыСубконто" category="Object">
<xr:TypeId>UUID-002</xr:TypeId>
<xr:ValueId>UUID-003</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesRef.ВидыСубконто" category="Ref">
<xr:TypeId>UUID-004</xr:TypeId>
<xr:ValueId>UUID-005</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesSelection.ВидыСубконто" category="Selection">
<xr:TypeId>UUID-006</xr:TypeId>
<xr:ValueId>UUID-007</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesList.ВидыСубконто" category="List">
<xr:TypeId>UUID-008</xr:TypeId>
<xr:ValueId>UUID-009</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="Characteristic.ВидыСубконто" category="Characteristic">
<xr:TypeId>UUID-010</xr:TypeId>
<xr:ValueId>UUID-011</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ChartOfCharacteristicTypesManager.ВидыСубконто" category="Manager">
<xr:TypeId>UUID-012</xr:TypeId>
<xr:ValueId>UUID-013</xr:ValueId>
</xr:GeneratedType>
</InternalInfo>
<Properties>
<Name>ВидыСубконто</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Виды субконто</v8:content>
</v8:item>
</Synonym>
<Comment/>
<UseStandardCommands>true</UseStandardCommands>
<IncludeHelpInContents>false</IncludeHelpInContents>
<CharacteristicExtValues/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>100</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<Hierarchical>false</Hierarchical>
<FoldersOnTop>true</FoldersOnTop>
<CodeLength>9</CodeLength>
<CodeAllowedLength>Variable</CodeAllowedLength>
<DescriptionLength>100</DescriptionLength>
<CodeSeries>WholeCharacteristicKind</CodeSeries>
<CheckUnique>true</CheckUnique>
<Autonumbering>true</Autonumbering>
<DefaultPresentation>AsDescription</DefaultPresentation>
<Characteristics/>
<PredefinedDataUpdate>Auto</PredefinedDataUpdate>
<EditType>InDialog</EditType>
<QuickChoice>false</QuickChoice>
<ChoiceMode>BothWays</ChoiceMode>
<InputByString>
<xr:Field>ChartOfCharacteristicTypes.ВидыСубконто.StandardAttribute.Description</xr:Field>
<xr:Field>ChartOfCharacteristicTypes.ВидыСубконто.StandardAttribute.Code</xr:Field>
</InputByString>
<CreateOnInput>DontUse</CreateOnInput>
<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>
<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>
<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<DefaultObjectForm/>
<DefaultFolderForm/>
<DefaultListForm/>
<DefaultChoiceForm/>
<DefaultFolderChoiceForm/>
<AuxiliaryObjectForm/>
<AuxiliaryFolderForm/>
<AuxiliaryListForm/>
<AuxiliaryChoiceForm/>
<AuxiliaryFolderChoiceForm/>
<BasedOn/>
<DataLockFields/>
<DataLockControlMode>Managed</DataLockControlMode>
<FullTextSearch>Use</FullTextSearch>
<ObjectPresentation/>
<ExtendedObjectPresentation/>
<ListPresentation/>
<ExtendedListPresentation/>
<Explanation/>
<DataHistory>DontUse</DataHistory>
<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>
<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>
</Properties>
<ChildObjects/>
</ChartOfCharacteristicTypes>
</MetaDataObject>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<PredefinedData xmlns="http://v8.1c.ru/8.3/xcf/predef" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="PlanOfCharacteristicKindPredefinedItems" version="2.17">
<Item id="UUID-001">
<Name>Номенклатура</Name>
<Code/>
<Description>Номенклатура</Description>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>100</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<IsFolder>false</IsFolder>
</Item>
</PredefinedData>
@@ -246,6 +246,7 @@
</Properties> </Properties>
<ChildObjects> <ChildObjects>
<Language>Русский</Language> <Language>Русский</Language>
<ChartOfCharacteristicTypes>ВидыСубконто</ChartOfCharacteristicTypes>
<ChartOfAccounts>Хозрасчетный</ChartOfAccounts> <ChartOfAccounts>Хозрасчетный</ChartOfAccounts>
</ChildObjects> </ChildObjects>
</Configuration> </Configuration>
+6 -46
View File
@@ -135,9 +135,8 @@ function extractTypeRefs(input) {
function getStructuralDeps(input) { function getStructuralDeps(input) {
const deps = []; const deps = [];
const hostEdits = []; // edits applied to the host (the input that triggered the dep)
const inputs = Array.isArray(input) ? input : [input]; const inputs = Array.isArray(input) ? input : [input];
if (!inputs[0] || !inputs[0].type) return { deps, hostEdits }; if (!inputs[0] || !inputs[0].type) return deps;
for (const inp of inputs) { for (const inp of inputs) {
const regTypePrefix = { const regTypePrefix = {
@@ -178,28 +177,9 @@ function getStructuralDeps(input) {
} }
} }
break; break;
case 'ChartOfAccounts': {
// ExtDimensionTypes (виды субконто) link is required when the chart uses
// any ExtDimension at all — otherwise platform rejects forms that bind to
// Объект.ExtDimensionTypes.*.
const usesExtDim = (inp.maxExtDimensionCount && inp.maxExtDimensionCount > 0)
|| (Array.isArray(inp.extDimensionAccountingFlags) && inp.extDimensionAccountingFlags.length > 0);
if (usesExtDim && !inp.extDimensionTypes) {
const stubName = `ВидыСубконтоСтаб${inp.name}`;
deps.push({
type: 'ChartOfCharacteristicTypes', name: stubName,
dsl: { type: 'ChartOfCharacteristicTypes', name: stubName, codeLength: 9, descriptionLength: 100 },
});
hostEdits.push({
hostType: 'ChartOfAccounts', hostName: inp.name,
op: 'modify-property', val: `ExtDimensionTypes=ChartOfCharacteristicTypes.${stubName}`,
});
}
break;
}
} }
} }
return { deps, hostEdits }; return deps;
} }
// ─── Stub creation ────────────────────────────────────────────────────────── // ─── Stub creation ──────────────────────────────────────────────────────────
@@ -483,10 +463,10 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) {
if (configDir && allInputs.length > 0) { if (configDir && allInputs.length > 0) {
const mainNames = new Set(allInputs.map(i => `${i.type}.${i.name}`)); const mainNames = new Set(allInputs.map(i => `${i.type}.${i.name}`));
// Structural deps (scanned across both main input and preRun inputs) // Structural deps (scanned across both main input and preRun inputs).
const { deps: structDeps, hostEdits: structHostEdits } = getStructuralDeps(allInputs); // NB: только зависимости (стабы объектов, на которые ССЫЛАЕТСЯ вход). Верификатор НЕ правит сам
// Stash host edits on the result so we can apply them after preRun. // проверяемый объект — иначе в 1С грузится не то, что выдал компилятор (маскировка дефекта DSL).
result._structHostEdits = structHostEdits; const structDeps = getStructuralDeps(allInputs);
const structDSLs = new Map(); const structDSLs = new Map();
const structPostEdits = new Map(); const structPostEdits = new Map();
for (const dep of structDeps) { for (const dep of structDeps) {
@@ -550,26 +530,6 @@ async function verifyCase(skillName, caseName, skillConfig, caseData, opts) {
return result; return result;
} }
// ── Step 3.5: host-level structural edits (apply to objects created in preRun) ──
if (configDir && result._structHostEdits?.length) {
for (const he of result._structHostEdits) {
const dir = TYPE_TO_DIR[he.hostType];
const objPath = dir ? join(configDir, dir, he.hostName) : null;
if (!objPath || !existsSync(objPath)) {
result.warnings.push(`HostEdit skipped (object not found): ${he.hostType}.${he.hostName}`);
continue;
}
try {
execSkill(opts.runtime, 'meta-edit/scripts/meta-edit',
['-ObjectPath', objPath, '-Operation', he.op, '-Value', he.val]);
log(`hostEdit: ${he.hostType}.${he.hostName}`, true, `${he.op} ${he.val}`);
} catch (e) {
log(`hostEdit: ${he.hostType}.${he.hostName}`, false, e.stderr || e.message);
result.warnings.push(`HostEdit failed: ${he.hostType}.${he.hostName}`);
}
}
}
// ── Step 4: Main skill script ── // ── Step 4: Main skill script ──
let inputFile = null; let inputFile = null;
if (caseData.input !== undefined) { if (caseData.input !== undefined) {