Add more access test for projects
This commit is contained in:
parent
36268581f9
commit
bb694e443d
|
|
@ -2,9 +2,15 @@ from django.test import TestCase, RequestFactory
|
||||||
from graphene.test import Client
|
from graphene.test import Client
|
||||||
|
|
||||||
from api.schema import schema
|
from api.schema import schema
|
||||||
|
from core.tests.helpers import GQLResult
|
||||||
from users.models import SchoolClass, User
|
from users.models import SchoolClass, User
|
||||||
from users.services import create_users
|
from users.services import create_users
|
||||||
|
|
||||||
|
class GQLClient(Client):
|
||||||
|
def get_result(self, *args, **kwargs):
|
||||||
|
return GQLResult(self.execute(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SkillboxTestCase(TestCase):
|
class SkillboxTestCase(TestCase):
|
||||||
def createDefault(self) -> None:
|
def createDefault(self) -> None:
|
||||||
|
|
@ -22,4 +28,6 @@ class SkillboxTestCase(TestCase):
|
||||||
if user is None:
|
if user is None:
|
||||||
user = self.teacher
|
user = self.teacher
|
||||||
request.user = user
|
request.user = user
|
||||||
return Client(schema=schema, context_value=request)
|
return GQLClient(schema=schema, context_value=request)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,20 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_extensions.db.models import TitleSlugDescriptionModel
|
from django_extensions.db.models import TitleSlugDescriptionModel
|
||||||
|
from graphql_relay import to_global_id
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
class Project(TitleSlugDescriptionModel):
|
class GraphqlNodeMixin:
|
||||||
|
def default_node_name(self):
|
||||||
|
return f'{self.__class__.__name__}Node'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def graphql_id(self):
|
||||||
|
return to_global_id(self.default_node_name(), self.id)
|
||||||
|
|
||||||
|
class Project(TitleSlugDescriptionModel, GraphqlNodeMixin):
|
||||||
objectives = models.TextField(blank=True)
|
objectives = models.TextField(blank=True)
|
||||||
appearance = models.CharField(blank=True, null=False, max_length=255)
|
appearance = models.CharField(blank=True, null=False, max_length=255)
|
||||||
student = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='projects')
|
student = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='projects')
|
||||||
|
|
@ -13,6 +24,10 @@ class Project(TitleSlugDescriptionModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
def is_viewable_by(self, user: User):
|
||||||
|
return user.id == self.student.id or (
|
||||||
|
self.final and self.student.get_teacher().id == user.id
|
||||||
|
)
|
||||||
|
|
||||||
class ProjectEntry(models.Model):
|
class ProjectEntry(models.Model):
|
||||||
activity = models.TextField(blank=True)
|
activity = models.TextField(blank=True)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from graphene_django import DjangoObjectType
|
||||||
|
|
||||||
from api.utils import get_by_id_or_slug
|
from api.utils import get_by_id_or_slug
|
||||||
from portfolio.models import Project, ProjectEntry
|
from portfolio.models import Project, ProjectEntry
|
||||||
from users.models import Role, UserRole
|
from users.models import Role, UserRole, User
|
||||||
|
|
||||||
|
|
||||||
class ProjectEntryNode(DjangoObjectType):
|
class ProjectEntryNode(DjangoObjectType):
|
||||||
|
|
@ -52,4 +52,9 @@ class PortfolioQuery(object):
|
||||||
return Project.objects.filter(student=user)
|
return Project.objects.filter(student=user)
|
||||||
|
|
||||||
def resolve_project(self, info, **kwargs):
|
def resolve_project(self, info, **kwargs):
|
||||||
return get_by_id_or_slug(Project, **kwargs)
|
user = info.context.user # type: User
|
||||||
|
project = get_by_id_or_slug(Project, **kwargs) #type: Project
|
||||||
|
|
||||||
|
if project.is_viewable_by(user):
|
||||||
|
return project
|
||||||
|
return None
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,29 @@
|
||||||
from django.test import TestCase, RequestFactory
|
|
||||||
from graphene.test import Client
|
|
||||||
from graphql_relay import to_global_id
|
|
||||||
|
|
||||||
from api.schema import schema
|
|
||||||
from core.tests.base_test import SkillboxTestCase
|
from core.tests.base_test import SkillboxTestCase
|
||||||
from portfolio.factories import ProjectFactory
|
from portfolio.factories import ProjectFactory
|
||||||
from portfolio.models import Project
|
from portfolio.models import Project
|
||||||
from rooms.models import Room
|
|
||||||
from users.factories import SchoolClassFactory
|
from users.factories import SchoolClassFactory
|
||||||
from users.models import User, SchoolClass
|
from users.models import User
|
||||||
from users.services import create_users
|
|
||||||
|
project_query = """
|
||||||
|
query ProjectQuery($id: ID!) {
|
||||||
|
project(id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ProjectQuery(SkillboxTestCase):
|
class ProjectQueryTestCaswe(SkillboxTestCase):
|
||||||
|
def _test_direct_project_access(self, user: User, should_have_access: bool):
|
||||||
|
result = self.get_client(user).get_result(project_query, variables={
|
||||||
|
'id': self.project1.graphql_id
|
||||||
|
})
|
||||||
|
self.assertIsNone(result.errors)
|
||||||
|
if should_have_access:
|
||||||
|
self.assertEqual(result.data.get('project').get('id'), self.project1.graphql_id)
|
||||||
|
else:
|
||||||
|
self.assertIsNone(result.data.get('project'))
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.createDefault()
|
self.createDefault()
|
||||||
school_class1 = SchoolClassFactory(users=[self.teacher, self.student1])
|
school_class1 = SchoolClassFactory(users=[self.teacher, self.student1])
|
||||||
|
|
@ -71,14 +82,34 @@ class ProjectQuery(SkillboxTestCase):
|
||||||
self.assertEqual(result.get('data').get('projects')[0].get('title'),
|
self.assertEqual(result.get('data').get('projects')[0].get('title'),
|
||||||
self.project1.title)
|
self.project1.title)
|
||||||
|
|
||||||
|
|
||||||
def test_other_teacher_should_not_see_projects(self):
|
def test_other_teacher_should_not_see_projects(self):
|
||||||
self.project1.final = True
|
self.project1.final = True
|
||||||
self.project1.save()
|
self.project1.save()
|
||||||
self.assertEqual(Project.objects.count(), 1)
|
self.assertEqual(Project.objects.count(), 1)
|
||||||
|
|
||||||
|
|
||||||
result = self.get_client(self.teacher2).execute(self.query)
|
result = self.get_client(self.teacher2).execute(self.query)
|
||||||
|
|
||||||
self.assertIsNone(result.get('errors'))
|
self.assertIsNone(result.get('errors'))
|
||||||
self.assertEqual(len(result.get('data').get('projects')), 0)
|
self.assertEqual(len(result.get('data').get('projects')), 0)
|
||||||
|
|
||||||
|
def test_direct_project_access(self):
|
||||||
|
# student can access own project directly
|
||||||
|
self._test_direct_project_access(self.student1, True)
|
||||||
|
# teacher can't access project, as it's not final
|
||||||
|
self._test_direct_project_access(self.teacher, False)
|
||||||
|
self._test_direct_project_access(self.teacher2, False)
|
||||||
|
# non-owner can't access project
|
||||||
|
self._test_direct_project_access(self.student2, False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_direct_final_project_access(self):
|
||||||
|
self.project1.final = True
|
||||||
|
self.project1.save()
|
||||||
|
# student can access own project directly
|
||||||
|
self._test_direct_project_access(self.student1, True)
|
||||||
|
# teacher of student can access project, as it's final
|
||||||
|
self._test_direct_project_access(self.teacher, True)
|
||||||
|
# other teacher can't access project, as it's not final
|
||||||
|
self._test_direct_project_access(self.teacher2, False)
|
||||||
|
# non-owner can't access project
|
||||||
|
self._test_direct_project_access(self.student2, False)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue