Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: SQLAlchemy Model Tests

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
run_test_suite:
name: Run Test Suite
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
# Match the Python version specified in pyproject.toml
python-version: "3.10"
cache: "pip"

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Run Test Suite
# GitHub's Ubuntu runners come with Docker pre-installed so the testcontainers
# package should work out of the box.
run: |
pytest tests/
75 changes: 74 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,75 @@
# database-schema
CARPI MySQL database schemas.

This repository is a Python package that contains SQLAlchemy data models for use in the CARPI Course Planner project.

## Installing as a Dependency

In your project's dependency list (often named `requirements.txt` or similar), add the following line:

```
carpi-data-model @ git+https://github.com/Project-CARPI/database-schema.git
```

If installing directly on the command line using pip, use the following command:

```bash
pip3 install git+https://github.com/Project-CARPI/database-schema.git
```

## Local Development & Testing

Along with the core SQLAlchemy models, this repository includes a PyTest-based test suite.

### Prerequisites

- **Python >= 3.10**: If you don't have it installed, [download it here.](https://www.python.org/)
- **Docker**: Required to automatically spin up temporary MySQL containers during testing. [Download Docker Desktop here.](https://www.docker.com/products/docker-desktop/)

### Setup Instructions

**1. Set Up a Virtual Environment**
To avoid cluttering your global Python environment, create a virtual environment in the project root:

```bash
# Create a virtual environment directory named .venv
python -m venv .venv
```

**2. Activate the Environment**
Depending on your operating system, activate the virtual environment:

- **Windows:**
```powershell
.venv\Scripts\activate
```
- **macOS / Linux:**
```bash
source .venv/bin/activate
```

You will see a `(.venv)` prefix in your terminal prompt when the environment is successfully active:

```bash
(.venv) raymond@Macbook-Pro database-schema %
```

**3. Install Required Dependencies**
With the virtual environment active, install the required packages for testing:

```bash
pip install -r requirements.txt
```

### Running the Tests

Once your environment is set up and **Docker is running** in the background, you can execute the test suite using pytest:

```bash
pytest tests/
```

To exit the virtual environment when you are finished, run the deactivate command:

```bash
deactivate
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
name = "carpi-data-model"
version = "1.0"
dependencies = ["SQLAlchemy>=2.0.0"]
requires-python = ">=3.7"
requires-python = ">=3.10"
authors = [{ name = "Raymond Chen" }, { name = "Jack Zgombic" }]
maintainers = [{ name = "Raymond Chen" }]
description = "The MySQL database schema used in Project CARPI."
Expand Down
25 changes: 24 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
SQLAlchemy==2.0.44
# Self-dependency that allows PyTest to import the package being tested.
#
# As an "editable" dependency, Python will automatically track changes to the source code
# without needing to reinstall the package after every change, which is ideal for
# development environments.
-e .

# Other third-party dependencies
certifi==2026.2.25
charset-normalizer==3.4.7
docker==7.1.0
idna==3.11
iniconfig==2.3.0
mysql-connector-python==9.6.0
packaging==26.0
pluggy==1.6.0
Pygments==2.20.0
pytest==9.0.3
python-dotenv==1.2.2
requests==2.33.1
SQLAlchemy==2.0.49
testcontainers==4.14.2
typing_extensions==4.15.0
urllib3==2.6.3
wrapt==2.1.2
80 changes: 80 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import pytest
from sqlalchemy import create_engine, inspect
from sqlalchemy.orm import Session
from testcontainers.mysql import MySqlContainer

from carpi_data_model.models import (
Attribute,
Base,
Course,
Course_Attribute,
Course_Faculty,
Course_Offering,
Course_Relationship,
Course_Restriction,
Faculty,
Restriction,
Subject,
)


@pytest.fixture(scope="session")
def engine():
"""
Spin up a temporary MySQL Docker container for the whole test session. This
requires Docker to be running on your machine.
"""
with MySqlContainer("mysql:8.0", dialect="mysqlconnector") as mysql:
db_engine = create_engine(mysql.get_connection_url(), echo=False)
try:
yield db_engine
finally:
db_engine.dispose()


@pytest.fixture(autouse=True)
def setup_database(engine):
"""
Creates all tables before each test and drops them after to ensure a clean
state for every test.
"""
Base.metadata.create_all(bind=engine)
yield
Base.metadata.drop_all(bind=engine)


def test_create_tables(engine):
"""
Test that the SQLAlchemy schema can be translated into DDL and executed
in a Native MySQL database without throwing any mapping or constraint
errors. Ensures the tables actually exist in the database catalog.
"""
inspector = inspect(engine)
existing_tables = inspector.get_table_names()

assert Subject.__tablename__ in existing_tables
assert Attribute.__tablename__ in existing_tables
assert Restriction.__tablename__ in existing_tables
assert Faculty.__tablename__ in existing_tables
assert Course.__tablename__ in existing_tables
assert Course_Attribute.__tablename__ in existing_tables
assert Course_Relationship.__tablename__ in existing_tables
assert Course_Restriction.__tablename__ in existing_tables
assert Course_Offering.__tablename__ in existing_tables
assert Course_Faculty.__tablename__ in existing_tables


def test_insert_and_query(engine):
"""
Test basic database operations (insert and query) on a table to ensure the
ORM definitions functionally work.
"""
with Session(engine) as session:
subj = Subject(subj_code="CSCI", title="Computer Science")
session.add(subj)
session.commit()

fetched = session.query(Subject).filter_by(subj_code="CSCI").first()
assert fetched is not None
assert fetched.subj_code == "CSCI"
assert fetched.title == "Computer Science"
Loading