Artifex/tests/test_feature_catalog_v1.py

60 lines
2.5 KiB
Python
Raw Normal View History

from __future__ import annotations
import json
import pytest
from django.core.management import call_command
from control_plane.trading_studio.indicators.definitions import IndicatorVariant, Role
from control_plane.trading_studio.indicators.feature_catalog import (
FEATURE_CATALOG_V1,
FEATURE_CATALOG_V1_PATH,
FEATURE_SAFETY_AUDIT_V1_PATH,
CausalClassification,
FeatureSupportState,
FeatureUnavailableError,
feature_catalog_artifact_bytes,
feature_definition,
feature_safety_audit_artifact_bytes,
require_validated_features,
)
def test_catalog_explicitly_records_validated_coverage_and_source_only_scope():
assert len(FEATURE_CATALOG_V1) == 1107
validated = {
item.key
for item in FEATURE_CATALOG_V1
if item.support_state == FeatureSupportState.VALIDATED
}
assert len(validated) == 80
artifact = json.loads(feature_catalog_artifact_bytes())
assert artifact["implementation_coverage"]["coverage"] == "80 of 1107"
assert artifact["evaluation"] == "not evaluated via reference runtime; no strategy search"
def test_safety_audit_derives_causal_classification_from_source_semantics():
trailing = feature_definition(IndicatorVariant(30, 14, 0.0, Role.OSC))
index_based = feature_definition(IndicatorVariant(117, 1, 0.0, Role.TREND))
alias = feature_definition(IndicatorVariant(25, 14, 0.0, Role.LEVEL))
assert trailing.causal_classification == CausalClassification.TRAILING_WINDOW_CAUSAL
assert index_based.causal_classification == CausalClassification.INDEX_POSITION_CAUSAL
assert alias.support_state == FeatureSupportState.ALIAS_REFUSED
policy = json.loads(feature_safety_audit_artifact_bytes())["fail_closed_policy"]
assert policy.startswith("Any requested")
def test_non_validated_historical_variants_fail_closed_without_baseline_expansion():
validated = IndicatorVariant(30, 14, 0.0, Role.OSC)
unvalidated = IndicatorVariant(30, 12, 0.0, Role.OSC)
with pytest.raises(FeatureUnavailableError, match="fails closed"):
require_validated_features([validated, unvalidated])
def test_generator_writes_machine_readable_catalog_and_audit(tmp_path):
call_command("tradingstudio_generate_feature_catalog", "--output-dir", str(tmp_path))
catalog = tmp_path / FEATURE_CATALOG_V1_PATH.name
audit = tmp_path / FEATURE_SAFETY_AUDIT_V1_PATH.name
assert json.loads(catalog.read_text())["catalog"] == "FEATURE_CATALOG_V1"
assert json.loads(audit.read_text())["audit"] == "FEATURE_SAFETY_AUDIT_V1"