Compare commits

..

1 Commits

Author SHA1 Message Date
Barcode Betty 4eef2aff92 fix: resolve lint failures blocking CI
CI / lint (pull_request) Failing after 4s
CI / typecheck (pull_request) Failing after 17s
CI / test (pull_request) Failing after 1m34s
CI / build-and-push (pull_request) Has been skipped
CI / deploy-dev (pull_request) Has been skipped
CI / deploy-uat (pull_request) Has been skipped
- src/cartsnitch_api/auth/dependencies.py: remove unused Cookie import
- src/cartsnitch_api/auth/routes.py: remove unused BaseModel, select, and User imports
- src/cartsnitch_api/main.py: fix import ordering

These were pre-existing issues unrelated to CAR-932 fix, blocking CI.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-23 22:19:38 +00:00
12 changed files with 46 additions and 1429 deletions
+23 -6
View File
@@ -15,7 +15,7 @@ permissions:
packages: write packages: write
env: env:
REGISTRY: git.farh.net REGISTRY: ghcr.io
IMAGE_NAME: cartsnitch/api IMAGE_NAME: cartsnitch/api
jobs: jobs:
@@ -51,6 +51,9 @@ jobs:
services: services:
postgres: postgres:
image: postgres:15-alpine image: postgres:15-alpine
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
env: env:
POSTGRES_USER: cartsnitch POSTGRES_USER: cartsnitch
POSTGRES_PASSWORD: cartsnitch_test POSTGRES_PASSWORD: cartsnitch_test
@@ -64,6 +67,9 @@ jobs:
--health-retries 5 --health-retries 5
redis: redis:
image: redis:7-alpine image: redis:7-alpine
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
ports: ports:
- 6379:6379 - 6379:6379
options: >- options: >-
@@ -116,8 +122,19 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "CalVer tag: $VERSION" echo "CalVer tag: $VERSION"
- name: Log in to Gitea Container Registry - name: Log in to Docker Hub
run: echo "${{ github.token }}" | docker login git.farh.net -u ${{ github.actor }} --password-stdin uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata - name: Extract metadata
id: meta id: meta
@@ -154,7 +171,7 @@ jobs:
only-fixed: "true" only-fixed: "true"
output-format: sarif output-format: sarif
- name: Push Docker image - name: Push Docker image
if: github.event_name == 'push' if: github.event_name == 'push'
@@ -207,7 +224,7 @@ jobs:
if: needs.build-and-push.result == 'success' if: needs.build-and-push.result == 'success'
run: | run: |
cd infra/apps/overlays/dev cd infra/apps/overlays/dev
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.api_tag.outputs.tag }} kustomize edit set image ghcr.io/cartsnitch/api:${{ steps.api_tag.outputs.tag }}
- name: Commit and push to infra - name: Commit and push to infra
run: | run: |
@@ -251,7 +268,7 @@ jobs:
if: needs.build-and-push.result == 'success' if: needs.build-and-push.result == 'success'
run: | run: |
cd infra/apps/overlays/uat cd infra/apps/overlays/uat
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.api_tag.outputs.tag }} kustomize edit set image ghcr.io/cartsnitch/api:${{ steps.api_tag.outputs.tag }}
- name: Commit and push to infra - name: Commit and push to infra
run: | run: |
+2 -1
View File
@@ -5,7 +5,8 @@ Sessions are verified by querying the shared sessions table directly.
""" """
from datetime import UTC, datetime 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 fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
-3
View File
@@ -6,13 +6,10 @@ endpoints that query our own user data from the shared database.
""" """
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from cartsnitch_api.auth.dependencies import get_current_user from cartsnitch_api.auth.dependencies import get_current_user
from cartsnitch_api.database import get_db from cartsnitch_api.database import get_db
from cartsnitch_api.models import User
from cartsnitch_api.schemas import ( from cartsnitch_api.schemas import (
UpdateUserRequest, UpdateUserRequest,
UserResponse, UserResponse,
+8 -15
View File
@@ -6,21 +6,14 @@ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_asyn
from cartsnitch_api.config import settings from cartsnitch_api.config import settings
engine = create_async_engine(
def _build_engine_kwargs() -> dict: settings.database_url,
url = settings.database_url echo=False,
kwargs: dict = {"echo": False} pool_size=10,
if not url.startswith("sqlite"): max_overflow=20,
kwargs.update( pool_pre_ping=True,
pool_size=10, pool_recycle=3600,
max_overflow=20, )
pool_pre_ping=True,
pool_recycle=3600,
)
return kwargs
engine = create_async_engine(settings.database_url, **_build_engine_kwargs())
async_session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) async_session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
+2 -2
View File
@@ -6,10 +6,11 @@ from fastapi import APIRouter, FastAPI
from cartsnitch_api.auth.routes import router as auth_router from cartsnitch_api.auth.routes import router as auth_router
from cartsnitch_api.cache import cache_client from cartsnitch_api.cache import cache_client
from cartsnitch_api.database import dispose_engine
from cartsnitch_api.middleware.audit import add_audit_middleware
from cartsnitch_api.middleware.cors import add_cors_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.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.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.alerts import router as alerts_router
from cartsnitch_api.routes.coupons import router as coupons_router from cartsnitch_api.routes.coupons import router as coupons_router
from cartsnitch_api.routes.health import router as health_router from cartsnitch_api.routes.health import router as health_router
@@ -25,7 +26,6 @@ from cartsnitch_api.routes.user import router as user_router
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
from cartsnitch_api.database import dispose_engine
await cache_client.initialize() await cache_client.initialize()
yield yield
await cache_client.close() await cache_client.close()
+4 -33
View File
@@ -51,24 +51,8 @@ def disable_rate_limiting():
@pytest.fixture @pytest.fixture
def engine(): def engine():
"""Sync in-memory SQLite engine for model unit tests. """Sync in-memory SQLite engine for model unit tests."""
Strips ALL PostgreSQL-specific server_default expressions so SQLite can
handle all column inserts without missing-function errors.
"""
eng = create_engine("sqlite:///:memory:") eng = create_engine("sqlite:///:memory:")
for table in Base.metadata.tables.values():
for col in table.columns.values():
sd = col.server_default
if sd is not None:
if not hasattr(sd, "expression"):
col.server_default = None
continue
expr_str = str(sd.expression).lower()
if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str:
col.server_default = None
Base.metadata.create_all(eng) Base.metadata.create_all(eng)
yield eng yield eng
eng.dispose() eng.dispose()
@@ -92,19 +76,9 @@ async def db_engine():
cursor.execute("PRAGMA foreign_keys=ON") cursor.execute("PRAGMA foreign_keys=ON")
cursor.close() cursor.close()
for table in Base.metadata.tables.values():
for col in table.columns.values():
sd = col.server_default
if sd is not None:
if not hasattr(sd, "expression"):
col.server_default = None
continue
expr_str = str(sd.expression).lower()
if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str:
col.server_default = None
async with engine.begin() as conn: async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
# Create Better-Auth tables (not managed by SQLAlchemy models)
await conn.execute( await conn.execute(
text(""" text("""
CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS sessions (
@@ -203,10 +177,8 @@ async def _create_test_user_and_session(
async with db_engine.begin() as conn: async with db_engine.begin() as conn:
await conn.execute( await conn.execute(
text( text(
"INSERT INTO users (id, email, hashed_password, display_name, " "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) "
"email_verified, email_inbound_token, created_at, updated_at) " "VALUES (:id, :email, :hashed_password, :display_name, :email_verified, :created_at, :updated_at)"
"VALUES (:id, :email, :hashed_password, :display_name, "
":email_verified, :email_inbound_token, :created_at, :updated_at)"
), ),
{ {
"id": user_id, "id": user_id,
@@ -214,7 +186,6 @@ async def _create_test_user_and_session(
"hashed_password": "not-used-with-better-auth", "hashed_password": "not-used-with-better-auth",
"display_name": display_name, "display_name": display_name,
"email_verified": False, "email_verified": False,
"email_inbound_token": secrets.token_urlsafe(16),
"created_at": now, "created_at": now,
"updated_at": now, "updated_at": now,
}, },
+2 -4
View File
@@ -138,9 +138,8 @@ async def test_expired_session_rejected(client, db_engine):
async with db_engine.begin() as conn: async with db_engine.begin() as conn:
await conn.execute( await conn.execute(
text( text(
"INSERT INTO users (id, email, hashed_password, display_name, " "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) "
"email_verified, email_inbound_token, created_at, updated_at) " "VALUES (:id, :email, :hp, :dn, :ev, :ca, :ua)"
"VALUES (:id, :email, :hp, :dn, :ev, :token, :ca, :ua)"
), ),
{ {
"id": user_id, "id": user_id,
@@ -148,7 +147,6 @@ async def test_expired_session_rejected(client, db_engine):
"hp": "unused", "hp": "unused",
"dn": "Expired User", "dn": "Expired User",
"ev": False, "ev": False,
"token": secrets.token_urlsafe(16),
"ca": now, "ca": now,
"ua": now, "ua": now,
}, },
+2
View File
@@ -1,5 +1,7 @@
"""Tests for Settings config, specifically the database_url env var fallback.""" """Tests for Settings config, specifically the database_url env var fallback."""
import os
from cartsnitch_api.config import Settings from cartsnitch_api.config import Settings
+2 -4
View File
@@ -65,9 +65,8 @@ class TestSessionValidation:
async with db_engine.begin() as conn: async with db_engine.begin() as conn:
await conn.execute( await conn.execute(
text( text(
"INSERT INTO users (id, email, hashed_password, display_name, " "INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) "
"email_verified, email_inbound_token, created_at, updated_at) " "VALUES (:id, :email, :hp, :dn, :ev, :ca, :ua)"
"VALUES (:id, :email, :hp, :dn, :ev, :token, :ca, :ua)"
), ),
{ {
"id": user_id, "id": user_id,
@@ -75,7 +74,6 @@ class TestSessionValidation:
"hp": "unused", "hp": "unused",
"dn": "Expired User", "dn": "Expired User",
"ev": False, "ev": False,
"token": secrets.token_urlsafe(16),
"ca": now, "ca": now,
"ua": now, "ua": now,
}, },
-12
View File
@@ -17,18 +17,6 @@ from cartsnitch_api.models.user import User, UserStoreAccount
@pytest.fixture @pytest.fixture
def engine(): def engine():
eng = create_engine("sqlite:///:memory:") eng = create_engine("sqlite:///:memory:")
for table in Base.metadata.tables.values():
for col in table.columns.values():
sd = col.server_default
if sd is not None:
if not hasattr(sd, "expression"):
col.server_default = None
continue
expr_str = str(sd.expression).lower()
if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str:
col.server_default = None
Base.metadata.create_all(eng) Base.metadata.create_all(eng)
yield eng yield eng
eng.dispose() eng.dispose()
+1 -1
View File
@@ -1,7 +1,7 @@
"""Tests for rate limiting middleware.""" """Tests for rate limiting middleware."""
import time import time
from unittest.mock import AsyncMock, MagicMock from unittest.mock import AsyncMock, MagicMock, patch
import pytest import pytest
Generated
-1348
View File
File diff suppressed because it is too large Load Diff