Doing some additional cleaning up of things, getting the project's

testing really in place.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-09-20 22:12:06 -04:00
parent 72ea9eeab2
commit 74ff483c95
7 changed files with 887 additions and 59 deletions

View File

@@ -114,43 +114,6 @@ def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
break
@session(name="pre-commit", python=python_versions[0])
def precommit(session: Session) -> None:
"""Lint using pre-commit.
Args:
session: The Nox session object.
"""
args = session.posargs or [
"run",
"--all-files",
"--hook-stage=manual",
"--show-diff-on-failure",
]
session.install(
"black",
"darglint",
"flake8",
"flake8-bandit",
"flake8-bugbear",
"flake8-docstrings",
"flake8-rst-docstrings",
"isort",
"pep8-naming",
"pre-commit",
"pre-commit-hooks",
"pyupgrade",
)
session.run("pre-commit", *args)
# Run frontend lint using yarn lint
import os
os.chdir("../frontend")
session.run("yarn", "lint", external=True)
if args and args[0] == "install":
activate_virtualenv_in_precommit_hooks(session)
@session(python=python_versions[0])
def safety(session: Session) -> None:
"""Scan dependencies for insecure packages.

View File

@@ -70,6 +70,18 @@ async def get_booking(session: AsyncSession, booking_id: int) -> Booking:
async def _validate_new_booking_room_exists(session: AsyncSession, room_id: int) -> Any:
"""Validate that the room exists for a new booking.
Args:
session (AsyncSession): Database session.
room_id (int): ID of the room to validate.
Returns:
Any: The room object if it exists.
Raises:
ValueError: If the room does not exist.
"""
try:
return await get_room(session, room_id)
except Exception as e:
@@ -80,6 +92,16 @@ async def _validate_new_booking_room_exists(session: AsyncSession, room_id: int)
def _validate_new_booking_time_constraints(
start: datetime, end: datetime, now: datetime
) -> None:
"""Validate booking time constraints for a new booking.
Args:
start (datetime): Start time of the booking.
end (datetime): End time of the booking.
now (datetime): Current time.
Raises:
ValueError: If booking is in the past or start >= end.
"""
if start < now or end < now:
logger.warning("Attempted to create booking in the past.")
raise ValueError("Bookings cannot be made in the past.")
@@ -91,6 +113,17 @@ def _validate_new_booking_time_constraints(
def _validate_new_booking_max_future(
start: datetime, end: datetime, now: datetime, max_months: int
) -> None:
"""Validate that booking is not too far in the future.
Args:
start (datetime): Start time of the booking.
end (datetime): End time of the booking.
now (datetime): Current time.
max_months (int): Maximum months allowed in advance.
Raises:
ValueError: If booking is too far in the future.
"""
max_future = now + timedelta(days=30 * max_months)
if start > max_future or end > max_future:
logger.warning("Attempted to create booking too far in the future.")
@@ -102,6 +135,15 @@ def _validate_new_booking_max_future(
async def _validate_new_booking_no_overlap(
session: AsyncSession, booking: Booking
) -> None:
"""Validate that the new booking does not overlap with existing bookings.
Args:
session (AsyncSession): Database session.
booking (Booking): Booking object to validate.
Raises:
ValueError: If booking times overlap with an existing booking.
"""
overlap_stmt = select(Booking).where(
Booking.room_id == booking.room_id,
func.tstzrange(Booking.start_time, Booking.end_time, "[)").op("&&")(
@@ -195,6 +237,19 @@ async def _validate_room_exists(
room_id: int,
get_room: Callable[[AsyncSession, int], Awaitable[Any]],
) -> Any:
"""Validate that the room exists for an update.
Args:
session (AsyncSession): Database session.
room_id (int): ID of the room to validate.
get_room (Callable): Function to get the room.
Returns:
Any: The room object if it exists.
Raises:
ValueError: If the room does not exist.
"""
try:
return await get_room(session, room_id)
except Exception as e:
@@ -208,6 +263,17 @@ def _validate_time_constraints(
now: datetime,
max_months: int,
) -> None:
"""Validate booking time constraints for an update.
Args:
new_start (datetime): New start time.
new_end (datetime): New end time.
now (datetime): Current time.
max_months (int): Maximum months allowed in advance.
Raises:
ValueError: If booking is in the past, start >= end, or too far in the future.
"""
if new_start < now or new_end < now:
logger.warning("Attempted to update booking to be in the past.")
raise ValueError("Bookings cannot be made in the past.")
@@ -229,6 +295,18 @@ async def _validate_no_overlap(
new_start: datetime,
new_end: datetime,
) -> None:
"""Validate that the updated booking does not overlap with existing bookings.
Args:
session (AsyncSession): Database session.
booking_id (int): ID of the booking to update.
room_id (int): Room ID.
new_start (datetime): New start time.
new_end (datetime): New end time.
Raises:
ValueError: If booking times overlap with an existing booking.
"""
overlap_stmt = select(Booking).where(
Booking.room_id == room_id,
Booking.id != booking_id,

View File

@@ -0,0 +1,15 @@
module.exports = {
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
docs: {
autodocs: true,
},
};

View File

@@ -9,6 +9,54 @@ from pathlib import Path
import nox
@nox.session(name="typecheck")
def typecheck(session: nox.Session) -> None:
"""Run TypeScript type checks.
Args:
session: The Nox session object.
"""
frontend_dir = Path(__file__).parent
os.chdir(frontend_dir)
session.run("yarn", "tsc", "--noEmit", external=True)
@nox.session(name="coverage")
def coverage(session: nox.Session) -> None:
"""Run Jest with coverage reporting.
Args:
session: The Nox session object.
"""
frontend_dir = Path(__file__).parent
os.chdir(frontend_dir)
session.run("yarn", "jest", "--coverage", external=True)
@nox.session(name="audit")
def audit(session: nox.Session) -> None:
"""Run yarn audit for security checks.
Args:
session: The Nox session object.
"""
frontend_dir = Path(__file__).parent
os.chdir(frontend_dir)
session.run("yarn", "audit", external=True)
@nox.session(name="storybook")
def storybook(session: nox.Session) -> None:
"""Run Storybook for frontend documentation.
Args:
session: The Nox session object.
"""
frontend_dir = Path(__file__).parent
os.chdir(frontend_dir)
session.run("yarn", "storybook", external=True)
@nox.session(name="jest")
def jest(session: nox.Session) -> None:
"""Run frontend Jest tests using yarn.

View File

@@ -62,6 +62,10 @@
"@babel/preset-env": "^7.28.3",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1",
"@storybook/addon-essentials": "^8.6.14",
"@storybook/addon-interactions": "^8.6.14",
"@storybook/addon-links": "^9.1.7",
"@storybook/react-webpack5": "^9.1.7",
"@types/testing-library__user-event": "^4.2.0",
"babel-jest": "^30.1.2",
"jest": "^30.1.1",

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,46 @@
Run with `nox -s <session>` from the project root.
"""
import os
import nox
from nox_poetry import Session
python_versions = ["3.13"]
@nox.session(name="pre-commit", python=python_versions[0])
def precommit(session: Session) -> None:
"""Lint using pre-commit for the whole project.
Args:
session: The Nox session object.
"""
args = session.posargs or [
"run",
"--all-files",
"--hook-stage=manual",
"--show-diff-on-failure",
]
session.install(
"black",
"darglint",
"flake8",
"flake8-bandit",
"flake8-bugbear",
"flake8-docstrings",
"flake8-rst-docstrings",
"isort",
"pep8-naming",
"pre-commit",
"pre-commit-hooks",
"pyupgrade",
)
session.run("pre-commit", *args)
# Run frontend lint using yarn lint
os.chdir("frontend")
session.run("yarn", "lint", external=True)
backend_sessions = [
@@ -20,6 +59,10 @@ backend_sessions = [
frontend_sessions = [
"jest",
"typecheck",
"coverage",
"audit",
"storybook",
]