Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified CHANGELOG.md
Binary file not shown.
16 changes: 12 additions & 4 deletions src/schemaforge/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def check_directory(
failures.append(f" FAIL {name}: {e}")

if not converted:
return "No files could be converted to {}\n{}".format(
return "FAIL: No files could be converted to {}\n{}".format(
canonical, "\n".join(failures)
)

Expand Down Expand Up @@ -109,10 +109,18 @@ def check_directory(
lines.append("")
for m in mismatches:
lines.append(m)
lines.append("FAIL: Schema files are not equivalent")

# Deterministic exit-status lines. The CLI keys its process exit code off
# these. A conversion failure must fail the check even when there is no
# pairwise mismatch -- previously the "Mismatches: 0" report with no PASS
# line let CI report green while a schema silently failed to parse.
if mismatches or failures:
if mismatches:
lines.append("FAIL: Schema files are not equivalent")
if failures:
lines.append(f"FAIL: {len(failures)} file(s) failed to convert")
else:
lines.append(" Mismatches: 0")
if not failures:
lines.append("PASS: All schema files are equivalent")
lines.append("PASS: All schema files are equivalent")

return "\n".join(lines)
2 changes: 1 addition & 1 deletion src/schemaforge/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def check(directory: str, canonical: str, type_map_path: str | None) -> None:
try:
result = check_directory(directory, canonical=canonical, type_map_path=type_map_path)
click.echo(result)
if "FAIL" in result and "PASS" not in result:
if "FAIL:" in result:
sys.exit(1)
except (NotADirectoryError, ValueError, FileNotFoundError) as e:
click.echo(f"Error: {e}", err=True)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,20 @@ def test_check_directory_canonical_format():

result = check_directory(tmpdir, canonical="prisma")
assert "compared via prisma" in result


def test_check_directory_conversion_failure_reports_fail():
"""A file that fails to parse must produce a FAIL line (not a silent PASS).

Regression guard: previously a conversion failure with no pairwise mismatch
yielded "Mismatches: 0" and no PASS/FAIL line, so the CLI exited 0 (green)
while a schema silently failed to parse.
"""
with tempfile.TemporaryDirectory() as tmpdir:
Path(tmpdir, "schema.sql").write_text(SAMPLE_SQL)
Path(tmpdir, "broken.json").write_text("{ this is not valid json ,,, }")

result = check_directory(tmpdir)
assert "Conversion failures: 1" in result
assert "FAIL: 1 file(s) failed to convert" in result
assert "PASS:" not in result
27 changes: 26 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,10 +509,35 @@ def test_check_invalid_directory(self):
finally:
Path(f_path).unlink(missing_ok=True)

def test_check_cli_exits_nonzero_on_conversion_failure(self):
"""Regression: a schema that fails to parse must make `check` exit non-zero.

Previously the CLI only failed on mismatches, so a parse failure with no
pairwise mismatch reported "Mismatches: 0" and exited 0 (silent green).
"""
runner = CliRunner()
with tempfile.TemporaryDirectory() as tmpdir:
Path(tmpdir, "schema.sql").write_text(SAMPLE_SQL)
Path(tmpdir, "broken.json").write_text("{ this is not valid json ,,, }")

result = runner.invoke(main, ["check", "--dir", tmpdir])
assert result.exit_code != 0
assert "FAIL:" in result.output

def test_check_cli_exits_zero_when_all_equivalent(self):
"""Two identical schema files are trivially equivalent -> exit 0 with PASS."""
runner = CliRunner()
with tempfile.TemporaryDirectory() as tmpdir:
Path(tmpdir, "a.sql").write_text(SAMPLE_SQL)
Path(tmpdir, "b.sql").write_text(SAMPLE_SQL)

result = runner.invoke(main, ["check", "--dir", tmpdir])
assert result.exit_code == 0
assert "PASS: All schema files are equivalent" in result.output


# ═══════════════════════════════════════════════════════════════
# mcp command
# ═══════════════════════════════════════════════════════════════


class TestMcpCommand:
Expand Down
Loading