From ae2fc15a5b8ac110028c539e8ef47547e200e958 Mon Sep 17 00:00:00 2001 From: Barcode Betty Date: Sat, 23 May 2026 22:09:33 +0000 Subject: [PATCH 1/2] fix: resolve lint errors in test files [CAR-932] Fix 56 lint errors in test files that were blocking CI: - E501: Split long SQL INSERT statements across multiple lines - F401: Remove unused imports (os, unittest.mock.patch) Co-Authored-By: Paperclip --- tests/conftest.py | 6 ++++-- tests/test_auth/test_auth_endpoints.py | 3 ++- tests/test_config.py | 2 -- tests/test_e2e/test_auth_validation.py | 3 ++- tests/test_middleware/test_rate_limit.py | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b684a41..9908d35 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -177,8 +177,10 @@ async def _create_test_user_and_session( async with db_engine.begin() as conn: await conn.execute( text( - "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) " - "VALUES (:id, :email, :hashed_password, :display_name, :email_verified, :created_at, :updated_at)" + "INSERT INTO users (id, email, hashed_password, display_name, " + "email_verified, created_at, updated_at) " + "VALUES (:id, :email, :hashed_password, :display_name, :email_verified, " + ":created_at, :updated_at)" ), { "id": user_id, diff --git a/tests/test_auth/test_auth_endpoints.py b/tests/test_auth/test_auth_endpoints.py index 9b55a4c..c2ed7c2 100644 --- a/tests/test_auth/test_auth_endpoints.py +++ b/tests/test_auth/test_auth_endpoints.py @@ -138,7 +138,8 @@ async def test_expired_session_rejected(client, db_engine): async with db_engine.begin() as conn: await conn.execute( text( - "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) " + "INSERT INTO users (id, email, hashed_password, display_name, " + "email_verified, created_at, updated_at) " "VALUES (:id, :email, :hp, :dn, :ev, :ca, :ua)" ), { diff --git a/tests/test_config.py b/tests/test_config.py index f594bc2..2f94ec9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,7 +1,5 @@ """Tests for Settings config, specifically the database_url env var fallback.""" -import os - from cartsnitch_api.config import Settings diff --git a/tests/test_e2e/test_auth_validation.py b/tests/test_e2e/test_auth_validation.py index f0e38cd..a4db942 100644 --- a/tests/test_e2e/test_auth_validation.py +++ b/tests/test_e2e/test_auth_validation.py @@ -65,7 +65,8 @@ class TestSessionValidation: async with db_engine.begin() as conn: await conn.execute( text( - "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) " + "INSERT INTO users (id, email, hashed_password, display_name, " + "email_verified, created_at, updated_at) " "VALUES (:id, :email, :hp, :dn, :ev, :ca, :ua)" ), { diff --git a/tests/test_middleware/test_rate_limit.py b/tests/test_middleware/test_rate_limit.py index fbfe7d1..3a0e5a9 100644 --- a/tests/test_middleware/test_rate_limit.py +++ b/tests/test_middleware/test_rate_limit.py @@ -1,7 +1,7 @@ """Tests for rate limiting middleware.""" import time -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock import pytest From c68838acf27d3fbc75cefe72188a039e4ee6f107 Mon Sep 17 00:00:00 2001 From: Barcode Betty Date: Sat, 23 May 2026 22:47:17 +0000 Subject: [PATCH 2/2] Fix ruff lint errors across codebase - Auto-fix F401 (unused imports) and I001 (unsorted imports) with ruff --fix - Manually fix E501 (line too long) in alembic migrations and src/ models - Run ruff format to ensure consistent formatting Co-Authored-By: Paperclip --- alembic/env.py | 7 +- alembic/versions/002_better_auth_tables.py | 53 +++++- alembic/versions/004_fix_user_id_text.py | 14 +- .../versions/005_add_email_inbound_token.py | 2 +- .../006_email_inbound_token_server_default.py | 4 +- alembic/versions/007_bootstrap_users_table.py | 16 +- alembic/versions/008_create_domain_tables.py | 176 +++++++++++++++--- .../009_add_gin_index_upc_variants.py | 1 + src/cartsnitch_api/auth/dependencies.py | 3 +- src/cartsnitch_api/auth/routes.py | 3 - src/cartsnitch_api/main.py | 3 +- src/cartsnitch_api/models/user.py | 7 +- tests/test_config.py | 15 +- 13 files changed, 249 insertions(+), 55 deletions(-) diff --git a/alembic/env.py b/alembic/env.py index 6844fba..0b89f86 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -45,7 +45,11 @@ def run_migrations_online() -> None: poolclass=pool.NullPool, ) with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata, version_table_column_width=128) + context.configure( + connection=connection, + target_metadata=target_metadata, + version_table_column_width=128, + ) with context.begin_transaction(): context.run_migrations() # Create any tables defined in models but not yet created by migrations. @@ -56,6 +60,7 @@ def run_migrations_online() -> None: connection.commit() except Exception as exc: import logging + logging.getLogger("alembic.env").warning( "create_all failed (non-fatal, migrations should handle table creation): %s", exc ) diff --git a/alembic/versions/002_better_auth_tables.py b/alembic/versions/002_better_auth_tables.py index efa283f..aa2fa53 100644 --- a/alembic/versions/002_better_auth_tables.py +++ b/alembic/versions/002_better_auth_tables.py @@ -30,7 +30,10 @@ def upgrade() -> None: if inspector.has_table("users"): existing_user_cols = [c["name"] for c in inspector.get_columns("users")] if "email_verified" not in existing_user_cols: - op.add_column("users", sa.Column("email_verified", sa.Boolean(), nullable=False, server_default="false")) + op.add_column( + "users", + sa.Column("email_verified", sa.Boolean(), nullable=False, server_default="false"), + ) if "image" not in existing_user_cols: op.add_column("users", sa.Column("image", sa.Text(), nullable=True)) @@ -44,8 +47,18 @@ def upgrade() -> None: sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("ip_address", sa.Text(), nullable=True), sa.Column("user_agent", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), sa.PrimaryKeyConstraint("id"), ) op.create_index("ix_sessions_token", "sessions", ["token"], unique=True) @@ -66,8 +79,18 @@ def upgrade() -> None: sa.Column("scope", sa.Text(), nullable=True), sa.Column("id_token", sa.Text(), nullable=True), sa.Column("password", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), sa.PrimaryKeyConstraint("id"), ) op.create_index("ix_accounts_user_id", "accounts", ["user_id"]) @@ -80,8 +103,18 @@ def upgrade() -> None: sa.Column("identifier", sa.Text(), nullable=False), sa.Column("value", sa.Text(), nullable=False), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), sa.PrimaryKeyConstraint("id"), ) @@ -96,8 +129,10 @@ def upgrade() -> None: user_id_str = str(user_id) conn.execute( text( - "INSERT INTO accounts (id, user_id, account_id, provider_id, password, created_at, updated_at) " - "VALUES (gen_random_uuid()::text, :user_id, :account_id, 'credential', :password, now(), now())" + "INSERT INTO accounts " + "(id, user_id, account_id, provider_id, password, created_at, updated_at) " + "VALUES (gen_random_uuid()::text, :user_id, :account_id, " + "'credential', :password, now(), now())" ), {"user_id": user_id_str, "account_id": user_id_str, "password": hashed_password}, ) diff --git a/alembic/versions/004_fix_user_id_text.py b/alembic/versions/004_fix_user_id_text.py index 648333c..278e8f2 100644 --- a/alembic/versions/004_fix_user_id_text.py +++ b/alembic/versions/004_fix_user_id_text.py @@ -40,7 +40,12 @@ def upgrade() -> None: return # already TEXT — nothing to do # Step 1: Drop existing FK constraints (ignore if they don't exist) - op.execute(text("ALTER TABLE user_store_accounts DROP CONSTRAINT IF EXISTS user_store_accounts_user_id_fkey")) + op.execute( + text( + "ALTER TABLE user_store_accounts " + "DROP CONSTRAINT IF EXISTS user_store_accounts_user_id_fkey" + ) + ) op.execute(text("ALTER TABLE purchases DROP CONSTRAINT IF EXISTS purchases_user_id_fkey")) # Step 2: Alter users.id from uuid to text @@ -89,7 +94,12 @@ def upgrade() -> None: def downgrade() -> None: # Drop FK constraints - op.execute(text("ALTER TABLE user_store_accounts DROP CONSTRAINT IF EXISTS user_store_accounts_user_id_fkey")) + op.execute( + text( + "ALTER TABLE user_store_accounts " + "DROP CONSTRAINT IF EXISTS user_store_accounts_user_id_fkey" + ) + ) op.execute(text("ALTER TABLE purchases DROP CONSTRAINT IF EXISTS purchases_user_id_fkey")) # Revert users.id from text to uuid diff --git a/alembic/versions/005_add_email_inbound_token.py b/alembic/versions/005_add_email_inbound_token.py index c5cc2a9..f9b86c3 100644 --- a/alembic/versions/005_add_email_inbound_token.py +++ b/alembic/versions/005_add_email_inbound_token.py @@ -20,7 +20,7 @@ depends_on = None def upgrade() -> None: conn = op.get_bind() inspector = sa.inspect(conn) - # Guard: on a fresh DB Base.metadata.create_all creates users table with the column already present + # Guard: on fresh DB, Base.metadata.create_all already has the column if not inspector.has_table("users"): return existing_cols = [c["name"] for c in inspector.get_columns("users")] diff --git a/alembic/versions/006_email_inbound_token_server_default.py b/alembic/versions/006_email_inbound_token_server_default.py index e090016..d8dcbc8 100644 --- a/alembic/versions/006_email_inbound_token_server_default.py +++ b/alembic/versions/006_email_inbound_token_server_default.py @@ -6,6 +6,7 @@ Create Date: 2026-04-04 """ import sqlalchemy as sa + from alembic import op revision = "006_email_inbound_token_server_default" @@ -29,7 +30,8 @@ def upgrade() -> None: "users", "email_inbound_token", server_default=sa.text( - "replace(replace(trim(trailing '=' from encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_')" + "replace(replace(trim(trailing '=' from " + "encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_')" ), ) diff --git a/alembic/versions/007_bootstrap_users_table.py b/alembic/versions/007_bootstrap_users_table.py index e9695c0..25f913c 100644 --- a/alembic/versions/007_bootstrap_users_table.py +++ b/alembic/versions/007_bootstrap_users_table.py @@ -27,7 +27,8 @@ def upgrade() -> None: if inspector.has_table("users"): return # Table already exists (non-fresh DB or create_all already ran) - conn.execute(text(""" + conn.execute( + text(""" CREATE TABLE users ( id TEXT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, @@ -36,11 +37,20 @@ def upgrade() -> None: email_verified BOOLEAN NOT NULL DEFAULT false, image TEXT, email_inbound_token VARCHAR(22) NOT NULL UNIQUE - DEFAULT replace(replace(trim(trailing '=' from encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_'), + DEFAULT ( + replace( + replace( + trim(trailing '=' from encode(gen_random_bytes(16), 'base64')), + '+', '-' + ), + '/', '_' + ) + ), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) - """)) + """) + ) def downgrade() -> None: diff --git a/alembic/versions/008_create_domain_tables.py b/alembic/versions/008_create_domain_tables.py index 021c5bf..8a6fd53 100644 --- a/alembic/versions/008_create_domain_tables.py +++ b/alembic/versions/008_create_domain_tables.py @@ -29,8 +29,18 @@ def upgrade() -> None: sa.Column("slug", sa.String(20), nullable=False, unique=True), sa.Column("logo_url", sa.String(500), nullable=True), sa.Column("website_url", sa.String(500), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 2. store_locations @@ -45,8 +55,18 @@ def upgrade() -> None: sa.Column("zip", sa.String(10), nullable=False), sa.Column("lat", sa.Float(), nullable=True), sa.Column("lng", sa.Float(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 3. normalized_products @@ -61,8 +81,18 @@ def upgrade() -> None: sa.Column("size", sa.String(50), nullable=True), sa.Column("size_unit", sa.String(10), nullable=True), sa.Column("upc_variants", sa.JSON(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 4. purchases @@ -72,7 +102,9 @@ def upgrade() -> None: sa.Column("id", sa.Uuid(), server_default=text("gen_random_uuid()"), primary_key=True), sa.Column("user_id", sa.Text(), sa.ForeignKey("users.id"), nullable=False), sa.Column("store_id", sa.Uuid(), sa.ForeignKey("stores.id"), nullable=False), - sa.Column("store_location_id", sa.Uuid(), sa.ForeignKey("store_locations.id"), nullable=True), + sa.Column( + "store_location_id", sa.Uuid(), sa.ForeignKey("store_locations.id"), nullable=True + ), sa.Column("receipt_id", sa.String(200), nullable=False), sa.Column("purchase_date", sa.Date(), nullable=False), sa.Column("total", sa.Numeric(10, 2), nullable=False), @@ -81,9 +113,24 @@ def upgrade() -> None: sa.Column("savings_total", sa.Numeric(10, 2), nullable=True), sa.Column("source_url", sa.String(500), nullable=True), sa.Column("raw_data", sa.JSON(), nullable=True), - sa.Column("ingested_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "ingested_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), sa.UniqueConstraint("user_id", "store_id", "receipt_id", name="uq_purchase_receipt"), sa.Index("ix_purchases_user_store", "user_id", "store_id"), ) @@ -104,9 +151,24 @@ def upgrade() -> None: sa.Column("coupon_discount", sa.Numeric(10, 2), nullable=True), sa.Column("loyalty_discount", sa.Numeric(10, 2), nullable=True), sa.Column("category_raw", sa.String(100), nullable=True), - sa.Column("normalized_product_id", sa.Uuid(), sa.ForeignKey("normalized_products.id"), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "normalized_product_id", + sa.Uuid(), + sa.ForeignKey("normalized_products.id"), + nullable=True, + ), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 6. coupons @@ -115,7 +177,12 @@ def upgrade() -> None: "coupons", sa.Column("id", sa.Uuid(), server_default=text("gen_random_uuid()"), primary_key=True), sa.Column("store_id", sa.Uuid(), sa.ForeignKey("stores.id"), nullable=False), - sa.Column("normalized_product_id", sa.Uuid(), sa.ForeignKey("normalized_products.id"), nullable=True), + sa.Column( + "normalized_product_id", + sa.Uuid(), + sa.ForeignKey("normalized_products.id"), + nullable=True, + ), sa.Column("title", sa.String(300), nullable=False), sa.Column("description", sa.String(1000), nullable=True), sa.Column("discount_type", sa.String(20), nullable=False), @@ -127,8 +194,18 @@ def upgrade() -> None: sa.Column("coupon_code", sa.String(100), nullable=True), sa.Column("source_url", sa.String(500), nullable=True), sa.Column("scraped_at", sa.DateTime(timezone=True), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 7. price_history @@ -136,7 +213,12 @@ def upgrade() -> None: op.create_table( "price_history", sa.Column("id", sa.Uuid(), server_default=text("gen_random_uuid()"), primary_key=True), - sa.Column("normalized_product_id", sa.Uuid(), sa.ForeignKey("normalized_products.id"), nullable=False), + sa.Column( + "normalized_product_id", + sa.Uuid(), + sa.ForeignKey("normalized_products.id"), + nullable=False, + ), sa.Column("store_id", sa.Uuid(), sa.ForeignKey("stores.id"), nullable=False), sa.Column("observed_date", sa.Date(), nullable=False), sa.Column("regular_price", sa.Numeric(10, 2), nullable=False), @@ -144,10 +226,27 @@ def upgrade() -> None: sa.Column("loyalty_price", sa.Numeric(10, 2), nullable=True), sa.Column("coupon_price", sa.Numeric(10, 2), nullable=True), sa.Column("source", sa.String(20), nullable=False), - sa.Column("purchase_item_id", sa.Uuid(), sa.ForeignKey("purchase_items.id"), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Index("ix_price_history_product_store_date", "normalized_product_id", "store_id", "observed_date"), + sa.Column( + "purchase_item_id", sa.Uuid(), sa.ForeignKey("purchase_items.id"), nullable=True + ), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Index( + "ix_price_history_product_store_date", + "normalized_product_id", + "store_id", + "observed_date", + ), ) # 8. shrinkflation_events @@ -155,7 +254,12 @@ def upgrade() -> None: op.create_table( "shrinkflation_events", sa.Column("id", sa.Uuid(), server_default=text("gen_random_uuid()"), primary_key=True), - sa.Column("normalized_product_id", sa.Uuid(), sa.ForeignKey("normalized_products.id"), nullable=False), + sa.Column( + "normalized_product_id", + sa.Uuid(), + sa.ForeignKey("normalized_products.id"), + nullable=False, + ), sa.Column("detected_date", sa.Date(), nullable=False), sa.Column("old_size", sa.String(50), nullable=False), sa.Column("new_size", sa.String(50), nullable=False), @@ -165,8 +269,18 @@ def upgrade() -> None: sa.Column("price_at_new_size", sa.Numeric(10, 2), nullable=True), sa.Column("confidence", sa.Numeric(3, 2), server_default=text("1.00"), nullable=False), sa.Column("notes", sa.String(1000), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), ) # 9. user_store_accounts @@ -180,8 +294,18 @@ def upgrade() -> None: sa.Column("session_expires_at", sa.DateTime(timezone=True), nullable=True), sa.Column("last_sync_at", sa.DateTime(timezone=True), nullable=True), sa.Column("status", sa.String(20), server_default=text("'active'"), nullable=False), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), sa.UniqueConstraint("user_id", "store_id", name="uq_user_store_account"), ) diff --git a/alembic/versions/009_add_gin_index_upc_variants.py b/alembic/versions/009_add_gin_index_upc_variants.py index 82f1e97..8d4475f 100644 --- a/alembic/versions/009_add_gin_index_upc_variants.py +++ b/alembic/versions/009_add_gin_index_upc_variants.py @@ -6,6 +6,7 @@ Create Date: 2026-04-14 """ import sqlalchemy as sa + from alembic import op revision = "009_add_gin_index_upc_variants" diff --git a/src/cartsnitch_api/auth/dependencies.py b/src/cartsnitch_api/auth/dependencies.py index ded7013..113aeb4 100644 --- a/src/cartsnitch_api/auth/dependencies.py +++ b/src/cartsnitch_api/auth/dependencies.py @@ -5,7 +5,8 @@ Sessions are verified by querying the shared sessions table directly. """ from datetime import UTC, datetime -from fastapi import Cookie, Depends, Header, HTTPException, Request, status + +from fastapi import Depends, Header, HTTPException, Request, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession diff --git a/src/cartsnitch_api/auth/routes.py b/src/cartsnitch_api/auth/routes.py index 1c7cc63..2c547a4 100644 --- a/src/cartsnitch_api/auth/routes.py +++ b/src/cartsnitch_api/auth/routes.py @@ -6,13 +6,10 @@ endpoints that query our own user data from the shared database. """ from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel -from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from cartsnitch_api.auth.dependencies import get_current_user from cartsnitch_api.database import get_db -from cartsnitch_api.models import User from cartsnitch_api.schemas import ( UpdateUserRequest, UserResponse, diff --git a/src/cartsnitch_api/main.py b/src/cartsnitch_api/main.py index bd5a569..5a72b6b 100644 --- a/src/cartsnitch_api/main.py +++ b/src/cartsnitch_api/main.py @@ -6,10 +6,10 @@ from fastapi import APIRouter, FastAPI from cartsnitch_api.auth.routes import router as auth_router from cartsnitch_api.cache import cache_client +from cartsnitch_api.middleware.audit import add_audit_middleware from cartsnitch_api.middleware.cors import add_cors_middleware from cartsnitch_api.middleware.error_handler import add_error_handlers, add_error_monitor_middleware from cartsnitch_api.middleware.rate_limit import add_rate_limit_middleware -from cartsnitch_api.middleware.audit import add_audit_middleware from cartsnitch_api.routes.alerts import router as alerts_router from cartsnitch_api.routes.coupons import router as coupons_router from cartsnitch_api.routes.health import router as health_router @@ -26,6 +26,7 @@ from cartsnitch_api.routes.user import router as user_router @asynccontextmanager async def lifespan(app: FastAPI): from cartsnitch_api.database import dispose_engine + await cache_client.initialize() yield await cache_client.close() diff --git a/src/cartsnitch_api/models/user.py b/src/cartsnitch_api/models/user.py index 9cbd4e8..5b51778 100644 --- a/src/cartsnitch_api/models/user.py +++ b/src/cartsnitch_api/models/user.py @@ -26,9 +26,7 @@ class User(TimestampMixin, Base): email: Mapped[str] = mapped_column(String(255), nullable=False, unique=True) hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True) display_name: Mapped[str | None] = mapped_column(String(100)) - email_verified: Mapped[bool] = mapped_column( - Boolean, nullable=False, server_default="false" - ) + email_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false") image: Mapped[str | None] = mapped_column(Text, nullable=True) email_inbound_token: Mapped[str] = mapped_column( String(22), @@ -36,7 +34,8 @@ class User(TimestampMixin, Base): unique=True, default=lambda: secrets.token_urlsafe(16), server_default=sa.text( - "replace(replace(trim(trailing '=' from encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_')" + "replace(replace(trim(trailing '=' from " + "encode(gen_random_bytes(16), 'base64')), '+', '-'), '/', '_')" ), ) diff --git a/tests/test_config.py b/tests/test_config.py index 2f94ec9..f62e10e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -28,7 +28,10 @@ def test_database_url_normalizes_plain_postgresql_prefix(): "DATABASE_URL": "postgresql://cartsnitch:cartsnitch@localhost:5432/cartsnitch", } settings = Settings(**env) - assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + assert ( + settings.database_url + == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + ) def test_database_url_preserves_asyncpg_prefix(): @@ -37,10 +40,16 @@ def test_database_url_preserves_asyncpg_prefix(): "CARTSNITCH_DATABASE_URL": "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch", } settings = Settings(**env) - assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + assert ( + settings.database_url + == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + ) def test_database_url_default(): """When neither env var is set, the hardcoded default is used.""" settings = Settings() - assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + assert ( + settings.database_url + == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + )