Repository navigation
Add priority and due date columns to todos #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| -- Adds a priority and an optional due date to the todos table. | ||
| -- | ||
| -- New databases get these columns from the models. Apply this script once to an | ||
| -- existing SQLite database before deploying the matching backend version: | ||
| -- | ||
| -- sqlite3 todos.db < backend/migrations/20261009_add_priority_and_due_date.sql | ||
| -- | ||
| -- Existing rows get the default priority "medium" and no due date. | ||
|
|
||
| BEGIN; | ||
|
|
||
| ALTER TABLE todos ADD COLUMN priority VARCHAR(16) NOT NULL DEFAULT 'medium'; | ||
| ALTER TABLE todos ADD COLUMN due_date DATE; | ||
|
|
||
| -- Speeds up sorting and filtering by due date. | ||
| CREATE INDEX IF NOT EXISTS ix_todos_due_date ON todos (due_date); | ||
|
|
||
| COMMIT; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| """Tests for todo priority and due date, including the SQL migration.""" | ||
|
|
||
| import sqlite3 | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from httpx import AsyncClient | ||
|
|
||
| MIGRATION = Path(__file__).parent.parent / "migrations" / "20261009_add_priority_and_due_date.sql" | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_new_todos_default_to_medium_priority(client: AsyncClient): | ||
| response = await client.post("/api/todos", json={"title": "Plan sprint"}) | ||
| assert response.status_code == 201 | ||
| data = response.json() | ||
| assert data["priority"] == "medium" | ||
| assert data["due_date"] is None | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_create_todo_with_priority_and_due_date(client: AsyncClient): | ||
| response = await client.post( | ||
| "/api/todos", | ||
| json={"title": "Ship release", "priority": "high", "due_date": "2026-11-01"}, | ||
| ) | ||
| assert response.status_code == 201 | ||
| data = response.json() | ||
| assert data["priority"] == "high" | ||
| assert data["due_date"] == "2026-11-01" | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_update_priority_and_due_date(client: AsyncClient): | ||
| created = await client.post("/api/todos", json={"title": "Review PRs"}) | ||
| todo_id = created.json()["id"] | ||
|
|
||
| response = await client.patch( | ||
| f"/api/todos/{todo_id}", | ||
| json={"priority": "low", "due_date": "2026-12-24"}, | ||
| ) | ||
| assert response.status_code == 200 | ||
| data = response.json() | ||
| assert data["priority"] == "low" | ||
| assert data["due_date"] == "2026-12-24" | ||
| assert data["title"] == "Review PRs" | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_rejects_unknown_priority(client: AsyncClient): | ||
| response = await client.post("/api/todos", json={"title": "Bad", "priority": "urgent"}) | ||
| assert response.status_code == 422 | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_rejects_malformed_due_date(client: AsyncClient): | ||
| response = await client.post("/api/todos", json={"title": "Bad", "due_date": "next week"}) | ||
| assert response.status_code == 422 | ||
|
|
||
|
|
||
| def test_migration_upgrades_an_existing_database(tmp_path: Path): | ||
| db_path = tmp_path / "legacy.db" | ||
| connection = sqlite3.connect(db_path) | ||
| connection.executescript( | ||
| """ | ||
| CREATE TABLE todos ( | ||
| id INTEGER NOT NULL PRIMARY KEY, | ||
| title VARCHAR(255) NOT NULL, | ||
| description TEXT, | ||
| completed BOOLEAN NOT NULL, | ||
| archived_at DATETIME, | ||
| created_at DATETIME NOT NULL, | ||
| updated_at DATETIME NOT NULL | ||
| ); | ||
| INSERT INTO todos (title, completed, created_at, updated_at) | ||
| VALUES ('Existing todo', 0, '2026-01-01 00:00:00', '2026-01-01 00:00:00'); | ||
| """ | ||
| ) | ||
|
|
||
| connection.executescript(MIGRATION.read_text()) | ||
|
|
||
| columns = {row[1] for row in connection.execute("PRAGMA table_info(todos)")} | ||
| assert {"priority", "due_date"} <= columns | ||
| row = connection.execute("SELECT priority, due_date FROM todos").fetchone() | ||
| assert row == ("medium", None) | ||
| connection.close() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,15 @@ Save this as `docker-compose.override.yml` next to `docker-compose.yml`, and Com | |
|
|
||
| If you serve the frontend from your own domain, add it to `CORS_ORIGINS` on the backend, for example `CORS_ORIGINS=["https://todos.example.com"]`. See [configuration](configuration.md) for all settings. | ||
|
|
||
| ## Upgrade an existing database | ||
|
|
||
| The backend creates missing tables on startup but never alters existing ones. When a release adds columns, apply the matching script from `backend/migrations/` once before you deploy it. Take a copy of the database file first. | ||
|
|
||
| ```bash | ||
| cp todos.db todos.db.bak | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Use a consistent backup before the migration. If the backend is writing to SQLite during 🤖 Prompt for AI Agents |
||
| sqlite3 todos.db < backend/migrations/20261009_add_priority_and_due_date.sql | ||
|
Comment on lines
+44
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Run the upgrade against the configured database. The documented volume stores the database at 🤖 Prompt for AI Agents |
||
| ``` | ||
|
|
||
| ## Updating | ||
|
|
||
| ```bash | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: coderabbitai/ToDoRabbit
Length of output: 1230
🏁 Script executed:
Repository: coderabbitai/ToDoRabbit
Length of output: 7616
Reject explicit
nullforpriorityin PATCH requests.TodoUpdateaccepts{"priority": null}. The PATCH handler includes explicitly supplied fields and assigns them toTodo, so the request can attempt to storeNULLin the non-nullableprioritycolumn and fail on commit. Reject explicitnullduring validation while allowing clients to omitpriority.Suggested fix
🤖 Prompt for AI Agents