Skip to content

fix(orm): harden ConnectionFactory against bad port and unknown driver#194

Merged
tmgbedu merged 2 commits into
mainfrom
task/factory-hardening-1223
Jul 23, 2026
Merged

fix(orm): harden ConnectionFactory against bad port and unknown driver#194
tmgbedu merged 2 commits into
mainfrom
task/factory-hardening-1223

Conversation

@tmgbedu

@tmgbedu tmgbedu commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens ConnectionFactory.build_url() / make() so bad database connection configs fail fast with clear, framework-level errors instead of raw low-level exceptions surfaced deep inside SQLAlchemy. Addresses task #1223 (findings from investigation #1226, originating from PR #191 review).

Problems fixed

  1. Missing/empty port crash (mysql & postgres)port = config.get("port", "") defaulted to "", producing a URL with a bare trailing colon that crashed at engine creation with ValueError: invalid literal for int() with base 10: ''.
  2. Raw KeyError on unknown driverDRIVER_URLS[driver] raised a bare KeyError, not a friendly error.
  3. Dead fallbackmake()'s raise ValueError("Unsupported driver") was unreachable, because create_engine()build_url() always raised the raw KeyError first.

Changes

  • Per-driver default ports (mysql → 3306, postgres → 5432). An explicitly configured port still takes precedence, so the common/happy path is byte-for-byte unchanged.
  • Friendly DriverNotFound (an existing, previously-unused framework exception) raised from both build_url() and make(), with a message listing the supported drivers. The check in make() now runs before any engine is built — reachable and meaningful — replacing the dead match fallback with a CONNECTIONS lookup.
  • The documented url passthrough still short-circuits field assembly, so supplying a full url bypasses both driver and port validation (useful stopgap for anyone hitting this today).

Scope note

Finding (d) — upstream config validation in DatabaseManager/DatabaseProvider — was intentionally left out of this pass. The factory-level friendly errors already make bad configs fail with a clear message at connection resolution; broader manager/provider validation is a larger, separate change and was not in the task's enumerated asks (1–3).

Tests

New regression tests in tests/masoniteorm/config/test_db_url.py:

  • mysql/postgres with missing port → default applied
  • mysql/postgres with empty-string port → default applied
  • explicit port preserved (precedence)
  • unsupported driver via build_url() and make()DriverNotFound with a helpful message
  • url passthrough bypasses driver validation
  • existing valid sqlite/mysql/postgres URL assertions unchanged

Updated tests/masoniteorm/models/test_model_attributes.py::test_connection_raises_for_missing_driver: it previously mocked create_engine specifically to reach the dead fallback and asserted the raw ValueError("Unsupported driver"). It now asserts the friendly DriverNotFound that fires before any engine is built (mock no longer needed).

Verification

uv run pytest --cov --ignore=tests/masoniteorm/postgres1981 passed, 7 skipped, Required test coverage of 80.0% reached. Total coverage: 81.22%.

⚠️ Merge freeze in effect — please review but do not merge.

build_url()/make() previously surfaced bad connection configs as raw,
low-level errors:

- mysql/postgres with a missing/empty 'port' produced a URL with a bare
  trailing colon that crashed at engine creation with
  ValueError: invalid literal for int() with base 10: ''.
- an unsupported driver raised a raw KeyError from DRIVER_URLS[driver].
- make()'s 'Unsupported driver' fallback was unreachable dead code, since
  create_engine()/build_url() always raised the raw KeyError first.

Hardening:
- Add per-driver default ports (mysql 3306, postgres 5432); an explicit
  port still takes precedence, so the happy path is byte-for-byte unchanged.
- build_url() and make() now raise the existing framework-level
  DriverNotFound with a message listing the supported drivers. The check in
  make() runs before any engine is built, so it is reachable and meaningful;
  the dead match fallback is removed in favour of a CONNECTIONS lookup.

The documented 'url' passthrough still short-circuits field assembly, so
supplying a full url bypasses both driver and port validation.

Regression tests cover: mysql/postgres missing and empty-string ports,
explicit-port precedence, unsupported driver via build_url() and make(),
url passthrough, and unchanged valid sqlite/mysql/postgres URLs.
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@tmgbedu
tmgbedu merged commit df971af into main Jul 23, 2026
6 checks passed
@tmgbedu
tmgbedu deleted the task/factory-hardening-1223 branch July 23, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant