7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
199 lines
6.2 KiB
Python
199 lines
6.2 KiB
Python
"""Tests for authentication-related database models."""
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from local_deep_research.database.models import Base, User
|
|
|
|
|
|
class TestUserModel:
|
|
"""Test suite for the User model."""
|
|
|
|
@pytest.fixture
|
|
def engine(self):
|
|
"""Create an in-memory SQLite database for testing."""
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
yield engine
|
|
engine.dispose()
|
|
|
|
@pytest.fixture
|
|
def session(self, engine):
|
|
"""Create a database session for testing."""
|
|
Session = sessionmaker(bind=engine)
|
|
session = Session()
|
|
yield session
|
|
session.close()
|
|
|
|
def test_create_user(self, session):
|
|
"""Test creating a new user."""
|
|
user = User(username="testuser")
|
|
|
|
session.add(user)
|
|
session.commit()
|
|
|
|
# Retrieve the user
|
|
saved_user = session.query(User).filter_by(username="testuser").first()
|
|
assert saved_user is not None
|
|
assert saved_user.username == "testuser"
|
|
assert saved_user.created_at is not None
|
|
assert saved_user.database_version == 1
|
|
|
|
def test_unique_username_constraint(self, session):
|
|
"""Test that usernames must be unique."""
|
|
user1 = User(username="testuser")
|
|
session.add(user1)
|
|
session.commit()
|
|
|
|
# Try to create another user with the same username
|
|
user2 = User(username="testuser")
|
|
session.add(user2)
|
|
|
|
with pytest.raises(IntegrityError):
|
|
session.commit()
|
|
|
|
def test_user_timestamps(self, session):
|
|
"""Test that created_at is set correctly."""
|
|
user = User(username="testuser")
|
|
|
|
# Before saving, created_at should be None
|
|
assert user.created_at is None
|
|
|
|
session.add(user)
|
|
session.commit()
|
|
|
|
# After saving, created_at should be set
|
|
assert user.created_at is not None
|
|
assert isinstance(user.created_at, datetime)
|
|
|
|
# Update last_login
|
|
user.last_login = datetime.now(timezone.utc)
|
|
session.commit()
|
|
|
|
# Verify last_login is set
|
|
assert user.last_login is not None
|
|
assert user.last_login >= user.created_at
|
|
|
|
def test_user_last_login(self, session):
|
|
"""Test last_login timestamp."""
|
|
user = User(username="testuser")
|
|
|
|
session.add(user)
|
|
session.commit()
|
|
|
|
# Initially last_login should be None
|
|
assert user.last_login is None
|
|
|
|
# Update last_login
|
|
login_time = datetime.now(timezone.utc)
|
|
user.last_login = login_time
|
|
session.commit()
|
|
|
|
# Verify last_login is set
|
|
saved_user = session.query(User).filter_by(username="testuser").first()
|
|
assert saved_user.last_login is not None
|
|
assert abs((saved_user.last_login - login_time).total_seconds()) < 1
|
|
|
|
def test_user_representation(self):
|
|
"""Test string representation of User model."""
|
|
user = User(username="testuser")
|
|
|
|
# Should have a meaningful string representation
|
|
assert repr(user) == "<User testuser>"
|
|
|
|
def test_database_path_property(self):
|
|
"""Test the database_path property generates consistent paths."""
|
|
user1 = User(username="testuser")
|
|
user2 = User(username="testuser")
|
|
user3 = User(username="different")
|
|
|
|
# Same username should generate same path
|
|
assert user1.database_path == user2.database_path
|
|
|
|
# Different username should generate different path
|
|
assert user1.database_path != user3.database_path
|
|
|
|
# Path should have expected format
|
|
assert user1.database_path.startswith("ldr_user_")
|
|
assert user1.database_path.endswith(".db")
|
|
|
|
def test_database_version_default(self, session):
|
|
"""Test that database_version defaults to 1."""
|
|
user = User(username="testuser")
|
|
|
|
session.add(user)
|
|
session.commit()
|
|
|
|
assert user.database_version == 1
|
|
|
|
# Can update version
|
|
user.database_version = 2
|
|
session.commit()
|
|
|
|
saved_user = session.query(User).filter_by(username="testuser").first()
|
|
assert saved_user.database_version == 2
|
|
|
|
def test_username_with_special_characters(self, session):
|
|
"""Test usernames with special characters."""
|
|
special_users = [
|
|
"user@email.com",
|
|
"user-with-dashes",
|
|
"user_with_underscores",
|
|
"user.with.dots",
|
|
"user123",
|
|
"UPPERCASE",
|
|
"مستخدم", # Arabic
|
|
"用户", # Chinese
|
|
"🎉user", # Emoji
|
|
]
|
|
|
|
for username in special_users:
|
|
user = User(username=username)
|
|
session.add(user)
|
|
|
|
session.commit()
|
|
|
|
# All should be saved successfully
|
|
assert session.query(User).count() == len(special_users)
|
|
|
|
# Each should have a valid database path
|
|
for username in special_users:
|
|
user = session.query(User).filter_by(username=username).first()
|
|
assert user is not None
|
|
assert user.database_path.startswith("ldr_user_")
|
|
assert user.database_path.endswith(".db")
|
|
# Path should be filesystem-safe (no special chars)
|
|
assert all(c.isalnum() or c in "_." for c in user.database_path)
|
|
|
|
def test_user_query_operations(self, session):
|
|
"""Test various query operations on User model."""
|
|
# Create multiple users
|
|
users = []
|
|
for i in range(5):
|
|
user = User(username=f"user{i}")
|
|
users.append(user)
|
|
session.add(user)
|
|
|
|
session.commit()
|
|
|
|
# Query all users
|
|
all_users = session.query(User).all()
|
|
assert len(all_users) == 5
|
|
|
|
# Query by username
|
|
user2 = session.query(User).filter_by(username="user2").first()
|
|
assert user2 is not None
|
|
assert user2.username == "user2"
|
|
|
|
# Query ordered by created_at
|
|
ordered_users = session.query(User).order_by(User.created_at).all()
|
|
assert len(ordered_users) == 5
|
|
|
|
# Query with limit
|
|
limited_users = session.query(User).limit(3).all()
|
|
assert len(limited_users) == 3
|