Skip to content

chore: harden production schema compatibility (Phase 5)#124

Merged
DaTiC0 merged 4 commits into
mainfrom
upgrade/phase-5-production-hardening
May 4, 2026
Merged

chore: harden production schema compatibility (Phase 5)#124
DaTiC0 merged 4 commits into
mainfrom
upgrade/phase-5-production-hardening

Conversation

@DaTiC0

@DaTiC0 DaTiC0 commented May 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Finalize production hardening for runtime environment and legacy password-schema compatibility.
  • Add tests for environment detection and SQL dialect mapping helpers.

Changes

  • app.py
    • Added _is_production_environment() helper and reused it in config/runtime checks.
    • Added _password_column_migration_sql(dialect) helper to centralize supported SQL mapping.
    • Improved _ensure_password_column_capacity() behavior:
      • keeps auto-migration for supported dialects (PostgreSQL/MySQL/MariaDB)
      • raises a runtime error in production for unsupported dialect + legacy schema instead of silently continuing
      • keeps warning-only behavior outside production.
  • tests/test_app.py
    • Added AppHardeningHelpersTest cases for:
      • production environment detection
      • supported/unsupported SQL mapping behavior.

Validation

  • pytest tests/ -q passed (25 passed)
  • Pre-commit path safety check passed

Risk

  • Low: targeted hardening around existing startup schema compatibility logic; behavior changes are explicit and tested.

Summary by Sourcery

Harden production runtime behavior around environment detection and legacy password column schema compatibility.

Enhancements:

  • Introduce a shared helper for detecting production-like environments and reuse it across configuration and runtime checks.
  • Centralize SQL dialect-specific password column migration statements in a dedicated helper and adjust legacy schema handling to error in production for unsupported dialects while remaining warning-only in non-production.

Tests:

  • Add unit tests covering production environment detection and SQL dialect mapping for password column migrations.

@sourcery-ai

sourcery-ai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Finalizes production hardening by centralizing environment detection and password-column migration SQL, and tightening behavior for unsupported dialects in production, with accompanying unit tests for the new helpers.

Sequence diagram for password column capacity hardening logic

sequenceDiagram
    participant AppStartup
    participant EnsurePasswordColumn as _ensure_password_column_capacity
    participant EnvHelper as _is_production_environment
    participant SqlHelper as _password_column_migration_sql
    participant DB as db.engine
    participant Logger

    AppStartup->>EnsurePasswordColumn: call
    EnsurePasswordColumn->>DB: inspect user.password length
    DB-->>EnsurePasswordColumn: current_length
    Note over EnsurePasswordColumn: If current_length >= 255
    EnsurePasswordColumn-->>AppStartup: return

    Note over EnsurePasswordColumn: Legacy length detected
    EnsurePasswordColumn->>DB: get dialect.name
    DB-->>EnsurePasswordColumn: dialect

    EnsurePasswordColumn->>SqlHelper: _password_column_migration_sql(dialect)
    SqlHelper-->>EnsurePasswordColumn: alter_sql or None

    alt Supported dialect
        Note over EnsurePasswordColumn: alter_sql is not None
        EnsurePasswordColumn->>Logger: info Expanding user.password column
        EnsurePasswordColumn->>DB: execute(alter_sql)
        DB-->>EnsurePasswordColumn: ok
        EnsurePasswordColumn->>DB: session.commit()
        DB-->>EnsurePasswordColumn: ok
        EnsurePasswordColumn-->>AppStartup: return
    else Unsupported dialect
        Note over EnsurePasswordColumn: alter_sql is None
        EnsurePasswordColumn->>EnvHelper: _is_production_environment()
        EnvHelper-->>EnsurePasswordColumn: True or False
        alt Production environment
            Note over EnsurePasswordColumn: Raise instead of continuing
            EnsurePasswordColumn->>EnsurePasswordColumn: raise RuntimeError
            EnsurePasswordColumn-->>AppStartup: exception
        else Non production environment
            EnsurePasswordColumn->>Logger: warning manual migration required
            EnsurePasswordColumn-->>AppStartup: return
        end
    end
Loading

Flow diagram for production environment detection and config usage

flowchart TD
    A[Start] --> B[Read APP_ENV]
    B --> C[Is APP_ENV set?]
    C -->|Yes| D[environment = APP_ENV]
    C -->|No| E[Read FLASK_ENV or default to development]
    E --> D
    D --> F[Lowercase environment]
    F --> G{environment in production or prod?}
    G -->|Yes| H[is_production_environment returns True]
    G -->|No| I[is_production_environment returns False]

    H --> J[Use config.ProductionConfig]
    I --> K[Use config.DevelopmentConfig]

    H --> L[Do not set AUTHLIB_INSECURE_TRANSPORT]
    I --> M[Set AUTHLIB_INSECURE_TRANSPORT=1]

    J --> N[Flask app created with production config]
    K --> N

    N --> O[End]
Loading

File-Level Changes

Change Details Files
Centralize runtime environment detection and reuse it for config selection and runtime behavior toggles.
  • Introduced a private _is_production_environment() helper that derives a production-like flag from APP_ENV/FLASK_ENV values.
  • Refactored _get_config_object() to select ProductionConfig/DevelopmentConfig using _is_production_environment() instead of inlined environment checks.
  • Changed the AUTHLIB_INSECURE_TRANSPORT setup to rely on _is_production_environment(), enabling insecure transport only outside production.
app.py
Centralize password-column migration SQL generation and harden behavior for unsupported dialects in production.
  • Extracted dialect-specific SQL for expanding the password column into a new _password_column_migration_sql(dialect) helper to encapsulate supported dialect mappings.
  • Updated _ensure_password_column_capacity() to use _password_column_migration_sql(), logging a warning and returning when no SQL is available for the current dialect.
  • Modified _ensure_password_column_capacity() to raise a RuntimeError in production when encountering a legacy password schema on an unsupported dialect, while keeping warning-only behavior in non-production environments.
app.py
Add unit tests for new hardening helpers to validate environment detection and SQL mapping behavior.
  • Added AppHardeningHelpersTest test case class to cover _is_production_environment() behavior for production-like and development APP_ENV values.
  • Added tests validating _password_column_migration_sql() output for supported dialects (PostgreSQL, MySQL, MariaDB) and None for unsupported dialects like SQLite.
tests/test_app.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors environment detection and database migration logic by introducing helper functions _is_production_environment and _password_column_migration_sql. It also improves error handling by raising a RuntimeError when an unsupported database dialect is encountered in production and includes new unit tests for these helpers. A review comment suggests adding a return type hint to the _password_column_migration_sql function to maintain consistency with the rest of the codebase.

Comment thread app.py Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Consider normalizing the dialect argument in _password_column_migration_sql (e.g., dialect.lower()) so that case differences in engine names don’t silently bypass the supported-dialect logic.
  • In _is_production_environment(), you might want to strip whitespace from the resolved environment value before comparing, to avoid misclassification when env vars include trailing or leading spaces.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider normalizing the `dialect` argument in `_password_column_migration_sql` (e.g., `dialect.lower()`) so that case differences in engine names don’t silently bypass the supported-dialect logic.
- In `_is_production_environment()`, you might want to strip whitespace from the resolved environment value before comparing, to avoid misclassification when env vars include trailing or leading spaces.

## Individual Comments

### Comment 1
<location path="tests/test_app.py" line_range="92-101" />
<code_context>
+class AppHardeningHelpersTest(unittest.TestCase):
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests covering `_ensure_password_column_capacity` behavior for unsupported dialects in production vs non-production environments.

The tests currently verify `_password_column_migration_sql`’s dialect mapping but not `_ensure_password_column_capacity`’s new behavior. Please add tests that directly exercise `_ensure_password_column_capacity` with a mocked `db.engine.dialect.name` and a legacy short column length, and assert:

- Prod: a `RuntimeError` is raised for unsupported dialects.
- Non-prod: only a warning is logged and no exception is raised.

This will ensure the startup hardening behavior is explicitly covered and protected against regressions.

Suggested implementation:

```python
from app import _is_production_environment, _password_column_migration_sql, _ensure_password_column_capacity  # noqa: E402

```

```python
    def test_is_production_environment_false_for_development(self):
        with patch.dict(os.environ, {'APP_ENV': 'development'}, clear=False):
            self.assertFalse(_is_production_environment())

    def test_ensure_password_column_capacity_unsupported_dialect_raises_in_production(self):
        # Simulate production environment
        with patch.dict(os.environ, {'APP_ENV': 'production'}, clear=False):
            # Force an unsupported dialect name
            with patch.object(db.engine, "dialect") as mock_dialect:
                mock_dialect.name = "unsupported_dialect"

                # In production, unsupported dialects should hard-fail
                with self.assertRaises(RuntimeError):
                    _ensure_password_column_capacity()

    def test_ensure_password_column_capacity_unsupported_dialect_warns_in_non_production(self):
        # Simulate non-production environment
        with patch.dict(os.environ, {'APP_ENV': 'development'}, clear=False):
            # Force an unsupported dialect name
            with patch.object(db.engine, "dialect") as mock_dialect:
                mock_dialect.name = "unsupported_dialect"

                # Patch logger to capture warnings
                with patch("app.logger") as mock_logger:
                    # In non-production, unsupported dialects should only emit a warning
                    _ensure_password_column_capacity()

                    # No exception should be raised and a warning should be logged
                    mock_logger.warning.assert_called()

```

If `db.engine.dialect` is not patchable as an attribute in your SQLAlchemy setup, you may need to instead:
1. Patch the path actually used inside `_ensure_password_column_capacity`, for example:
   - `with patch("app.db.engine.dialect") as mock_dialect:` or
   - `with patch("models.db.engine.dialect") as mock_dialect:`
   depending on how `db` is imported and referenced in `app.py`.
2. If `_ensure_password_column_capacity` uses a different logger (e.g., `current_app.logger` or `flask_app.logger`), adjust the `patch("app.logger")` line to target the exact logger path used there.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/test_app.py
DaTiC0 and others added 3 commits May 4, 2026 11:10
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@DaTiC0
DaTiC0 merged commit e7460fe into main May 4, 2026
7 checks passed
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