Skip to content

Commit

Permalink
Merge pull request #31 from Valbou/add-users-endpoints-#11
Browse files Browse the repository at this point in the history
Add users endpoints #11
  • Loading branch information
Valbou authored Oct 13, 2023
2 parents a824a39 + ab5136d commit ba848ba
Show file tree
Hide file tree
Showing 99 changed files with 2,947 additions and 548 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ You can follow the [Roadmap](https://github.com/Valbou/vtaskr-backend/blob/maste

![License LGPLv3](https://img.shields.io/badge/license-LGPLv3-blue "License LGPLv3")
![Python v3.8](https://img.shields.io/badge/python-v3.8-blue "Python v3.8")
![Tests 253 passed](https://img.shields.io/badge/tests-253%20passed-green "Tests 253 passed")
![Tests 291 passed](https://img.shields.io/badge/tests-291%20passed-green "Tests 291 passed")
![Coverage 94%](https://img.shields.io/badge/coverage-94%25-green "Coverage 94%")
[![CodeFactor](https://www.codefactor.io/repository/github/valbou/vtaskr-backend/badge)](https://www.codefactor.io/repository/github/valbou/vtaskr-backend)

Expand Down
14 changes: 10 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,23 @@ As vTaskr is an non profit project without fulltime dev, no release date can be
- [x] Start a frontend developpement in an other git repository

## Go to v1.1.0
- [ ] User can manage groups (create, read, update, delete and achieve)
- [ ] User can manage roles (create, read, update, delete and achieve)
- [x] User can manage groups (create, read, update, delete and achieve)
- [x] User can manage roles (create, read, update, delete and achieve)
- [x] User can manage role types (create, read, update, delete and achieve)
- [x] User can manage rights (create, read, update, delete and achieve)
- [x] User can't access to tasks not owned by at least one of his groups
- [ ] Add Keycloak integration
- [x] User can delete his account efficiently (cannot delete a user if user has admin role on 2 groups or more !)
- [x] Add filters on list endpoints (filter groups according to role or filter rights according to a group etc...)

## Go to v1.2.0
- [ ] Add Keycloak integration
- [ ] Grouping tasks in todolists
- [ ] Transform a todolist into a template to reuse it.
- [ ] Task reccurence
- [ ] Weekly/Daily email with tasks of the period

## Go to v1.3.0
- [ ] User can't modify tasks without the required role
- [x] User can't modify tasks without the required role
- [ ] User can invite another user in a group with a specific role
- [ ] Sub tasks
- [ ] Tasks status (blocking) that block parent task
Expand Down
5 changes: 5 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@
nosql_class=TestNoSQLService,
notification_class=TestNotificationService,
)

print("All registered routes:")
for rule in APP.url_map.iter_rules():
print(f"{rule.endpoint:-<30} {rule.rule}")
print(" ----- ")
7 changes: 3 additions & 4 deletions tests/libs/flask/test_querystring.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional
from unittest import TestCase

from vtaskr.libs.flask.querystring import Filter, Operations, QueryStringFilter
Expand Down Expand Up @@ -195,10 +194,10 @@ class TestDTO:
integer: int = 42
floating: float = 0.0
boolean: bool = False
opt_str: Optional[str] = None
opt_int: Optional[int] = None
opt_str: str | None = None
opt_int: int | None = None
datation: datetime = datetime.now()
opt_datation: Optional[date] = date.today()
opt_datation: date | None = date.today()


class TestQueryStringFilterLimited(QSTestMixin):
Expand Down
3 changes: 1 addition & 2 deletions tests/libs/sqlalchemy/test_queryset.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from dataclasses import dataclass
from typing import Optional
from unittest import TestCase

from sqlalchemy import Column, Integer, String, Table
Expand All @@ -14,7 +13,7 @@ class TestUser:
id: int
name: str
age: int
fullname: Optional[str]
fullname: str | None


test_table = Table(
Expand Down
10 changes: 9 additions & 1 deletion tests/tasks/hmi/flask/test_tags_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ def setUp(self) -> None:
self.tag_db = TagDB()

def test_create_tag_no_login(self):
tag_data = {"nodata": "nodata"}
title = self.fake.text(max_nb_chars=50)
tag_data = {
"title": title,
"tenant_id": "fake_id",
}
response = self.client.post(
f"{URL_API}/tags", json=tag_data, headers=self.headers
)
self.assertEqual(response.status_code, 401)

with self.app.sql.get_session() as session:
self.assertFalse(self.tag_db.exists(session, response.json.get("id")))

def test_create_tag(self):
headers = self.get_token_headers()
title = self.fake.text(max_nb_chars=50)
tag_data = {
"title": title,
"tenant_id": self.group.id,
}
response = self.client.post(f"{URL_API}/tags", json=tag_data, headers=headers)
self.assertEqual(response.status_code, 201)
Expand Down
10 changes: 9 additions & 1 deletion tests/tasks/hmi/flask/test_tasks_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ def setUp(self) -> None:
self.task_db = TaskDB()

def test_create_task_no_login(self):
task_data = {"nodata": "nodata"}
title = self.fake.sentence(nb_words=8)
task_data = {
"title": title,
"tenant_id": "fake_id",
}
response = self.client.post(
f"{URL_API}/tasks", json=task_data, headers=self.headers
)
self.assertEqual(response.status_code, 401)

with self.app.sql.get_session() as session:
self.assertFalse(self.task_db.exists(session, response.json.get("id")))

def test_create_task(self):
headers = self.get_token_headers()
title = self.fake.sentence(nb_words=8)
task_data = {
"title": title,
"tenant_id": self.group.id,
}
response = self.client.post(f"{URL_API}/tasks", json=task_data, headers=headers)
self.assertEqual(response.status_code, 201)
Expand Down
5 changes: 2 additions & 3 deletions tests/tasks/models/test_tag.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import datetime
from typing import Optional
from unittest import TestCase

from faker import Faker
Expand All @@ -21,8 +20,8 @@ def test_tag_table_fields(self):
self.assertEqual(Tag.__annotations__.get("id"), str)
self.assertEqual(Tag.__annotations__.get("title"), str)
self.assertEqual(Tag.__annotations__.get("tenant_id"), str)
self.assertEqual(Tag.__annotations__.get("color"), Optional[Color])
self.assertEqual(Tag.__annotations__.get("created_at"), Optional[datetime])
self.assertEqual(Tag.__annotations__.get("color"), Color | None)
self.assertEqual(Tag.__annotations__.get("created_at"), datetime | None)

def test_add_and_remove_tasks(self):
task_1 = Task(self.tenant_id, self.fake.text(max_nb_chars=50))
Expand Down
9 changes: 4 additions & 5 deletions tests/tasks/models/test_task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import datetime, timedelta
from typing import Optional
from unittest import TestCase

from faker import Faker
Expand All @@ -25,10 +24,10 @@ def test_task_table_fields(self):
self.assertEqual(Task.__annotations__.get("description"), str)
self.assertEqual(Task.__annotations__.get("emergency"), bool)
self.assertEqual(Task.__annotations__.get("important"), bool)
self.assertEqual(Task.__annotations__.get("created_at"), Optional[datetime])
self.assertEqual(Task.__annotations__.get("scheduled_at"), Optional[datetime])
self.assertEqual(Task.__annotations__.get("duration"), Optional[timedelta])
self.assertEqual(Task.__annotations__.get("done"), Optional[datetime])
self.assertEqual(Task.__annotations__.get("created_at"), datetime | None)
self.assertEqual(Task.__annotations__.get("scheduled_at"), datetime | None)
self.assertEqual(Task.__annotations__.get("duration"), timedelta | None)
self.assertEqual(Task.__annotations__.get("done"), datetime | None)

def test_is_done(self):
self.assertFalse(self.task.is_done())
Expand Down
12 changes: 0 additions & 12 deletions tests/users/hmi/flask/test_get_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,3 @@ def test_get_me(self):
response.json.get("created_at"),
self.user.created_at.astimezone(timezone("UTC")).isoformat(),
)

def test_no_put(self):
response = self.client.put(f"{URL_API_USERS}/me", headers=self.headers)
self.assertEqual(response.status_code, 405)

def test_no_patch(self):
response = self.client.patch(f"{URL_API_USERS}/me", headers=self.headers)
self.assertEqual(response.status_code, 405)

def test_no_delete(self):
response = self.client.delete(f"{URL_API_USERS}/me", headers=self.headers)
self.assertEqual(response.status_code, 405)
93 changes: 93 additions & 0 deletions tests/users/hmi/flask/test_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from tests.base_test import BaseTestCase
from vtaskr.users.services import GroupService

URL_API = "/api/v1"


class TestGroupAPI(BaseTestCase):
def setUp(self) -> None:
super().setUp()
self.headers = self.get_json_headers()

def test_get_group_no_login(self):
self.create_user()
response = self.client.get(
f"{URL_API}/group/{self.group.id}", headers=self.headers
)
self.assertEqual(response.status_code, 401)

def test_get_group(self):
headers = self.get_token_headers()
response = self.client.get(f"{URL_API}/group/{self.group.id}", headers=headers)
self.assertEqual(response.status_code, 200)

def test_create_group(self):
headers = self.get_token_headers()

name = self.fake.word()
data = {"name": name}
response = self.client.post(f"{URL_API}/groups", json=data, headers=headers)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json.get("name"), name)

def test_get_all_groups(self):
headers = self.get_token_headers()
response = self.client.get(f"{URL_API}/groups", headers=headers)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json), 1)

def test_update_group_put(self):
headers = self.get_token_headers()

new_name = self.fake.word()
data = {"name": new_name}
response = self.client.put(
f"{URL_API}/group/{self.group.id}", json=data, headers=headers
)

self.assertEqual(response.status_code, 200)
self.assertNotEqual(self.group.name, new_name)
self.assertEqual(response.json.get("name"), new_name)

def test_update_group_patch(self):
headers = self.get_token_headers()

new_name = self.fake.word()
data = {"name": new_name}
response = self.client.patch(
f"{URL_API}/group/{self.group.id}", json=data, headers=headers
)

self.assertEqual(response.status_code, 200)
self.assertNotEqual(self.group.name, new_name)
self.assertEqual(response.json.get("name"), new_name)

def test_delete_private_group(self):
headers = self.get_token_headers()
response = self.client.delete(
f"{URL_API}/group/{self.group.id}", headers=headers
)
self.assertEqual(response.status_code, 204)

response = self.client.get(f"{URL_API}/group/{self.group.id}", headers=headers)
self.assertEqual(response.status_code, 404)

def test_delete_group(self):
headers = self.get_token_headers()

with self.app.sql.get_session() as session:
self.group_service = GroupService(session=session)
group = self.group_service.create_group(
user_id=self.user.id, group_name=self.fake.word()
)

response = self.client.get(f"{URL_API}/group/{group.id}", headers=headers)
self.assertEqual(response.status_code, 200)

response = self.client.delete(
f"{URL_API}/group/{group.id}", headers=headers
)
self.assertEqual(response.status_code, 204)

response = self.client.get(f"{URL_API}/group/{group.id}", headers=headers)
self.assertEqual(response.status_code, 404)
Loading

0 comments on commit ba848ba

Please sign in to comment.