add abandoned status to distinguish given-up habits from completed ones

Completed and abandoned habits were both lumped under a single terminal
state, making it impossible to tell "finished successfully" apart from
"gave up" when reviewing habit history.
This commit is contained in:
2026-07-17 20:19:35 +09:00
parent d0dd530e26
commit 55918d56d1
11 changed files with 167 additions and 3 deletions
+12
View File
@@ -70,6 +70,18 @@ def test_complete_and_reactivate_habit(auth_client, db_session, test_user):
assert reactivate_res.json()["status"] == "active"
def test_abandon_and_reactivate_habit(auth_client, db_session, test_user):
habit = _make_habit(db_session, test_user.id)
abandon_res = auth_client.post(f"/api/habits/{habit.id}/abandon")
assert abandon_res.status_code == 200
assert abandon_res.json()["status"] == "abandoned"
reactivate_res = auth_client.post(f"/api/habits/{habit.id}/reactivate")
assert reactivate_res.status_code == 200
assert reactivate_res.json()["status"] == "active"
def test_reorder_endpoint(auth_client, db_session, test_user):
a = _make_habit(db_session, test_user.id, name="A")
b = _make_habit(db_session, test_user.id, name="B")
+24
View File
@@ -86,6 +86,30 @@ def test_complete_and_reactivate_habit(db_session, test_user):
assert reactivated.completed_at is None
def test_abandon_and_reactivate_habit(db_session, test_user):
habit = _make_habit(db_session, test_user.id)
abandoned = habit_service.abandon_habit(db_session, habit)
assert abandoned.status == HabitStatus.ABANDONED
assert abandoned.abandoned_at is not None
reactivated = habit_service.reactivate_habit(db_session, habit)
assert reactivated.status == HabitStatus.ACTIVE
assert reactivated.abandoned_at is None
def test_list_habits_filters_by_abandoned_status(db_session, test_user):
build = _make_habit(db_session, test_user.id, name="빌드")
quit_ = _make_habit(db_session, test_user.id, name="퀴트", habit_type=HabitType.QUIT)
habit_service.abandon_habit(db_session, quit_)
active = habit_service.list_habits(db_session, test_user.id, status=HabitStatus.ACTIVE)
assert [h.id for h in active] == [build.id]
abandoned = habit_service.list_habits(db_session, test_user.id, status=HabitStatus.ABANDONED)
assert [h.id for h in abandoned] == [quit_.id]
def test_reorder_habits_applies_given_order(db_session, test_user):
a = _make_habit(db_session, test_user.id, name="A")
b = _make_habit(db_session, test_user.id, name="B")
+45
View File
@@ -0,0 +1,45 @@
from app.models.habit import ALL_WEEKDAYS_MASK, HabitStatus, HabitType
from app.schemas.habit import HabitCreate
from app.services import habit_service
def _make_habit(db_session, user_id, name="테스트 습관", **overrides):
data = HabitCreate(
name=name,
habit_type=overrides.pop("habit_type", HabitType.BUILD),
weekdays_mask=overrides.pop("weekdays_mask", ALL_WEEKDAYS_MASK),
)
return habit_service.create_habit(db_session, user_id, data)
def test_abandon_habit_page_moves_habit_to_abandoned_tab(auth_client, db_session, test_user):
habit = _make_habit(db_session, test_user.id, name="포기할 습관")
response = auth_client.post(f"/habits/{habit.id}/abandon")
assert response.status_code == 200
assert response.headers["hx-redirect"] == "/habits?tab=build"
db_session.refresh(habit)
assert habit.status == HabitStatus.ABANDONED
abandoned_tab = auth_client.get("/habits?tab=abandoned")
assert "포기할 습관" in abandoned_tab.text
def test_abandon_other_users_habit_returns_404(auth_client, db_session, other_user):
others_habit = _make_habit(db_session, other_user.id, name="남의 습관")
response = auth_client.post(f"/habits/{others_habit.id}/abandon")
assert response.status_code == 404
def test_reactivate_habit_page_redirects_to_abandoned_tab(auth_client, db_session, test_user):
habit = _make_habit(db_session, test_user.id, name="다시 시작할 습관")
habit_service.abandon_habit(db_session, habit)
response = auth_client.post(f"/habits/{habit.id}/reactivate")
assert response.status_code == 200
assert response.headers["hx-redirect"] == "/habits?tab=abandoned"
db_session.refresh(habit)
assert habit.status == HabitStatus.ACTIVE