Skip to content
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

Tests for backend #572

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install flake8 pytest pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 app tests --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 app tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
- name: Test with pytest and coverage
run: |
pytest
pytest --cov=./ --cov-report=term
Empty file added backend/tests/api/__init__.py
Empty file.
212 changes: 212 additions & 0 deletions backend/tests/api/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import pytest
from app import app, db


@pytest.fixture
def client():
app_context = app.app_context()
app_context.push()
app.config['TESTING'] = True
db.create_all()
client = app.test_client()
yield client
db.session.rollback()
db.drop_all()
app_context.pop()


@pytest.fixture
def username():
return "testuser"


@pytest.fixture
def name():
return "testname"


@pytest.fixture
def password():
return "testpwd"


@pytest.fixture
def admin_username():
return "testadmin"


@pytest.fixture
def admin_name():
return "adminname"


@pytest.fixture
def admin_password():
return "adminpwd"


@pytest.fixture
def household_name():
return "testhousehold"


@pytest.fixture
def item_name():
return "testitem"

@pytest.fixture
def recipe_name():
return "Test Recipe"

@pytest.fixture
def recipe_description():
return "A test recipe description"

@pytest.fixture
def recipe_yields():
return 4

@pytest.fixture
def recipe_time():
return 30


@pytest.fixture
def onboarded_client(client, admin_username, admin_name, admin_password):
onboard_data = {
'username': admin_username,
'name': admin_name,
'password': admin_password
}
response = client.post('/api/onboarding', json=onboard_data)
return client


@pytest.fixture
def admin_client(client, admin_username, admin_name, admin_password):
onboard_data = {
'username': admin_username,
'name': admin_name,
'password': admin_password
}
response = client.post('/api/onboarding', json=onboard_data)
data = response.get_json()
client.environ_base['HTTP_AUTHORIZATION'] = f'Bearer {data["access_token"]}'
return client


@pytest.fixture
def user_client(admin_client, username, name, password):
data = {
'username': username,
'name': name,
'password': password
}
response = admin_client.post('/api/user/new', json=data)
data = {
'username': username,
'password': password
}
response = admin_client.post('/api/auth', json=data)
data = response.get_json()
admin_client.environ_base['HTTP_AUTHORIZATION'] = f'Bearer {data["access_token"]}'
return admin_client


@pytest.fixture
def user_client_with_household(user_client, household_name):
response = user_client.get('/api/user',)
assert response.status_code == 200
data = response.get_json()
user_id = data['id']
data = {
'name': household_name,
'member': [user_id]
}
response = user_client.post('/api/household', json=data)
return user_client


@pytest.fixture
def household_id(user_client_with_household):
response = user_client_with_household.get('/api/household',)
assert response.status_code == 200
data = response.get_json()
assert len(data) == 1
assert "id" in data[0]
return data[0]["id"]


@pytest.fixture
def shoppinglist_id(user_client_with_household, household_id):
response = user_client_with_household.get(
f'/api/household/{household_id}/shoppinglist',)
assert response.status_code == 200
data = response.get_json()
assert len(data) == 1
assert "id" in data[0]
return data[0]["id"]


@pytest.fixture
def shoppinglist_id_with_item(user_client_with_household, shoppinglist_id, item_name):
data = {"name": item_name}
response = user_client_with_household.post(
f'/api/shoppinglist/{shoppinglist_id}/add-item-by-name', json=data)
assert response.status_code == 200
return shoppinglist_id


@pytest.fixture
def item_id(user_client_with_household, shoppinglist_id_with_item):
response = user_client_with_household.get(
f'/api/shoppinglist/{shoppinglist_id_with_item}/items')
assert response.status_code == 200
data = response.get_json()
assert len(data) == 1
assert "id" in data[0]
return data[0]["id"]

@pytest.fixture
def recipe_with_items(user_client_with_household, household_id, recipe_name, recipe_description, recipe_yields, recipe_time, item_name):
# Create recipe with the item
recipe_data = {
'name': recipe_name,
'description': recipe_description,
'yields': recipe_yields,
'time': recipe_time,
'items': [{'name': item_name, 'description': '2 pieces'}]
}

response = user_client_with_household.post(
f'/api/household/{household_id}/recipe',
json=recipe_data
)
assert response.status_code == 200
recipe = response.get_json()
assert 'id' in recipe
return recipe['id']

@pytest.fixture
def planned_recipe(user_client_with_household, household_id, recipe_with_items):
"""Fixture that creates a meal plan with the test recipe"""
plan_data = {
'recipe_id': recipe_with_items,
'day': 0 # Plan for today
}
response = user_client_with_household.post(
f'/api/household/{household_id}/planner/recipe',
json=plan_data
)
assert response.status_code == 200

# Verify plan was created
response = user_client_with_household.get(
f'/api/household/{household_id}/planner'
)
assert response.status_code == 200
planned_meals = response.get_json()
assert len(planned_meals) > 0
assert any(meal['recipe']['id'] == recipe_with_items for meal in planned_meals)

return recipe_with_items # Return recipe_id for convenience
42 changes: 42 additions & 0 deletions backend/tests/api/test_api_household.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest


def test_get_all_households_empty(admin_client):
response = admin_client.get('/api/household',)
assert response.status_code == 200
data = response.get_json()
assert len(data) == 0


def test_add_household_admin(admin_client, household_name):
response = admin_client.get('/api/user',)
assert response.status_code == 200
data = response.get_json()
admin_user_id = data['id']
data = {
'name': household_name,
'member': [admin_user_id]
}
response = admin_client.post('/api/household', json=data)
assert response.status_code == 200


def test_add_household_user(user_client, household_name):
response = user_client.get('/api/user',)
assert response.status_code == 200
data = response.get_json()
user_id = data['id']
data = {
'name': household_name,
'member': [user_id]
}
response = user_client.post('/api/household', json=data)
assert response.status_code == 200


def test_get_all_households(user_client_with_household, household_name):
response = user_client_with_household.get('/api/household',)
assert response.status_code == 200
data = response.get_json()
assert len(data) == 1
assert data[0]["name"] == household_name
30 changes: 30 additions & 0 deletions backend/tests/api/test_api_onboarding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pytest


def test_onboarding_status_true(client):
response = client.get('/api/onboarding',)
assert response.status_code == 200
data = response.get_json()
assert "onboarding" in data
assert data["onboarding"] == True


def test_onboarding_status_false(onboarded_client):
response = onboarded_client.get('/api/onboarding',)
assert response.status_code == 200
data = response.get_json()
assert "onboarding" in data
assert data["onboarding"] == False


def test_onboarding(client, admin_username, admin_name, admin_password):
onboard_data = {
'username': admin_username,
'name': admin_name,
'password': admin_password
}
response = client.post('/api/onboarding', json=onboard_data)
assert response.status_code == 200
data = response.get_json()
assert "access_token" in data
assert "refresh_token" in data
66 changes: 66 additions & 0 deletions backend/tests/api/test_api_planner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
def test_meal_planning_basic(user_client_with_household, household_id, planned_recipe):
"""Test basic meal planning operations"""
# Get planned meals and verify the recipe is there
response = user_client_with_household.get(
f'/api/household/{household_id}/planner'
)
assert response.status_code == 200
planned_meals = response.get_json()
assert len(planned_meals) > 0
assert any(meal['recipe']['id'] == planned_recipe for meal in planned_meals)


def test_meal_planning_remove(user_client_with_household, household_id, planned_recipe):
"""Test removing meals from plan"""
# Remove from meal plan
response = user_client_with_household.delete(
f'/api/household/{household_id}/planner/recipe/{planned_recipe}',
json={'day': 0}
)
assert response.status_code == 200

# Verify removal
response = user_client_with_household.get(
f'/api/household/{household_id}/planner'
)
assert response.status_code == 200
planned_meals = response.get_json()
assert not any(meal['recipe']['id'] == planned_recipe for meal in planned_meals)


def test_recent_planned_recipes(user_client_with_household, household_id, planned_recipe):
"""Test getting recently planned recipes"""
# First remove the recipe from the plan
response = user_client_with_household.delete(
f'/api/household/{household_id}/planner/recipe/{planned_recipe}',
json={'day': 0}
)
assert response.status_code == 200

# Now get recent recipes - should include our recently dropped recipe
response = user_client_with_household.get(
f'/api/household/{household_id}/planner/recent-recipes'
)
assert response.status_code == 200
recent_recipes = response.get_json()
assert len(recent_recipes) > 0
assert any(recipe['id'] == planned_recipe for recipe in recent_recipes)


def test_suggested_recipes(user_client_with_household, household_id, recipe_with_items):
"""Test recipe suggestions functionality"""
# Get suggested recipes
response = user_client_with_household.get(
f'/api/household/{household_id}/planner/suggested-recipes'
)
assert response.status_code == 200
suggested_recipes = response.get_json()
assert isinstance(suggested_recipes, list) # Should return a list, even if empty

# Refresh suggestions
response = user_client_with_household.get(
f'/api/household/{household_id}/planner/refresh-suggested-recipes'
)
assert response.status_code == 200
refreshed_suggestions = response.get_json()
assert isinstance(refreshed_suggestions, list)
Loading