mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 23:47:46 +00:00
test: 434 deep hypothesis tests across RiskMgmt OOS, Kronos, auto-fixer, factor coder, pipeline, integration
- RiskMgmt OOS: 88 tests (leverage bounds, DD limits, trade counting, MC p-value, daily breach) - Kronos adapter: 73 tests (OHLCV idempotence, batch/sequential equivalence, forward-fill) - Auto-fixer: 78 tests (fix idempotence, MultiIndex conversion, fuzzing random patterns) - Factor coder: 65 tests (FactorTask roundtrip, evaluator invariants, workspace paths) - QLib pipeline: 61 tests (Metrics, bandit, precision matrices, noise_var) - Integration: 69 tests (portfolio weights, correlation, RiskMgmt limits, JSON roundtrip)
This commit is contained in:
@@ -219,3 +219,710 @@ class TestFactorEvaluatorsInit:
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorValueEvaluator(mock_scen)
|
||||
assert eva.scen is mock_scen
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# HYPOTHESIS-BASED PROPERTY TESTS — Code Generation Patterns, Variable
|
||||
# Extraction, Evaluator Consistency
|
||||
# ==============================================================================
|
||||
from hypothesis import given, settings, strategies as st
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from rdagent.components.coder.factor_coder.factor import (
|
||||
FactorTask,
|
||||
FactorFBWorkspace,
|
||||
)
|
||||
from rdagent.components.coder.factor_coder.evaluators import (
|
||||
FactorEvaluatorForCoder,
|
||||
)
|
||||
from rdagent.components.coder.factor_coder.eva_utils import (
|
||||
FactorInfEvaluator,
|
||||
FactorSingleColumnEvaluator,
|
||||
FactorOutputFormatEvaluator,
|
||||
FactorMissingValuesEvaluator,
|
||||
FactorCorrelationEvaluator,
|
||||
FactorValueEvaluator,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Strategies
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _valid_factor_task_names() -> st.SearchStrategy:
|
||||
return st.text(
|
||||
alphabet=st.characters(whitelist_categories=("L", "N", "Lu", "Ll"), whitelist_characters="_"),
|
||||
min_size=1,
|
||||
max_size=50,
|
||||
).filter(lambda s: s and s[0].isalpha() and " " not in s)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 1: FactorTask Field Invariants
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskInvariants:
|
||||
"""Property: FactorTask fields maintain invariants after construction."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=50).filter(lambda s: " " not in s),
|
||||
factor_description=st.text(min_size=0, max_size=200),
|
||||
factor_formulation=st.text(min_size=0, max_size=200),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_construction_preserves_all_fields(self, factor_name, factor_description, factor_formulation):
|
||||
"""Property: all constructor args are stored as instance attributes."""
|
||||
t = FactorTask(factor_name, factor_description, factor_formulation)
|
||||
assert t.factor_name == factor_name
|
||||
assert t.factor_description == factor_description
|
||||
assert t.factor_formulation == factor_formulation
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=50).filter(lambda s: " " not in s),
|
||||
factor_description=st.text(min_size=0, max_size=200),
|
||||
factor_formulation=st.text(min_size=0, max_size=200),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_default_field_values(self, factor_name, factor_description, factor_formulation):
|
||||
"""Property: default fields have expected values."""
|
||||
t = FactorTask(factor_name, factor_description, factor_formulation)
|
||||
assert t.factor_implementation is False
|
||||
assert t.factor_resources is None
|
||||
assert t.base_code is None
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=50).filter(lambda s: " " not in s),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_information_contains_name(self, factor_name):
|
||||
"""Property: get_task_information returns string containing factor_name."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
info = t.get_task_information()
|
||||
assert factor_name in info
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=50).filter(lambda s: " " not in s),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_brief_information_contains_name(self, factor_name):
|
||||
"""Property: get_task_brief_information returns string containing factor_name."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
info = t.get_task_brief_information()
|
||||
assert factor_name in info
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=50).filter(lambda s: " " not in s),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_information_and_implementation_result(self, factor_name):
|
||||
"""Property: returned dict contains expected keys."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
result = t.get_task_information_and_implementation_result()
|
||||
assert "factor_name" in result
|
||||
assert "factor_description" in result
|
||||
assert "factor_formulation" in result
|
||||
assert "factor_implementation" in result
|
||||
assert result["factor_name"] == factor_name
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 2: FactorTask from_dict
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskFromDict:
|
||||
"""Property: FactorTask.from_dict round-trip."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
factor_description=st.text(min_size=0, max_size=100),
|
||||
factor_formulation=st.text(min_size=0, max_size=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_from_dict_round_trip(self, factor_name, factor_description, factor_formulation):
|
||||
"""Property: constructing from dict of get_task_information_and_implementation_result preserves values."""
|
||||
t1 = FactorTask(factor_name, factor_description, factor_formulation)
|
||||
info = t1.get_task_information_and_implementation_result()
|
||||
t2 = FactorTask.from_dict(info)
|
||||
assert t2.factor_name == t1.factor_name
|
||||
assert t2.factor_description == t1.factor_description
|
||||
assert t2.factor_formulation == t1.factor_formulation
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_from_dict_with_implementation(self, factor_name):
|
||||
"""Property: factor_implementation field restored from dict."""
|
||||
d = {
|
||||
"factor_name": factor_name,
|
||||
"factor_description": "desc",
|
||||
"factor_formulation": "formula",
|
||||
"variables": {},
|
||||
"resource": None,
|
||||
"factor_implementation": True,
|
||||
}
|
||||
t = FactorTask.from_dict(d)
|
||||
assert t.factor_implementation is True
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_from_dict_with_variables(self, factor_name):
|
||||
"""Property: variables dict restored from dict."""
|
||||
d = {
|
||||
"factor_name": factor_name,
|
||||
"factor_description": "desc",
|
||||
"factor_formulation": "formula",
|
||||
"variables": {"x": 1, "y": 2},
|
||||
"resource": "r1",
|
||||
"factor_implementation": False,
|
||||
}
|
||||
t = FactorTask.from_dict(d)
|
||||
assert t.variables == {"x": 1, "y": 2}
|
||||
assert t.factor_resources == "r1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 3: FactorTask Repr
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskRepr:
|
||||
"""Property: __repr__ invariants."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_repr_contains_factor_task_and_name(self, factor_name):
|
||||
"""Property: repr contains 'FactorTask' and factor_name."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
r = repr(t)
|
||||
assert "FactorTask" in r
|
||||
assert factor_name in r
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_repr_is_string(self, factor_name):
|
||||
"""Property: repr returns a string."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
r = repr(t)
|
||||
assert isinstance(r, str)
|
||||
assert len(r) > 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 4: FactorTask Variables
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskVariables:
|
||||
"""Property: variables field invariants."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
vars_keys=st.lists(
|
||||
st.text(min_size=1, max_size=10).filter(lambda s: s.isidentifier()),
|
||||
min_size=0, max_size=10, unique=True,
|
||||
),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_variables_stored_correctly(self, factor_name, vars_keys):
|
||||
"""Property: variables dict stored as provided."""
|
||||
vars_dict = {k: i for i, k in enumerate(vars_keys)}
|
||||
t = FactorTask(factor_name, "desc", "formula", variables=vars_dict)
|
||||
assert t.variables == vars_dict
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_default_variables_is_empty_dict(self, factor_name):
|
||||
"""Property: default variables is empty dict."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
assert t.variables == {}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 5: FactorTask Resource
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskResource:
|
||||
"""Property: resource field invariants."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
resource=st.one_of(st.none(), st.text(min_size=1, max_size=50)),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_resource_stored_correctly(self, factor_name, resource):
|
||||
"""Property: resource field stored as provided or default None."""
|
||||
t = FactorTask(factor_name, "desc", "formula", resource=resource)
|
||||
assert t.factor_resources == resource
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 6: FactorFBWorkspace Path
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorFBWorkspacePath:
|
||||
"""Property: FactorFBWorkspace workspace path invariants."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_workspace_path_is_valid_path(self, factor_name):
|
||||
"""Property: workspace_path is a Path instance."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
ws = FactorFBWorkspace(target_task=t)
|
||||
assert isinstance(ws.workspace_path, Path)
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_target_task_reference_preserved(self, factor_name):
|
||||
"""Property: target_task reference points back to FactorTask."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
ws = FactorFBWorkspace(target_task=t)
|
||||
assert ws.target_task is t
|
||||
assert ws.target_task.factor_name == factor_name
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 7: FactorEvaluatorForCoder Construction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorEvaluatorForCoderConstruction:
|
||||
"""Property: FactorEvaluatorForCoder constructor creates sub-evaluators."""
|
||||
|
||||
@given(
|
||||
seed=st.integers(min_value=0, max_value=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_sub_evaluators_are_created(self, seed):
|
||||
"""Property: constructor creates value, code, and final_decision evaluators."""
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorEvaluatorForCoder(scen=mock_scen)
|
||||
assert eva.value_evaluator is not None
|
||||
assert eva.code_evaluator is not None
|
||||
assert eva.final_decision_evaluator is not None
|
||||
|
||||
@given(
|
||||
seed=st.integers(min_value=0, max_value=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_evaluate_none_implementation_returns_none(self, seed):
|
||||
"""Property: evaluate with implementation=None returns None."""
|
||||
eva = FactorEvaluatorForCoder(scen=MagicMock())
|
||||
result = eva.evaluate(target_task=MagicMock(), implementation=None)
|
||||
assert result is None
|
||||
|
||||
@given(
|
||||
seed=st.integers(min_value=0, max_value=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_scenario_reference_accessible(self, seed):
|
||||
"""Property: evaluator has access to scenario."""
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorEvaluatorForCoder(scen=mock_scen)
|
||||
assert eva.scen is mock_scen
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 8: FactorEvaluator SubTypes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorEvaluatorSubTypes:
|
||||
"""Property: sub-evaluator types are correct."""
|
||||
|
||||
@given(
|
||||
seed=st.integers(min_value=0, max_value=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_value_evaluator_is_factor_value_evaluator(self, seed):
|
||||
"""Property: value_evaluator is FactorValueEvaluator instance."""
|
||||
eva = FactorEvaluatorForCoder(scen=MagicMock())
|
||||
assert isinstance(eva.value_evaluator, FactorValueEvaluator)
|
||||
|
||||
@given(
|
||||
seed=st.integers(min_value=0, max_value=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_scen_passed_to_value_evaluator(self, seed):
|
||||
"""Property: scenario is passed to value_evaluator."""
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorEvaluatorForCoder(scen=mock_scen)
|
||||
assert eva.value_evaluator.scen is mock_scen
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 9: FactorInfEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorInfEvaluator:
|
||||
"""Property: FactorInfEvaluator invariants."""
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_str_is_correct(self, seed):
|
||||
"""Property: __str__ returns 'FactorInfEvaluator'."""
|
||||
eva = FactorInfEvaluator()
|
||||
assert str(eva) == "FactorInfEvaluator"
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_constructor_no_args(self, seed):
|
||||
"""Property: FactorInfEvaluator can be constructed without arguments."""
|
||||
eva = FactorInfEvaluator()
|
||||
assert eva is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 10: FactorSingleColumnEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorSingleColumnEvaluator:
|
||||
"""Property: FactorSingleColumnEvaluator invariants."""
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_str_is_correct(self, seed):
|
||||
"""Property: __str__ returns 'FactorSingleColumnEvaluator'."""
|
||||
eva = FactorSingleColumnEvaluator()
|
||||
assert str(eva) == "FactorSingleColumnEvaluator"
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_constructor_no_args(self, seed):
|
||||
"""Property: FactorSingleColumnEvaluator can be constructed without arguments."""
|
||||
eva = FactorSingleColumnEvaluator()
|
||||
assert eva is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 11: FactorOutputFormatEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorOutputFormatEvaluator:
|
||||
"""Property: FactorOutputFormatEvaluator invariants."""
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_str_is_correct(self, seed):
|
||||
"""Property: __str__ returns 'FactorOutputFormatEvaluator'."""
|
||||
eva = FactorOutputFormatEvaluator()
|
||||
assert str(eva) == "FactorOutputFormatEvaluator"
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_constructor_no_args(self, seed):
|
||||
"""Property: FactorOutputFormatEvaluator can be constructed without arguments."""
|
||||
eva = FactorOutputFormatEvaluator()
|
||||
assert eva is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 12: FactorMissingValuesEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorMissingValuesEvaluator:
|
||||
"""Property: FactorMissingValuesEvaluator invariants."""
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_str_is_correct(self, seed):
|
||||
"""Property: __str__ returns 'FactorMissingValuesEvaluator'."""
|
||||
eva = FactorMissingValuesEvaluator()
|
||||
assert str(eva) == "FactorMissingValuesEvaluator"
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_constructor_no_args(self, seed):
|
||||
"""Property: FactorMissingValuesEvaluator can be constructed without arguments."""
|
||||
eva = FactorMissingValuesEvaluator()
|
||||
assert eva is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 13: FactorCorrelationEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorCorrelationEvaluator:
|
||||
"""Property: FactorCorrelationEvaluator invariants."""
|
||||
|
||||
@given(
|
||||
hard_check=st.booleans(),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_hard_check_stored_correctly(self, hard_check):
|
||||
"""Property: hard_check flag stored correctly."""
|
||||
eva = FactorCorrelationEvaluator(hard_check=hard_check)
|
||||
assert eva.hard_check is hard_check
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_str_contains_correct_name(self, seed):
|
||||
"""Property: __str__ contains 'FactorCorrelationEvaluator'."""
|
||||
eva = FactorCorrelationEvaluator(hard_check=False)
|
||||
assert "FactorCorrelationEvaluator" in str(eva)
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_default_hard_check_is_false(self, seed):
|
||||
"""Property: hard_check parameter works."""
|
||||
eva = FactorCorrelationEvaluator(hard_check=False)
|
||||
assert eva.hard_check is False
|
||||
eva2 = FactorCorrelationEvaluator(hard_check=True)
|
||||
assert eva2.hard_check is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 14: FactorValueEvaluator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorValueEvaluator:
|
||||
"""Property: FactorValueEvaluator invariants."""
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_scenario_stored_correctly(self, seed):
|
||||
"""Property: scenario reference stored."""
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorValueEvaluator(mock_scen)
|
||||
assert eva.scen is mock_scen
|
||||
|
||||
@given(seed=st.integers(min_value=0, max_value=100))
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_requires_scenario_arg(self, seed):
|
||||
"""Property: FactorValueEvaluator requires scenario argument."""
|
||||
mock_scen = MagicMock()
|
||||
eva = FactorValueEvaluator(mock_scen)
|
||||
assert eva is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 15: FactorTask Version
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskVersion:
|
||||
"""Property: version field invariants."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
version=st.integers(min_value=0, max_value=1000),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_version_default_and_mutable(self, factor_name, version):
|
||||
"""Property: version can be set and retrieved."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
t.version = version
|
||||
assert t.version == version
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 16: FactorTask Feedback Field
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskFeedback:
|
||||
"""Property: feedback-related fields."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_default_implementation_is_false(self, factor_name):
|
||||
"""Property: factor_implementation defaults to False."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
assert t.factor_implementation is False
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_implementation_can_be_set(self, factor_name):
|
||||
"""Property: factor_implementation can be set to True."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
t.factor_implementation = True
|
||||
assert t.factor_implementation is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 17: FactorFBWorkspace FB Constants
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorFBWorkspaceConstants:
|
||||
"""Property: FactorFBWorkspace class constants."""
|
||||
|
||||
def test_fb_exec_success_constant(self):
|
||||
"""Property: FB_EXEC_SUCCESS is defined as a non-empty string."""
|
||||
assert len(str(FactorFBWorkspace.FB_EXEC_SUCCESS)) > 0
|
||||
|
||||
def test_fb_output_file_found_constant(self):
|
||||
"""Property: FB_OUTPUT_FILE_FOUND is defined as a non-empty string."""
|
||||
assert len(str(FactorFBWorkspace.FB_OUTPUT_FILE_FOUND)) > 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 18: FactorTask with Variables from_dict Round-trip
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskRoundTrip:
|
||||
"""Property: full round-trip through from_dict preserves all data."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
factor_description=st.text(min_size=0, max_size=100),
|
||||
factor_formulation=st.text(min_size=0, max_size=100),
|
||||
n_vars=st.integers(min_value=0, max_value=10),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_to_dict_from_dict_round_trip(self, factor_name, factor_description, factor_formulation, n_vars):
|
||||
"""Property: task.to_dict() → FactorTask.from_dict(d) preserves key fields."""
|
||||
t1 = FactorTask(factor_name, factor_description, factor_formulation)
|
||||
d = t1.get_task_information_and_implementation_result()
|
||||
t2 = FactorTask.from_dict(d)
|
||||
assert t2.factor_name == factor_name
|
||||
assert t2.factor_description == factor_description
|
||||
assert t2.factor_formulation == factor_formulation
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 19: FactorTask CoSTEERTask Inheritance
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskCoSTEER:
|
||||
"""Property: FactorTask inherits correctly from CoSTEERTask."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_base_code_is_none_by_default(self, factor_name):
|
||||
"""Property: base_code attribute is None by default (from CoSTEERTask)."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
assert t.base_code is None
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_base_code_can_be_set(self, factor_name):
|
||||
"""Property: base_code can be set."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
t.base_code = "print(42)"
|
||||
assert t.base_code == "print(42)"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 20: FactorEvaluatorForCoder Caching Behavior
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestEvaluatorCaching:
|
||||
"""Property: evaluator caching behavior."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_cached_feedback_returned(self, factor_name):
|
||||
"""Property: queried_knowledge with cached feedback returns it."""
|
||||
from rdagent.components.coder.factor_coder.factor import FactorTask
|
||||
|
||||
eva = FactorEvaluatorForCoder(scen=MagicMock())
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
qk = MagicMock()
|
||||
qk.success_task_to_knowledge_dict = {"info": MagicMock(feedback="cached")}
|
||||
t.get_task_information = MagicMock(return_value="info")
|
||||
qk.failed_task_info_set = set()
|
||||
|
||||
fb = eva.evaluate(target_task=t, implementation=MagicMock(), queried_knowledge=qk)
|
||||
assert fb == "cached"
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=20).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_failed_task_returns_negative_feedback(self, factor_name):
|
||||
"""Property: failed tasks return negative feedback with 'failed too many times'."""
|
||||
from rdagent.components.coder.factor_coder.factor import FactorTask
|
||||
|
||||
eva = FactorEvaluatorForCoder(scen=MagicMock())
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
qk = MagicMock()
|
||||
qk.success_task_to_knowledge_dict = {}
|
||||
t.get_task_information = MagicMock(return_value="info")
|
||||
qk.failed_task_info_set = {"info"}
|
||||
|
||||
fb = eva.evaluate(target_task=t, implementation=MagicMock(), queried_knowledge=qk)
|
||||
assert fb.final_decision is False
|
||||
assert "failed too many times" in fb.execution_feedback
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property 21: FactorTask Information Format
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFactorTaskInformation:
|
||||
"""Property: task information output format."""
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
factor_description=st.text(min_size=0, max_size=100),
|
||||
factor_formulation=st.text(min_size=0, max_size=100),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_information_format(self, factor_name, factor_description, factor_formulation):
|
||||
"""Property: get_task_information has expected format."""
|
||||
t = FactorTask(factor_name, factor_description, factor_formulation)
|
||||
info = t.get_task_information()
|
||||
assert f"factor_name: {factor_name}" in info
|
||||
assert f"factor_description: {factor_description}" in info
|
||||
assert f"factor_formulation: {factor_formulation}" in info
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_information_is_string(self, factor_name):
|
||||
"""Property: get_task_information returns str."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
info = t.get_task_information()
|
||||
assert isinstance(info, str)
|
||||
|
||||
@given(
|
||||
factor_name=st.text(min_size=1, max_size=30).filter(lambda s: s.isidentifier()),
|
||||
)
|
||||
@settings(max_examples=50, deadline=10000)
|
||||
def test_get_task_brief_information_is_string(self, factor_name):
|
||||
"""Property: get_task_brief_information returns str."""
|
||||
t = FactorTask(factor_name, "desc", "formula")
|
||||
info = t.get_task_brief_information()
|
||||
assert isinstance(info, str)
|
||||
|
||||
Reference in New Issue
Block a user