Skip to content

Commit

Permalink
Further update the README.md & add to the tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkoov committed May 2, 2024
1 parent a384a87 commit f01578f
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 25 deletions.
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ it with more tests to go,
since my goal is 'to cover it all'. Please
find in the
description below the tools
and stack used. Made with ❤️ on the SublimeText4 and continued on the
and stack used and the motivation behind the project. Made with ❤️ on the
SublimeText4 and continued on the
PyCharm 2024.
1.1
(Community
Expand All @@ -37,12 +38,15 @@ Edition), no affiliation to either implied.
Ever wanted to become a gourmet or a real meal maker? Try out others' recipes to both your and their satisfaction or otherwise? Or build on this to come up with a better-looking thingy, featuring more languages than just Eng/Rus? Well, [this website](https://foodgram.zapto.org/) may be your starting point. Sign up/in to post/edit/delete your recipes, add others' as your favourites or subscriptions & generate downloadable pdf shopping lists in line with the recipes you'd like to try. The shop items just sum up if duplicate, and go alphabetically. [The admin zone](https://foodgram.zapto.org/admin/) & [the docs](https://foodgram.zapto.org/api/docs/) (in Russian) follow.

This project helped me a lot in further grasping the following:
- How a Django app should be set up to interact with third-party APIs;
- How to create a custom API based on a Django project & as per its requirements;
- The way a React SPA can be connected to one's backend app to perform as one;
- Docker image & container building & deploying locally & remotely;
- DevOps, including CI/CD;
- Using both [DjDT](https://django-debug-toolbar.readthedocs.io/en/latest/) for the dev & Telegram bot notifications about GitHub Actions deploys - for better performance & automated deploys.
- Set up a Django app for it to interact with third-party APIs;
- Create a custom API based on a Django project & as per its
tech docs/requirements;
- Connect friendlily a React SPA to a backend app for both to
perform as one;
- Build & deploy both locally & remotely docker images & containers;
- Enjoy more of DevOps, including CI/CD;
- Use further both [DjDT](https://django-debug-toolbar.readthedocs.
io/en/latest/) for the dev & Telegram bot notifications about GitHub Actions deploys - for better performance & automated deploys.

Tools & stack: #Python #Django #DRF #Json #Yaml #API #Docker #Nginx #PostgreSQL #Gunicorn #Djoser #JWT #Postman #TelegramBot #Flake8 #Ruff #Black #Mypy #DjDT #Django-cleanup

Expand Down
4 changes: 3 additions & 1 deletion backend/backend/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

NUM_CHARS_INGREDIENT_NAME = NUM_CHARS_RECIPE_NAME = 200
NUM_CHARS_MEALTIME_HEX = 7
NUM_CHARS_MEALTIME_NAME = NUM_CHARS_MEALTIME_SLUG = NUM_CHARS_MEASUREMENT_UNIT = 200
NUM_CHARS_MEALTIME_NAME = NUM_CHARS_MEALTIME_SLUG = (
NUM_CHARS_MEASUREMENT_UNIT
) = 200

HEX_FIELD_REQ = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
SLUG_FIELD_REQ = "^[-a-zA-Z0-9_]+$"
Expand Down
19 changes: 10 additions & 9 deletions backend/recipes/tests.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import json
import random
from typing import List

import pytest
from django.contrib.auth import get_user_model
from django.db import IntegrityError
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient, APITestCase

from backend.constants import (
NUM_CHARS_INGREDIENT_NAME,
Expand All @@ -17,11 +16,7 @@
NUM_CHARS_MEASUREMENT_UNIT,
TEST_NUM_INGREDIENTS,
TEST_NUM_TAGS,
TEST_NUM_RECIPES,
MIN_COOKING_TIME_MINS,
MAX_COOKING_TIME_MINS,
)
from conftest import get_standard_user_data
from .models import Ingredient, Recipe, Tag
from .validators import validate_hex_color, validate_slug_field

Expand Down Expand Up @@ -172,7 +167,9 @@ def test_list_ingredients(self):
)
for x in Ingredient.objects.all():
self.assertTrue(len(x.name) <= NUM_CHARS_INGREDIENT_NAME)
self.assertTrue(len(x.measurement_unit) <= NUM_CHARS_MEASUREMENT_UNIT)
self.assertTrue(
len(x.measurement_unit) <= NUM_CHARS_MEASUREMENT_UNIT
)
tmp_ingredients.append(x.name)
self.assertEqual(Ingredient.objects.count(), len(set(tmp_ingredients)))

Expand All @@ -183,7 +180,9 @@ def test_ingredient_search(self):
measurement_unit="shovel",
)
self.assertEqual(Ingredient.objects.count(), count_ini + 1)
response = self.client.get(f"{self.ingredients_url}?name=find_me%20ingredient")
response = self.client.get(
f"{self.ingredients_url}?name=find_me%20ingredient"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
if TEST_NUM_INGREDIENTS == 2000:
self.assertEqual(
Expand All @@ -199,7 +198,9 @@ def test_ingredient_search(self):
response = self.client.get(f"{self.ingredients_url}?name=Ingredient")
self.assertEqual(response.status_code, status.HTTP_200_OK)
if TEST_NUM_INGREDIENTS == 2000:
self.assertEqual(len(json.loads(response.content)), TEST_NUM_INGREDIENTS)
self.assertEqual(
len(json.loads(response.content)), TEST_NUM_INGREDIENTS
)

def test_ingredient_detail(self):
id_ = len(self.test_ingredients)
Expand Down
34 changes: 26 additions & 8 deletions backend/users/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,22 @@ def test_list_users(
):
test_users = []
for idx in range(1, test_users_num):
assert len(get_standard_user_data["data"]["username"]) <= NUM_CHARS_USERNAME
assert (
validate_username_field(get_standard_user_data["data"]["username"]) is True
len(get_standard_user_data["data"]["username"])
<= NUM_CHARS_USERNAME
)
assert (
validate_username_field(get_standard_user_data["data"]["username"])
is True
)
assert (
len(get_standard_user_data["data"]["first_name"])
<= NUM_CHARS_FIRSTNAME
)
assert (
len(get_standard_user_data["data"]["last_name"])
<= NUM_CHARS_LASTNAME
)
assert len(get_standard_user_data["data"]["first_name"]) <= NUM_CHARS_FIRSTNAME
assert len(get_standard_user_data["data"]["last_name"]) <= NUM_CHARS_LASTNAME
assert len(get_standard_user_data["data"]["email"]) <= NUM_CHARS_EMAIL
user = User(
username=f"{get_standard_user_data['data']['username']}{idx}",
Expand Down Expand Up @@ -69,7 +79,9 @@ def test_list_users(
f"{get_standard_user_data['url']}?limit={test_users_list_limit}"
)
assert response.status_code == status.HTTP_200_OK
assert len(json.loads(response.content)["results"]) == test_users_list_limit
assert (
len(json.loads(response.content)["results"]) == test_users_list_limit
)


@pytest.mark.django_db
Expand Down Expand Up @@ -150,7 +162,9 @@ def test_get_user_me_url(api_client, get_standard_user_data):
f"{get_standard_user_data['token_url']}", test_data, format="json"
)
assert "auth_token" in json.loads(response.content)
token = Token.objects.get(user__username=get_standard_user_data["data"]["username"])
token = Token.objects.get(
user__username=get_standard_user_data["data"]["username"]
)
api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
response = api_client.get(f"{get_standard_user_data['url']}me/")
api_client.logout()
Expand All @@ -173,7 +187,9 @@ def test_user_pwd_change(api_client, get_standard_user_data):
format="json",
)
assert response.status_code == status.HTTP_201_CREATED
user = User.objects.get(username=get_standard_user_data["data"]["username"])
user = User.objects.get(
username=get_standard_user_data["data"]["username"]
)
api_client.force_authenticate(user=user)
pwd_data = {
"new_password": "what_eVa$",
Expand Down Expand Up @@ -224,7 +240,9 @@ def test_user_gets_deletes_token(api_client, get_standard_user_data):
)
assert response.status_code == status.HTTP_200_OK # Tho 201 in the Docs...
assert "auth_token" in json.loads(response.content)
token = Token.objects.get(user__username=get_standard_user_data["data"]["username"])
token = Token.objects.get(
user__username=get_standard_user_data["data"]["username"]
)
assert token is not None

api_client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
Expand Down

0 comments on commit f01578f

Please sign in to comment.