Merge pull request #9 from minhtrannhat/backend
Fix(testing): Ctxmanager was unable to authenticate users
This commit is contained in:
commit
c596838489
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -2,7 +2,7 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [backend, frontend]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
28
README.md
28
README.md
@ -6,6 +6,10 @@
|
||||
|
||||
- `prettier`: Formatter
|
||||
|
||||
### Frontend Technical Write-up
|
||||
|
||||
Inside the `backend\` folder
|
||||
|
||||
## Backend
|
||||
|
||||
### Development workflow
|
||||
@ -42,26 +46,4 @@
|
||||
|
||||
### Backend Technical Write-up
|
||||
|
||||
#### Quart specific terminologies
|
||||
|
||||
`blueprint`: a collection of route handlers/API functionalities.
|
||||
|
||||
#### API route trailing slashes
|
||||
|
||||
API paths should end with a slash i.e: `/sessions/` rather than `/session`.
|
||||
This is because requests sent to `/sessions` will be redirected to `/sessions/` whereas `/sessions/` won't get redirected.
|
||||
|
||||
#### Difference between database schema and database model
|
||||
|
||||
- A schema defines the structure of data within the database.
|
||||
- A model is a class that can be represented as rows in the database, i.e ID row, age row as class member.
|
||||
|
||||
#### Managing user's sessions (Authentication)
|
||||
|
||||
- Login should results in a cookie being set in the user's browser, which is being sent in every subsequent request.
|
||||
The presence and value of this cookie are used to determine whether the member is logged in, and which member made the request.
|
||||
- Logout results in the cookie being deleted.
|
||||
|
||||
#### Idempotent routes
|
||||
|
||||
Idempotence is a property of a route where the final state is achieved no matter how many times the route is called, that is, calling the route once or 10 times has the same effect. This is a useful property as it means the route can be safely retried if the request fails. For RESTful and HTTP APIs, the routes using GET, PUT, and DELETE verbs are expected to be idempotent.
|
||||
Inside the `backend\` folder
|
||||
|
27
backend/README.md
Normal file
27
backend/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Backend Technical Write Up
|
||||
|
||||
## General Bits of Information
|
||||
|
||||
### Quart specific terminologies
|
||||
|
||||
`blueprint`: a collection of route handlers/API functionalities.
|
||||
|
||||
### API route trailing slashes
|
||||
|
||||
API paths should end with a slash i.e: `/sessions/` rather than `/session`.
|
||||
This is because requests sent to `/sessions` will be redirected to `/sessions/` whereas `/sessions/` won't get redirected.
|
||||
|
||||
### Difference between database schema and database model
|
||||
|
||||
- A schema defines the structure of data within the database.
|
||||
- A model is a class that can be represented as rows in the database, i.e ID row, age row as class member.
|
||||
|
||||
### Managing user's sessions (Authentication)
|
||||
|
||||
- Login should results in a cookie being set in the user's browser, which is being sent in every subsequent request.
|
||||
The presence and value of this cookie are used to determine whether the member is logged in, and which member made the request.
|
||||
- Logout results in the cookie being deleted.
|
||||
|
||||
### Idempotent routes
|
||||
|
||||
Idempotence is a property of a route where the final state is achieved no matter how many times the route is called, that is, calling the route once or 10 times has the same effect. This is a useful property as it means the route can be safely retried if the request fails. For RESTful and HTTP APIs, the routes using GET, PUT, and DELETE verbs are expected to be idempotent.
|
@ -3,7 +3,7 @@ name = "Todo_api"
|
||||
version = ""
|
||||
description = ""
|
||||
authors = [
|
||||
{name = "Minh Tran Nhat", email = "minhtrannhat@minhtrannhat.com"},
|
||||
{name = "Minh Tran Nhat", email = "minh@minhtrannhat.com"},
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
license = {text = "Private"}
|
||||
@ -58,7 +58,7 @@ ignore_missing_imports = true
|
||||
|
||||
[tool.pdm.scripts]
|
||||
format-black = "black src/ tests/"
|
||||
format-djhtml = "djhtml src/backend/templates -t 2 --in-place"
|
||||
format-djhtml = "djhtml src/backend/templates -t 2"
|
||||
format-isort = "isort src tests"
|
||||
format = {composite = ["format-black", "format-djhtml", "format-isort"]}
|
||||
|
||||
|
@ -29,7 +29,7 @@ async def select_todos(
|
||||
WHERE member_id = :member_id
|
||||
AND complete = :complete"""
|
||||
values = {"member_id": member_id, "complete": complete}
|
||||
return [Todo(**row) async for row in await connection.iterate(query, values)]
|
||||
return [Todo(**row) async for row in connection.iterate(query, values)]
|
||||
|
||||
|
||||
async def select_todo(
|
||||
|
@ -109,12 +109,14 @@ def recreate_db() -> None:
|
||||
f"CREATE DATABASE {db_url.path.removeprefix('/')}",
|
||||
],
|
||||
)
|
||||
call(
|
||||
|
||||
call( # nosec
|
||||
[
|
||||
"psql",
|
||||
"-U",
|
||||
"postgres",
|
||||
"-c",
|
||||
f"ALTER DATABASE {db_url.path.removeprefix('/')} OWNER TO {db_url.username}",
|
||||
f"ALTER DATABASE \
|
||||
{db_url.path.removeprefix('/')} OWNER TO {db_url.username}",
|
||||
]
|
||||
)
|
||||
|
@ -1,35 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Todo - email</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
|
||||
<body
|
||||
style="
|
||||
<body style="
|
||||
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
margin: 0;
|
||||
"
|
||||
>
|
||||
<table
|
||||
width="100%"
|
||||
height="100%"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
>
|
||||
">
|
||||
<table width="100%" height="100%" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table
|
||||
height="100%"
|
||||
cellpadding="20"
|
||||
cellspacing="0"
|
||||
border="0"
|
||||
style="max-width: 540px"
|
||||
>
|
||||
<table height="100%" cellpadding="20" cellspacing="0" border="0" style="max-width: 540px">
|
||||
<tr>
|
||||
<td align="left" width="540">
|
||||
{% block welcome %} Hello, {% endblock %}
|
||||
@ -48,4 +35,5 @@
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -2,6 +2,7 @@ import pytest
|
||||
from freezegun import freeze_time
|
||||
from itsdangerous import URLSafeTimedSerializer
|
||||
from quart import Quart
|
||||
from quart_auth import authenticated_client
|
||||
|
||||
from backend.blueprints.members import EMAIL_VERIFICATION_SALT
|
||||
|
||||
@ -44,7 +45,7 @@ async def test_change_password(app: Quart, caplog: pytest.LogCaptureFixture) ->
|
||||
"password": "testPassword2$",
|
||||
}
|
||||
response = await test_client.post("/members/", json=data)
|
||||
async with test_client.authenticated("2"): # type: ignore
|
||||
async with authenticated_client(test_client, auth_id=2): # type: ignore
|
||||
response = await test_client.put(
|
||||
"/members/password/",
|
||||
json={
|
||||
|
@ -1,9 +1,10 @@
|
||||
from quart import Quart
|
||||
from quart_auth import authenticated_client
|
||||
|
||||
|
||||
async def test_post_todo(app: Quart) -> None:
|
||||
test_client = app.test_client()
|
||||
async with test_client.authenticated("1"): # type: ignore
|
||||
async with authenticated_client(test_client, auth_id=1): # type: ignore
|
||||
response = await test_client.post(
|
||||
"/todos/",
|
||||
json={"complete": False, "due": None, "task": "Test task"},
|
||||
@ -14,7 +15,7 @@ async def test_post_todo(app: Quart) -> None:
|
||||
|
||||
async def test_get_todo(app: Quart) -> None:
|
||||
test_client = app.test_client()
|
||||
async with test_client.authenticated("1"): # type: ignore
|
||||
async with authenticated_client(test_client, auth_id=1): # type: ignore
|
||||
response = await test_client.get("/todos/1/")
|
||||
assert response.status_code == 200
|
||||
assert (await response.get_json())["task"] == "Test Task"
|
||||
@ -22,7 +23,7 @@ async def test_get_todo(app: Quart) -> None:
|
||||
|
||||
async def test_put_todo(app: Quart) -> None:
|
||||
test_client = app.test_client()
|
||||
async with test_client.authenticated("1"): # type: ignore
|
||||
async with authenticated_client(test_client, auth_id=1): # type: ignore
|
||||
response = await test_client.post(
|
||||
"/todos/",
|
||||
json={"complete": False, "due": None, "task": "Test task"},
|
||||
@ -41,7 +42,7 @@ async def test_put_todo(app: Quart) -> None:
|
||||
|
||||
async def test_delete_todo(app: Quart) -> None:
|
||||
test_client = app.test_client()
|
||||
async with test_client.authenticated("1"): # type: ignore
|
||||
async with authenticated_client(test_client, auth_id=1): # type: ignore
|
||||
response = await test_client.post(
|
||||
"/todos/",
|
||||
json={"complete": False, "due": None, "task": "Test task"},
|
||||
|
Loading…
x
Reference in New Issue
Block a user