| """Smoke tests for the speculators CLI.""" |
|
|
| import click |
| from typer.testing import CliRunner |
|
|
| from speculators.cli import app |
|
|
| runner = CliRunner() |
|
|
|
|
| def unstyled_output(result): |
| """Return CLI output without terminal styling for stable assertions.""" |
| return click.unstyle(result.output) |
|
|
|
|
| class TestRootApp: |
| def test_no_args_shows_help(self): |
| result = runner.invoke(app, []) |
| assert "Usage" in unstyled_output(result) |
|
|
| def test_help(self): |
| result = runner.invoke(app, ["--help"]) |
| assert result.exit_code == 0 |
| assert "Pipeline" in unstyled_output(result) |
| assert "Tools" in unstyled_output(result) |
|
|
| def test_version(self): |
| result = runner.invoke(app, ["--version"]) |
| assert result.exit_code == 0 |
| assert "speculators version:" in unstyled_output(result) |
|
|
| def test_pipeline_commands_in_help(self): |
| result = runner.invoke(app, ["--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "prepare-data" in output |
| assert "stitch-mtp" in output |
| assert "generate-offline-data" in output |
| assert "regenerate-responses" in output |
| assert "train" in output |
|
|
| def test_tools_commands_in_help(self): |
| result = runner.invoke(app, ["--help"]) |
| assert result.exit_code == 0 |
| assert "convert" in unstyled_output(result) |
|
|
|
|
| class TestConvertCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["convert", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--verifier" in output |
| assert "--algorithm" in output |
|
|
| def test_algorithm_choices_in_help(self): |
| result = runner.invoke(app, ["convert", "--help"]) |
| assert result.exit_code == 0 |
| for algo in ("eagle3", "mtp", "dflash"): |
| assert algo in unstyled_output(result) |
|
|
| def test_missing_required_args(self): |
| result = runner.invoke(app, ["convert"]) |
| assert result.exit_code != 0 |
|
|
|
|
| class TestPrepareDataCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["prepare-data", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--model" in output |
| assert "--data" in output |
| assert "--output" in output |
| assert "--seq-length" in output |
|
|
| def test_missing_required_args(self): |
| result = runner.invoke(app, ["prepare-data"]) |
| assert result.exit_code != 0 |
|
|
| def test_allow_empty_output_in_help(self): |
| result = runner.invoke(app, ["prepare-data", "--help"]) |
| assert result.exit_code == 0 |
| assert "--allow-empty-output" in unstyled_output(result) |
|
|
| def test_overwrite_in_help(self): |
| result = runner.invoke(app, ["prepare-data", "--help"]) |
| assert result.exit_code == 0 |
| assert "--overwrite" in unstyled_output(result) |
|
|
| def test_render_endpoint_in_help(self): |
| result = runner.invoke(app, ["prepare-data", "--help"]) |
| assert result.exit_code == 0 |
| assert "--render-endpoint" in unstyled_output(result) |
|
|
|
|
| class TestStitchCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["stitch-mtp", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "finetuned_checkpoint" in output |
| assert "verifier_path" in output |
|
|
| def test_missing_required_args(self): |
| result = runner.invoke(app, ["stitch-mtp"]) |
| assert result.exit_code != 0 |
|
|
|
|
| class TestGenerateOfflineDataCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["generate-offline-data", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--endpoint" in output |
| assert "--preprocessed-data" in output |
| assert "--concurrency" in output |
| assert "--world-size" in output |
| assert "--rank" in output |
|
|
| def test_fail_on_error_in_help(self): |
| result = runner.invoke(app, ["generate-offline-data", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--fail-on-error" in output |
| assert "--max-retries" in output |
| assert "--validate-outputs" in output |
|
|
| def test_invalid_rank(self): |
| result = runner.invoke( |
| app, ["generate-offline-data", "--rank", "5", "--world-size", "2"] |
| ) |
| assert result.exit_code != 0 |
|
|
| def test_invalid_concurrency(self): |
| result = runner.invoke(app, ["generate-offline-data", "--concurrency", "0"]) |
| assert result.exit_code != 0 |
|
|
|
|
| class TestRegenerateResponsesCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["regenerate-responses", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--endpoint" in output |
| assert "--dataset" in output |
| assert "--concurrency" in output |
| assert "--max-tokens" in output |
|
|
| def test_invalid_max_retries(self): |
| result = runner.invoke(app, ["regenerate-responses", "--max-retries", "-1"]) |
| assert result.exit_code != 0 |
|
|
| def test_invalid_sampling_params(self): |
| result = runner.invoke( |
| app, ["regenerate-responses", "--sampling-params", "not-json"] |
| ) |
| assert result.exit_code != 0 |
|
|
| def test_sampling_params_must_be_object(self): |
| result = runner.invoke( |
| app, ["regenerate-responses", "--sampling-params", "[1,2,3]"] |
| ) |
| assert result.exit_code != 0 |
|
|
| def test_split_only_applies_to_presets(self, tmp_path): |
| dataset = tmp_path / "prompts.jsonl" |
| dataset.touch() |
| result = runner.invoke( |
| app, |
| [ |
| "regenerate-responses", |
| "--dataset", |
| str(dataset), |
| "--split", |
| "custom", |
| ], |
| ) |
| assert result.exit_code != 0 |
| assert "only apply to dataset presets" in unstyled_output(result) |
|
|
| def test_invalid_temperature_cycle(self): |
| result = runner.invoke( |
| app, ["regenerate-responses", "--temperature-cycle", "0.6,notnum"] |
| ) |
| assert result.exit_code != 0 |
|
|
|
|
| class TestTrainCommand: |
| def test_help(self): |
| result = runner.invoke(app, ["train", "--help"]) |
| assert result.exit_code == 0 |
| output = unstyled_output(result) |
| assert "--verifier-name-or-path" in output |
| assert "--config" in output |
| assert "--speculator-type" in output |
|
|
| def test_train_appears_in_pipeline_panel(self): |
| result = runner.invoke(app, ["--help"]) |
| assert result.exit_code == 0 |
| assert "train" in unstyled_output(result) |
|
|