#!/usr/bin/env python3 """ Tests for enhanced GET /api/habits endpoint with streak and checkedToday fields. """ import json import sys import urllib.request from pathlib import Path from datetime import datetime, timedelta BASE_URL = 'http://localhost:8088' KANBAN_DIR = Path(__file__).parent def test_habits_get_includes_streak_field(): """Test that each habit includes a 'streak' field.""" # Create test habit with completions today = datetime.now().date() yesterday = today - timedelta(days=1) two_days_ago = today - timedelta(days=2) test_data = { 'habits': [ { 'id': 'habit-test1', 'name': 'Test Habit', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [ two_days_ago.isoformat(), yesterday.isoformat(), today.isoformat() ] } ], 'lastUpdated': datetime.now().isoformat() } habits_file = KANBAN_DIR / 'habits.json' habits_file.write_text(json.dumps(test_data, indent=2), encoding='utf-8') # Test GET req = urllib.request.Request(f'{BASE_URL}/api/habits') with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) assert 'habits' in result, "Response should contain habits array" assert len(result['habits']) == 1, "Should have one habit" habit = result['habits'][0] assert 'streak' in habit, "Habit should include 'streak' field" assert isinstance(habit['streak'], int), "Streak should be an integer" assert habit['streak'] == 3, f"Expected streak of 3, got {habit['streak']}" print("✓ Each habit includes 'streak' field") def test_habits_get_includes_checked_today_field(): """Test that each habit includes a 'checkedToday' field.""" today = datetime.now().date().isoformat() test_data = { 'habits': [ { 'id': 'habit-test1', 'name': 'Checked Today', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [today] }, { 'id': 'habit-test2', 'name': 'Not Checked Today', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': ['2026-02-01'] } ], 'lastUpdated': datetime.now().isoformat() } habits_file = KANBAN_DIR / 'habits.json' habits_file.write_text(json.dumps(test_data, indent=2), encoding='utf-8') # Test GET req = urllib.request.Request(f'{BASE_URL}/api/habits') with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) assert len(result['habits']) == 2, "Should have two habits" habit1 = result['habits'][0] assert 'checkedToday' in habit1, "Habit should include 'checkedToday' field" assert isinstance(habit1['checkedToday'], bool), "checkedToday should be boolean" assert habit1['checkedToday'] is True, "Habit checked today should have checkedToday=True" habit2 = result['habits'][1] assert 'checkedToday' in habit2, "Habit should include 'checkedToday' field" assert habit2['checkedToday'] is False, "Habit not checked today should have checkedToday=False" print("✓ Each habit includes 'checkedToday' boolean field") def test_habits_get_calculates_streak_correctly(): """Test that streak is calculated using the streak utility function.""" today = datetime.now().date() yesterday = today - timedelta(days=1) two_days_ago = today - timedelta(days=2) three_days_ago = today - timedelta(days=3) four_days_ago = today - timedelta(days=4) test_data = { 'habits': [ { 'id': 'habit-daily', 'name': 'Daily Habit', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [ four_days_ago.isoformat(), three_days_ago.isoformat(), two_days_ago.isoformat(), yesterday.isoformat(), today.isoformat() ] }, { 'id': 'habit-broken', 'name': 'Broken Streak', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [ four_days_ago.isoformat(), three_days_ago.isoformat() # Missing two_days_ago - streak broken ] }, { 'id': 'habit-weekly', 'name': 'Weekly Habit', 'frequency': 'weekly', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [ today.isoformat(), (today - timedelta(days=7)).isoformat(), (today - timedelta(days=14)).isoformat() ] } ], 'lastUpdated': datetime.now().isoformat() } habits_file = KANBAN_DIR / 'habits.json' habits_file.write_text(json.dumps(test_data, indent=2), encoding='utf-8') # Test GET req = urllib.request.Request(f'{BASE_URL}/api/habits') with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) assert len(result['habits']) == 3, "Should have three habits" daily_habit = result['habits'][0] assert daily_habit['streak'] == 5, f"Expected daily streak of 5, got {daily_habit['streak']}" broken_habit = result['habits'][1] assert broken_habit['streak'] == 0, f"Expected broken streak of 0, got {broken_habit['streak']}" weekly_habit = result['habits'][2] assert weekly_habit['streak'] == 3, f"Expected weekly streak of 3, got {weekly_habit['streak']}" print("✓ Streak is calculated correctly using utility function") def test_habits_get_empty_habits_array(): """Test GET with empty habits array.""" test_data = { 'habits': [], 'lastUpdated': datetime.now().isoformat() } habits_file = KANBAN_DIR / 'habits.json' habits_file.write_text(json.dumps(test_data, indent=2), encoding='utf-8') # Test GET req = urllib.request.Request(f'{BASE_URL}/api/habits') with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) assert result['habits'] == [], "Should return empty array" assert 'lastUpdated' in result, "Should include lastUpdated" print("✓ Empty habits array handled correctly") def test_habits_get_preserves_original_fields(): """Test that all original habit fields are preserved.""" today = datetime.now().date().isoformat() test_data = { 'habits': [ { 'id': 'habit-test1', 'name': 'Test Habit', 'frequency': 'daily', 'createdAt': '2026-02-01T10:00:00Z', 'completions': [today] } ], 'lastUpdated': '2026-02-10T10:00:00Z' } habits_file = KANBAN_DIR / 'habits.json' habits_file.write_text(json.dumps(test_data, indent=2), encoding='utf-8') # Test GET req = urllib.request.Request(f'{BASE_URL}/api/habits') with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) habit = result['habits'][0] assert habit['id'] == 'habit-test1', "Original id should be preserved" assert habit['name'] == 'Test Habit', "Original name should be preserved" assert habit['frequency'] == 'daily', "Original frequency should be preserved" assert habit['createdAt'] == '2026-02-01T10:00:00Z', "Original createdAt should be preserved" assert habit['completions'] == [today], "Original completions should be preserved" assert 'streak' in habit, "Should add streak field" assert 'checkedToday' in habit, "Should add checkedToday field" print("✓ All original habit fields are preserved") if __name__ == '__main__': try: print("\n=== Testing Enhanced GET /api/habits ===\n") test_habits_get_includes_streak_field() test_habits_get_includes_checked_today_field() test_habits_get_calculates_streak_correctly() test_habits_get_empty_habits_array() test_habits_get_preserves_original_fields() print("\n=== All Enhanced GET Tests Passed ✓ ===\n") sys.exit(0) except AssertionError as e: print(f"\n❌ Test failed: {e}\n") sys.exit(1) except Exception as e: print(f"\n❌ Error: {e}\n") import traceback traceback.print_exc() sys.exit(1)