# lawref.lawtest

*module*

Исполнение теста [§267](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#267-test-syntax) оракулом.

Оракул не разбирает `.lawtest` — парсер живёт в компиляторе, и второго
фронтенда здесь не заводится. На вход приходит УЖЕ ПОНИЖЕННЫЙ тест-документ
(`lawc lower-test`): `title` + `case` + `query` + `expectations`.

**Зачем оракулу свой раннер, если документ вычисления уже под байтовым
контрактом.** Байты сравнивает `check_differential`; здесь сравнивается
ВЕРДИКТ — то есть ещё одно место, где реализации могут разойтись: сопоставление
ожидания [§267](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#267-test-syntax) с полем результата [§174](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#174-evaluationresult). Оно короткое, но своё у каждой стороны,
и молчаливое расхождение здесь означало бы, что один и тот же тест на двух
тулчейнах даёт разные ответы. [§269](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#269-differential-testing) объявляет обмен тестами между независимыми
реализациями — значит вердикт обязан быть предметом differential, а не
доверия.

## lawref.lawtest.EXPECT_FIELDS

*attribute* · *module attribute*

```python
EXPECT_FIELDS = {'applicability_status': 'applicabilityStatus', 'evaluation_status': 'evaluationStatus', 'normative_status': 'normativeStatus', 'result_kind': 'resultKind', 'trigger_status': 'triggerStatus', 'truth_status': 'truthStatus', 'value': 'value'}
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L59-L67)

## lawref.lawtest.EXPECT_VOCABULARIES

*attribute* · *module attribute*

```python
EXPECT_VOCABULARIES = {'applicability_status': 'ApplicabilityStatus', 'evaluation_status': 'EvaluationStatus', 'normative_status': 'NormativeStatus', 'result_kind': 'ResultKind', 'trigger_status': 'TriggerStatus', 'truth_status': 'TruthStatus'}
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L78-L85)

## lawref.lawtest.SEVERITY_ORDER

*attribute* · *module attribute*

```python
SEVERITY_ORDER = {'none': -1, 'info': 0, 'warning': 1, 'error': 2, 'fatal': 3}
```

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L491-L491)

## lawref.lawtest.check_expectations

*function*

```python
def check_expectations(expectations: list[dict], evaluation: dict, ir: dict) -> list[str]
```

Сопоставление ожиданий [§267](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#267-test-syntax) с документом вычисления [§174](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#174-evaluationresult).

Вынесено из `run_test` отдельной функцией: её зовёт и раннер, и сверка
переноса сценариев, которой нужно применить ожидания к ПОДМЕНЁННОМУ
документу без запуска движка.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L338-L387)

## lawref.lawtest.declared_ids

*function*

```python
def declared_ids(ir: dict) -> set[str]
```

Объявленные символы программы: id узлов плюс id параметров отношения
[§45.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#451-relation-keys). Список тот же, что строит `unresolved_refs` в
`law-hir/src/lower/mod.rs`, — и он ОБЯЗАН быть тем же: иначе одно и то же
имя было бы свободной переменной у одной реализации и константой у
другой.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L123-L138)

## lawref.lawtest.expect_vocabulary

*function*

```python
def expect_vocabulary(field: str) -> list[str] | None
```

Словарь перечислимого поля [§174.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/25-part-xxiv-queries-and-results.ru.md#1741-result-kinds-и-обязательные-dimensions); `None` — словаря у поля нет.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L88-L93)

## lawref.lawtest.free_generator_refs

*function*

```python
def free_generator_refs(query: dict, ir: dict) -> list[tuple[str, str]]
```

`(id переменной comprehension, id const_ref)` для каждой ссылки на
константу внутри generator-а [§52.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#521-finite-relation-comprehensions) терма ЗАПРОСА.

**Почему это отказ, а не законная ссылка.** Компилятор объявил ровно этот
запрет для тела правила: `LDC-E1317` (`law-hir/src/lower/mod.rs`) говорит,
что имя в generator-е comprehension, не связанное ни биндером правила, ни
`let`, ни переменной самого comprehension, есть свободная переменная [§188](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/27-part-xxvi-static-semantics-and-diagnostics.ru.md#188-name-checks),
и молчаливая подстановка константы дала бы норму, которая не срабатывает
никогда. До 01.09.2026 та же формула, поставленная в ТЕРМ ЗАПРОСА теста
[§267](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#267-test-syntax), проходила молча: `lower-test` понижает файл теста отдельно от
программы, и голое имя становилось `const_ref` на символ, которого в
пакете нет. Ответом была пустая коллекция, а ожидания
`result_kind == COLLECTION` пустую коллекцию принимают — тест оставался бы
зелёным при удалённой норме (DECISION-0085, случай FNMA-14).

**Почему проверяется generator, а не всякий `const_ref` запроса.**
Замерено 01.09.2026 по всем 8675 понижённым тестам репозитория: ссылок на
НЕОБЪЯВЛЕННУЮ константу в термах запроса 373 на 163 различных имени, и это
рабочая идиома, а не долг — непрозрачная константа (`FixedBaseIncome` у
us.fnma.selling_income) нигде не объявлена и работает тем, что одно и то
же имя стоит и в факте, и в теле правила. Сверять `const_ref` со списком
объявленных символов значило бы отвергнуть 453 имени корпуса. Отдельно
отвергнуть «имя, которого в программе нет вовсе» тоже нельзя: открытый мир
делает вопрос о неизвестной константе законным, и корпус такие вопросы
задаёт намеренно (`ind-44`/`ind-47`, «unchecked … open world»).
Generator же измерен отдельно: `const_ref` внутри него — НОЛЬ на весь
репозиторий, то есть запрет E1317 корпус уже соблюдает, и перенос его на
запрос ничего не отнимает.

**Критерий тот же, что у E1317, а не строже.** Имя отвергается, только
если оно НЕ объявлено символом предъявленной программы (`declared_ids`).
Ссылка на объявленную константу в generator-е законна и здесь, и в теле
правила. Именно поэтому проверка живёт в раннере, а не в `lower-test`:
состав символов приносит `--program`, и без него отличить объявленную
константу от свободной переменной нечем.

Имя ЧУЖОГО namespace пропускается по той же причине, по какой его
пропускает компилятор: состав экспортов зависимости предъявляет резолвер,
а пустой список означает «предъявить нечем», а не «символа нет».

**Границы, оставленные сознательно.** (1) Тело квантора [§52](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/08-part-viii-type-system.ru.md#52-finite-domains) не
проверяется: E1317 говорит про generator comprehension, и расширять запрет
шире прозы значило бы вводить своё правило. (2) Дело [§71](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/11-part-xi-assertions-facts-evidence.ru.md#71-assertion) не проверяется:
assertion ВВОДИТ факт, и непрозрачная константа в нём законна по
построению. (3) Свойства [§270](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#270-property-based-tests) не проверяются: их `domain` и `violations`
строит компилятор из биндеров `forall`, автор их не пишет.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L141-L204)

## lawref.lawtest.run_property

*function*

```python
def run_property(document: dict, ir: dict, case: dict, resource_root=None, *, evaluator: Callable[[EvaluationRequest], dict] | None = None) -> dict
```

Вердикт по свойству [§270](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/36-part-xxxiv-conformance-tests.ru.md#270-property-based-tests): `{"name", "domain", "counterexamples", "skipped"}`.

Понижение свойства — работа компилятора (`lawc lower-property`); здесь
только складывается мир и задаются два запроса. Зачем оракулу своя
половина: вердикт свойства иначе приходит от ОДНОЙ реализации, а весь
контур корректности здесь стоит на том, что их две. Составленная
программа (закон + наблюдающие правила) ни в один differential не входит
по построению — она собирается на время исполнения свойства, — значит
сверять её ответы больше негде.

`skipped` — наблюдающие правила, которых движок не исполнил. Пустой
список есть условие того, что «контрпримеров нет» вообще что-то значит:
`NON_EXECUTABLE_RULE` приходит ПРЕДУПРЕЖДЕНИЕМ, то есть молча для того,
кто читает только `results` (DECISION-0035 [§7.1](https://github.com/arxohq/law/blob/master/spec/SPEC.ru/03-part-iii-architecture-and-conformance.ru.md#71-parser)).

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L390-L458)

## lawref.lawtest.run_test

*function*

```python
def run_test(document: dict, ir: dict, resource_root=None, *, evaluator: Callable[[EvaluationRequest], dict] | None = None, calendar: Callable[[dict, dict], bytes | None] | None = None, builder: Callable[..., EvaluationRequest] | None = None) -> dict
```

Вердикт по тест-документу: `{"title", "passed", "failures"}`.

`failures` — список причин в порядке ожиданий; пустой список и есть
прохождение. Отказ движка — тоже вердикт, а не исключение: правовой
вопрос, на который нет ответа, обязан быть виден провалом ожидания.

`evaluator` позволяет корпусу сохранить перехват вызовов для differential
и сертификатов. По умолчанию исполняется независимый оракул.

[View source](https://github.com-arxohq/arxo-io/law/blob/2edc2b92ce22b52e03f4081d2769a58229684379/engines/lawref/lawref/lawtest.py#L207-L265)
