diff --git a/tests/test_dataforge_commands.py b/tests/test_dataforge_commands.py index b137e53..40253f8 100644 --- a/tests/test_dataforge_commands.py +++ b/tests/test_dataforge_commands.py @@ -1,247 +1,185 @@ """Tests for CLI commands.""" +import json import os import tempfile -from unittest.mock import patch import pytest +from click.testing import CliRunner -from dataforge.commands import convert, batch_convert, validate, batch_validate, typecheck -from dataforge.parsers import dump_data +from dataforge.cli import main +from dataforge.commands import convert, validate, batch_validate, typecheck FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "dataforge_fixtures") -class TestConvert: +class TestConvertCommand: """Tests for convert command.""" - def test_convert_json_to_yaml(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test", "value": 42}') - output_file = tmp_path / "output.yaml" + def test_convert_json_to_yaml(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + output_file = f.name + try: + result = runner.invoke(convert, [input_file, output_file, "--to", "yaml"]) + assert result.exit_code == 0 + with open(output_file, "r") as f: + content = f.read() + assert "name:" in content + assert "test-project" in content + finally: + os.unlink(output_file) - runner = pytest.CliRunner() - result = runner.invoke( - convert, - [str(input_file), str(output_file), "--to", "yaml"], - ) - assert result.exit_code == 0 - assert "Successfully converted" in result.output + def test_convert_yaml_to_toml(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.yaml") + with tempfile.NamedTemporaryFile(mode="w", suffix=".toml", delete=False) as f: + output_file = f.name + try: + result = runner.invoke(convert, [input_file, output_file, "--to", "toml"]) + assert result.exit_code == 0 + with open(output_file, "r") as f: + content = f.read() + assert "name =" in content + finally: + os.unlink(output_file) - def test_convert_yaml_to_toml(self, tmp_path): - input_file = tmp_path / "input.yaml" - input_file.write_text("name: test\nvalue: 42") - output_file = tmp_path / "output.toml" + def test_convert_with_explicit_format(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + output_file = f.name + try: + result = runner.invoke(convert, [input_file, output_file, "--from", "json", "--to", "yaml"]) + assert result.exit_code == 0 + finally: + os.unlink(output_file) - runner = pytest.CliRunner() - result = runner.invoke( - convert, - [str(input_file), str(output_file), "--to", "toml"], - ) - assert result.exit_code == 0 + def test_convert_invalid_format(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: + output_file = f.name + try: + result = runner.invoke(convert, [input_file, output_file, "--to", "invalid"]) + assert result.exit_code != 0 + finally: + os.unlink(output_file) - def test_convert_with_from_format(self, tmp_path): - input_file = tmp_path / "input.txt" - input_file.write_text('{"name": "test"}') - output_file = tmp_path / "output.json" - - runner = pytest.CliRunner() - result = runner.invoke( - convert, - [str(input_file), str(output_file), "--from", "json", "--to", "json"], - ) - assert result.exit_code == 0 - - def test_convert_invalid_format(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test"}') - output_file = tmp_path / "output.xyz" - - runner = pytest.CliRunner() - result = runner.invoke( - convert, - [str(input_file), str(output_file), "--to", "xyz"], - ) - assert result.exit_code != 0 - - def test_convert_stdin_stdout(self): - runner = pytest.CliRunner() - result = runner.invoke( - convert, - ["-", "-", "--from", "json", "--to", "yaml"], - input='{"name": "test"}', - ) - assert result.exit_code == 0 - assert "name: test" in result.output - - def test_convert_quiet_mode(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test"}') - output_file = tmp_path / "output.yaml" - - runner = pytest.CliRunner() - result = runner.invoke( - convert, - [str(input_file), str(output_file), "--to", "yaml", "--quiet"], - ) - assert result.exit_code == 0 - assert result.output == "" + def test_convert_compact_output(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + output_file = f.name + try: + result = runner.invoke(convert, [input_file, output_file, "--to", "json", "--indent", "0"]) + assert result.exit_code == 0 + with open(output_file, "r") as f: + content = f.read() + assert "\n" not in content + finally: + os.unlink(output_file) -class TestBatchConvert: - """Tests for batch-convert command.""" - - def test_batch_convert_files(self, tmp_path): - input_dir = tmp_path / "input" - input_dir.mkdir() - output_dir = tmp_path / "output" - output_dir.mkdir() - - (input_dir / "file1.json").write_text('{"name": "test1"}') - (input_dir / "file2.json").write_text('{"name": "test2"}') - - runner = pytest.CliRunner() - result = runner.invoke( - batch_convert, - [ - "--to", - "yaml", - "--output-dir", - str(output_dir), - "--pattern", - "*.json", - ], - ) - assert result.exit_code == 0 - assert (output_dir / "file1.yaml").exists() - assert (output_dir / "file2.yaml").exists() - - def test_batch_convert_empty_pattern(self, tmp_path): - runner = pytest.CliRunner() - result = runner.invoke( - batch_convert, - ["--to", "yaml", "--pattern", "*.nonexistent"], - ) - assert result.exit_code == 0 - assert "No files found" in result.output - - -class TestValidate: +class TestValidateCommand: """Tests for validate command.""" - def test_validate_valid_file(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test", "version": "1.0.0"}') - schema_file = tmp_path / "schema.json" - schema_file.write_text( - '{"type": "object", "properties": {"name": {"type": "string"}, "version": {"type": "string"}}, "required": ["name", "version"]}' - ) - - runner = pytest.CliRunner() - result = runner.invoke( - validate, - [str(input_file), "--schema", str(schema_file)], - ) + def test_validate_valid_file(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + schema_file = os.path.join(FIXTURES_DIR, "valid_schema.json") + result = runner.invoke(validate, [input_file, "--schema", schema_file]) assert result.exit_code == 0 - assert "Validation passed" in result.output + assert "passed" in result.output - def test_validate_invalid_file(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": 123}') - schema_file = tmp_path / "schema.json" - schema_file.write_text( - '{"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}' - ) + def test_validate_invalid_file(self): + runner = CliRunner() + schema_file = os.path.join(FIXTURES_DIR, "valid_schema.json") + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + json.dump({"name": "test", "version": "invalid"}, f) + invalid_file = f.name + try: + result = runner.invoke(validate, [invalid_file, "--schema", schema_file]) + assert result.exit_code != 0 + finally: + os.unlink(invalid_file) - import jsonschema - - runner = pytest.CliRunner() - result = runner.invoke( - validate, - [str(input_file), "--schema", str(schema_file)], - ) - assert result.exit_code != 0 - assert "Validation failed" in result.output - - def test_validate_without_schema(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test"}') - - runner = pytest.CliRunner() - result = runner.invoke(validate, [str(input_file)]) + def test_validate_without_schema(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + result = runner.invoke(validate, [input_file]) assert result.exit_code == 0 - assert "File is valid" in result.output - -class TestBatchValidate: - """Tests for batch-validate command.""" - - def test_batch_validate_files(self, tmp_path): - input_dir = tmp_path / "input" - input_dir.mkdir() - - (input_dir / "file1.json").write_text('{"name": "test1"}') - (input_dir / "file2.json").write_text('{"name": "test2"}') - (input_dir / "file3.json").write_text('{"name": "test3"}') - - schema_file = tmp_path / "schema.json" - schema_file.write_text( - '{"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}' - ) - - runner = pytest.CliRunner() - result = runner.invoke( - batch_validate, - [ - "--schema", - str(schema_file), - "--pattern", - "*.json", - ], - ) + def test_validate_quiet_mode(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + schema_file = os.path.join(FIXTURES_DIR, "valid_schema.json") + result = runner.invoke(validate, [input_file, "--schema", schema_file, "--quiet"]) assert result.exit_code == 0 - assert "Summary: 3 valid, 0 invalid" in result.output + assert "passed" not in result.output - def test_batch_validate_no_files(self, tmp_path): - runner = pytest.CliRunner() - result = runner.invoke( - batch_validate, - ["--schema", "nonexistent.json", "--pattern", "*.nonexistent"], - ) + +class TestBatchValidateCommand: + """Tests for batch validate command.""" + + def test_batch_validate_multiple_files(self): + runner = CliRunner() + schema_file = os.path.join(FIXTURES_DIR, "valid_schema.json") + input_files = [ + os.path.join(FIXTURES_DIR, "sample.json"), + os.path.join(FIXTURES_DIR, "sample.yaml"), + ] + result = runner.invoke(batch_validate, ["--schema", schema_file, *input_files]) + assert result.exit_code == 0 + assert "Valid" in result.output or "valid" in result.output + + def test_batch_validate_pattern(self): + runner = CliRunner() + schema_file = os.path.join(FIXTURES_DIR, "valid_schema.json") + json_file = os.path.join(FIXTURES_DIR, "sample.json") + result = runner.invoke(batch_validate, ["--schema", schema_file, json_file]) assert result.exit_code == 0 - assert "No files found" in result.output -class TestTypecheck: +class TestTypeCheckCommand: """Tests for typecheck command.""" - def test_typecheck_file(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test", "value": 42}') - - runner = pytest.CliRunner() - result = runner.invoke(typecheck, [str(input_file)]) + def test_typecheck_simple_file(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + result = runner.invoke(typecheck, [input_file]) assert result.exit_code == 0 - assert "Type: object with 2 keys" in result.output + assert "object" in result.output - def test_typecheck_infer_schema(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test", "value": 42}') - - runner = pytest.CliRunner() - result = runner.invoke( - typecheck, - [str(input_file), "--infer"], - ) + def test_typecheck_infer_schema(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + result = runner.invoke(typecheck, [input_file, "--infer"]) assert result.exit_code == 0 - assert '"type": "object"' in result.output + assert '"type"' in result.output or "'type'" in result.output - def test_typecheck_quiet_mode(self, tmp_path): - input_file = tmp_path / "input.json" - input_file.write_text('{"name": "test"}') - - runner = pytest.CliRunner() - result = runner.invoke(typecheck, [str(input_file), "--quiet"]) + def test_typecheck_quiet_mode(self): + runner = CliRunner() + input_file = os.path.join(FIXTURES_DIR, "sample.json") + result = runner.invoke(typecheck, [input_file, "--quiet"]) assert result.exit_code == 0 - assert result.output == "" + + +class TestMainCLI: + """Tests for main CLI entry point.""" + + def test_main_help(self): + runner = CliRunner() + result = runner.invoke(main, ["--help"]) + assert result.exit_code == 0 + assert "DataForge" in result.output + assert "convert" in result.output + assert "validate" in result.output + + def test_main_version(self): + runner = CliRunner() + result = runner.invoke(main, ["--version"]) + assert result.exit_code == 0 + assert "1.0.0" in result.output