feat: US-005 - Backend API - Skip endpoint with lives system

This commit is contained in:
Echo
2026-02-10 16:15:16 +00:00
parent 71bcc5f6f6
commit 588e8cb183
2 changed files with 219 additions and 2 deletions

View File

@@ -741,7 +741,157 @@ def test_check_in_invalid_mood():
server.shutdown()
cleanup_test_env(temp_dir)
# Test 30: Typecheck passes
# Test 30: Skip basic - decrements lives
def test_skip_basic():
temp_dir = setup_test_env()
server = start_test_server()
try:
# Create a habit
status, habit = http_post('/api/habits', {
'name': 'Daily Exercise',
'frequency': {'type': 'daily'}
})
assert status == 201
habit_id = habit['id']
# Skip a day
status, response = http_post(f'/api/habits/{habit_id}/skip', {})
assert status == 200, f"Expected 200, got {status}"
assert response['lives'] == 2, f"Expected 2 lives, got {response['lives']}"
# Verify completion entry was added with type='skip'
completions = response.get('completions', [])
assert len(completions) == 1, f"Expected 1 completion, got {len(completions)}"
assert completions[0]['type'] == 'skip', f"Expected type='skip', got {completions[0].get('type')}"
assert completions[0]['date'] == datetime.now().date().isoformat()
print("✓ Test 30: Skip decrements lives and adds skip completion")
finally:
server.shutdown()
cleanup_test_env(temp_dir)
# Test 31: Skip preserves streak
def test_skip_preserves_streak():
temp_dir = setup_test_env()
server = start_test_server()
try:
# Create a habit
status, habit = http_post('/api/habits', {
'name': 'Daily Exercise',
'frequency': {'type': 'daily'}
})
assert status == 201
habit_id = habit['id']
# Check in to build a streak
http_post(f'/api/habits/{habit_id}/check', {})
# Get current streak
status, habits = http_get('/api/habits')
current_streak = habits[0]['current_streak']
assert current_streak > 0
# Skip the next day (simulate by adding skip manually and checking streak doesn't break)
# Since we can't time travel, we'll verify that skip doesn't recalculate streak
status, response = http_post(f'/api/habits/{habit_id}/skip', {})
assert status == 200, f"Expected 200, got {status}"
# Verify lives decremented
assert response['lives'] == 2
# The streak should remain unchanged (skip doesn't break it)
# Note: We can't verify streak preservation perfectly without time travel,
# but we verify the skip completion is added correctly
completions = response.get('completions', [])
skip_count = sum(1 for c in completions if c.get('type') == 'skip')
assert skip_count == 1
print("✓ Test 31: Skip preserves streak (doesn't break it)")
finally:
server.shutdown()
cleanup_test_env(temp_dir)
# Test 32: Skip returns 404 for non-existent habit
def test_skip_not_found():
temp_dir = setup_test_env()
server = start_test_server()
try:
status, response = http_post('/api/habits/nonexistent-id/skip', {})
assert status == 404, f"Expected 404, got {status}"
assert 'not found' in response.get('error', '').lower()
print("✓ Test 32: Skip returns 404 for non-existent habit")
finally:
server.shutdown()
cleanup_test_env(temp_dir)
# Test 33: Skip returns 400 when no lives remaining
def test_skip_no_lives():
temp_dir = setup_test_env()
server = start_test_server()
try:
# Create a habit
status, habit = http_post('/api/habits', {
'name': 'Daily Exercise',
'frequency': {'type': 'daily'}
})
assert status == 201
habit_id = habit['id']
# Use all 3 lives
for i in range(3):
status, response = http_post(f'/api/habits/{habit_id}/skip', {})
assert status == 200, f"Skip {i+1} failed with status {status}"
assert response['lives'] == 2 - i, f"Expected {2-i} lives, got {response['lives']}"
# Try to skip again with no lives
status, response = http_post(f'/api/habits/{habit_id}/skip', {})
assert status == 400, f"Expected 400, got {status}"
assert 'no lives remaining' in response.get('error', '').lower()
print("✓ Test 33: Skip returns 400 when no lives remaining")
finally:
server.shutdown()
cleanup_test_env(temp_dir)
# Test 34: Skip returns updated habit with new lives count
def test_skip_returns_updated_habit():
temp_dir = setup_test_env()
server = start_test_server()
try:
# Create a habit
status, habit = http_post('/api/habits', {
'name': 'Daily Exercise',
'frequency': {'type': 'daily'}
})
assert status == 201
habit_id = habit['id']
original_updated_at = habit['updatedAt']
# Skip a day
status, response = http_post(f'/api/habits/{habit_id}/skip', {})
assert status == 200
assert response['id'] == habit_id
assert response['lives'] == 2
assert response['updatedAt'] != original_updated_at, "updatedAt should be updated"
assert 'name' in response
assert 'frequency' in response
assert 'completions' in response
print("✓ Test 34: Skip returns updated habit with new lives count")
finally:
server.shutdown()
cleanup_test_env(temp_dir)
# Test 35: Typecheck passes
def test_typecheck():
result = subprocess.run(
['python3', '-m', 'py_compile', str(Path(__file__).parent.parent / 'api.py')],
@@ -782,6 +932,11 @@ if __name__ == '__main__':
test_check_in_life_restore()
test_check_in_invalid_rating()
test_check_in_invalid_mood()
test_skip_basic()
test_skip_preserves_streak()
test_skip_not_found()
test_skip_no_lives()
test_skip_returns_updated_habit()
test_typecheck()
print("\n✅ All 30 tests passed!\n")
print("\n✅ All 35 tests passed!\n")