Files
chore/backend/tests/test_chore_schedule_api.py
Ryan Kegel 234adbe05f
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m45s
Add TimeSelector and ScheduleModal components with tests
- Implemented TimeSelector component for selecting time with AM/PM toggle and minute/hour increment/decrement functionality.
- Created ScheduleModal component for scheduling chores with options for specific days or intervals.
- Added utility functions for scheduling logic in scheduleUtils.ts.
- Developed comprehensive tests for TimeSelector and scheduleUtils functions to ensure correct behavior.
2026-02-23 15:44:55 -05:00

244 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pytest
import os
from werkzeug.security import generate_password_hash
from flask import Flask
from api.chore_schedule_api import chore_schedule_api
from api.auth_api import auth_api
from db.db import users_db, child_db, chore_schedules_db, task_extensions_db
from tinydb import Query
TEST_EMAIL = "sched_test@example.com"
TEST_PASSWORD = "testpass"
TEST_CHILD_ID = "sched-child-1"
TEST_TASK_ID = "sched-task-1"
TEST_USER_ID = "sched-user-1"
def add_test_user():
users_db.remove(Query().email == TEST_EMAIL)
users_db.insert({
"id": TEST_USER_ID,
"first_name": "Sched",
"last_name": "Tester",
"email": TEST_EMAIL,
"password": generate_password_hash(TEST_PASSWORD),
"verified": True,
"image_id": "",
})
def add_test_child():
child_db.remove(Query().id == TEST_CHILD_ID)
child_db.insert({
"id": TEST_CHILD_ID,
"user_id": TEST_USER_ID,
"name": "Test Child",
"points": 0,
"image_id": "",
"tasks": [TEST_TASK_ID],
"rewards": [],
})
def login_and_set_cookie(client):
resp = client.post('/auth/login', json={"email": TEST_EMAIL, "password": TEST_PASSWORD})
assert resp.status_code == 200
@pytest.fixture
def client():
app = Flask(__name__)
app.register_blueprint(chore_schedule_api)
app.register_blueprint(auth_api, url_prefix='/auth')
app.config['TESTING'] = True
app.config['SECRET_KEY'] = 'supersecretkey'
with app.test_client() as client:
add_test_user()
add_test_child()
chore_schedules_db.truncate()
task_extensions_db.truncate()
login_and_set_cookie(client)
yield client
# ---------------------------------------------------------------------------
# GET schedule
# ---------------------------------------------------------------------------
def test_get_schedule_not_found(client):
resp = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp.status_code == 404
def test_get_schedule_returns_404_for_unknown_child(client):
resp = client.get('/child/bad-child/task/bad-task/schedule')
assert resp.status_code == 404
# ---------------------------------------------------------------------------
# PUT (set) schedule days mode
# ---------------------------------------------------------------------------
def test_set_schedule_days_mode(client):
payload = {
"mode": "days",
"day_configs": [
{"day": 1, "hour": 8, "minute": 0},
{"day": 3, "hour": 9, "minute": 30},
],
}
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
assert resp.status_code == 200
data = resp.get_json()
assert data["mode"] == "days"
assert len(data["day_configs"]) == 2
assert data["child_id"] == TEST_CHILD_ID
assert data["task_id"] == TEST_TASK_ID
def test_get_schedule_after_set(client):
payload = {"mode": "days", "day_configs": [{"day": 0, "hour": 7, "minute": 0}]}
client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
resp = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp.status_code == 200
data = resp.get_json()
assert data["mode"] == "days"
assert data["day_configs"][0]["day"] == 0
# ---------------------------------------------------------------------------
# PUT (set) schedule interval mode
# ---------------------------------------------------------------------------
def test_set_schedule_interval_mode(client):
payload = {
"mode": "interval",
"interval_days": 3,
"anchor_weekday": 2,
"interval_hour": 14,
"interval_minute": 30,
}
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
assert resp.status_code == 200
data = resp.get_json()
assert data["mode"] == "interval"
assert data["interval_days"] == 3
assert data["anchor_weekday"] == 2
assert data["interval_hour"] == 14
assert data["interval_minute"] == 30
def test_set_schedule_interval_bad_days(client):
# interval_days = 1 is out of range [27]
payload = {"mode": "interval", "interval_days": 1, "anchor_weekday": 0}
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
assert resp.status_code == 400
def test_set_schedule_interval_bad_weekday(client):
# anchor_weekday = 7 is out of range [06]
payload = {"mode": "interval", "interval_days": 2, "anchor_weekday": 7}
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
assert resp.status_code == 400
def test_set_schedule_invalid_mode(client):
payload = {"mode": "weekly", "day_configs": []}
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
assert resp.status_code == 400
def test_set_schedule_upserts_existing(client):
# Set once with days mode
client.put(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule',
json={"mode": "days", "day_configs": [{"day": 1, "hour": 8, "minute": 0}]},
)
# Overwrite with interval mode
client.put(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule',
json={"mode": "interval", "interval_days": 2, "anchor_weekday": 0, "interval_hour": 9, "interval_minute": 0},
)
resp = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp.status_code == 200
assert resp.get_json()["mode"] == "interval"
# ---------------------------------------------------------------------------
# DELETE schedule
# ---------------------------------------------------------------------------
def test_delete_schedule(client):
client.put(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule',
json={"mode": "days", "day_configs": []},
)
resp = client.delete(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp.status_code == 200
# Verify gone
resp2 = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp2.status_code == 404
def test_delete_schedule_not_found(client):
chore_schedules_db.truncate()
resp = client.delete(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
assert resp.status_code == 404
# ---------------------------------------------------------------------------
# POST extend
# ---------------------------------------------------------------------------
def test_extend_chore_time(client):
resp = client.post(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend',
json={"date": "2025-01-15"},
)
assert resp.status_code == 200
data = resp.get_json()
assert data["child_id"] == TEST_CHILD_ID
assert data["task_id"] == TEST_TASK_ID
assert data["date"] == "2025-01-15"
def test_extend_chore_time_duplicate_returns_409(client):
task_extensions_db.truncate()
client.post(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend',
json={"date": "2025-01-15"},
)
resp = client.post(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend',
json={"date": "2025-01-15"},
)
assert resp.status_code == 409
def test_extend_chore_time_different_dates_allowed(client):
task_extensions_db.truncate()
r1 = client.post(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend',
json={"date": "2025-01-15"},
)
r2 = client.post(
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend',
json={"date": "2025-01-16"},
)
assert r1.status_code == 200
assert r2.status_code == 200
def test_extend_chore_time_missing_date(client):
resp = client.post(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/extend', json={})
assert resp.status_code == 400
def test_extend_chore_time_bad_child(client):
resp = client.post('/child/bad-child/task/bad-task/extend', json={"date": "2025-01-15"})
assert resp.status_code == 404