Files
wehub-resource-sync 7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

506 lines
19 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Extra coverage tests for benchmarks/optimization/optuna_optimizer.py.
Targets the 74 missing lines not covered by test_optuna_optimizer_coverage.py:
- _get_default_param_space structure
- _objective int/categorical param suggestion paths
- _objective with sanitize_data
- _run_experiment success with combined score
- _save_results with/without study, numpy arrays
- _create_visualizations paths (PLOTTING_AVAILABLE, trial counts)
- optimize() starting callback and no-callback paths
- _optimization_callback with study.best_value
"""
import numpy as np
from unittest.mock import Mock, patch
MODULE = "local_deep_research.benchmarks.optimization.optuna_optimizer"
def _make_optimizer(**kwargs):
from local_deep_research.benchmarks.optimization.optuna_optimizer import (
OptunaOptimizer,
)
defaults = {"base_query": "extra coverage query"}
defaults.update(kwargs)
return OptunaOptimizer(**defaults)
# ---------------------------------------------------------------------------
# _get_default_param_space
# ---------------------------------------------------------------------------
class TestGetDefaultParamSpace:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_param_space_contains_required_keys(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
space = optimizer._get_default_param_space()
assert "iterations" in space
assert "questions_per_iteration" in space
assert "search_strategy" in space
assert "max_results" in space
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_iterations_is_int_type(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
space = optimizer._get_default_param_space()
assert space["iterations"]["type"] == "int"
assert space["iterations"]["low"] == 1
assert space["iterations"]["high"] == 5
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_search_strategy_is_categorical_with_choices(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
space = optimizer._get_default_param_space()
assert space["search_strategy"]["type"] == "categorical"
assert "source-based" in space["search_strategy"]["choices"]
assert "focused-iteration" in space["search_strategy"]["choices"]
# ---------------------------------------------------------------------------
# _objective int and categorical suggestion paths
# ---------------------------------------------------------------------------
class TestObjectiveParamSuggestionTypes:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_int_param_suggested_with_step(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_trial = Mock()
mock_trial.number = 0
mock_trial.suggest_int.return_value = 3
mock_trial.suggest_categorical.return_value = "rapid"
with patch.object(optimizer, "_run_experiment") as mock_run:
mock_run.return_value = {"score": 0.5}
param_space = {
"iterations": {"type": "int", "low": 1, "high": 5, "step": 1}
}
optimizer._objective(mock_trial, param_space=param_space)
mock_trial.suggest_int.assert_called_once_with(
"iterations", 1, 5, step=1
)
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_int_param_suggested_without_step(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_trial = Mock()
mock_trial.number = 1
mock_trial.suggest_int.return_value = 2
with patch.object(optimizer, "_run_experiment") as mock_run:
mock_run.return_value = {"score": 0.4}
param_space = {"count": {"type": "int", "low": 1, "high": 10}}
optimizer._objective(mock_trial, param_space=param_space)
mock_trial.suggest_int.assert_called_once_with("count", 1, 10, step=1)
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_categorical_param_suggested(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_trial = Mock()
mock_trial.number = 2
mock_trial.suggest_categorical.return_value = "standard"
with patch.object(optimizer, "_run_experiment") as mock_run:
mock_run.return_value = {"score": 0.6}
param_space = {
"strategy": {
"type": "categorical",
"choices": ["standard", "rapid"],
}
}
optimizer._objective(mock_trial, param_space=param_space)
mock_trial.suggest_categorical.assert_called_once_with(
"strategy", ["standard", "rapid"]
)
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_objective_returns_score_from_run_experiment(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_trial = Mock()
mock_trial.number = 0
mock_trial.suggest_int.return_value = 2
mock_trial.suggest_categorical.return_value = "iterdrag"
with patch.object(optimizer, "_run_experiment") as mock_run:
mock_run.return_value = {"score": 0.73}
param_space = optimizer._get_default_param_space()
result = optimizer._objective(mock_trial, param_space=param_space)
assert result == 0.73
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_objective_appends_to_trials_history(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_trial = Mock()
mock_trial.number = 5
mock_trial.suggest_int.return_value = 1
mock_trial.suggest_categorical.return_value = "source_based"
with patch.object(optimizer, "_run_experiment") as mock_run:
mock_run.return_value = {"score": 0.55}
param_space = optimizer._get_default_param_space()
optimizer._objective(mock_trial, param_space=param_space)
assert len(optimizer.trials_history) == 1
assert optimizer.trials_history[0]["score"] == 0.55
# ---------------------------------------------------------------------------
# _save_results sanitize_data path
# ---------------------------------------------------------------------------
class TestSaveResultsSanitizeData:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.joblib")
@patch(f"{MODULE}.sanitize_data", side_effect=lambda x: x)
@patch(
"local_deep_research.security.file_write_verifier.write_json_verified"
)
def test_sanitize_data_called_during_save(
self,
mock_write_json,
mock_sanitize,
mock_joblib,
mock_evaluator,
tmp_path,
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(output_dir=str(tmp_path))
optimizer.study = None
optimizer.trials_history = [
{"trial_number": 0, "score": 0.5, "params": {}}
]
optimizer._save_results()
mock_sanitize.assert_called()
# ---------------------------------------------------------------------------
# _run_experiment combined score calculation
# ---------------------------------------------------------------------------
class TestRunExperimentCombinedScore:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.SpeedProfiler")
def test_combined_score_with_quality_and_speed_weights(
self, mock_profiler_cls, mock_evaluator
):
mock_eval_instance = Mock()
mock_eval_instance.evaluate.return_value = {
"quality_score": 0.9,
"benchmark_results": {},
}
mock_evaluator.return_value = mock_eval_instance
mock_profiler = Mock()
mock_profiler.get_summary.return_value = {"total_duration": 60.0}
mock_profiler_cls.return_value = mock_profiler
optimizer = _make_optimizer(
metric_weights={"quality": 0.7, "speed": 0.3}
)
result = optimizer._run_experiment(
{"iterations": 2, "questions_per_iteration": 2}
)
assert result["success"] is True
assert result["quality_score"] == 0.9
assert 0.0 <= result["score"] <= 1.0
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.SpeedProfiler")
def test_run_experiment_includes_timing_info(
self, mock_profiler_cls, mock_evaluator
):
mock_eval_instance = Mock()
mock_eval_instance.evaluate.return_value = {
"quality_score": 0.7,
"benchmark_results": {},
}
mock_evaluator.return_value = mock_eval_instance
mock_profiler = Mock()
mock_profiler.get_summary.return_value = {"total_duration": 45.0}
mock_profiler_cls.return_value = mock_profiler
optimizer = _make_optimizer()
result = optimizer._run_experiment({"iterations": 1})
assert "total_duration" in result
assert result["total_duration"] == 45.0
# ---------------------------------------------------------------------------
# _save_results edge cases
# ---------------------------------------------------------------------------
class TestSaveResultsEdgeCases:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.joblib")
@patch(
"local_deep_research.security.file_write_verifier.write_json_verified"
)
def test_save_results_with_empty_trials_history(
self, mock_write_json, mock_joblib, mock_evaluator, tmp_path
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(output_dir=str(tmp_path))
optimizer.study = None
optimizer.trials_history = []
optimizer._save_results()
mock_write_json.assert_called_once()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.joblib")
@patch(
"local_deep_research.security.file_write_verifier.write_json_verified"
)
def test_save_results_numpy_array_converted(
self, mock_write_json, mock_joblib, mock_evaluator, tmp_path
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(output_dir=str(tmp_path))
optimizer.study = None
optimizer.trials_history = [
{
"trial_number": 0,
"score": np.float32(0.65),
"params": {"max_results": np.int32(50)},
}
]
optimizer._save_results()
written_data = mock_write_json.call_args_list[0][0][1]
assert isinstance(written_data[0]["score"], float)
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.joblib")
@patch(
"local_deep_research.security.file_write_verifier.write_json_verified"
)
def test_save_results_with_study_writes_best_params_json(
self, mock_write_json, mock_joblib, mock_evaluator, tmp_path
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(output_dir=str(tmp_path))
mock_study = Mock()
mock_study.best_params = {"iterations": 3, "max_results": 60}
mock_study.best_value = 0.88
mock_study.trials = [Mock(), Mock()]
optimizer.study = mock_study
optimizer.trials_history = []
optimizer._save_results()
# 2 JSON writes: trials + best params
assert mock_write_json.call_count == 2
# Also dumps the study via joblib
mock_joblib.dump.assert_called_once()
# ---------------------------------------------------------------------------
# _create_visualizations
# ---------------------------------------------------------------------------
class TestCreateVisualizations:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.PLOTTING_AVAILABLE", False)
def test_create_visualizations_returns_early_without_plotting(
self, mock_evaluator
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
optimizer.study = Mock()
optimizer.study.trials = [Mock(), Mock()]
with patch(f"{MODULE}.plot_optimization_history") as mock_plot:
optimizer._create_visualizations()
mock_plot.assert_not_called()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.PLOTTING_AVAILABLE", True)
def test_create_visualizations_returns_early_when_no_study(
self, mock_evaluator
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
optimizer.study = None
with patch(f"{MODULE}.plot_optimization_history") as mock_plot:
optimizer._create_visualizations()
mock_plot.assert_not_called()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.PLOTTING_AVAILABLE", True)
def test_create_visualizations_returns_early_fewer_than_2_trials(
self, mock_evaluator
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_study = Mock()
mock_study.trials = [Mock()] # only 1 trial
optimizer.study = mock_study
with patch(f"{MODULE}.plot_optimization_history") as mock_plot:
optimizer._create_visualizations()
mock_plot.assert_not_called()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.PLOTTING_AVAILABLE", True)
@patch(f"{MODULE}.plot_optimization_history")
@patch(f"{MODULE}.plot_param_importances")
@patch(f"{MODULE}.plot_contour")
@patch(f"{MODULE}.plot_slice")
def test_create_visualizations_calls_all_plots_with_sufficient_trials(
self,
mock_slice,
mock_contour,
mock_importances,
mock_history,
mock_evaluator,
tmp_path,
):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(output_dir=str(tmp_path))
mock_study = Mock()
mock_study.trials = [Mock() for _ in range(5)]
optimizer.study = mock_study
for mock_fn in [
mock_history,
mock_importances,
mock_contour,
mock_slice,
]:
mock_fig = Mock()
mock_fn.return_value = mock_fig
optimizer._create_visualizations()
mock_history.assert_called_once_with(mock_study)
# ---------------------------------------------------------------------------
# optimize() starting callback and study creation
# ---------------------------------------------------------------------------
class TestOptimizeStartingCallback:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.optuna")
def test_starting_callback_fired_before_optimize(
self, mock_optuna, mock_evaluator
):
mock_evaluator.return_value = Mock()
callback = Mock()
mock_study = Mock()
mock_study.best_params = {"iterations": 2}
mock_study.best_value = 0.5
mock_study.trials = [Mock()]
mock_optuna.create_study.return_value = mock_study
mock_optuna.samplers.TPESampler.return_value = Mock()
optimizer = _make_optimizer(n_trials=1, progress_callback=callback)
with (
patch.object(optimizer, "_save_results"),
patch.object(optimizer, "_create_visualizations"),
):
optimizer.optimize()
starting_calls = [
c
for c in callback.call_args_list
if c[0][2].get("status") == "starting"
]
assert len(starting_calls) == 1
assert starting_calls[0][0][2]["stage"] == "initialization"
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
@patch(f"{MODULE}.optuna")
def test_optimize_no_callback_does_not_raise(
self, mock_optuna, mock_evaluator
):
mock_evaluator.return_value = Mock()
mock_study = Mock()
mock_study.best_params = {"iterations": 1}
mock_study.best_value = 0.3
mock_study.trials = []
mock_optuna.create_study.return_value = mock_study
mock_optuna.samplers.TPESampler.return_value = Mock()
optimizer = _make_optimizer(n_trials=1)
assert optimizer.progress_callback is None
with (
patch.object(optimizer, "_save_results"),
patch.object(optimizer, "_create_visualizations"),
):
params, value = optimizer.optimize()
assert params == {"iterations": 1}
assert value == 0.3
# ---------------------------------------------------------------------------
# _optimization_callback best_value logging path
# ---------------------------------------------------------------------------
class TestOptimizationCallbackBestValue:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_callback_at_trial_1_does_not_save(self, mock_evaluator):
"""Trial 1 is not a multiple of 10, so no save is triggered."""
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_study = Mock()
mock_study.best_value = 0.77
mock_trial = Mock()
mock_trial.number = 1 # 1 % 10 != 0, no save
with patch.object(optimizer, "_save_results") as mock_save:
optimizer._optimization_callback(mock_study, mock_trial)
mock_save.assert_not_called()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_callback_at_trial_10_triggers_save(self, mock_evaluator):
"""Trial 10 is a multiple of 10 and > 0, so save is triggered."""
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_study = Mock()
mock_trial = Mock()
mock_trial.number = 10
with (
patch.object(optimizer, "_save_results") as mock_save,
patch.object(optimizer, "_create_quick_visualizations") as mock_viz,
):
optimizer._optimization_callback(mock_study, mock_trial)
mock_save.assert_called_once()
mock_viz.assert_called_once()
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_callback_at_multiple_of_10_triggers_save(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer()
mock_study = Mock()
mock_trial = Mock()
mock_trial.number = 30
with (
patch.object(optimizer, "_save_results") as mock_save,
patch.object(optimizer, "_create_quick_visualizations") as mock_viz,
):
optimizer._optimization_callback(mock_study, mock_trial)
mock_save.assert_called_once()
mock_viz.assert_called_once()
# ---------------------------------------------------------------------------
# metric_weights normalization
# ---------------------------------------------------------------------------
class TestMetricWeightsNormalization:
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_weights_normalized_to_sum_one(self, mock_evaluator):
mock_evaluator.return_value = Mock()
optimizer = _make_optimizer(
metric_weights={"quality": 3.0, "speed": 1.0}
)
total = sum(optimizer.metric_weights.values())
assert abs(total - 1.0) < 1e-9
@patch(f"{MODULE}.CompositeBenchmarkEvaluator")
def test_benchmark_weights_stored(self, mock_evaluator):
mock_evaluator.return_value = Mock()
weights = {"simpleqa": 0.6, "browsecomp": 0.4}
optimizer = _make_optimizer(benchmark_weights=weights)
assert optimizer.benchmark_weights == weights