Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • djangoldp-packages/djangoldp
  • decentral1se/djangoldp
  • femmefaytale/djangoldp
  • jvtrudel/djangoldp
4 results
Show changes
Showing
with 3539 additions and 619 deletions
......@@ -3,9 +3,7 @@ import json
from django.test import TestCase
from rest_framework.test import APIClient
from djangoldp.permissions import LDPPermissions
from djangoldp.tests.models import JobOffer
from djangoldp.views import LDPViewSet
class TestAnonymousUserPermissions(TestCase):
......@@ -26,6 +24,8 @@ class TestAnonymousUserPermissions(TestCase):
response = self.client.post('/job-offers/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 403)
# TODO: test POST request for anonymous user where it's allowed
def test_put_request_for_anonymousUser(self):
body = {'title':"job_updated"}
response = self.client.put('/job-offers/{}/'.format(self.job.pk), data=json.dumps(body),
......
import json
from django.contrib.auth import get_user_model
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from rest_framework.test import APIClient, APIRequestFactory, APITestCase
from djangoldp.tests.models import UserProfile
......@@ -18,18 +19,8 @@ class TestAutoAuthor(APITestCase):
def test_save_with_anonymous_user(self):
post = {
'@graph': [{'http://happy-dev.fr/owl/#content': "post content"}]}
response = self.client.post('/posts/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEquals(response.data['content'], "post content")
def test_auto_author_field(self):
self.client.force_authenticate(user=self.user)
post = {
'@graph': [{'http://happy-dev.fr/owl/#content': "post content"}]}
'@graph': [{'https://cdn.startinblox.com/owl#content': "post content"}]}
response = self.client.post('/posts/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEquals(response.data['content'], "post content")
self.assertIsNotNone(response.data['author'])
self.assertEqual(response.data['content'], "post content")
\ No newline at end of file
import json
import copy
import time
import uuid
from django.contrib.auth import get_user_model
from django.db import IntegrityError
from django.test import override_settings
from rest_framework.test import APIClient, APITestCase
from djangoldp.tests.models import Circle, CircleMember, Project, UserProfile, DateModel, DateChild
from djangoldp.models import Activity, Follower
from djangoldp.activities.services import (BACKLINKS_ACTOR, ActivityPubService,
ActivityQueueService)
from djangoldp.models import Activity, ScheduledActivity
from djangoldp.tests.models import Circle, Project
class TestsBacklinksService(APITestCase):
......@@ -22,7 +26,9 @@ class TestsBacklinksService(APITestCase):
urlid = 'https://distant.com/users/' + username
return get_user_model().objects.create_user(username=username, email=email, password='test', urlid=urlid)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# TODO: inbox discovery (https://git.startinblox.com/djangoldp-packages/djangoldp/issues/233)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_local_object_with_distant_foreign_key(self):
# a local Circle with a distant owner
local_circle = Circle.objects.create(description='Test')
......@@ -54,24 +60,24 @@ class TestsBacklinksService(APITestCase):
local_circle.delete()
self.assertEqual(Activity.objects.all().count(), 4)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_local_object_with_external_m2m_join_leave(self):
# a local project with three distant users
project = Project.objects.create(description='Test')
external_a = self._get_random_external_user()
external_b = self._get_random_external_user()
external_c = self._get_random_external_user()
project.team.add(external_a)
project.team.add(external_b)
project.team.add(external_c)
project.members.add(external_a)
project.members.add(external_b)
project.members.add(external_c)
self.assertEqual(Activity.objects.all().count(), 3)
# remove one individual
project.team.remove(external_a)
project.members.remove(external_a)
self.assertEqual(Activity.objects.all().count(), 4)
# clear the rest
project.team.clear()
project.members.clear()
self.assertEqual(Activity.objects.all().count(), 6)
prior_count = Activity.objects.all().count()
......@@ -79,12 +85,241 @@ class TestsBacklinksService(APITestCase):
project.delete()
self.assertEqual(Activity.objects.all().count(), prior_count)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_local_object_with_external_m2m_delete_parent(self):
project = Project.objects.create(description='Test')
external_a = self._get_random_external_user()
project.team.add(external_a)
project.members.add(external_a)
prior_count = Activity.objects.all().count()
project.delete()
self.assertEqual(Activity.objects.all().count(), prior_count + 1)
# test that older ScheduledActivity is discarded for newer ScheduledActivity
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_old_invalid_scheduled_activity_discarded(self):
def send_two_activities_and_assert_old_discarded(obj):
# there are two scheduled activities with the same object, (and different time stamps)
old_activity = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='old')
old_scheduled = ActivityQueueService._save_sent_activity(old_activity, ScheduledActivity)
new_activity = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Update', summary='new')
new_scheduled = ActivityQueueService._save_sent_activity(new_activity, ScheduledActivity)
# both are sent to the ActivityQueueService
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', old_scheduled)
time.sleep(0.1)
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', new_scheduled)
time.sleep(0.1)
# assert that all scheduled activities were cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
# assert that ONLY the newly scheduled activity was sent
activities = Activity.objects.all()
self.assertEqual(Activity.objects.count(), 1)
astream = activities[0].to_activitystream()
self.assertEqual(astream['summary'], new_activity['summary'])
activities[0].delete()
# variation using expanded syntax
obj = {
'@id': 'https://test.com/users/test/'
}
send_two_activities_and_assert_old_discarded(obj)
# variation using id-only syntax
obj = 'https://test.com/users/test/'
send_two_activities_and_assert_old_discarded(obj)
# test that older ScheduledActivity is still sent if it's on a different object
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_old_valid_scheduled_activity_sent(self):
# there are two scheduled activities with different objects
obj = 'https://test.com/users/test1/'
activity_a = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='A')
scheduled_a = ActivityQueueService._save_sent_activity(activity_a, ScheduledActivity)
obj = 'https://test.com/users/test2/'
activity_b = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='B')
scheduled_b = ActivityQueueService._save_sent_activity(activity_b, ScheduledActivity)
# both are sent to the same inbox
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_a)
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_b)
# assert that both scheduled activities were sent, and the scheduled activities were cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 2)
# variation on the previous test where the two activities are working on different models (using the same object)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_old_valid_scheduled_activity_sent_same_object(self):
obj = 'https://test.com/users/test1/'
target = {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/4/'}
activity_a = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Add', summary='A', target=target)
scheduled_a = ActivityQueueService._save_sent_activity(activity_a, ScheduledActivity)
obj = 'https://test.com/users/test1/'
target = {'@type': 'hd:joboffer', '@id': 'https://api.test1.startinblox.com/job-offers/1/'}
activity_b = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Add', summary='B', target=target)
scheduled_b = ActivityQueueService._save_sent_activity(activity_b, ScheduledActivity)
# both are sent to the same inbox
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_a)
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_b)
# assert that both scheduled activities were sent, and the scheduled activities were cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 2)
# variation using an Add and a Remove (one defines target, the other origin)
# also tests that an unnecessary add is not sent
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_matching_origin_and_target_not_sent(self):
a = {'type': 'Add', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'target': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
scheduled_a = ActivityQueueService._save_sent_activity(a, ScheduledActivity)
b = {'type': 'Remove', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'origin': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
scheduled_b = ActivityQueueService._save_sent_activity(b, ScheduledActivity)
# both are sent to the same inbox
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_a)
ActivityQueueService._activity_queue_worker('http://127.0.0.1:8001/idontexist/', scheduled_b)
# assert that both scheduled activities were sent, and the scheduled activities were cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_unnecessary_add_not_sent(self):
# an add activity was sent previously
a = {'type': 'Add', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'target': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
ActivityQueueService._save_activity_from_response({'status_code': '201'}, 'https://distant.com/inbox/', a)
# no remove has since been sent, but a new Add is scheduled
scheduled_b = ActivityQueueService._save_sent_activity(a, ScheduledActivity, success=False, type='add',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled_b)
# assert that only the previous activity was sent, and the scheduled activites cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_unnecessary_remove_not_sent(self):
# an remove activity was sent previously
a = {'type': 'Remove', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'target': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
ActivityQueueService._save_activity_from_response({'status_code': '201'}, 'https://distant.com/inbox/', a)
# no add has since been sent, but a new Remove is scheduled
scheduled_b = ActivityQueueService._save_sent_activity(a, ScheduledActivity, success=False, type='remove',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled_b)
# assert that only the previous activity was sent, and the scheduled activites cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_necessary_add_sent(self):
# a remove activity was sent previously
a = {'type': 'Remove', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'target': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
ActivityQueueService._save_activity_from_response({'status_code': '201'}, 'https://distant.com/inbox/', a)
# an add is now being sent
scheduled_b = ActivityQueueService._save_sent_activity(a, ScheduledActivity, type='add',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled_b)
# assert that both activities sent, and the scheduled activites cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 2)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_first_add_sent(self):
# no activity has been sent with this target, before this add
a = {'type': 'Add', 'actor': {'type': 'Service', 'name': 'Backlinks Service'},
'object': {'@type': 'foaf:user', '@id': 'https://api.test2.startinblox.com/users/calum/'},
'target': {'@type': 'hd:skill', '@id': 'https://api.test1.startinblox.com/skills/3/'}}
scheduled = ActivityQueueService._save_sent_activity(a, ScheduledActivity, success=True, type='add',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled)
# assert that the activity was sent, and the scheduled activites cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
# validate Update activity objects have new info before sending the notification
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_unnecessary_update_not_sent(self):
# an object was sent in one activity
obj = {
'@type': 'hd:circle',
'@id': 'https://test.com/circles/8/',
'owner': {'@id': 'https://distant.com/users/john/',
'@type': 'foaf:user'}
}
activity_a = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='A')
ActivityQueueService._save_activity_from_response({'status_code': '201'}, 'https://distant.com/inbox/', activity_a)
# now I'm sending an update, which doesn't change anything about the object
activity_b = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='B')
scheduled_b = ActivityQueueService._save_sent_activity(activity_b, ScheduledActivity, type='update',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled_b)
# assert that only the previous activity was sent, and the scheduled activites cleaned up
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_necessary_update_is_sent(self):
# an object was sent in one activity
obj = {
'@type': 'hd:circle',
'@id': 'https://test.com/circles/8/',
'owner': {'@id': 'https://distant.com/users/john/',
'@type': 'foaf:user'}
}
activity_a = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='A')
ActivityQueueService._save_activity_from_response({'status_code': '201'}, 'https://distant.com/inbox/', activity_a)
# now I'm sending an update, which changes the owner of the circle
obj['owner']['@id'] = 'https://distant.com/users/mark/'
activity_b = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='B')
scheduled_b = ActivityQueueService._save_sent_activity(activity_b, ScheduledActivity, type='update',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled_b)
# assert that both activities were sent
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 2)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_first_update_is_sent(self):
# no prior activity was sent for this object - should send
obj = {
'@type': 'hd:circle',
'@id': 'https://test.com/circles/8/',
'owner': {'@id': 'https://distant.com/users/john/',
'@type': 'foaf:user'}
}
activity = ActivityPubService.build_activity(BACKLINKS_ACTOR, obj, activity_type='Create', summary='A')
scheduled = ActivityQueueService._save_sent_activity(activity, ScheduledActivity, type='update',
external_id='https://distant.com/inbox/')
ActivityQueueService._activity_queue_worker('https://distant.com/inbox/', scheduled)
self.assertEqual(ScheduledActivity.objects.count(), 0)
self.assertEqual(Activity.objects.count(), 1)
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.utils import json
from djangoldp.tests.models import Circle, Conversation, Project
class TestCache(TestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion', first_name='John')
self.client.force_authenticate(self.user)
def tearDown(self):
setattr(Circle._meta, 'depth', 0)
setattr(Circle._meta, 'empty_containers', [])
# test container cache after new resource added
@override_settings(SERIALIZER_CACHE=True)
def test_save_fk_graph_with_nested(self):
response = self.client.get('/batchs/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
post = {
'@graph': [
{
'https://cdn.startinblox.com/owl#title': "title",
'https://cdn.startinblox.com/owl#invoice': {
'@id': "_.123"
}
},
{
'@id': "_.123",
'https://cdn.startinblox.com/owl#title': "title 2"
}
]
}
response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
response = self.client.get('/batchs/', content_type='application/ld+json')
self.assertIn('ldp:contains', response.data)
self.assertEqual(response.data['ldp:contains'][0]['title'], "title")
self.assertEqual(response.data['ldp:contains'][0]['invoice']['title'], "title 2")
# test resource cache after it is updated
@override_settings(SERIALIZER_CACHE=True)
def test_update_with_new_fk_relation(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'https://cdn.startinblox.com/owl#description': "conversation update",
'https://cdn.startinblox.com/owl#peer_user': {
'@id': self.user.urlid,
}
}
]
response = self.client.put('/conversations/{}/'.format(conversation.pk), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
self.assertIn('peer_user', response.data)
self.assertEqual('conversation update', response.data['description'])
self.assertEqual(response.data['peer_user']['@id'], self.user.urlid)
self.assertIn('@type', response.data['peer_user'])
# test resource cache after it is updated - external resource
@override_settings(SERIALIZER_CACHE=True)
def test_update_with_new_fk_relation_external(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
external_user = get_user_model().objects.create_user(username='external', email='jlennon@beatles.com',
password='glass onion', urlid='https://external.com/users/external/')
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'https://cdn.startinblox.com/owl#description': "conversation update",
'https://cdn.startinblox.com/owl#peer_user': {
'@id': external_user.urlid,
}
}
]
response = self.client.put('/conversations/{}/'.format(conversation.pk), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
self.assertIn('peer_user', response.data)
self.assertEqual('conversation update', response.data['description'])
self.assertIn('@id', response.data['peer_user'])
# serialize external id and only external id
self.assertEqual(response.data['peer_user']['@id'], external_user.urlid)
self.assertIn('@type', response.data['peer_user'])
self.assertEqual(len(response.data['peer_user']), 2)
# test container cache after member is deleted by view
@override_settings(SERIALIZER_CACHE=True)
def test_cached_container_deleted_resource_view(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
response = self.client.delete('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 204)
response = self.client.get('/conversations/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
# test container cache after member is deleted manually
@override_settings(SERIALIZER_CACHE=True)
def test_cached_container_deleted_resource_manual(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
conversation.delete()
response = self.client.get('/conversations/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
# test resource cache after it is deleted manually
@override_settings(SERIALIZER_CACHE=True)
def test_cached_resource_deleted_resource_manual(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
conversation.delete()
response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 404)
# test container cache following m2m_changed - Project (which inherits from djangoldp.models.Model)
@override_settings(SERIALIZER_CACHE=True)
def test_cached_container_m2m_changed_project(self):
project = Project.objects.create(description='Test')
response = self.client.get('/projects/{}/members/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
project.members.add(self.user)
response = self.client.get('/projects/{}/members/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
project.members.remove(self.user)
response = self.client.get('/projects/{}/members/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
project.members.add(self.user)
project.members.clear()
response = self.client.get('/projects/{}/members/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
# test container cache following m2m_changed - Conversation (which does not inherit from djangoldp.models.Model)
@override_settings(SERIALIZER_CACHE=True)
def test_cached_container_m2m_changed_conversation(self):
conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
conversation.observers.add(self.user)
response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
conversation.observers.remove(self.user)
response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
conversation.observers.add(self.user)
conversation.observers.clear()
response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
# test cache working inside of the nested field (serializer) of another object
@override_settings(SERIALIZER_CACHE=True)
def test_cached_container_serializer_nested_field(self):
project = Project.objects.create(description='Test')
response = self.client.get('/projects/{}/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['ldp:contains']), 0)
project.members.add(self.user)
response = self.client.get('/projects/{}/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['ldp:contains']), 1)
project.members.remove(self.user)
response = self.client.get('/projects/{}/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['ldp:contains']), 0)
project.members.add(self.user)
project.members.clear()
response = self.client.get('/projects/{}/'.format(project.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['ldp:contains']), 0)
# test cache working on a serialized nested field at higher depth
@override_settings(SERIALIZER_CACHE=True)
def test_cache_depth_2(self):
setattr(Circle._meta, 'depth', 2)
circle = Circle.objects.create(description='Test')
response = self.client.get('/circles/{}/'.format(circle.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['user_set']), 0)
circle.members.user_set.add(self.user)
response = self.client.get('/circles/{}/'.format(circle.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['user_set']), 1)
# assert the depth is applied
self.assertIn('first_name', response.data['members']['user_set'][0])
self.assertEqual(response.data['members']['user_set'][0]['first_name'], self.user.first_name)
# make a change to the _user_
self.user.first_name = "Alan"
self.user.save()
# assert that the use under the circles members has been updated
response = self.client.get('/circles/{}/'.format(circle.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['members']['user_set']), 1)
self.assertIn('first_name', response.data['members']['user_set'][0])
self.assertEqual(response.data['members']['user_set'][0]['first_name'], self.user.first_name)
# test the cache behaviour when empty_containers is an active setting
@override_settings(SERIALIZER_CACHE=True)
def test_cache_empty_container(self):
setattr(Circle._meta, 'depth', 1)
setattr(Circle._meta, 'empty_containers', ['members'])
circle = Circle.objects.create(name='test', description='test', owner=self.user)
circle.members.user_set.add(self.user)
# make one call on the parent
response = self.client.get('/circles/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('ldp:contains', response.data)
self.assertEqual(len(response.data['ldp:contains']), 1)
self.assertIn('members', response.data['ldp:contains'][0])
self.assertIn('@id', response.data['ldp:contains'][0]['members'])
self.assertIn('user_set', response.data['ldp:contains'][0]['members'])
self.assertEqual(len(response.data['ldp:contains'][0]['members']['user_set']), 1)
self.assertIn('@id', response.data['ldp:contains'][0]['members']['user_set'][0])
self.assertEqual(response.data['ldp:contains'][0]['members']['user_set'][0]['@type'], 'foaf:user')
# and a second on the child
response = self.client.get(response.data['ldp:contains'][0]['members']['@id'], content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('@id', response.data)
self.assertEqual(len(response.data['user_set']), 1)
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from datetime import datetime
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from djangoldp.tests.models import Post, Invoice, JobOffer, Skill, Batch, DateModel
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient, APIRequestFactory, APITestCase
from djangoldp.serializers import GLOBAL_SERIALIZER_CACHE
from djangoldp.tests.models import (Batch, Circle, Conversation, DateModel,
Invoice, JobOffer, Message, Post, Skill,
User, UserProfile)
class TestGET(APITestCase):
......@@ -10,16 +14,37 @@ class TestGET(APITestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.client = APIClient()
self.ordered_fields = ['@context', '@type', '@id']
setattr(Circle._meta, 'depth', 0)
setattr(Circle._meta, 'empty_containers', [])
def tearDown(self):
pass
GLOBAL_SERIALIZER_CACHE.reset()
def test_get_resource(self):
post = Post.objects.create(content="content")
response = self.client.get('/posts/{}/'.format(post.pk), content_type='application/ld+json')
response = self.client.get('/posts/{}/'.format(post.pk), content_type='application/ld+json', HTTP_ORIGIN='http://localhost:8080/test/')
self.assertEqual(response.status_code, 200)
self.assertEquals(response.data['content'], "content")
self.assertEqual(response.data['content'], "content")
self.assertIn('author', response.data)
self.assertIn('@type', response.data)
# test headers returned
self.assertEqual(response['Content-Type'], 'application/ld+json')
self.assertEqual(response['Accept-Post'], 'application/ld+json')
self.assertEqual(response['Allow'], 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS')
self.assertEqual(response['Access-Control-Allow-Origin'], 'http://localhost:8080/test/')
self.assertIn('DPoP', response['Access-Control-Allow-Headers'])
def test_get_resource_urlid(self):
user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion')
UserProfile.objects.create(user=user)
post = Post.objects.create(content="content", author=user)
response = self.client.get('/posts/{}/'.format(post.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['content'], "content")
self.assertEqual(response.data['author']['@id'], user.urlid)
def test_get_container(self):
Post.objects.create(content="content")
......@@ -27,26 +52,26 @@ class TestGET(APITestCase):
Post.objects.create(content="federated", urlid="https://external.com/posts/1/")
response = self.client.get('/posts/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('permissions', response.data)
self.assertEquals(1, len(response.data['ldp:contains']))
self.assertEquals(2, len(response.data['permissions'])) # read and add
self.assertEqual(1, len(response.data['ldp:contains']))
self.assertIn('@type', response.data)
self.assertIn('@type', response.data['ldp:contains'][0])
self.assertNotIn('permissions', response.data['ldp:contains'][0])
Invoice.objects.create(title="content")
response = self.client.get('/invoices/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('permissions', response.data)
self.assertEquals(1, len(response.data['permissions'])) # read only
self.assertEqual(1, len(response.data['permissions'])) # read only
def test_get_empty_container(self):
Post.objects.all().delete()
response = self.client.get('/posts/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEquals(0, len(response.data['ldp:contains']))
self.assertEqual(0, len(response.data['ldp:contains']))
def test_get_filtered_fields(self):
skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1")
skill2 = Skill.objects.create(title="Java", obligatoire="ok", slug="2")
skill3 = Skill.objects.create(urlid="http://external/skills/1")
skill3 = Skill.objects.create(urlid="http://happy-dev.hubl.fr/skills/1")
job = JobOffer.objects.create(title="job", slug="1")
job.skills.add(skill)
job.skills.add(skill2)
......@@ -56,7 +81,11 @@ class TestGET(APITestCase):
self.assertEqual(response.status_code, 200)
self.assertIn('recent_skills', response.data)
self.assertEqual(response.data['recent_skills']['@id'], "http://happy-dev.fr/job-offers/1/recent_skills/")
self.assertEqual(response.data['skills']['ldp:contains'][2]['@id'], "http://external/skills/1")
# the external resource should be serialized with its @id and @type.. and only these fields
self.assertEqual(response.data['skills']['ldp:contains'][2]['@id'], "http://happy-dev.hubl.fr/skills/1")
self.assertIn('@type', response.data['skills']['ldp:contains'][1])
self.assertIn('@type', response.data['skills']['ldp:contains'][2])
self.assertEqual(len(response.data['skills']['ldp:contains'][2].items()), 2)
def test_get_reverse_filtered_fields(self):
skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1")
......@@ -78,7 +107,10 @@ class TestGET(APITestCase):
response = self.client.get('/job-offers/{}/'.format(job.slug), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('some_skill', response.data)
self.assertEqual(response.data['some_skill']['@id'], "http://testserver/skills/1/")
self.assertEqual(response.data['some_skill']['@id'], skill.urlid)
response = self.client.get('/job-offers/{}/recent_skills/'.format(job.slug), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 2)
def test_get_nested(self):
invoice = Invoice.objects.create(title="invoice")
......@@ -86,19 +118,122 @@ class TestGET(APITestCase):
distant_batch = Batch.objects.create(invoice=invoice, title="distant", urlid="https://external.com/batch/1/")
response = self.client.get('/invoices/{}/batches/'.format(invoice.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEquals(response.data['@id'], 'http://happy-dev.fr/invoices/{}/batches/'.format(invoice.pk))
self.assertEquals(len(response.data['ldp:contains']), 2)
self.assertEquals(response.data['ldp:contains'][0]['invoice']['@id'], 'http://happy-dev.fr/invoices/{}/'.format(invoice.pk))
self.assertEqual(response.data['@id'], 'http://happy-dev.fr/invoices/{}/batches/'.format(invoice.pk))
self.assertIn('permissions', response.data)
self.assertEqual(len(response.data['ldp:contains']), 2)
self.assertIn('@type', response.data['ldp:contains'][0])
self.assertIn('@type', response.data['ldp:contains'][1])
self.assertNotIn('permissions', response.data['ldp:contains'][0])
self.assertNotIn('permissions', response.data['ldp:contains'][1])
self.assertEqual(response.data['ldp:contains'][0]['invoice']['@id'], invoice.urlid)
self.assertEqual(response.data['ldp:contains'][1]['@id'], distant_batch.urlid)
def test_get_nested_without_related_name(self):
user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion')
conversation = Conversation.objects.create(author_user=user)
message = Message.objects.create(conversation=conversation, author_user=user)
response = self.client.get('/conversations/{}/message_set/'.format(conversation.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['@id'], 'http://happy-dev.fr/conversations/{}/message_set/'.format(conversation.pk))
self.assertEqual(len(response.data['ldp:contains']), 1)
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/335
# test getting a route with multiple nested fields (/job-offers/X/skills/Y/)
'''def test_get_twice_nested(self):
job = JobOffer.objects.create(title="job", slug="slug1")
skill = Skill.objects.create(title='old', obligatoire='old', slug='skill1')
job.skills.add(skill)
self.assertEqual(job.skills.count(), 1)
response = self.client.get('/job-offers/{}/skills/{}/'.format(job.slug, skill.slug))
self.assertEqual(response.status_code, 200)'''
def test_serializer_excludes(self):
date = DateModel.objects.create(excluded='test', value=datetime.now())
response = self.client.get('/dates/{}/'.format(date.pk), content_type='application/ld+json')
response = self.client.get('/datemodels/{}/'.format(date.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertNotIn('excluded', response.data.keys())
def test_serializer_excludes_serializer_fields_set_also(self):
setattr(DateModel._meta, 'serializer_fields', ['value', 'excluded'])
date = DateModel.objects.create(excluded='test', value=datetime.now())
response = self.client.get('/dates/{}/'.format(date.pk), content_type='application/ld+json')
response = self.client.get('/datemodels/{}/'.format(date.pk), content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertNotIn('excluded', response.data.keys())
def _set_up_circle_and_user(self):
user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion')
circle = Circle.objects.create(name='test', description='test', owner=user)
self.client.force_authenticate(user)
circle.members.user_set.add(user)
return user
# tests for functionality allowing me to set containers to be serialized without content\
# test for normal functioning (without setting)
def test_empty_container_serialization_nested_serializer_no_empty(self):
setattr(Circle._meta, 'depth', 1)
self._set_up_circle_and_user()
response = self.client.get('/circles/', content_type='application/ld+json')
self.assertEqual(response.data['@type'], 'ldp:Container')
self.assertIn('@id', response.data)
self.assertIn('permissions', response.data)
self.assertIn('members', response.data['ldp:contains'][0])
self.assertEqual(response.data['ldp:contains'][0]['members']['@type'], 'foaf:Group')
self.assertIn('@id', response.data['ldp:contains'][0]['members'])
self.assertEqual(len(response.data['ldp:contains'][0]['members']['user_set']), 1)
# test for functioning with setting
def test_empty_container_serialization_nested_serializer_empty(self):
setattr(User._meta, 'depth', 1)
setattr(User._meta, 'empty_containers', ['owned_circles'])
self._set_up_circle_and_user()
response = self.client.get('/users/', content_type='application/ld+json')
self.assertEqual(response.data['@type'], 'ldp:Container')
self.assertIn('owned_circles', response.data['ldp:contains'][0])
self.assertIn('@id', response.data['ldp:contains'][0]['owned_circles'])
self.assertNotIn('permissions', response.data['ldp:contains'][0]['owned_circles'])
self.assertNotIn('ldp:contains', response.data['ldp:contains'][0]['owned_circles'])
# should serialize as normal on the nested viewset (directly asking for the container)
# test for normal functioning (without setting)
def test_empty_container_serialization_nested_viewset_no_empty(self):
user = self._set_up_circle_and_user()
response = self.client.get(f'/users/{user.pk}/owned_circles/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['@type'], 'ldp:Container')
self.assertIn('@id', response.data)
self.assertIn('ldp:contains', response.data)
self.assertIn('permissions', response.data)
self.assertIn('owner', response.data['ldp:contains'][0])
# test for functioning with setting
def test_empty_container_serialization_nested_viewset_empty(self):
setattr(User._meta, 'empty_containers', ['owned_circles'])
user = self._set_up_circle_and_user()
response = self.client.get(f'/users/{user.pk}/owned_circles/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['@type'], 'ldp:Container')
self.assertIn('@id', response.data)
self.assertIn('ldp:contains', response.data)
self.assertIn('permissions', response.data)
self.assertIn('owner', response.data['ldp:contains'][0])
# # test for checking fields ordering
# def test_ordered_field(self):
# self._set_up_circle_and_user()
# response = self.client.get('/users/', content_type='application/ld+json')
# fields_to_test = [
# response.data.keys(),
# response.data['ldp:contains'][-1],
# response.data['ldp:contains'][-1]['circle_set']
# ]
# for test_fields in fields_to_test:
# test_fields = list(test_fields)
# o_f = [field for field in self.ordered_fields if field in test_fields]
# self.assertEqual(o_f, test_fields[:len(o_f)])
import json
import uuid
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from rest_framework.test import APIClient, APITestCase
from guardian.shortcuts import assign_perm
from .models import PermissionlessDummy, Dummy
from djangoldp.permissions import LDPPermissions
from .models import PermissionlessDummy, Dummy, LDPDummy
class TestsGuardian(APITestCase):
......@@ -15,45 +16,116 @@ class TestsGuardian(APITestCase):
def setUpLoggedInUser(self):
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion')
self.group = Group.objects.create(name='Test')
self.user.groups.add(self.group)
self.user.save()
self.client.force_authenticate(user=self.user)
# optional setup for testing PermissionlessDummy model with parameterised perms
def setUpGuardianDummyWithPerms(self, perms=[]):
self.dummy = PermissionlessDummy.objects.create(some='test', slug='test')
def _get_dummy_with_perms(self, perms=None, parent=None, group=False):
if perms is None:
perms = []
dummy = PermissionlessDummy.objects.create(some='test', slug=uuid.uuid4(), parent=parent)
model_name = PermissionlessDummy._meta.model_name
for perm in perms:
assign_perm(perm + '_' + model_name, self.user, self.dummy)
perm = perm + '_' + model_name
if group:
#assigns container-level and object-level perms
assign_perm('tests.'+perm, self.group)
assign_perm(perm, self.group, dummy)
else:
assign_perm('tests.'+perm, self.user)
assign_perm(perm, self.user, dummy)
return dummy
# optional setup for testing PermissionlessDummy model with parameterised perms
def setUpGuardianDummyWithPerms(self, perms=None, parent=None, group=False):
self.dummy = self._get_dummy_with_perms(perms, parent, group)
# auxiliary function converts permission format for test
def _unpack_permissions(self, perms_from_response):
return perms_from_response
# test that dummy with no permissions set returns no results
def test_get_dummy_no_permissions(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms()
response = self.client.get('/permissionless-dummys/{}/'.format(self.dummy.slug))
self.assertEqual(response.status_code, 403)
self.assertEqual(response.status_code, 404)
# test with anonymous user
def test_get_dummy_anonymous_user(self):
self.setUpGuardianDummyWithPerms()
response = self.client.get('/permissionless-dummys/')
# I have no object permissions - I should receive a 403
self.assertEqual(response.status_code, 403)
def test_list_dummy_exception(self):
self.setUpLoggedInUser()
# I have permission on a permissionless dummy, but not in general
dummy_a = self._get_dummy_with_perms()
dummy_b = self._get_dummy_with_perms(['view'])
response = self.client.get('/permissionless-dummys/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
containees = [d['@id'] for d in response.data['ldp:contains']]
self.assertNotIn(dummy_a.urlid, containees)
self.assertIn(dummy_b.urlid, containees)
def test_list_dummy_group_exception(self):
self.setUpLoggedInUser()
dummy_a = self._get_dummy_with_perms()
dummy_b = self._get_dummy_with_perms(['view'], group=True)
response = self.client.get('/permissionless-dummys/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
containees = [d['@id'] for d in response.data['ldp:contains']]
self.assertNotIn(dummy_a.urlid, containees)
self.assertIn(dummy_b.urlid, containees)
def test_list_dummy_exception_nested_view(self):
self.setUpLoggedInUser()
parent = LDPDummy.objects.create(some="test")
# two dummies, one I have permission to view and one I don't
dummy_a = self._get_dummy_with_perms(parent=parent)
dummy_b = self._get_dummy_with_perms(['view'], parent)
response = self.client.get('/ldpdummys/{}/anons/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
def test_list_dummy_exception_nested_serializer(self):
self.setUpLoggedInUser()
parent = LDPDummy.objects.create(some="test")
# two dummies, one I have permission to view and one I don't
dummy_a = self._get_dummy_with_perms(parent=parent)
dummy_b = self._get_dummy_with_perms(['view'], parent)
response = self.client.get('/ldpdummys/{}/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['anons']['ldp:contains']), 1)
def test_get_dummy_permission_granted(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms(['view'])
response = self.client.get('/permissionless-dummys/{}/'.format(self.dummy.slug))
self.assertEqual(response.status_code, 200)
def test_get_dummy_group_permission_granted(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms(['view'], group=True)
response = self.client.get('/permissionless-dummys/{}/'.format(self.dummy.slug))
self.assertEqual(response.status_code, 200)
def test_get_dummy_permission_rejected(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms(['view'])
dummy_without = PermissionlessDummy.objects.create(some='test2', slug='test2')
response = self.client.get('/permissionless-dummys/{}/'.format(dummy_without.slug))
self.assertEqual(response.status_code, 403)
self.assertEqual(response.status_code, 404)
def test_patch_dummy_permission_granted(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms(['change'])
self.setUpGuardianDummyWithPerms(['view', 'change'])
body = {'some': "some_new"}
response = self.client.patch('/permissionless-dummys/{}/'.format(self.dummy.slug), data=json.dumps(body),
content_type='application/ld+json')
......@@ -66,16 +138,17 @@ class TestsGuardian(APITestCase):
body = {'some': "some_new"}
response = self.client.patch('/permissionless-dummys/{}/'.format(dummy_without.slug), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 403)
self.assertEqual(response.status_code, 404)
# TODO: PUT container of many objects approved on specific resource for which I do not have _model_ permissions
# test that custom permissions are returned on a model
def test_custom_permissions(self):
self.setUpLoggedInUser()
self.setUpGuardianDummyWithPerms(['custom_permission'])
self.setUpGuardianDummyWithPerms(['custom_permission', 'view'])
permissions = LDPPermissions()
result = permissions.user_permissions(self.user, self.dummy)
self.assertIn('custom_permission', result)
response = self.client.get('/permissionless-dummys/{}/'.format(self.dummy.slug))
self.assertIn('custom_permission', self._unpack_permissions(response.data['permissions']))
# test that duplicate permissions aren't returned
def test_no_duplicate_permissions(self):
......@@ -85,6 +158,11 @@ class TestsGuardian(APITestCase):
assign_perm('view_' + model_name, self.user, dummy)
permissions = LDPPermissions()
result = permissions.user_permissions(self.user, dummy)
self.assertEqual(result.count('view'), 1)
response = self.client.get('/dummys/{}/'.format(dummy.slug))
self.assertEqual(response.status_code, 200)
perms = self._unpack_permissions(response.data['permissions'])
self.assertIn('view', perms)
view_perms = [perm for perm in perms if perm == 'view']
self.assertEqual(len(view_perms), 1)
# TODO: attempting to migrate my object permissions by changing FK reference
import json
from django.contrib.auth import get_user_model
from django.conf import settings
from django.db import IntegrityError
from django.contrib.auth import get_user_model
from django.test import override_settings
from rest_framework.test import APIClient, APITestCase
from djangoldp.tests.models import Circle, CircleMember, Project, UserProfile, DateModel, DateChild
from djangoldp.models import Activity, Follower
from djangoldp.tests.models import Circle, DateChild, DateModel, Project
class TestsInbox(APITestCase):
......@@ -19,7 +20,7 @@ class TestsInbox(APITestCase):
res = {
"@context": [
"https://www.w3.org/ns/activitystreams",
{"hd": "http://happy-dev.fr/owl/#"}
{"hd": "https://cdn.startinblox.com/owl#"}
],
"summary": "Something happened",
"type": type,
......@@ -49,12 +50,17 @@ class TestsInbox(APITestCase):
def _assert_activity_created(self, response, activity_len=1):
'''Auxiliary function asserts that the activity was created and returned correctly'''
activities = Activity.objects.all()
self.assertEquals(len(activities), activity_len)
self.assertEqual(len(activities), activity_len)
self.assertIn(response["Location"], activities.values_list('urlid', flat=True))
def _assert_follower_created(self, local_urlid, external_urlid):
existing_followers = Follower.objects.filter(object=local_urlid).values_list('follower', flat=True)
self.assertTrue(external_urlid in existing_followers)
#
# CREATE ACTIVITY
#
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
def test_create_activity_circle(self):
obj = {
"@type": "hd:circle",
......@@ -72,14 +78,49 @@ class TestsInbox(APITestCase):
# assert that the circle was created and the user associated as owner
circles = Circle.objects.all()
self.assertEquals(len(circles), 1)
self.assertEqual(len(circles), 1)
self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
self.assertEqual(circles[0].owner, self.user)
self._assert_activity_created(response)
# assert external circle member now following local user
self.assertEqual(Follower.objects.count(), 1)
self._assert_follower_created(self.user.urlid, "https://distant.com/circles/1/")
# # tests creation, and tests that consequential creation also happens
# # i.e. that I pass it an external circle which it doesn't know about, and it creates that too
# def test_create_activity_circle_member(self):
# obj = {
# "@type": "hd:circlemember",
# "@id": "https://distant.com/circlemembers/1/",
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": "https://distant.com/circles/1/"
# }
# }
# payload = self._get_activity_request_template("Create", obj)
# response = self.client.post('/inbox/',
# data=json.dumps(payload), content_type='application/ld+json')
# self.assertEqual(response.status_code, 201)
# # assert that the circle was created and the user associated as member
# circles = Circle.objects.all()
# self.assertEqual(len(circles), 1)
# self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
# self.assertTrue(circles[0].members.filter(user=self.user).exists())
# self._assert_activity_created(response)
# # assert external circle member now following local user
# self._assert_follower_created(self.user.urlid, "https://distant.com/circlemembers/1/")
# sender has sent a circle with a local user that doesn't exist
def test_create_activity_circle_local(self):
urlid = '{}{}'.format(settings.SITE_URL, 'someonewhodoesntexist')
urlid = '{}/{}'.format(settings.SITE_URL, 'someonewhodoesntexist')
obj = {
"@type": "hd:circle",
"@id": "https://distant.com/circles/1/",
......@@ -97,13 +138,14 @@ class TestsInbox(APITestCase):
self.assertEqual(response.status_code, 404)
# assert that the circle was not created neither a backlinked user
self.assertEquals(Circle.objects.count(), 0)
self.assertEquals(get_user_model().objects.count(), prior_users_length)
self.assertEqual(Circle.objects.count(), 0)
self.assertEqual(get_user_model().objects.count(), prior_users_length)
#
# ADD ACTIVITIES
#
# project model has a direct many-to-many with User
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
def test_add_activity_project(self):
obj = {
"@type": "hd:project",
......@@ -118,76 +160,93 @@ class TestsInbox(APITestCase):
# assert that the project backlink(s) & activity were created
projects = Project.objects.all()
user_projects = self.user.projects.all()
self.assertEquals(len(projects), 1)
self.assertEquals(len(user_projects), 1)
self.assertEqual(len(projects), 1)
self.assertEqual(len(user_projects), 1)
self.assertIn("https://distant.com/projects/1/", projects.values_list('urlid', flat=True))
self.assertIn("https://distant.com/projects/1/", user_projects.values_list('urlid', flat=True))
self._assert_activity_created(response)
# circle model has a many-to-many with user, through an intermediate model
def test_add_activity_circle(self):
obj = {
"@type": "hd:circlemember",
"@id": "https://distant.com/circle-members/1/",
"user": {
"@type": "foaf:user",
"@id": self.user.urlid
},
"circle": {
"@type": "hd:circle",
"@id": "https://distant.com/circles/1/"
}
}
payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
response = self.client.post('/inbox/',
data=json.dumps(payload), content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
self.assertEqual(response.status_code, 201)
# assert that the circle backlink(s) & activity were created
circles = Circle.objects.all()
user_circles = self.user.circles.all()
self.assertEquals(len(circles), 1)
self.assertEquals(len(user_circles), 1)
self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
self.assertIn("https://distant.com/circle-members/1/", user_circles.values_list('urlid', flat=True))
self._assert_activity_created(response)
# assert external circle member now following local user
self.assertEqual(Follower.objects.count(), 1)
self._assert_follower_created(self.user.urlid, "https://distant.com/projects/1/")
#TODO: write a new test for the new circle architecture
# # circle model has a many-to-many with user, through an intermediate model
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_circle(self):
# ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# ext_circle_urlid = "https://distant.com/circles/1/"
# obj = {
# "@type": "hd:circlemember",
# "@id": ext_circlemember_urlid,
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": ext_circle_urlid
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# response = self.client.post('/inbox/',
# data=json.dumps(payload), content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self.assertEqual(response.status_code, 201)
# # assert that the circle backlink(s) & activity were created
# circles = Circle.objects.all()
# user_circles = self.user.circles.all()
# self.assertEqual(len(circles), 1)
# self.assertEqual(len(user_circles), 1)
# self.assertIn(ext_circle_urlid, circles.values_list('urlid', flat=True))
# self.assertIn(ext_circlemember_urlid, user_circles.values_list('urlid', flat=True))
# self._assert_activity_created(response)
# # assert external circle member now following local user
# self.assertEqual(Follower.objects.count(), 1)
# self._assert_follower_created(self.user.urlid, ext_circlemember_urlid)
# test sending an add activity when the backlink already exists
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
def test_add_activity_object_already_added(self):
circle = Circle.objects.create(urlid="https://distant.com/circles/1/")
CircleMember.objects.create(urlid="https://distant.com/circle-members/1/", circle=circle, user=self.user)
obj = {
"@type": "hd:circlemember",
"@id": "https://distant.com/circle-members/1/",
"user": {
"@type": "foaf:user",
"@id": self.user.urlid
},
"circle": {
"@type": "hd:circle",
"@id": "https://distant.com/circles/1/"
}
}
payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
prior_count = Activity.objects.count()
response = self.client.post('/inbox/',
data=json.dumps(payload),
content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
self.assertEqual(response.status_code, 201)
# assert that the circle backlink(s) & activity were created
circles = Circle.objects.all()
user_circles = self.user.circles.all()
self.assertEquals(len(circles), 1)
self.assertEquals(len(user_circles), 1)
self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
self.assertIn("https://distant.com/circle-members/1/", user_circles.values_list('urlid', flat=True))
self._assert_activity_created(response)
self.assertEqual(Activity.objects.count(), prior_count + 1)
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_object_already_added(self):
# circle = Circle.objects.create(urlid="https://distant.com/circles/1/")
# circle.members.user_set.add(self.user)
# obj = {
# "@type": "hd:circlemember",
# "@id": "https://distant.com/circle-members/1/",
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": "https://distant.com/circles/1/"
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# prior_count = Activity.objects.count()
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self.assertEqual(response.status_code, 201)
# # assert that the circle backlink(s) & activity were created
# circles = Circle.objects.all()
# user_circles = self.user.circles.all()
# self.assertEqual(len(circles), 1)
# self.assertEqual(len(user_circles), 1)
# self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
# self.assertIn("https://distant.com/circle-members/1/", user_circles.values_list('urlid', flat=True))
# self._assert_activity_created(response)
# self.assertEqual(Activity.objects.count(), prior_count + 1)
# # assert that followers exist for the external urlids
# self.assertEqual(Follower.objects.count(), 1)
# self._assert_follower_created(self.user.urlid, '') #TODO: replace with an existing model
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/250
def test_add_activity_str_parameter(self):
......@@ -198,6 +257,8 @@ class TestsInbox(APITestCase):
content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
self.assertEqual(response.status_code, 400)
# TODO: may pass an object without an explicit urlid e.g. Person actor, or Collection target
# error behaviour - unknown model
def test_add_activity_unknown(self):
obj = {
......@@ -209,11 +270,144 @@ class TestsInbox(APITestCase):
data=json.dumps(payload), content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
self.assertEqual(response.status_code, 404)
def _test_fail_behaviour(self, response, status_code=400):
self.assertEqual(response.status_code, 400)
# assert that nothing was created
self.assertEqual(Circle.objects.count(), 0)
self.assertEqual(self.user.owned_circles.count(), 0)
self.assertEqual(Activity.objects.count(), 0)
self.assertEqual(Follower.objects.count(), 0)
# # error behaviour - invalid url
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_empty_url(self):
# # an invalid url
# ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# ext_circle_urlid = ""
# obj = {
# "@type": "hd:circlemember",
# "@id": ext_circlemember_urlid,
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": ext_circle_urlid
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self._test_fail_behaviour(response, 400)
# # error behaviour - invalid url
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_invalid_url(self):
# # an invalid url
# ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# ext_circle_urlid = "not$valid$url"
# obj = {
# "@type": "hd:circlemember",
# "@id": ext_circlemember_urlid,
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": ext_circle_urlid
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self._test_fail_behaviour(response, 400)
# # # error behaviour - None url
# # @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# # def test_add_activity_none_url(self):
# # # an invalid url
# # ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# # ext_circle_urlid = None
# # obj = {
# # "@type": "hd:circlemember",
# # "@id": ext_circlemember_urlid,
# # "user": {
# # "@type": "foaf:user",
# # "@id": self.user.urlid
# # },
# # "circle": {
# # "@type": "hd:circle",
# # "@id": ext_circle_urlid
# # }
# # }
# # payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# # response = self.client.post('/inbox/',
# # data=json.dumps(payload),
# # content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# # self._test_fail_behaviour(response, 400)
# # missing @id on a sub-object
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_no_id(self):
# ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# obj = {
# "@type": "hd:circlemember",
# "@id": ext_circlemember_urlid,
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle"
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self._test_fail_behaviour(response, 400)
# # missing @type on a sub-object
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_add_activity_no_type(self):
# ext_circlemember_urlid = "https://distant.com/circle-members/1/"
# obj = {
# "@type": "hd:circlemember",
# "@id": ext_circlemember_urlid,
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@id": "https://distant.com/circles/1/"
# }
# }
# payload = self._get_activity_request_template("Add", obj, self._build_target_from_user(self.user))
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self._test_fail_behaviour(response, 404)
def test_invalid_activity_missing_actor(self):
payload = {
"@context": [
"https://www.w3.org/ns/activitystreams",
{"hd": "http://happy-dev.fr/owl/#"}
{"hd": "https://cdn.startinblox.com/owl#"}
],
"summary": "Test was added to Test Circle",
"type": "Add",
......@@ -248,8 +442,8 @@ class TestsInbox(APITestCase):
# assert that the project backlink(s) & activity were created
projects = Project.objects.all()
user_projects = self.user.projects.all()
self.assertEquals(len(projects), 1)
self.assertEquals(len(user_projects), 1)
self.assertEqual(len(projects), 1)
self.assertEqual(len(user_projects), 1)
self.assertIn("https://distant.com/projects/1/", projects.values_list('urlid', flat=True))
self.assertIn("https://distant.com/projects/1/", user_projects.values_list('urlid', flat=True))
self._assert_activity_created(response)
......@@ -257,6 +451,7 @@ class TestsInbox(APITestCase):
self.assertNotEqual(backlink.pk, 100)
def test_missing_not_null_field_activity(self):
# TODO: catch the warning
# DateChild must not have a null reference to parent
# and parent must not have a null field 'date', which here is missing
obj = {
......@@ -280,9 +475,13 @@ class TestsInbox(APITestCase):
# REMOVE & DELETE ACTIVITIES
#
# project model has a direct many-to-many with User
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
def test_remove_activity_project_using_origin(self):
project = Project.objects.create(urlid="https://distant.com/projects/1/")
self.user.projects.add(project)
Follower.objects.create(object=self.user.urlid, inbox='https://distant.com/inbox/',
follower=project.urlid, is_backlink=True)
prior_activity_count = Activity.objects.count()
obj = {
"@type": "hd:project",
......@@ -297,12 +496,13 @@ class TestsInbox(APITestCase):
# assert that the circle backlink(s) were removed & activity were created
projects = Project.objects.all()
user_projects = self.user.projects.all()
self.assertEquals(len(projects), 1)
self.assertEquals(len(user_projects), 0)
self.assertEqual(len(projects), 1)
self.assertEqual(len(user_projects), 0)
self.assertIn("https://distant.com/projects/1/", projects.values_list('urlid', flat=True))
self._assert_activity_created(response)
self._assert_activity_created(response, prior_activity_count + 1)
self.assertEqual(Follower.objects.count(), 0)
# TODO: test_remove_activity_project_using_target
# TODO: test_remove_activity_project_using_target (https://git.startinblox.com/djangoldp-packages/djangoldp/issues/231)
# error behaviour - project does not exist on user
def test_remove_activity_nonexistent_project(self):
......@@ -319,7 +519,7 @@ class TestsInbox(APITestCase):
self.assertEqual(response.status_code, 201)
self._assert_activity_created(response)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
@override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX='DEBUG')
def test_removing_object_twice(self):
project = Project.objects.create(urlid="https://distant.com/projects/1/")
self.user.projects.add(project)
......@@ -347,36 +547,41 @@ class TestsInbox(APITestCase):
# just received, did not send
self.assertEqual(Activity.objects.all().count(), prior_count + 1)
# Delete CircleMember
def test_delete_activity_circle_using_origin(self):
circle = Circle.objects.create(urlid="https://distant.com/circles/1/", allow_create_backlink=False)
CircleMember.objects.create(urlid="https://distant.com/circle-members/1/",circle=circle, user=self.user)
obj = {
"@type": "hd:circlemember",
"@id": "https://distant.com/circle-members/1/",
"user": {
"@type": "foaf:user",
"@id": self.user.urlid
},
"circle": {
"@type": "hd:circle",
"@id": "https://distant.com/circles/1/"
}
}
payload = self._get_activity_request_template("Delete", obj)
response = self.client.post('/inbox/',
data=json.dumps(payload),
content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
self.assertEqual(response.status_code, 201)
# assert that the CircleMember was deleted and activity was created
circles = Circle.objects.all()
user_circles = self.user.circles.all()
self.assertEquals(len(circles), 1)
self.assertEquals(len(user_circles), 0)
self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
self._assert_activity_created(response)
# # Delete CircleMember
# @override_settings(SEND_BACKLINKS=True, DISABLE_OUTBOX=True)
# def test_delete_activity_circle_using_origin(self):
# circle = Circle.objects.create(urlid="https://distant.com/circles/1/", allow_create_backlink=False)
# circle.members.user_set.add(self.user)
# Follower.objects.create(object=self.user.urlid, inbox='https://distant.com/inbox/',
# follower=circle.urlid, is_backlink=True)
# obj = {
# "@type": "hd:circlemember",
# "@id": "https://distant.com/circle-members/1/",
# "user": {
# "@type": "foaf:user",
# "@id": self.user.urlid
# },
# "circle": {
# "@type": "hd:circle",
# "@id": "https://distant.com/circles/1/"
# }
# }
# payload = self._get_activity_request_template("Delete", obj)
# response = self.client.post('/inbox/',
# data=json.dumps(payload),
# content_type='application/ld+json;profile="https://www.w3.org/ns/activitystreams"')
# self.assertEqual(response.status_code, 201)
# # assert that the Circle member was removed and activity was created
# circles = Circle.objects.all()
# user_circles = self.user.circles.all()
# self.assertEqual(len(circles), 1)
# self.assertEqual(circle.members.count(), 0)
# self.assertEqual(len(user_circles), 0)
# self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
# self._assert_activity_created(response)
# self.assertEqual(Follower.objects.count(), 0)
# TODO: test_delete_activity_circle_using_target
......@@ -387,6 +592,8 @@ class TestsInbox(APITestCase):
circle = Circle.objects.create(urlid="https://distant.com/circles/1/", owner=self.user)
self.assertEqual(circle.owner, self.user)
prior_user_count = get_user_model().objects.count()
obj = {
"@type": "hd:circle",
"@id": "https://distant.com/circles/1/",
......@@ -403,8 +610,8 @@ class TestsInbox(APITestCase):
# assert that the circle was created and the user associated as owner
circles = Circle.objects.all()
users = get_user_model().objects.all()
self.assertEquals(len(circles), 1)
self.assertEquals(len(users), 2)
self.assertEqual(len(circles), 1)
self.assertEqual(len(users), prior_user_count + 1)
distant_user = get_user_model().objects.get(urlid="https://distant.com/users/1/")
self.assertIn("https://distant.com/circles/1/", circles.values_list('urlid', flat=True))
self.assertEqual(circles[0].owner, distant_user)
......@@ -427,7 +634,7 @@ class TestsInbox(APITestCase):
# assert that Follower was created with correct values
followers = Follower.objects.all()
self.assertEquals(len(followers), 1)
self.assertEqual(len(followers), 1)
self._assert_activity_created(response)
follower = followers[0]
self.assertEqual("http://127.0.0.1:8000/inbox/", follower.inbox)
......@@ -438,10 +645,10 @@ class TestsInbox(APITestCase):
circle = Circle.objects.create(description='Test Description')
Follower.objects.create(object=circle.urlid, inbox="http://127.0.0.1:8000/inbox/")
followers = Follower.objects.all()
self.assertEquals(len(followers), 1)
self.assertEqual(len(followers), 1)
circle.delete()
followers = Follower.objects.all()
self.assertEquals(len(followers), 0)
self.assertEqual(len(followers), 0)
#
# GET Inbox
......@@ -449,3 +656,6 @@ class TestsInbox(APITestCase):
def test_get_inbox(self):
response = self.client.get('/inbox/')
self.assertEqual(response.status_code, 405)
# TODO: GET inbox for specific resource - should return a list of activities sent to this inbox
# TODO: view to access outbox (https://git.startinblox.com/djangoldp-packages/djangoldp/issues/284)
import unittest
from django.test import TestCase
from djangoldp.models import Model
from djangoldp.tests.models import Dummy, LDPDummy, Circle, CircleMember
from djangoldp.tests.models import (Dummy, JobOffer, LDPDummy,
NoSuperUsersAllowedModel)
class LDPModelTest(TestCase):
def test_class_not_inheriting_ldp_model(self):
dummy = Dummy.objects.create(some="text")
self.assertEquals("/dummys/", Model.container_id(dummy))
self.assertEquals("/dummys/{}/".format(dummy.slug), Model.resource_id(dummy))
self.assertEqual("/dummys/", Model.container_id(dummy))
self.assertEqual("/dummys/{}/".format(dummy.slug), Model.resource_id(dummy))
def test_class_inheriting_ldp_model(self):
dummy = LDPDummy.objects.create(some="text")
self.assertEquals("/ldpdummys/", dummy.get_container_id())
self.assertEquals("http://happy-dev.fr/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url())
self.assertEquals("/ldpdummys/", Model.container_id(dummy))
self.assertEquals("/ldpdummys/{}/".format(dummy.pk), Model.resource_id(dummy))
self.assertEqual("/ldpdummys/", dummy.get_container_id())
self.assertEqual("http://happy-dev.fr/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url())
self.assertEqual("/ldpdummys/", Model.container_id(dummy))
self.assertEqual("/ldpdummys/{}/".format(dummy.pk), Model.resource_id(dummy))
def test_from_resolve_id(self):
saved_instance = Dummy.objects.create(some="text", slug="someid")
result = Model.resolve_id("/dummys/{}/".format(saved_instance.slug))
self.assertEquals(saved_instance, result)
self.assertEqual(saved_instance, result)
def test_resolve_container(self):
result = Model.resolve_container("/dummys/")
self.assertEquals(Dummy, result)
self.assertEqual(Dummy, result)
def test_auto_url(self):
from django.urls import get_resolver
......@@ -35,7 +34,7 @@ class LDPModelTest(TestCase):
view_name = '{}-list'.format(dummy._meta.object_name.lower())
path = 'http://happy-dev.fr/{}{}/'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk)
self.assertEquals(path, dummy.get_absolute_url())
self.assertEqual(path, dummy.get_absolute_url())
def test_ldp_manager_local_objects(self):
local = LDPDummy.objects.create(some='text')
......@@ -44,21 +43,4 @@ class LDPModelTest(TestCase):
local_queryset = LDPDummy.objects.local()
self.assertEqual(local_queryset.count(), 1)
self.assertIn(local, local_queryset)
self.assertNotIn(external, local_queryset)
def test_ldp_manager_nested_fields_auto(self):
nested_fields = Circle.objects.nested_fields()
expected_nested_fields = ['team', 'members']
self.assertEqual(len(nested_fields), len(expected_nested_fields))
for expected in expected_nested_fields:
self.assertIn(expected, nested_fields)
nested_fields = CircleMember.objects.nested_fields()
expected_nested_fields = []
self.assertEqual(nested_fields, expected_nested_fields)
def test_ldp_manager_nested_fields_exclude(self):
setattr(Circle.Meta, 'nested_fields_exclude', ['team'])
nested_fields = Circle.objects.nested_fields()
expected_nested_fields = ['members']
self.assertEqual(nested_fields, expected_nested_fields)
self.assertNotIn(external, local_queryset)
\ No newline at end of file
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from djangoldp.tests.models import User, Circle, Project
from djangoldp.serializers import LDPSerializer
from djangoldp.related import get_prefetch_fields
class LDPViewSet(APITestCase):
user_serializer_fields = ['@id', 'username', 'first_name', 'last_name', 'email', 'userprofile', 'conversation_set', 'projects']
user_expected_fields = {'userprofile', 'conversation_set', 'projects', 'conversation_set__author_user', 'conversation_set__peer_user'}
project_serializer_fields = ['@id', 'description', 'members']
project_expected_fields = {'members', 'members__userprofile'}
def setUpLoggedInUser(self):
self.factory = APIRequestFactory()
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion', first_name='John')
self.client.force_authenticate(self.user)
def _get_serializer(self, model, depth, fields):
meta_args = {'model': model, 'depth': depth, 'fields': fields}
meta_class = type('Meta', (), meta_args)
return (type(LDPSerializer)('TestSerializer', (LDPSerializer,), {'Meta': meta_class}))()
def test_get_prefetch_fields_user(self):
model = User
depth = 0
serializer_fields = self.user_serializer_fields
expected_fields = self.user_expected_fields
serializer = self._get_serializer(model, depth, serializer_fields)
result = get_prefetch_fields(model, serializer, depth)
self.assertEqual(expected_fields, result)
def test_get_prefetch_fields_circle(self):
model = Circle
depth = 0
serializer_fields = ['@id', 'name', 'description', 'owner', 'members']
expected_fields = {'owner', 'members', 'admins', 'space'}
serializer = self._get_serializer(model, depth, serializer_fields)
result = get_prefetch_fields(model, serializer, depth)
self.assertEqual(expected_fields, result)
def test_get_prefetch_fields_project(self):
model = Project
depth = 0
serializer_fields = self.project_serializer_fields
expected_fields = self.project_expected_fields
serializer = self._get_serializer(model, depth, serializer_fields)
result = get_prefetch_fields(model, serializer, depth)
self.assertEqual(expected_fields, result)
# TODO: dynamically generating serializer fields is necessary to retrieve many-to-many fields at depth > 0,
# but the _all_ default has issues detecting reverse many-to-many fields
'''def test_get_prefetch_fields_depth_1(self):
model = Project
depth = 2
serializer_fields = self.project_serializer_fields
user_expected = set(['team__' + x for x in self.user_expected_fields])
expected_fields = self.project_expected_fields.union(user_expected)
serializer = self._get_serializer(model, depth, serializer_fields)
result = get_prefetch_fields(model, serializer, depth)
self.assertEqual(expected_fields, result)'''
def test_get_shape_param(self):
self.setUpLoggedInUser()
circle = Circle.objects.create(name='test circle')
# request id and name only
fields_shape = '["@id", "name"]'
response = self.client.get('/circles/', HTTP_ACCEPT_MODEL_FIELDS=fields_shape)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response_data_keys = response.data['ldp:contains'][0].keys()
self.assertIn(len(response_data_keys), (3,4))
self.assertIn('@id', response_data_keys)
self.assertIn('name', response_data_keys)
self.assertIn('@type', response_data_keys)
if len(response_data_keys)>3:
self.assertIn('@context', response_data_keys)
def test_search_fields_basic(self):
self.setUpLoggedInUser()
lowercase_circle = Circle.objects.create(name='test circle')
uppercase_circle = Circle.objects.create(name='hello world', description='test')
response = self.client.get('/circles/?search-fields=name&search-terms=test&search-method=basic')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['ldp:contains']), 1)
self.assertEqual(response.data['ldp:contains'][0]['name'], lowercase_circle.name)
# test multiple search fields
response = self.client.get('/circles/?search-fields=name,description&search-terms=test&search-method=basic')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['ldp:contains']), 2)
def test_search_fields_ibasic(self):
self.setUpLoggedInUser()
lowercase_circle = Circle.objects.create(name='test circle')
uppercase_circle = Circle.objects.create(name='TEST')
response = self.client.get('/circles/?search-fields=name&search-terms=test&search-method=ibasic')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['ldp:contains']), 2)
def test_search_fields_exact(self):
self.setUpLoggedInUser()
lowercase_circle = Circle.objects.create(name='test circle')
uppercase_circle = Circle.objects.create(name='TEST')
response = self.client.get('/circles/?search-fields=name&search-terms=test&search-method=exact')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['ldp:contains']), 0)
response = self.client.get('/circles/?search-fields=name&search-terms=test%20circle&search-method=exact')
self.assertEqual(response.data['ldp:contains'][0]['name'], lowercase_circle.name)
from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from rest_framework.test import APIClient, APIRequestFactory
from djangoldp.serializers import LDPSerializer
from djangoldp.tests.models import (Batch, Conversation, Invoice, JobOffer,
Message, ModelTask, Skill)
class LDPModelSerializerTestCase(TestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion')
def _get_serializer_class(self, model, depth, fields):
meta_args = {'model': model, 'depth': depth, 'fields': fields}
meta_class = type('Meta', (), meta_args)
return type(LDPSerializer)('TestSerializer', (LDPSerializer,), {'Meta': meta_class})
def test_update_container_new_resource_replace(self):
# 2 pre-existing skills, one will be replaced and the other updated
redundant_skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug1")
pre_existing_skill = Skill.objects.create(title="to keep", obligatoire="obligatoire", slug="slug2")
job = JobOffer.objects.create(title="job test")
job.skills.add(redundant_skill)
job.skills.add(pre_existing_skill)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"title": "job test updated",
"skills": {
"ldp:contains": [
{"title": "new skill", "obligatoire": "okay"},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, pre_existing_skill.slug), "title": "z"},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
self.assertEqual(result.title, "job test updated")
self.assertIs(result.skills.count(), 2)
skills = result.skills.all().order_by("title")
self.assertEqual(skills[0].title, "new skill")
self.assertEqual(skills[0].obligatoire, "okay")
self.assertEqual(skills[1].title, "z") # updated
self.assertEqual(skills[1].obligatoire, pre_existing_skill.obligatoire)
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/326
'''
def test_update_container_edit_and_new_resource_append(self):
pre_existing_skill_a = Skill.objects.create(title="to keep", obligatoire="obligatoire", slug="slug1")
pre_existing_skill_b = Skill.objects.create(title="to keep", obligatoire="obligatoire", slug="slug2")
job = JobOffer.objects.create(title="job test")
job.skills.add(pre_existing_skill_a)
job.skills.add(pre_existing_skill_b)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"title": "new skill", "obligatoire": "okay"},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, pre_existing_skill_b.slug), "title": "z"},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save(partial=True)
self.assertEqual(result.title, job.title)
self.assertIs(result.skills.count(), 3)
skills = result.skills.all().order_by('title')
self.assertEqual(skills[0].title, "new skill") # new skill
self.assertEqual(skills[1].title, pre_existing_skill_a.title) # old skill unchanged
self.assertEqual(skills[2].title, "z") # updated
self.assertEqual(skills[2].obligatoire, pre_existing_skill_b.obligatoire) # another field not updated
'''
def test_update_container_edit_and_new_external_resources(self):
job = JobOffer.objects.create(title="job test")
pre_existing_external = Skill.objects.create(title="to keep", obligatoire="obligatoire",
urlid="https://external.com/skills/2/")
job.skills.add(pre_existing_external)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "https://external.com/skills/1/", "title": "external skill", "obligatoire": "okay"},
{"@id": "https://external.com/skills/2/", "title": "to keep", "obligatoire": "okay"},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
skills = result.skills.all().order_by('urlid')
self.assertEqual(result.title, job.title)
self.assertEqual(result.pk, job.pk)
self.assertEqual(result.urlid, job.urlid)
self.assertIs(result.skills.count(), 2)
self.assertEqual(skills[0].title, "external skill") # new skill
self.assertEqual(skills[0].urlid, "https://external.com/skills/1/") # new skill
self.assertEqual(skills[0].obligatoire, "okay")
self.assertEqual(skills[1].title, pre_existing_external.title) # old skill unchanged
self.assertEqual(skills[1].urlid, pre_existing_external.urlid)
self.assertEqual(skills[1].obligatoire, "okay")
self.assertEqual(skills[1].pk, pre_existing_external.pk)
def test_update_container_attach_existing_resource(self):
job = JobOffer.objects.create(title="job test")
another_job = JobOffer.objects.create(title="job2")
pre_existing_skill = Skill.objects.create(title="to keep", obligatoire="obligatoire")
another_job.skills.add(pre_existing_skill)
self.assertIs(job.skills.count(), 0)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "{}/skills/{}/".format(settings.BASE_URL, pre_existing_skill.slug)},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
skills = result.skills.all().order_by('urlid')
self.assertEqual(result.title, job.title)
self.assertEqual(result.pk, job.pk)
self.assertEqual(result.urlid, job.urlid)
self.assertIs(result.skills.count(), 1)
self.assertEqual(skills[0].urlid, pre_existing_skill.urlid)
self.assertIs(another_job.skills.count(), 1)
self.assertIs(Skill.objects.count(), 1)
def test_update_container_attach_existing_resource_external(self):
job = JobOffer.objects.create(title="job test")
another_job = JobOffer.objects.create(title="job2")
pre_existing_external = Skill.objects.create(title="to keep", obligatoire="obligatoire",
urlid="https://external.com/skills/2/")
another_job.skills.add(pre_existing_external)
self.assertIs(job.skills.count(), 0)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": pre_existing_external.urlid},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
skills = result.skills.all().order_by('urlid')
self.assertEqual(result.title, job.title)
self.assertEqual(result.pk, job.pk)
self.assertEqual(result.urlid, job.urlid)
self.assertIs(result.skills.count(), 1)
self.assertEqual(skills[0].urlid, pre_existing_external.urlid)
self.assertIs(another_job.skills.count(), 1)
self.assertIs(Skill.objects.count(), 1)
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/344
def test_update_container_mismatched_type_urlid(self):
job = JobOffer.objects.create(title="job test")
another_job = JobOffer.objects.create(title="job2")
# contains internal urlid which refers to a different type of object entirely, and one which refers to container
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "{}/job-offers/{}/".format(settings.BASE_URL, another_job.slug)},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/345
'''
def test_update_container_mismatched_type_urlid_2(self):
job = JobOffer.objects.create(title="job test")
# contains internal urlid which refers to a container
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "{}/skills/".format(settings.BASE_URL)},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
# TODO: assert correct error is thrown
'''
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/344
def test_update_container_mismatched_type_urlid_external(self):
job = JobOffer.objects.create(title="job test")
# contains external mismatched urlids which refers to a container
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "https://external.com/skills/"},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/346
'''def test_update_container_attach_nonexistent_local_resource(self):
job = JobOffer.objects.create(title="job test")
self.assertEqual(JobOffer.objects.count(), 1)
self.assertEqual(job.skills.count(), 0)
self.assertEqual(Skill.objects.count(), 0)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "{}/skills/404/".format(settings.BASE_URL)},
]}
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=post, instance=job)
serializer.is_valid(raise_exception=True)
result = serializer.save()
self.assertEqual(JobOffer.objects.count(), 1)
self.assertEqual(job.skills.count(), 0)
self.assertEqual(Skill.objects.count(), 0)'''
# CircleMember is different to Skill because it represents a many-to-many relationship via a through model
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/333
'''def test_update_m2m_relationship_with_through_model_add_and_edit(self):
circle = Circle.objects.create(name='test')
pre_existing = CircleMember.objects.create(user=self.user, circle=circle, is_admin=False)
another_user = get_user_model().objects.create_user(username='u2', email='u2@b.com', password='pw')
post = {
"@id": "{}/circles/{}/".format(settings.BASE_URL, circle.pk),
"name": "Updated Name",
"members": {
"ldp:contains": [
{"@id": "{}/circle-members/{}/".format(settings.BASE_URL, pre_existing.pk), "is_admin": True},
{"user": {"@id": another_user.urlid }, "is_admin": False},
]
}
}
serializer_class = self._get_serializer_class(Circle, 2, ("@id", "name", "description", "members", "team"))
serializer = serializer_class(data=post, instance=circle)
serializer.is_valid(raise_exception=True)
result = serializer.save()
self.assertEqual(result.name, circle.name)
self.assertEqual(result.pk, circle.pk)
self.assertEqual(result.urlid, circle.urlid)
self.assertIs(result.members.count(), 2)
self.assertIs(result.team.count(), 2)
members = result.members.all().order_by('pk')
self.assertEqual(members[0].user, self.user)
self.assertEqual(members[0].urlid, pre_existing.urlid)
self.assertEqual(members[0].pk, pre_existing.pk)
self.assertEqual(members[0].is_admin, True)
self.assertEqual(members[1].user, another_user)
self.assertEqual(members[1].is_admin, False)
# TODO: variation on the above using external resources
def test_update_m2m_relationship_with_through_model_add_and_edit_external_resources(self):
pass
# NOTE: this test if failing due to missing the 'invoice_id' field (see #333)
# variation of this test exists in tests_update.py with different behaviour
def test_update_container_twice_nested(self):
invoice = Invoice.objects.create(title='test')
pre_existing_batch = Batch.objects.create(title='batch1', invoice=invoice)
pre_existing_task = ModelTask.objects.create(title='task1', batch=pre_existing_batch)
post = {
"@id": "{}/invoices/{}/".format(settings.BASE_URL, invoice.pk),
"title": "new",
"batches": [
{
"@id": "{}/batchs/{}/".format(settings.BASE_URL, pre_existing_batch.pk),
"title": "new",
"tasks": [
{
"@id": "{}/modeltasks/{}/".format(settings.BASE_URL, pre_existing_task.pk),
"title": "new"
},
{
"title": "tache 2"
}
]
},
{
"title": "z",
}
]
}
serializer_class = self._get_serializer_class(Invoice, 2, ("@id", "title", "batches"))
serializer = serializer_class(data=post, instance=invoice)
serializer.is_valid(raise_exception=True)
result = serializer.save()
self.assertEqual(result.title, "new")
self.assertEqual(result.urlid, invoice.urlid)
self.assertEqual(result.pk, invoice.pk)
self.assertIs(result.batches.count(), 2)
batches = result.batches.all().order_by('title')
self.assertEqual(batches[0].title, "new")
self.assertEqual(batches[0].urlid, pre_existing_batch.urlid)
self.assertEqual(batches[1].title, "z")
self.assertIs(batches[0].tasks.count(), 2)
tasks = batches[0].tasks.all().order_by('title')
self.assertEqual(tasks[0].title, "new")
self.assertEqual(tasks[0].urlid, pre_existing_task.urlid)
self.assertEqual(tasks[1].title, "tache 2")
# variation on the above test with external resources
def test_update_container_twice_nested_external_resources(self):
invoice = Invoice.objects.create(urlid='https://external.com/invoices/1/', title='test')
pre_existing_batch = Batch.objects.create(urlid='https://external.com/batchs/1/', title='batch1', invoice=invoice)
pre_existing_task = ModelTask.objects.create(urlid='https://external.com/tasks/1/', title='task1', batch=pre_existing_batch)
post = {
"@id": invoice.urlid,
"title": "new",
"batches": [
{
"@id": pre_existing_batch.urlid,
"title": "new",
"tasks": [
{
"@id": pre_existing_task.urlid,
"title": "new"
},
{
"@id": "https://anotherexternal.com/tasks/1/",
"title": "tache 2"
}
]
},
{
"@id": "https://yetanotherexternal.com/batchs/1/",
"title": "z"
}
]
}
serializer_class = self._get_serializer_class(Invoice, 2, ("@id", "title", "batches"))
serializer = serializer_class(data=post, instance=invoice)
serializer.is_valid(raise_exception=True)
result = serializer.save()
self.assertEqual(result.title, "new")
self.assertEqual(result.urlid, invoice.urlid)
self.assertEqual(result.pk, invoice.pk)
self.assertIs(result.batches.count(), 2)
batches = result.batches.all().order_by('title')
self.assertEqual(batches[0].title, "new")
self.assertEqual(batches[0].urlid, pre_existing_batch.urlid)
self.assertEqual(batches[1].title, "z")
self.assertIs(batches[0].tasks.count(), 2)
tasks = batches[0].tasks.all().order_by('title')
self.assertEqual(tasks[0].title, "new")
self.assertEqual(tasks[0].urlid, pre_existing_task.urlid)
self.assertEqual(tasks[1].title, "tache 2")'''
# variation on the test where a field is omitted on each level (no changes are made)
def test_update_container_twice_nested_no_changes_missing_fields(self):
invoice = Invoice.objects.create(title='test')
pre_existing_batch = Batch.objects.create(title='batch1', invoice=invoice)
pre_existing_task = ModelTask.objects.create(title='task1', batch=pre_existing_batch)
post = {
"@id": "{}/invoices/{}/".format(settings.BASE_URL, invoice.pk),
"batches": [
{
"@id": "{}/batchs/{}/".format(settings.BASE_URL, pre_existing_batch.pk),
"tasks": [
{
"@id": "{}/tasks/{}/".format(settings.BASE_URL, pre_existing_task.pk),
}
]
}
]
}
serializer_class = self._get_serializer_class(Invoice, 2, ("@id", "title", "batches"))
serializer = serializer_class(data=post, instance=invoice)
serializer.is_valid(raise_exception=True)
result = serializer.save(partial=True)
self.assertEqual(result.title, invoice.title)
self.assertEqual(result.urlid, invoice.urlid)
self.assertEqual(result.pk, invoice.pk)
self.assertIs(result.batches.count(), 1)
batches = result.batches.all()
self.assertEqual(batches[0].title, pre_existing_batch.title)
self.assertEqual(batches[0].urlid, pre_existing_batch.urlid)
self.assertIs(batches[0].tasks.count(), 1)
tasks = batches[0].tasks.all()
self.assertEqual(tasks[0].title, pre_existing_task.title)
def test_update_graph_edit_and_new_resource(self):
redundant_skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug1")
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug2")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug3")
job1 = JobOffer.objects.create(title="job test", slug="slug4")
job1.skills.add(redundant_skill)
job = {"@graph":
[
{
"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job1.slug),
"title": "job test updated",
"skills": {
"ldp:contains": [
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill1.slug)},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill2.slug)},
{"@id": "_.123"},
]}
},
{
"@id": "_.123",
"title": "new skill",
"obligatoire": "okay"
},
{
"@id": "{}/skills/{}/".format(settings.BASE_URL, skill1.slug),
},
{
"@id": "{}/skills/{}/".format(settings.BASE_URL, skill2.slug),
"title": "skill2 UP"
}
]
}
serializer_class = self._get_serializer_class(JobOffer, 2, ("@id", "title", "skills"))
serializer = serializer_class(data=job, instance=job1)
serializer.is_valid(raise_exception=True)
result = serializer.save()
skills = result.skills.all().order_by('title')
self.assertEqual(result.title, "job test updated")
self.assertIs(result.skills.count(), 3)
self.assertEqual(skills[0].title, "new skill") # new skill
self.assertEqual(skills[1].title, "skill1") # no change
self.assertEqual(skills[2].title, "skill2 UP") # title updated
def test_update_graph_2(self):
skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug")
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug1")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug2")
job1 = JobOffer.objects.create(title="job test", slug="slug1")
job1.skills.add(skill)
job = {"@graph":
[
{
"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job1.slug),
"title": "job test updated",
"skills": {
"@id": "{}/job-offers/{}/skills/".format(settings.BASE_URL, job1.slug)
}
},
{
"@id": "_.123",
"title": "new skill",
"obligatoire": "okay"
},
{
"@id": "{}/skills/{}/".format(settings.BASE_URL, skill1.slug),
},
{
"@id": "{}/skills/{}/".format(settings.BASE_URL, skill2.slug),
"title": "skill2 UP"
},
{
'@id': "{}/job-offers/{}/skills/".format(settings.BASE_URL, job1.slug),
"ldp:contains": [
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill1.slug)},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill2.slug)},
{"@id": "_.123"},
]
}
]
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job, instance=job1)
serializer.is_valid(raise_exception=True)
result = serializer.save()
skills = result.skills.all().order_by('title')
self.assertEqual(result.title, "job test updated")
self.assertIs(result.skills.count(), 3)
self.assertEqual(skills[0].title, "new skill") # new skill
self.assertEqual(skills[1].title, "skill1") # no change
self.assertEqual(skills[2].title, "skill2 UP") # title updated
self.assertEqual(skill, skill._meta.model.objects.get(pk=skill.pk)) # title updated
def test_update_list_with_reverse_relation(self):
user1 = get_user_model().objects.create()
conversation = Conversation.objects.create(description="Conversation 1", author_user=user1)
message1 = Message.objects.create(text="Message 1", conversation=conversation, author_user=user1)
message2 = Message.objects.create(text="Message 2", conversation=conversation, author_user=user1)
json = {"@graph": [
{
"@id": "{}/messages/{}/".format(settings.BASE_URL, message1.pk),
"text": "Message 1 UP"
},
{
"@id": "{}/messages/{}/".format(settings.BASE_URL, message2.pk),
"text": "Message 2 UP"
},
{
'@id': "{}/conversations/{}/".format(settings.BASE_URL, conversation.pk),
'description': "Conversation 1 UP",
"message_set": [
{"@id": "{}/messages/{}/".format(settings.BASE_URL, message1.pk)},
{"@id": "{}/messages/{}/".format(settings.BASE_URL, message2.pk)},
]
}
]
}
meta_args = {'model': Conversation, 'depth': 2, 'fields': ("@id", "description", "message_set")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('ConversationSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=json, instance=conversation)
serializer.is_valid(raise_exception=True)
result = serializer.save()
messages = result.message_set.all().order_by('text')
self.assertEqual(result.description, "Conversation 1 UP")
self.assertIs(result.message_set.count(), 2)
self.assertEqual(messages[0].text, "Message 1 UP")
self.assertEqual(messages[1].text, "Message 2 UP")
def test_add_new_element_with_foreign_key_id(self):
user1 = get_user_model().objects.create()
conversation = Conversation.objects.create(description="Conversation 1", author_user=user1)
message1 = Message.objects.create(text="Message 1", conversation=conversation, author_user=user1)
message2 = Message.objects.create(text="Message 2", conversation=conversation, author_user=user1)
json = {"@graph": [
{
"@id": "{}/messages/{}/".format(settings.BASE_URL, message1.pk),
"text": "Message 1 UP",
"author_user": {
'@id': "{}/users/{}/".format(settings.BASE_URL, user1.pk)
}
},
{
"@id": "{}/messages/{}/".format(settings.BASE_URL, message2.pk),
"text": "Message 2 UP",
"author_user": {
'@id': user1.urlid
}
},
{
"@id": "_:b1",
"text": "Message 3 NEW",
"author_user": {
'@id': user1.urlid
}
},
{
'@id': "{}/conversations/{}/".format(settings.BASE_URL, conversation.pk),
"author_user": {
'@id': user1.urlid
},
'description': "Conversation 1 UP",
'message_set': {
"@id": "{}/conversations/{}/message_set/".format(settings.BASE_URL, conversation.pk)
}
},
{
'@id': "{}/conversations/{}/message_set/".format(settings.BASE_URL, conversation.pk),
"ldp:contains": [
{"@id": "{}/messages/{}/".format(settings.BASE_URL, message1.pk)},
{"@id": "{}/messages/{}/".format(settings.BASE_URL, message2.pk)},
{"@id": "_:b1"}
]
}
]
}
meta_args = {'model': Conversation, 'depth': 2, 'fields': ("@id", "description", "message_set")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('ConversationSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=json, instance=conversation)
serializer.is_valid(raise_exception=True)
result = serializer.save()
messages = result.message_set.all().order_by('text')
self.assertEqual(result.description, "Conversation 1 UP")
self.assertIs(result.message_set.count(), 3)
self.assertEqual(messages[0].text, "Message 1 UP")
self.assertEqual(messages[1].text, "Message 2 UP")
self.assertEqual(messages[2].text, "Message 3 NEW")
# TODO: variation on https://git.startinblox.com/djangoldp-packages/djangoldp/issues/344
'''def test_update_container_invalid_fk_reference_given(self):
pass'''
def test_save_m2m_graph_with_many_nested(self):
invoice = {
"@graph": [
{
"@id": "./",
"batches": {"@id": "_:b381"},
"title": "Nouvelle facture",
"date": ""
},
{
"@id": "_:b381",
"tasks": {"@id": "_:b382"},
"title": "Batch 1"
},
{
"@id": "_:b382",
"title": "Tache 1"
}
]
}
meta_args = {'model': Invoice, 'depth': 2, 'fields': ("@id", "title", "batches", "date")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('InvoiceSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=invoice)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "Nouvelle facture")
self.assertIs(result.batches.count(), 1)
self.assertEqual(result.batches.all()[0].title, "Batch 1")
self.assertIs(result.batches.all()[0].tasks.count(), 1)
self.assertEqual(result.batches.all()[0].tasks.all()[0].title, "Tache 1")
def test_save_m2m(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug1")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug2")
job = {"title": "job test",
"slug": "slug1",
"skills": {
"ldp:contains": [
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill1.slug)},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, skill2.slug), "title": "skill2 UP"},
{"title": "skill3", "obligatoire": "obligatoire", "slug": "slug3"},
]}
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "job test")
self.assertIs(result.skills.count(), 3)
self.assertEqual(result.skills.all()[0].title, "skill1") # no change
self.assertEqual(result.skills.all()[1].title, "skill2 UP") # title updated
self.assertEqual(result.skills.all()[2].title, "skill3") # creation on the fly
# variation switching the http prefix of the BASE_URL in the request
@override_settings(BASE_URL='http://happy-dev.fr/')
def test_save_m2m_switch_base_url_prefix(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug1")
job = {"title": "job test",
"slug": "slug1",
"skills": {
"ldp:contains": [
{"@id": "http://happy-dev.fr/skills/{}/".format(skill1.slug)},
]}
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "job test")
self.assertIs(result.skills.count(), 1)
self.assertEqual(result.skills.all()[0].title, "skill1") # no change
def test_save_m2m_graph_simple(self):
job = {"@graph": [
{"title": "job test", "slug": "slugjob",
},
]}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "job test")
self.assertIs(result.skills.count(), 0)
def test_save_m2m_graph_with_nested(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
job = {"@graph": [
{"title": "job test",
"slug": "slugj",
"skills": {"@id": "_.123"}
},
{"@id": "_.123", "title": "skill3 NEW", "obligatoire": "obligatoire", "slug": "skill3"},
]}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "job test")
self.assertIs(result.skills.count(), 1)
self.assertEqual(result.skills.all()[0].title, "skill3 NEW") # creation on the fly
def test_save_without_nested_fields(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
job = {"title": "job test", "slug": "c"}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEqual(result.title, "job test")
self.assertIs(result.skills.count(), 0)
def test_save_on_sub_iri(self):
"""
POST /job-offers/1/skills/
"""
job = JobOffer.objects.create(title="job test")
skill = {"title": "new SKILL"}
meta_args = {'model': Skill, 'depth': 2, 'fields': ("@id", "title")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('SkillSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=skill)
serializer.is_valid()
kwargs = {}
kwargs['joboffer'] = job
result = serializer.save(**kwargs)
self.assertEqual(result.title, "new SKILL")
self.assertIs(result.joboffer_set.count(), 1)
self.assertEqual(result.joboffer_set.get(), job)
self.assertIs(result.joboffer_set.get().skills.count(), 1)
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from rest_framework.test import APIClient, APIRequestFactory, APITestCase
from djangoldp.tests.models import Post
......@@ -8,7 +8,7 @@ class TestPagination(APITestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.client = APIClient()
for i in range(0, 10):
for i in range(0, 30):
Post.objects.create(content="content {}".format(i))
def tearDown(self):
......@@ -17,12 +17,12 @@ class TestPagination(APITestCase):
def test_next(self):
response = self.client.get('/posts/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('link', response._headers)
self.assertEquals(response._headers['link'][1], '<http://testserver/posts/?limit=5&offset=5>; rel="next"')
self.assertIn('link', response.headers)
self.assertEqual(response.headers['link'], '<http://testserver/posts/?p=2>; rel="next"')
def test_previous(self):
response = self.client.get('/posts/?offset=2&limit=2', content_type='application/ld+json')
response = self.client.get('/posts/?p=2', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('link', response._headers)
self.assertEquals(response._headers['link'][1],
'<http://testserver/posts/?limit=2>; rel="prev", <http://testserver/posts/?limit=2&offset=4>; rel="next"')
self.assertIn('link', response.headers)
self.assertEqual(response.headers['link'],
'<http://testserver/posts/>; rel="prev", <http://testserver/posts/?p=3>; rel="next"')
import datetime
import platform
import time
from django.contrib.auth import get_user_model
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from statistics import mean, variance
import cProfile, io, pstats
from djangoldp.tests.models import Post, JobOffer, Skill, Project, User
class TestPerformanceGET(APITestCase):
posts = []
skills = []
jobs = []
test_volume = 100
result_line = []
withAuth = True
fixtures = ['test.json']
@classmethod
def setUpClass(cls):
super().setUpClass()
print("Init", end='', flush=True)
step = cls.test_volume / 10
cls.factory = APIRequestFactory()
for i in range(cls.test_volume):
get_user_model().objects.create_user(username='john{}'.format(i), email='jlennon{}@beatles.com'.format(i),
password='glass onion')
cls.posts.append(Post.objects.create(content="content"))
cls.skills.append(Skill.objects.create(title="Java", obligatoire="ok", slug=str(i)))
for i in range(cls.test_volume):
job = JobOffer.objects.create(title="job", slug=str(i))
for skill in cls.skills:
job.skills.add(skill)
if i % step == 0:
print(".", end='', flush=True)
job.save()
cls.jobs.append(job)
cls.result_line.append(platform.node())
cls.result_line.append(datetime.datetime.today().strftime("%b %d %Y %H:%M:%S"))
cls.result_line.append(cls.withAuth)
cls.result_line.append(cls.withPermsCache)
cls.result_line.append(cls.test_volume)
cls.result_line.append("N/A")
cls.result_line.append("N/A")
cls.result_line.append("N/A")
cls.result_line.append("N/A")
cls.result_line.append("N/A")
cls.result_line.append("N/A")
cls.result_line.append("N/A")
def setUp(self):
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion', is_active=True)
self.client.force_authenticate(user=self.user)
print('there are ' + str(Project.objects.count()) + ' projects in the database')
print('there are ' + str(User.objects.count()) + ' users in the database')
@classmethod
def tearDownClass(cls):
import csv
with open('perf_result.csv', 'a', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
writer.writerow(cls.result_line)
def test_get_resource(self):
times = []
for post in self.posts:
start_time = time.time()
response = self.client.get('/posts/{}/'.format(post.pk), content_type='application/ld+json')
end_time = time.time()
times.append(end_time - start_time)
self.result_line[5] = str(mean(times))
print("Variance execution time :" + str(variance(times)))
def test_get_container(self):
times = []
for post in self.posts:
start_time = time.time()
response = self.client.get('/posts/', content_type='application/ld+json')
end_time = time.time()
times.append(end_time - start_time)
self.result_line[6] = str(mean(times))
print("Variance execution time :" + str(variance(times)))
def test_get_filtered_fields(self):
times = []
for job in self.jobs:
start_time = time.time()
response = self.client.get('/job-offers/{}/'.format(job.slug), content_type='application/ld+json')
end_time = time.time()
times.append(end_time - start_time)
self.result_line[7] = str(mean(times))
print("Variance execution time :" + str(variance(times)))
def test_get_reverse_filtered_fields(self):
times = []
for skill in self.skills:
start_time = time.time()
response = self.client.get('/skills/{}/'.format(skill.slug), content_type='application/ld+json')
end_time = time.time()
times.append(end_time - start_time)
self.result_line[8] = str(mean(times))
print("Variance execution time :" + str(variance(times)))
def test_get_nested(self):
times = []
for job in self.jobs:
start_time = time.time()
response = self.client.get('/jobs/{}/skills'.format(job.slug), content_type='application/ld+json')
end_time = time.time()
times.append(end_time - start_time)
self.result_line[9] = str(mean(times))
print("Variance execution time :" + str(variance(times)))
def _print_stats(self, pr):
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.sort_stats('time').print_stats(50)
print(s.getvalue())
def _enable_new_profiler(self):
pr = cProfile.Profile()
pr.enable()
return pr
def test_get_users_container(self):
pr = self._enable_new_profiler()
response = self.client.get('/projects/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
print('counted ' + str(len(response.data['ldp:contains'])) + ' projects')
pr.disable()
self._print_stats(pr)
pr = self._enable_new_profiler()
start_time = time.time()
response = self.client.get('/users/', content_type='application/ld+json')
end_time = time.time()
self.assertEqual(response.status_code, 200)
print('counted ' + str(len(response.data['ldp:contains'])) + ' users')
self.result_line[10] = str(end_time-start_time)
pr.disable()
self._print_stats(pr)
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient, APITestCase
from djangoldp.tests.models import User, Project
import cProfile, pstats, io
class TestPerformance(APITestCase):
fixtures = ['test.json',]
def setUp(self):
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
password='glass onion')
self.client.force_authenticate(user=self.user)
print('there are ' + str(Project.objects.count()) + ' projects in the database')
print('there are ' + str(User.objects.count()) + ' users in the database')
def _print_stats(self, pr):
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.print_stats()
print(s.getvalue())
def _enable_new_profiler(self):
pr = cProfile.Profile()
pr.enable()
return pr
def test_get_container(self):
pr = self._enable_new_profiler()
response = self.client.get('/projects/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
print('counted ' + str(len(response.data['ldp:contains'])) + ' projects')
pr.disable()
self._print_stats(pr)
pr = self._enable_new_profiler()
response = self.client.get('/users/', content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
print('counted ' + str(len(response.data['ldp:contains'])) + ' users')
pr.disable()
self._print_stats(pr)
import json
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from guardian.models import GroupObjectPermission
from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from djangoldp.tests.models import AnonymousReadOnlyPost, AuthenticatedOnlyPost, ReadOnlyPost, DoubleInheritModel, \
ReadAndCreatePost, OwnedResource, RestrictedCircle, RestrictedResource, ANDPermissionsDummy, ORPermissionsDummy
class TestPermissions(APITestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.client = APIClient()
def authenticate(self):
self.user = get_user_model().objects.create_user(username='random', email='random@user.com', password='Imrandom')
self.client = APIClient(enforce_csrf_checks=True)
self.client.force_authenticate(user=self.user)
def check_can_add(self, url, status_code=201, field='content', extra_content={}):
data = extra_content
extra_content[f"https://cdn.startinblox.com/owl#{field}"] = "new post"
response = self.client.post(url, data=json.dumps(data), content_type='application/ld+json')
self.assertEqual(response.status_code, status_code)
if status_code == 201:
self.assertIn('@id', response.data)
return response.data['@id']
def check_can_change(self, id, status_code=200, field='content'):
data = { f"https://cdn.startinblox.com/owl#{field}": "changed post" }
response = self.client.put(id, data=json.dumps(data), content_type='application/ld+json')
self.assertEqual(response.status_code, status_code)
if status_code == 200:
self.assertIn('@id', response.data)
self.assertEqual(response.data['@id'], id)
def check_can_view_one(self, id, status_code=200):
response = self.client.get(id, content_type='application/ld+json')
self.assertEqual(response.status_code, status_code)
if status_code == 200:
self.assertEqual(response.data['@id'], id)
def check_can_view(self, url, ids, status_code=200):
response = self.client.get(url, content_type='application/ld+json')
self.assertEqual(response.status_code, status_code)
if status_code == 200:
self.assertEqual(len(response.data['ldp:contains']), len(ids))
for resource, id in zip(response.data['ldp:contains'], ids):
self.assertEqual(resource['@id'], id)
for id in ids:
self.check_can_view_one(id, status_code)
def test_permissionless_model(self):
id = self.check_can_add('/posts/')
self.check_can_view('/posts/', [id])
def test_anonymous_readonly(self):
post = AnonymousReadOnlyPost.objects.create(content = "test post")
self.check_can_view('/anonymousreadonlyposts/', [post.urlid])
self.check_can_add('/anonymousreadonlyposts/', 403)
self.check_can_change(post.urlid, 403)
self.authenticate()
self.check_can_add('/anonymousreadonlyposts/')
self.check_can_change(post.urlid)
def test_authenticated_only(self):
post = AuthenticatedOnlyPost.objects.create(content = "test post")
self.check_can_view('/authenticatedonlyposts/', [post.urlid], 403)
self.check_can_add('/authenticatedonlyposts/', 403)
self.check_can_change(post.urlid, 403)
post.delete()
self.authenticate()
#When authenticated it should behave like a non protected model
id = self.check_can_add('/authenticatedonlyposts/')
self.check_can_view('/authenticatedonlyposts/', [id])
self.check_can_change(id)
def test_readonly(self):
post = ReadOnlyPost.objects.create(content = "test post")
self.check_can_view('/readonlyposts/', [post.urlid])
self.check_can_add('/readonlyposts/', 403)
self.check_can_change(post.urlid, 403)
def test_readandcreate(self):
post = ReadAndCreatePost.objects.create(content = "test post")
self.check_can_view('/readandcreateposts/', [post.urlid])
self.check_can_add('/readandcreateposts/')
self.check_can_change(post.urlid, 403)
def test_owner_permissions(self):
self.authenticate()
them = get_user_model().objects.create_user(username='them', email='them@user.com', password='itstheirsecret')
mine = OwnedResource.objects.create(description="Mine!", user=self.user)
theirs = OwnedResource.objects.create(description="Theirs", user=them)
noones = OwnedResource.objects.create(description="I belong to NO ONE!")
self.check_can_view('/ownedresources/', [mine.urlid]) #checks I can access mine and only mine
self.check_can_change(mine.urlid)
self.check_can_view_one(theirs.urlid, 404)
self.check_can_change(theirs.urlid, 404)
self.check_can_view_one(noones.urlid, 404)
self.check_can_change(noones.urlid, 404)
def check_permissions(self, obj, group, required_perms):
perms = GroupObjectPermission.objects.filter(group=group)
for perm in perms:
self.assertEqual(perm.content_type.model, obj._meta.model_name)
self.assertEqual(perm.object_pk, str(obj.pk))
self.assertEqual(set(perms.values_list('permission__codename', flat=True)),
{f'{perm}_{obj._meta.model_name}' for perm in required_perms})
def create_circles(self):
self.authenticate()
self.user.user_permissions.add(Permission.objects.get(codename='view_restrictedcircle'))
them = get_user_model().objects.create_user(username='them', email='them@user.com', password='itstheirsecret')
mine = RestrictedCircle.objects.create(name="mine", description="Mine!", owner=self.user)
theirs = RestrictedCircle.objects.create(name="theirs", description="Theirs", owner=them)
noones = RestrictedCircle.objects.create(name="no one's", description="I belong to NO ONE!")
return mine, theirs, noones
def test_role_permissions(self):
mine, theirs, noones = self.create_circles()
self.assertIn(self.user, mine.members.user_set.all())
self.assertIn(self.user, mine.admins.user_set.all())
self.assertNotIn(self.user, theirs.members.user_set.all())
self.assertNotIn(self.user, theirs.admins.user_set.all())
self.assertNotIn(self.user, noones.members.user_set.all())
self.assertNotIn(self.user, noones.admins.user_set.all())
self.check_can_view('/restrictedcircles/', [mine.urlid]) #check filtering
self.check_permissions(mine, mine.members, RestrictedCircle._meta.permission_roles['members']['perms'])
self.check_permissions(mine, mine.admins, RestrictedCircle._meta.permission_roles['admins']['perms'])
def test_inherit_permissions(self):
mine, theirs, noones = self.create_circles()
myresource = RestrictedResource.objects.create(content="mine", circle=mine)
their_resource = RestrictedResource.objects.create(content="theirs", circle=theirs)
noones_resource = RestrictedResource.objects.create(content="noones", circle=noones)
self.check_can_view('/restrictedresources/', [myresource.urlid])
self.check_can_change(myresource.urlid)
self.check_can_change(their_resource.urlid, 404)
self.check_can_change(noones_resource.urlid, 404)
def test_inherit_several_permissions(self):
mine, theirs, noones = self.create_circles()
ro_resource = ReadOnlyPost.objects.create(content="read only")
myresource = DoubleInheritModel.objects.create(content="mine", circle=mine, ro_ancestor=None)
some = DoubleInheritModel.objects.create(content="some", circle=theirs, ro_ancestor=ro_resource)
other = DoubleInheritModel.objects.create(content="other", circle=noones, ro_ancestor=None)
self.check_can_view('/doubleinheritmodels/', [myresource.urlid, some.urlid])
self.check_can_change(myresource.urlid)
self.check_can_change(some.urlid, 403)
self.check_can_change(other.urlid, 404)
def test_inherit_permissions_none(self):
id = self.check_can_add('/doubleinheritmodels/')
resource = DoubleInheritModel.objects.get(urlid=id)
self.check_can_view('/doubleinheritmodels/', [resource.urlid])
circle = RestrictedCircle.objects.create()
id = self.check_can_add('/doubleinheritmodels/', 404, extra_content={'https://cdn.startinblox.com/owl#circle': {'@id': circle.urlid}})
def test_and_permissions(self):
self.authenticate()
abc = ANDPermissionsDummy.objects.create(title='ABC')
youpi = ANDPermissionsDummy.objects.create(title='youpi woopaa')
wonder = ANDPermissionsDummy.objects.create(title='A Wonderful World!!')
plop = ANDPermissionsDummy.objects.create(title='plop')
self.check_can_view('/andpermissionsdummys/', [wonder.urlid], 403)
self.check_can_add('/andpermissionsdummys/', 403, field='title')
self.check_can_change(wonder.urlid, 403, field='title')
self.user.username = 'toto'
self.user.save()
self.check_can_view('/andpermissionsdummys/', [wonder.urlid])
self.check_can_view_one(abc.urlid, 404)
self.check_can_view_one(youpi.urlid, 404)
self.check_can_view_one(plop.urlid, 404)
self.check_can_add('/andpermissionsdummys/', 403, field='title')
self.check_can_change(wonder.urlid, 403, field='title')
self.check_can_change(youpi.urlid, 403, field='title')
def test_or_permissions(self):
self.authenticate()
abc = ORPermissionsDummy.objects.create(title='ABC')
youpi = ORPermissionsDummy.objects.create(title='youpi woopaa')
wonder = ORPermissionsDummy.objects.create(title='A Wonderful World!!')
plop = ORPermissionsDummy.objects.create(title='plop')
self.check_can_view('/orpermissionsdummys/', [abc.urlid, wonder.urlid])
self.check_can_add('/andpermissionsdummys/', 403, field='title')
self.check_can_change(wonder.urlid, 403, field='title')
self.user.username = 'toto'
self.user.save()
self.check_can_view('/orpermissionsdummys/', [abc.urlid, youpi.urlid, wonder.urlid])
self.check_can_view_one(plop.urlid, 404)
self.check_can_add('/orpermissionsdummys/', field='title')
self.check_can_change(wonder.urlid, field='title')
self.check_can_change(plop.urlid, 404, field='title')
\ No newline at end of file
from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIRequestFactory, APIClient
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.utils import json
from djangoldp.models import Model
from djangoldp.serializers import LDPSerializer
from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource, Post, Circle, Project
from djangoldp.tests.models import (Circle, Invoice, LDPDummy, Post, Project,
Resource, Space)
class Save(TestCase):
class PostTestCase(TestCase):
def setUp(self):
self.factory = APIRequestFactory()
......@@ -17,163 +17,18 @@ class Save(TestCase):
password='glass onion')
self.client.force_authenticate(self.user)
def tearDown(self):
pass
def test_save_m2m_graph_with_many_nested(self):
invoice = {
"@graph": [
{
"@id": "./",
"batches": {"@id": "_:b381"},
"title": "Nouvelle facture",
"date": ""
},
{
"@id": "_:b381",
"tasks": {"@id": "_:b382"},
"title": "Batch 1"
},
{
"@id": "_:b382",
"title": "Tache 1"
}
]
}
meta_args = {'model': Invoice, 'depth': 2, 'fields': ("@id", "title", "batches", "date")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('InvoiceSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=invoice)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "Nouvelle facture")
self.assertIs(result.batches.count(), 1)
self.assertEquals(result.batches.all()[0].title, "Batch 1")
self.assertIs(result.batches.all()[0].tasks.count(), 1)
self.assertEquals(result.batches.all()[0].tasks.all()[0].title, "Tache 1")
def test_save_m2m(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug1")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug2")
job = {"title": "job test",
"slug": "slug1",
"skills": {
"ldp:contains": [
{"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug)},
{"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug), "title": "skill2 UP"},
{"title": "skill3", "obligatoire": "obligatoire", "slug": "slug3"},
]}
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "job test")
self.assertIs(result.skills.count(), 3)
self.assertEquals(result.skills.all()[0].title, "skill1") # no change
self.assertEquals(result.skills.all()[1].title, "skill2 UP") # title updated
self.assertEquals(result.skills.all()[2].title, "skill3") # creation on the fly
def test_save_m2m_graph_simple(self):
job = {"@graph": [
{"title": "job test", "slug": "slugjob",
},
]}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "job test")
self.assertIs(result.skills.count(), 0)
def test_save_m2m_graph_with_nested(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
job = {"@graph": [
{"title": "job test",
"slug": "slugj",
"skills": {"@id": "_.123"}
},
{"@id": "_.123", "title": "skill3 NEW", "obligatoire": "obligatoire", "slug": "skill3"},
]}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "job test")
self.assertIs(result.skills.count(), 1)
self.assertEquals(result.skills.all()[0].title, "skill3 NEW") # creation on the fly
def test_save_without_nested_fields(self):
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
job = {"title": "job test", "slug": "c"}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "job test")
self.assertIs(result.skills.count(), 0)
def test_save_on_sub_iri(self):
"""
POST /job-offers/1/skills/
"""
job = JobOffer.objects.create(title="job test")
skill = {"title": "new SKILL"}
meta_args = {'model': Skill, 'depth': 2, 'fields': ("@id", "title")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('SkillSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=skill)
serializer.is_valid()
kwargs = {}
kwargs['joboffer'] = job
result = serializer.save(**kwargs)
self.assertEquals(result.title, "new SKILL")
self.assertIs(result.joboffer_set.count(), 1)
self.assertEquals(result.joboffer_set.get(), job)
self.assertIs(result.joboffer_set.get().skills.count(), 1)
def test_save_fk_graph_with_nested(self):
post = {
'@graph': [
{
'http://happy-dev.fr/owl/#title': "title",
'http://happy-dev.fr/owl/#invoice': {
'https://cdn.startinblox.com/owl#title': "title",
'https://cdn.startinblox.com/owl#invoice': {
'@id': "_.123"
}
},
{
'@id': "_.123",
'http://happy-dev.fr/owl/#title': "title 2"
'https://cdn.startinblox.com/owl#title': "title 2"
}
]
}
......@@ -181,17 +36,17 @@ class Save(TestCase):
response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertNotIn('author', response.data)
self.assertEquals(response.data['title'], "title")
self.assertEquals(response.data['invoice']['title'], "title 2")
self.assertEqual(response.data['title'], "title")
self.assertEqual(response.data['invoice']['title'], "title 2")
def test_save_fk_graph_with_existing_nested(self):
invoice = Invoice.objects.create(title="title 3")
post = {
'@graph': [
{
'http://happy-dev.fr/owl/#title': "title",
'http://happy-dev.fr/owl/#invoice': {
'@id': "https://happy-dev.fr{}{}/".format(Model.container_id(invoice), invoice.id)
'https://cdn.startinblox.com/owl#title': "title",
'https://cdn.startinblox.com/owl#invoice': {
'@id': "http://happy-dev.fr{}{}/".format(Model.container_id(invoice), invoice.id)
}
}
]
......@@ -200,27 +55,27 @@ class Save(TestCase):
response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertNotIn('author', response.data)
self.assertEquals(response.data['title'], "title")
self.assertEquals(response.data['invoice']['title'], "title 3")
self.assertEqual(response.data['title'], "title")
self.assertEqual(response.data['invoice']['title'], "title 3")
def test_post_should_accept_missing_field_id_nullable(self):
body = [
{
'@id': "./",
'http://happy-dev.fr/owl/#content': "post update",
}
]
response = self.client.post('/posts/', data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertIn('peer_user', response.data)
body = [
{
'@id': "./",
'https://cdn.startinblox.com/owl#content': "post update",
}
]
response = self.client.post('/posts/', data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertIn('peer_user', response.data)
def test_post_should_accept_empty_field_if_nullable(self):
body = [
{
'@id': "./",
'http://happy-dev.fr/owl/#content': "post update",
'http://happy-dev.fr/owl/#peer_user': ""
'https://cdn.startinblox.com/owl#content': "post update",
'https://cdn.startinblox.com/owl#peer_user': ""
}
]
response = self.client.post('/posts/', data=json.dumps(body),
......@@ -234,17 +89,17 @@ class Save(TestCase):
body = [
{
'@id': "_:b216",
'http://happy-dev.fr/owl/#description': "user update",
'http://happy-dev.fr/owl/#ddummy': {
"@id": "https://happy-dev.fr{}{}/".format(Model.container_id(dummy), dummy.id)
'https://cdn.startinblox.com/owl#description': "user update",
'https://cdn.startinblox.com/owl#ddummy': {
"@id": "http://happy-dev.fr{}{}/".format(Model.container_id(dummy), dummy.id)
}
},
{
'@id': './',
"http://happy-dev.fr/owl/#first_name": "Alexandre",
"http://happy-dev.fr/owl/#last_name": "Bourlier",
"http://happy-dev.fr/owl/#username": "alex",
'http://happy-dev.fr/owl/#userprofile': {'@id': "_:b216"}
"https://cdn.startinblox.com/owl#first_name": "Alexandre",
"https://cdn.startinblox.com/owl#last_name": "Bourlier",
"https://cdn.startinblox.com/owl#username": "alex",
'https://cdn.startinblox.com/owl#userprofile': {'@id': "_:b216"}
}
]
response = self.client.post('/users/', data=json.dumps(body),
......@@ -262,7 +117,7 @@ class Save(TestCase):
}
],
'@context': {
"@vocab": "http://happy-dev.fr/owl/#",
"@vocab": "https://cdn.startinblox.com/owl#",
}
}
response = self.client.post('/posts/', data=json.dumps(body),
......@@ -272,23 +127,22 @@ class Save(TestCase):
def test_nested_container(self):
resource = Resource.objects.create()
body = {
'http://happy-dev.fr/owl/#title': "new job",
'http://happy-dev.fr/owl/#slug': "job1",
'https://cdn.startinblox.com/owl#title': "new job",
'https://cdn.startinblox.com/owl#slug': "job1",
}
response = self.client.post('/resources/{}/joboffers/'.format(resource.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'],
"http://testserver/resources/{}/".format(resource.pk))
self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], resource.urlid)
self.assertEqual(response.data['title'], "new job")
def test_nested_container_bis(self):
invoice = Invoice.objects.create()
body = {
'http://happy-dev.fr/owl/#title': "new batch",
'https://cdn.startinblox.com/owl#title': "new batch",
}
response = self.client.post('/invoices/{}/batches/'.format(invoice.pk),
......@@ -299,39 +153,20 @@ class Save(TestCase):
"http://happy-dev.fr/invoices/{}/".format(invoice.pk))
self.assertEqual(response.data['title'], "new batch")
def test_nested_container_ter(self):
circle = Circle.objects.create()
body = {
'user' : {
"username" : "hubl-workaround-493"
},
# 'circle' : {},
'@context': {
"@vocab": "http://happy-dev.fr/owl/#",
}
}
response = self.client.post('/circles/{}/members/'.format(circle.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['circle']['@id'],
"http://testserver/circles/{}/".format(circle.pk))
def test_nested_container_federated(self):
resource = Resource.objects.create()
body = {
'http://happy-dev.fr/owl/#@id': "http://external.job/job/1",
'https://cdn.startinblox.com/owl#@id': "http://external.job/job/1",
}
response = self.client.post('/resources/{}/joboffers/'.format(resource.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'],
"http://testserver/resources/{}/".format(resource.pk))
self.assertEqual(response.data['@id'], "http://external.job/job/1")
self.assertIn('@type', response.data)
response = self.client.get('/resources/{}/'.format(resource.pk))
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], "http://external.job/job/1")
def test_embedded_context_2(self):
body = {
......@@ -339,7 +174,7 @@ class Save(TestCase):
'content': "post update",
'peer_user': "",
'@context': {
"@vocab": "http://happy-dev.fr/owl/#",
"@vocab": "https://cdn.startinblox.com/owl#",
}
}
......@@ -353,7 +188,7 @@ class Save(TestCase):
'content': "post update",
'peer_user': "",
'@context': {
"@vocab": "http://happy-dev.fr/owl/#",
"@vocab": "https://cdn.startinblox.com/owl#",
}
}
......@@ -369,7 +204,7 @@ class Save(TestCase):
'content': "post update",
'peer_user': {'none': None},
'@context': {
"@vocab": "http://happy-dev.fr/owl/#",
"@vocab": "https://cdn.startinblox.com/owl#",
}
}
......@@ -380,13 +215,76 @@ class Save(TestCase):
def test_nested_container_user_federated(self):
project = Project.objects.create()
body = {
'http://happy-dev.fr/owl/#@id': "http://external.user/user/1/",
'https://cdn.startinblox.com/owl#@id': "http://external.user/user/1/",
}
response = self.client.post('/projects/{}/team/'.format(project.pk),
response = self.client.post('/projects/{}/members/'.format(project.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['projects']['ldp:contains'][0]['@id'],
"http://testserver/projects/{}/".format(project.pk))
self.assertEqual(response.data['@id'], "http://external.user/user/1/")
self.assertIn('@type', response.data)
response = self.client.get('/projects/{}/'.format(project.pk))
self.assertEqual(response.data['members']['ldp:contains'][0]['@id'], "http://external.user/user/1/")
# https://www.w3.org/TR/json-ld/#value-objects
def test_post_field_with_value_object(self):
post = {
'https://cdn.startinblox.com/owl#title': {
'@value': "title",
'@language': "en"
}
}
response = self.client.post('/invoices/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data['title'], "title")
# from JSON-LD spec: "The value associated with the @value key MUST be either a string, a number, true, false or null"
def test_save_field_with_invalid_value_object(self):
invoice = Invoice.objects.create(title="title 3")
post = {
'https://cdn.startinblox.com/owl#invoice': {
'@value': {'title': 'title',
'@id': "http://happy-dev.fr{}{}/".format(Model.container_id(invoice), invoice.id)}
}
}
response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 400)
# TODO: bug with PyLD: https://github.com/digitalbazaar/pyld/issues/142
# from JSON-LD spec: "If the value associated with the @type key is @json, the value MAY be either an array or an object"
'''
def test_save_field_with_object_value_object(self):
invoice = Invoice.objects.create(title="title 3")
post = {
'https://cdn.startinblox.com/owl#invoice': {
'@value': {'title': 'title', '@id': "http://happy-dev.fr{}{}/".format(Model.container_id(invoice), invoice.id)},
'@type': '@json'
}
}
response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
'''
# the below test is necessary because of an obscure bug where the OneToOne field is successfully applied
# during the life of the serializer (and response) but is not persisted in the database,
# when it is posted onto the reverse relation
def test_one_to_one_field_reverse_post(self):
self.assertEqual(Circle.objects.count(), 0)
self.assertEqual(Space.objects.count(), 0)
body = {
'@context': {'@vocab': "https://cdn.startinblox.com/owl#" },
'space': {'name': "Etablissement"}
}
response = self.client.post('/circles/', data=json.dumps(body), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
self.assertEqual(Circle.objects.count(), 1)
self.assertEqual(Space.objects.count(), 1)
circle = Circle.objects.all()[0]
space = circle.space
self.assertIsNotNone(space)
self.assertIsNotNone(space.circle)
......@@ -3,6 +3,10 @@ from django.test import TestCase
class TestSettings(TestCase):
def test_inexistent_returns_default(self):
"""Assert a inexistent key returns the provided default value."""
assert getattr(settings, 'INEXISTENT', 'something') == 'something'
def test_only_in_core_config(self):
"""Asserts values defined only in core config."""
assert settings.DEBUG == False
......@@ -13,15 +17,13 @@ class TestSettings(TestCase):
def test_only_in_user_config(self):
"""Asserts LDP packages are loaded from YAML file."""
assert settings.DJANGOLDP_PACKAGES == ['djangoldp.tests']
assert 'djangoldp.tests' in settings.DJANGOLDP_PACKAGES
def test_overrided_core_by_package_config(self):
# FIXME
pass
assert settings.USE_I18N == False
def test_overrided_package_by_user_config(self):
# FIXME
pass
assert settings.USE_TZ == False
def test_overrided_core_by_user_config(self):
"""Asserts values overrided from user configuration."""
......@@ -29,28 +31,21 @@ class TestSettings(TestCase):
def test_installed_apps_resolution(self):
"""Asserts LDP packages are referenced along with default installed apps."""
assert settings.INSTALLED_APPS == [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'djangoldp',
'guardian',
'djangoldp.tests',
'djangoldp.tests.dummy.apps.DummyConfig'
]
# test inclusion from server YAML settings
assert 'djangoldp.tests' in settings.INSTALLED_APPS
# test inclusion from ldppackage settings
assert 'djangoldp.tests.dummy.apps.DummyConfig' in settings.INSTALLED_APPS
# test inclusion from default core settings
assert 'djangoldp' in settings.INSTALLED_APPS
# test deduplication of dummy app
assert settings.INSTALLED_APPS.count('djangoldp.tests.dummy.apps.DummyConfig') == 1
# FIXME: We should check the order
def test_reference_middleware(self):
"""Asserts middlewares added in packages are added to the settings."""
assert settings.MIDDLEWARE == [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'djangoldp.tests.dummy.middleware.DummyMiddleware'
]
assert 'djangoldp.tests.dummy.middleware.DummyMiddleware' in settings.MIDDLEWARE
def test_extra_module(self):
#FIXME
pass
import json
from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIRequestFactory, APIClient
from djangoldp.models import LDPSource
from djangoldp.tests.models import Invoice, Circle
class TestTemp(TestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion')
self.client.force_authenticate(self.user)
def tearDown(self):
pass
import uuid
from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import TestCase
from rest_framework.test import APIRequestFactory, APIClient
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.utils import json
from djangoldp.serializers import LDPSerializer
from djangoldp.tests.models import Post, UserProfile, Resource, Circle
from djangoldp.tests.models import Skill, JobOffer, Conversation, Message, Project
from djangoldp.tests.models import (Batch, Conversation, Invoice, JobOffer,
NotificationSetting, Project, Resource,
Skill, Task, UserProfile)
class Update(TestCase):
......@@ -18,265 +20,60 @@ class Update(TestCase):
password='glass onion')
self.client.force_authenticate(user=self.user)
def tearDown(self):
pass
def test_update(self):
skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug1")
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug2")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug3")
job1 = JobOffer.objects.create(title="job test")
job1.skills.add(skill)
job = {"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.slug),
"title": "job test updated",
"skills": {
"ldp:contains": [
{"title": "new skill", "obligatoire": "okay"},
{"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug)},
{"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug), "title": "skill2 UP"},
]}
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job, instance=job1)
serializer.is_valid()
result = serializer.save()
self.assertEquals(result.title, "job test updated")
self.assertIs(result.skills.count(), 3)
skills = result.skills.all().order_by('title')
self.assertEquals(skills[0].title, "new skill") # new skill
self.assertEquals(skills[1].title, "skill1") # no change
self.assertEquals(skills[2].title, "skill2 UP") # title updated
def test_update_graph(self):
skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug1")
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug2")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug3")
job1 = JobOffer.objects.create(title="job test", slug="slug4")
job1.skills.add(skill)
job = {"@graph":
[
{
"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.slug),
"title": "job test updated",
"skills": {
"ldp:contains": [
{"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug)},
{"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug)},
{"@id": "_.123"},
]}
},
{
"@id": "_.123",
"title": "new skill",
"obligatoire": "okay"
},
{
"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug),
},
{
"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug),
"title": "skill2 UP"
}
]
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job, instance=job1)
serializer.is_valid()
result = serializer.save()
skills = result.skills.all().order_by('title')
self.assertEquals(result.title, "job test updated")
self.assertIs(result.skills.count(), 3)
self.assertEquals(skills[0].title, "new skill") # new skill
self.assertEquals(skills[1].title, "skill1") # no change
self.assertEquals(skills[2].title, "skill2 UP") # title updated
def test_update_graph_2(self):
skill = Skill.objects.create(title="to drop", obligatoire="obligatoire", slug="slug")
skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="slug1")
skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="slug2")
job1 = JobOffer.objects.create(title="job test", slug="slug1")
job1.skills.add(skill)
job = {"@graph":
[
{
"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.slug),
"title": "job test updated",
"skills": {
"@id": "https://happy-dev.fr/job-offers/{}/skills/".format(job1.slug)
}
},
{
"@id": "_.123",
"title": "new skill",
"obligatoire": "okay"
},
{
"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug),
},
{
"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug),
"title": "skill2 UP"
},
{
'@id': "https://happy-dev.fr/job-offers/{}/skills/".format(job1.slug),
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/326
'''
def test_update_container_append_resource(self):
pre_existing_skill_a = Skill.objects.create(title="to keep", obligatoire="obligatoire", slug="slug1")
pre_existing_skill_b = Skill.objects.create(title="to keep", obligatoire="obligatoire", slug="slug2")
job = JobOffer.objects.create(title="job test")
job.skills.add(pre_existing_skill_a)
job.skills.add(pre_existing_skill_b)
post = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "https://happy-dev.fr/skills/{}/".format(skill1.slug)},
{"@id": "https://happy-dev.fr/skills/{}/".format(skill2.slug)},
{"@id": "_.123"},
]
}
]
}
meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills", "slug")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=job, instance=job1)
serializer.is_valid()
result = serializer.save()
skills = result.skills.all().order_by('title')
self.assertEquals(result.title, "job test updated")
self.assertIs(result.skills.count(), 3)
self.assertEquals(skills[0].title, "new skill") # new skill
self.assertEquals(skills[1].title, "skill1") # no change
self.assertEquals(skills[2].title, "skill2 UP") # title updated
self.assertEquals(skill, skill._meta.model.objects.get(pk=skill.pk)) # title updated
def test_update_list_with_reverse_relation(self):
user1 = get_user_model().objects.create()
conversation = Conversation.objects.create(description="Conversation 1", author_user=user1)
message1 = Message.objects.create(text="Message 1", conversation=conversation, author_user=user1)
message2 = Message.objects.create(text="Message 2", conversation=conversation, author_user=user1)
json = {"@graph": [
{
"@id": "https://happy-dev.fr/messages/{}/".format(message1.pk),
"text": "Message 1 UP"
},
{
"@id": "https://happy-dev.fr/messages/{}/".format(message2.pk),
"text": "Message 2 UP"
},
{
'@id': "https://happy-dev.fr/conversations/{}/".format(conversation.pk),
'description': "Conversation 1 UP",
"message_set": [
{"@id": "https://happy-dev.fr/messages/{}/".format(message1.pk)},
{"@id": "https://happy-dev.fr/messages/{}/".format(message2.pk)},
]
}
]
}
meta_args = {'model': Conversation, 'depth': 2, 'fields': ("@id", "description", "message_set")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('ConversationSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=json, instance=conversation)
serializer.is_valid()
result = serializer.save()
messages = result.message_set.all().order_by('text')
self.assertEquals(result.description, "Conversation 1 UP")
self.assertIs(result.message_set.count(), 2)
self.assertEquals(messages[0].text, "Message 1 UP")
self.assertEquals(messages[1].text, "Message 2 UP")
def test_add_new_element_with_foreign_key_id(self):
user1 = get_user_model().objects.create()
conversation = Conversation.objects.create(description="Conversation 1", author_user=user1)
message1 = Message.objects.create(text="Message 1", conversation=conversation, author_user=user1)
message2 = Message.objects.create(text="Message 2", conversation=conversation, author_user=user1)
json = {"@graph": [
{
"@id": "https://happy-dev.fr/messages/{}/".format(message1.pk),
"text": "Message 1 UP",
"author_user": {
'@id': "https://happy-dev.fr/users/{}/".format(user1.pk)
}
},
{
"@id": "https://happy-dev.fr/messages/{}/".format(message2.pk),
"text": "Message 2 UP",
"author_user": {
'@id': "https://happy-dev.fr/users/{}/".format(user1.pk)
}
},
{
"@id": "_:b1",
"text": "Message 3 NEW",
"author_user": {
'@id': "https://happy-dev.fr/users/{}/".format(user1.pk)
}
},
{
'@id': "https://happy-dev.fr/conversations/{}/".format(conversation.pk),
"author_user": {
'@id': "https://happy-dev.fr/users/{}/".format(user1.pk)
},
'description': "Conversation 1 UP",
'message_set': {
"@id": "https://happy-dev.fr/conversations/{}/message_set/".format(conversation.pk)
{"title": "new skill", "obligatoire": "okay"},
{"@id": "{}/skills/{}/".format(settings.BASE_URL, pre_existing_skill_b.slug), "title": "z"},
]}
}
},
{
'@id': "https://happy-dev.fr/conversations/{}/message_set/".format(conversation.pk),
"ldp:contains": [
{"@id": "https://happy-dev.fr/messages/{}/".format(message1.pk)},
{"@id": "https://happy-dev.fr/messages/{}/".format(message2.pk)},
{"@id": "_:b1"}
]
}
]
}
meta_args = {'model': Conversation, 'depth': 2, 'fields': ("@id", "description", "message_set")}
meta_class = type('Meta', (), meta_args)
serializer_class = type(LDPSerializer)('ConversationSerializer', (LDPSerializer,), {'Meta': meta_class})
serializer = serializer_class(data=json, instance=conversation)
serializer.is_valid()
result = serializer.save()
messages = result.message_set.all().order_by('text')
response = self.client.patch('/job-offers/{}/'.format(job.slug),
data=json.dumps(post),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEquals(result.description, "Conversation 1 UP")
self.assertIs(result.message_set.count(), 3)
self.assertEquals(messages[0].text, "Message 1 UP")
self.assertEquals(messages[1].text, "Message 2 UP")
self.assertEquals(messages[2].text, "Message 3 NEW")
self.assertEqual(response.data['title'], job.title)
self.assertIs(job.skills.count(), 3)
skills = job.skills.all().order_by('title')
self.assertEqual(skills[0].title, "new skill") # new skill
self.assertEqual(skills[1].title, pre_existing_skill_a.title) # old skill unchanged
self.assertEqual(skills[2].title, "z") # updated
self.assertEqual(skills[2].obligatoire, pre_existing_skill_b.obligatoire) # another field not updated
'''
def test_put_resource(self):
post = Post.objects.create(content="content")
skill = Skill.objects.create(title='original', obligatoire='original', slug='skill1')
body = [{
'@id': 'http://testserver.com/posts/{}/'.format(post.pk),
'http://happy-dev.fr/owl/#content': "post content"}]
response = self.client.put('/posts/{}/'.format(post.pk), data=json.dumps(body),
'@id': '{}/skills/{}/'.format(settings.BASE_URL, skill.slug),
'https://cdn.startinblox.com/owl#title': "new", 'https://cdn.startinblox.com/owl#obligatoire': "new"}]
response = self.client.put('/skills/{}/'.format(skill.slug), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEquals(response.data['content'], "post content")
self.assertIn('location', response._headers)
self.assertEqual(response.data['title'], "new")
self.assertEqual(response.data['obligatoire'], "new")
self.assertIn('location', response.headers)
def test_patch_resource(self):
skill = Skill.objects.create(title='original', obligatoire='original', slug='skill1')
body = {
'@id': '{}/skills/{}'.format(settings.BASE_URL, skill.slug),
'https://cdn.startinblox.com/owl#title': 'new'
}
response = self.client.patch('/skills/{}/'.format(skill.slug), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['title'], "new")
self.assertEqual(response.data['obligatoire'], "original")
def test_create_sub_object_in_existing_object_with_existing_reverse_1to1_relation(self):
user = get_user_model().objects.create(username="alex", password="test")
......@@ -284,14 +81,14 @@ class Update(TestCase):
body = [
{
'@id': "/userprofiles/{}/".format(profile.pk),
'http://happy-dev.fr/owl/#description': "user update"
'https://cdn.startinblox.com/owl#description': "user update"
},
{
'@id': '/users/{}/'.format(user.pk),
"http://happy-dev.fr/owl/#first_name": "Alexandre",
"http://happy-dev.fr/owl/#last_name": "Bourlier",
"http://happy-dev.fr/owl/#username": "alex",
'http://happy-dev.fr/owl/#userprofile': {'@id': "/userprofiles/{}/".format(profile.pk)}
"https://cdn.startinblox.com/owl#first_name": "Alexandre",
"https://cdn.startinblox.com/owl#last_name": "Bourlier",
"https://cdn.startinblox.com/owl#username": "alex",
'https://cdn.startinblox.com/owl#userprofile': {'@id': "/userprofiles/{}/".format(profile.pk)}
}
]
response = self.client.put('/users/{}/'.format(user.pk), data=json.dumps(body),
......@@ -299,6 +96,22 @@ class Update(TestCase):
self.assertEqual(response.status_code, 200)
self.assertIn('userprofile', response.data)
def test_put_nonexistent_local_resource(self):
job = JobOffer.objects.create(title="job test")
# contains internal urlid which refers to non-existent resource
body = {"@id": "{}/job-offers/{}/".format(settings.BASE_URL, job.slug),
"skills": {
"ldp:contains": [
{"@id": "{}/skills/404/".format(settings.BASE_URL)},
]}
}
response = self.client.put('/job-offers/{}/'.format(job.slug), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(Skill.objects.count(), 0)
def test_create_sub_object_in_existing_object_with_reverse_fk_relation(self):
"""
Doesn't work with depth = 0 on UserProfile Model. Should it be ?
......@@ -307,14 +120,14 @@ class Update(TestCase):
body = [
{
'@id': "_:b975",
'http://happy-dev.fr/owl/#description': "conversation description"
'https://cdn.startinblox.com/owl#description': "conversation description"
},
{
'@id': '/users/{}/'.format(user.pk),
"http://happy-dev.fr/owl/#first_name": "Alexandre",
"http://happy-dev.fr/owl/#last_name": "Bourlier",
"http://happy-dev.fr/owl/#username": "alex",
'http://happy-dev.fr/owl/#conversation_set': {'@id': "_:b975"}
"https://cdn.startinblox.com/owl#first_name": "Alexandre",
"https://cdn.startinblox.com/owl#last_name": "Bourlier",
"https://cdn.startinblox.com/owl#username": "alex",
'https://cdn.startinblox.com/owl#conversation_set': {'@id': "_:b975"}
}
]
response = self.client.put('/users/{}/'.format(user.pk), data=json.dumps(body),
......@@ -328,14 +141,14 @@ class Update(TestCase):
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'http://happy-dev.fr/owl/#description': "conversation update"
'https://cdn.startinblox.com/owl#description': "conversation update"
},
{
'@id': '/users/{}/'.format(user.pk),
"http://happy-dev.fr/owl/#first_name": "Alexandre",
"http://happy-dev.fr/owl/#last_name": "Bourlier",
"http://happy-dev.fr/owl/#username": "alex",
'http://happy-dev.fr/owl/#conversation_set': {'@id': "/conversations/{}/".format(conversation.pk)}
"https://cdn.startinblox.com/owl#first_name": "Alexandre",
"https://cdn.startinblox.com/owl#last_name": "Bourlier",
"https://cdn.startinblox.com/owl#username": "alex",
'https://cdn.startinblox.com/owl#conversation_set': {'@id': "/conversations/{}/".format(conversation.pk)}
}
]
response = self.client.put('/users/{}/'.format(user.pk), data=json.dumps(body),
......@@ -350,7 +163,7 @@ class Update(TestCase):
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'http://happy-dev.fr/owl/#description': "conversation update",
'https://cdn.startinblox.com/owl#description': "conversation update",
}
]
response = self.client.put('/conversations/{}/'.format(conversation.pk), data=json.dumps(body),
......@@ -365,8 +178,8 @@ class Update(TestCase):
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'http://happy-dev.fr/owl/#description': "conversation update",
'http://happy-dev.fr/owl/#peer_user': ""
'https://cdn.startinblox.com/owl#description': "conversation update",
'https://cdn.startinblox.com/owl#peer_user': ""
}
]
response = self.client.put('/conversations/{}/'.format(conversation.pk), data=json.dumps(body),
......@@ -378,12 +191,12 @@ class Update(TestCase):
resource = Resource.objects.create()
job = JobOffer.objects.create(title="first title", slug="job")
body = {
'http://happy-dev.fr/owl/#joboffers':
'https://cdn.startinblox.com/owl#joboffers':
{
'@id': "http://testserver.com/resources/{}/joboffers/".format(resource.pk),
'@id': "{}/resources/{}/joboffers/".format(settings.BASE_URL, resource.pk),
'ldp:contains': [
{'@id': 'http://testserver.com/job-offers/{}/'.format(job.slug),
'http://happy-dev.fr/owl/#title': "new job",
{'@id': job.urlid,
'https://cdn.startinblox.com/owl#title': "new job",
},
]
}
......@@ -393,16 +206,15 @@ class Update(TestCase):
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
"http://testserver.com/job-offers/{}/".format(job.slug))
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], job.urlid)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job")
def test_m2m_new_link_embedded(self):
resource = Resource.objects.create()
body = {
'http://happy-dev.fr/owl/#joboffers': {
'http://happy-dev.fr/owl/#slug': 'aaa',
'http://happy-dev.fr/owl/#title': "new job",
'https://cdn.startinblox.com/owl#joboffers': {
'https://cdn.startinblox.com/owl#slug': 'aaa',
'https://cdn.startinblox.com/owl#title': "new job",
}
}
......@@ -420,12 +232,12 @@ class Update(TestCase):
resource.joboffers.add(job)
resource.save()
body = {
'http://happy-dev.fr/owl/#joboffers': {
'https://cdn.startinblox.com/owl#joboffers': {
# '@id': "http://testserver/resources/{}/joboffers/".format(resource.pk),
'ldp:contains': [
{
'@id': 'http://testserver.com/job-offers/{}/'.format(job.slug),
'http://happy-dev.fr/owl/#title': "new job",
'@id': job.urlid,
'https://cdn.startinblox.com/owl#title': "new job",
}
]
}
......@@ -435,30 +247,14 @@ class Update(TestCase):
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
"http://testserver.com/job-offers/{}/".format(job.slug))
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'], job.urlid)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job")
def test_m2m_new_link_federated(self):
resource = Resource.objects.create()
body = {
'http://happy-dev.fr/owl/#joboffers': {
'http://happy-dev.fr/owl/#@id': 'http://external.job/job/1',
}
}
response = self.client.put('/resources/{}/'.format(resource.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
"http://external.job/job/1")
def test_m2m_new_link_external(self):
resource = Resource.objects.create()
body = {
'http://happy-dev.fr/owl/#joboffers': {
'@id': 'http://testserver.com/job-offers/stuff/',
'https://cdn.startinblox.com/owl#joboffers': {
'https://cdn.startinblox.com/owl#@id': 'http://external.job/job/1',
}
}
......@@ -467,13 +263,14 @@ class Update(TestCase):
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
"http://testserver.com/job-offers/stuff/")
"http://external.job/job/1")
self.assertIn('@type', response.data['joboffers']['ldp:contains'][0])
def test_m2m_new_link_local(self):
resource = Resource.objects.create()
job = JobOffer.objects.create(title="first title", slug="job")
body = {
'http://happy-dev.fr/owl/#joboffers': {
'https://cdn.startinblox.com/owl#joboffers': {
'@id': 'http://happy-dev.fr/job-offers/{}/'.format(job.slug),
}
}
......@@ -492,8 +289,8 @@ class Update(TestCase):
body = [
{
'@id': "/conversations/{}/".format(conversation.pk),
'http://happy-dev.fr/owl/#description': "conversation update",
'http://happy-dev.fr/owl/#peer_user': {
'https://cdn.startinblox.com/owl#description': "conversation update",
'https://cdn.startinblox.com/owl#peer_user': {
'@id': 'http://happy-dev.fr/users/{}'.format(self.user.pk),
}
}
......@@ -512,9 +309,9 @@ class Update(TestCase):
def test_m2m_user_link_federated(self):
project = Project.objects.create(description="project name")
body = {
'http://happy-dev.fr/owl/#description': 'project name',
'http://happy-dev.fr/owl/#team': {
'http://happy-dev.fr/owl/#@id': 'http://external.user/user/1',
'https://cdn.startinblox.com/owl#description': 'project name',
'https://cdn.startinblox.com/owl#members': {
'https://cdn.startinblox.com/owl#@id': 'http://external.user/user/1',
}
}
......@@ -522,16 +319,18 @@ class Update(TestCase):
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['team']['ldp:contains'][0]['@id'],
self.assertEqual(response.data['members']['ldp:contains'][0]['@id'],
"http://external.user/user/1")
self.assertIn('@type', response.data['members']['ldp:contains'][0])
self.assertEqual(len(response.data['members']['ldp:contains'][0].items()), 2)
def test_m2m_user_link_existing_external(self):
project = Project.objects.create(description="project name")
ext_user = get_user_model().objects.create(username=str(uuid.uuid4()), urlid='http://external.user/user/1')
body = {
'http://happy-dev.fr/owl/#description': 'project name',
'http://happy-dev.fr/owl/#team': {
'http://happy-dev.fr/owl/#@id': ext_user.urlid,
'https://cdn.startinblox.com/owl#description': 'project name',
'https://cdn.startinblox.com/owl#members': {
'https://cdn.startinblox.com/owl#@id': ext_user.urlid,
}
}
......@@ -539,10 +338,12 @@ class Update(TestCase):
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['team']['ldp:contains'][0]['@id'], ext_user.urlid)
self.assertEqual(response.data['members']['ldp:contains'][0]['@id'], ext_user.urlid)
self.assertIn('@type', response.data['members']['ldp:contains'][0])
self.assertEqual(len(response.data['members']['ldp:contains'][0].items()), 2)
project = Project.objects.get(pk=project.pk)
self.assertEqual(project.team.count(), 1)
self.assertEqual(project.members.count(), 1)
user = get_user_model().objects.get(pk=ext_user.pk)
self.assertEqual(user.projects.count(), 1)
......@@ -555,17 +356,17 @@ class Update(TestCase):
body = [
{
'@id': "_:b975",
'http://happy-dev.fr/owl/#description': "user description",
'http://happy-dev.fr/owl/#dummy': {
'https://cdn.startinblox.com/owl#description': "user description",
'https://cdn.startinblox.com/owl#dummy': {
'@id': './'
}
},
{
'@id': '/users/{}/'.format(user.pk),
"http://happy-dev.fr/owl/#first_name": "Alexandre",
"http://happy-dev.fr/owl/#last_name": "Bourlier",
"http://happy-dev.fr/owl/#username": "alex",
'http://happy-dev.fr/owl/#userprofile': {'@id': "_:b975"}
"https://cdn.startinblox.com/owl#first_name": "Alexandre",
"https://cdn.startinblox.com/owl#last_name": "Bourlier",
"https://cdn.startinblox.com/owl#username": "alex",
'https://cdn.startinblox.com/owl#userprofile': {'@id': "_:b975"}
}
]
response = self.client.put('/users/{}/'.format(user.pk), data=json.dumps(body),
......@@ -577,11 +378,11 @@ class Update(TestCase):
def test_m2m_user_link_remove_existing_link(self):
ext_user = get_user_model().objects.create(username=str(uuid.uuid4()), urlid='http://external.user/user/1')
project = Project.objects.create(description="project name")
project.team.add(ext_user)
project.members.add(ext_user)
project.save()
body = {
'http://happy-dev.fr/owl/#description': 'project name',
'http://happy-dev.fr/owl/#team': {
'https://cdn.startinblox.com/owl#description': 'project name',
'https://cdn.startinblox.com/owl#members': {
}
}
......@@ -591,7 +392,194 @@ class Update(TestCase):
self.assertEqual(response.status_code, 200)
project = Project.objects.get(pk=project.pk)
self.assertEqual(project.team.count(), 0)
self.assertEqual(project.members.count(), 0)
user = get_user_model().objects.get(pk=ext_user.pk)
self.assertEqual(user.projects.count(), 0)
def test_update_sub_object_with_urlid(self):
user = get_user_model().objects.create(username="alex", password="test")
profile = UserProfile.objects.create(user=user, description="user description")
body = {
'@id': '/users/{}/'.format(user.pk),
"first_name": "Alexandre",
"last_name": "Bourlier",
"username": "alex",
'userprofile': {
'@id': profile.urlid,
'description': "user update"
},
'@context': {
"@vocab": "https://cdn.startinblox.com/owl#",
}
}
response = self.client.put('/users/{}/'.format(user.pk), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertIn('userprofile', response.data)
response = self.client.get('/userprofiles/{}/'.format(profile.pk),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['description'], "user update")
# unit tests for a specific bug: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/307
def test_direct_boolean_field(self):
profile = UserProfile.objects.create(user=self.user)
setting = NotificationSetting.objects.create(user=profile, receiveMail=False)
body = {
'https://cdn.startinblox.com/owl#@id': setting.urlid,
'receiveMail': True,
"@context": {"@vocab": "https://cdn.startinblox.com/owl#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#", "ldp": "http://www.w3.org/ns/ldp#",
"foaf": "http://xmlns.com/foaf/0.1/", "name": "rdfs:label",
"acl": "http://www.w3.org/ns/auth/acl#", "permissions": "acl:accessControl",
"mode": "acl:mode", "geo": "http://www.w3.org/2003/01/geo/wgs84_pos#", "lat": "geo:lat",
"lng": "geo:long"}
}
response = self.client.patch('/notificationsettings/{}/'.format(setting.pk),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['receiveMail'], True)
def test_nested_container_boolean_field_no_slug(self):
profile = UserProfile.objects.create(user=self.user)
setting = NotificationSetting.objects.create(user=profile, receiveMail=False)
body = {
'settings': {
'https://cdn.startinblox.com/owl#@id': setting.urlid,
'receiveMail': True
},
"@context": {"@vocab": "https://cdn.startinblox.com/owl#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#", "ldp": "http://www.w3.org/ns/ldp#",
"foaf": "http://xmlns.com/foaf/0.1/", "name": "rdfs:label",
"acl": "http://www.w3.org/ns/auth/acl#", "permissions": "acl:accessControl",
"mode": "acl:mode", "geo": "http://www.w3.org/2003/01/geo/wgs84_pos#", "lat": "geo:lat",
"lng": "geo:long"}
}
response = self.client.patch('/userprofiles/{}/'.format(profile.slug),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['settings']['receiveMail'], True)
# variation where the lookup_field for NotificationSetting (pk) is provided
def test_nested_container_boolean_field_with_slug(self):
profile = UserProfile.objects.create(user=self.user)
setting = NotificationSetting.objects.create(user=profile, receiveMail=False)
body = {
'settings': {
'pk': setting.pk,
'https://cdn.startinblox.com/owl#@id': setting.urlid,
'receiveMail': True
},
"@context": {"@vocab": "https://cdn.startinblox.com/owl#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#", "ldp": "http://www.w3.org/ns/ldp#",
"foaf": "http://xmlns.com/foaf/0.1/", "name": "rdfs:label",
"acl": "http://www.w3.org/ns/auth/acl#", "permissions": "acl:accessControl",
"mode": "acl:mode", "geo": "http://www.w3.org/2003/01/geo/wgs84_pos#", "lat": "geo:lat",
"lng": "geo:long"}
}
response = self.client.patch('/userprofiles/{}/'.format(profile.slug),
data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['settings']['receiveMail'], True)
def test_update_container_twice_nested_view(self):
invoice = Invoice.objects.create(title='test')
pre_existing_batch = Batch.objects.create(title='batch1', invoice=invoice)
pre_existing_task = Task.objects.create(title='task1', batch=pre_existing_batch)
base_url = settings.BASE_URL
body = {
"@id": "{}/invoices/{}/".format(base_url, invoice.pk),
"https://cdn.startinblox.com/owl#title": "new",
"https://cdn.startinblox.com/owl#batches": [
{
"@id": "{}/batchs/{}/".format(base_url, pre_existing_batch.pk),
"https://cdn.startinblox.com/owl#title": "new",
"https://cdn.startinblox.com/owl#tasks": [
{
"@id": "{}/tasks/{}/".format(base_url, pre_existing_task.pk),
"https://cdn.startinblox.com/owl#title": "new"
},
{
"https://cdn.startinblox.com/owl#title": "tache 2"
}
]
},
{
"https://cdn.startinblox.com/owl#title": "z",
}
]
}
response = self.client.put('/invoices/{}/'.format(invoice.pk), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['title'], "new")
self.assertEqual(response.data['@id'], invoice.urlid)
invoice = Invoice.objects.get(pk=invoice.pk)
self.assertIs(invoice.batches.count(), 2)
batches = invoice.batches.all().order_by('title')
self.assertEqual(batches[0].title, "new")
self.assertEqual(batches[0].urlid, pre_existing_batch.urlid)
self.assertEqual(batches[1].title, "z")
self.assertIs(batches[0].tasks.count(), 2)
tasks = batches[0].tasks.all().order_by('title')
self.assertEqual(tasks[0].title, "new")
self.assertEqual(tasks[0].pk, pre_existing_task.pk)
self.assertEqual(tasks[1].title, "tache 2")
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/333
'''def test_update_container_nested_view(self):
circle = Circle.objects.create(name='test')
pre_existing = CircleMember.objects.create(user=self.user, circle=circle, is_admin=False)
another_user = get_user_model().objects.create_user(username='u2', email='u2@b.com', password='pw')
body = {
"@id": "{}/circles/{}/".format(settings.BASE_URL, circle.pk),
"https://cdn.startinblox.com/owl#name": "Updated Name",
"https://cdn.startinblox.com/owl#members": {
"ldp:contains": [
{"@id": "{}/circle-members/{}/".format(settings.BASE_URL, pre_existing.pk),
"https://cdn.startinblox.com/owl#is_admin": True},
{"https://cdn.startinblox.com/owl#user": {"@id": another_user.urlid},
"https://cdn.startinblox.com/owl#is_admin": False},
]
}
}
response = \
self.client.put('/circles/{}/'.format(circle.pk), data=json.dumps(body), content_type='application/ld+json')
print(str(self.user.urlid))
print(str(response.data))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['name'], circle.name)
self.assertEqual(response.data['@id'], circle.urlid)
self.assertIs(CircleMember.objects.count(), 2)
self.assertIs(circle.members.count(), 2)
self.assertIs(circle.team.count(), 2)
members = circle.members.all().order_by('pk')
self.assertEqual(members[0].user, self.user)
self.assertEqual(members[0].urlid, pre_existing.urlid)
self.assertEqual(members[0].pk, pre_existing.pk)
self.assertEqual(members[0].is_admin, True)
self.assertEqual(members[1].user, another_user)
self.assertEqual(members[1].is_admin, False)'''
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission, Group
from django.conf import settings
from django.test import override_settings
from rest_framework.test import APIClient, APITestCase
from .models import JobOffer
from djangoldp.tests.models import JobOffer, LDPDummy, PermissionlessDummy, UserProfile, OwnedResource, \
OwnedResourceNestedOwnership, OwnedResourceTwiceNestedOwnership
import json
class TestUserPermissions(APITestCase):
class UserPermissionsTestCase(APITestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion')
self.client = APIClient(enforce_csrf_checks=True)
self.client.force_authenticate(user=self.user)
self.job = JobOffer.objects.create(title="job", slug="slug1")
class TestUserPermissions(UserPermissionsTestCase):
def setUpGroup(self):
self.group = Group.objects.create(name='Test')
view_perm = Permission.objects.get(codename='view_permissionlessdummy')
self.group.permissions.add(view_perm)
self.group.save()
def _make_self_superuser(self):
self.user.is_superuser = True
self.user.save()
# list - simple
def test_get_for_authenticated_user(self):
response = self.client.get('/job-offers/')
self.assertEqual(response.status_code, 200)
# test serialized permissions
self.assertIn('view', response.data['permissions'])
self.assertNotIn('inherit', response.data['permissions'])
# self.assertNotIn('delete', response.data['permissions'])
# TODO: list - I do not have permission from the model, but I do have permission via a Group I am assigned
# https://git.startinblox.com/djangoldp-packages/djangoldp/issues/291
'''def test_group_list_access(self):
self.setUpGroup()
dummy = PermissionlessDummy.objects.create()
response = self.client.get('/permissionless-dummys/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
LDListMixin.to_representation_cache.reset()
LDPSerializer.to_representation_cache.reset()
self.user.groups.add(self.group)
self.user.save()
response = self.client.get('/permissionless-dummys/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
# repeat of the above test on nested field
def test_group_list_access_nested_field(self):
self.setUpGroup()
parent = LDPDummy.objects.create()
PermissionlessDummy.objects.create(parent=parent)
response = self.client.get('/ldpdummys/{}/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['anons']['ldp:contains']), 0)
LDListMixin.to_representation_cache.reset()
LDPSerializer.to_representation_cache.reset()
self.user.groups.add(self.group)
self.user.save()
response = self.client.get('/ldpdummys/{}/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['anons']['ldp:contains']), 1)
# repeat of the test on a nested viewset
def test_group_list_access_nested_viewset(self):
self.setUpGroup()
parent = LDPDummy.objects.create()
PermissionlessDummy.objects.create(parent=parent)
response = self.client.get('/ldpdummys/{}/anons/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
LDListMixin.to_representation_cache.reset()
LDPSerializer.to_representation_cache.reset()
self.user.groups.add(self.group)
self.user.save()
response = self.client.get('/ldpdummys/{}/anons/'.format(parent.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
# repeat for object-specific request
def test_group_object_access(self):
self.setUpGroup()
dummy = PermissionlessDummy.objects.create()
response = self.client.get('/permissionless-dummys/{}'.format(dummy))
self.assertEqual(response.status_code, 404)
LDListMixin.to_representation_cache.reset()
LDPSerializer.to_representation_cache.reset()
self.user.groups.add(self.group)
self.user.save()
response = self.client.get('/permissionless-dummys/{}/'.format(dummy))
self.assertEqual(response.status_code, 200)
# TODO: test for POST scenario
# TODO: test for PUT scenario
# TODO: test for DELETE scenario
'''
@override_settings(SERIALIZE_OBJECT_EXCLUDE_PERMISSIONS=['inherit'])
def test_get_1_for_authenticated_user(self):
response = self.client.get('/job-offers/{}/'.format(self.job.slug))
self.assertEqual(response.status_code, 200)
self.assertIn('view', response.data['permissions'])
self.assertNotIn('inherit', response.data['permissions'])
def test_post_request_for_authenticated_user(self):
post = {'title': "job_created", "slug": 'slug1'}
post = {'https://cdn.startinblox.com/owl#title': "job_created", "https://cdn.startinblox.com/owl#slug": 'slug2'}
response = self.client.post('/job-offers/', data=json.dumps(post), content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
# denied because I don't have model permissions
def test_post_request_denied_model_perms(self):
data = {'https://cdn.startinblox.com/owl#some': 'title'}
response = self.client.post('/permissionless-dummys/', data=json.dumps(data), content_type='application/ld+json')
self.assertEqual(response.status_code, 403)
def test_post_nested_view_authorized(self):
data = { "https://cdn.startinblox.com/owl#title": "new skill", "https://cdn.startinblox.com/owl#obligatoire": "okay" }
response = self.client.post('/job-offers/{}/skills/'.format(self.job.slug), data=json.dumps(data),
content_type='application/ld+json')
self.assertEqual(response.status_code, 201)
def test_post_nested_view_denied_model_perms(self):
parent = LDPDummy.objects.create(some='parent')
data = { "https://cdn.startinblox.com/owl#some": "title" }
response = self.client.post('/ldpdummys/{}/anons/'.format(parent.pk), data=json.dumps(data),
content_type='application/ld+json')
self.assertEqual(response.status_code, 403)
def test_put_request_for_authenticated_user(self):
body = {'title':"job_updated"}
body = {'https://cdn.startinblox.com/owl#title':"job_updated"}
response = self.client.put('/job-offers/{}/'.format(self.job.slug), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
......@@ -35,3 +156,273 @@ class TestUserPermissions(APITestCase):
response = self.client.patch('/job-offers/' + str(self.job.slug) + "/",
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
def test_put_request_denied_model_perms(self):
dummy = PermissionlessDummy.objects.create(some='some', slug='slug')
data = {'https://cdn.startinblox.com/owl#some': 'new'}
response = self.client.put('/permissionless-dummys/{}/'.format(dummy.slug), data=json.dumps(data),
content_type='application/ld+json')
self.assertEqual(response.status_code, 404)
def test_put_nested_view_denied_model_perms(self):
parent = LDPDummy.objects.create(some='parent')
child = PermissionlessDummy.objects.create(some='child', slug='child', parent=parent)
data = {"https://cdn.startinblox.com/owl#some": "new"}
response = self.client.put('/ldpdummys/{}/anons/{}/'.format(parent.pk, child.slug), data=json.dumps(data),
content_type='application/ld+json')
self.assertEqual(response.status_code, 404)
#TODO: check how this could ever work
# def test_patch_nested_container_attach_existing_resource_permission_denied(self):
# '''I am attempting to add a resource which I should not know exists'''
# parent = LDPDummy.objects.create(some='parent')
# dummy = PermissionlessDummy.objects.create(some='some', slug='slug')
# data = {
# 'https://cdn.startinblox.com/owl#anons': [
# {'@id': '{}/permissionless-dummys/{}/'.format(settings.SITE_URL, dummy.slug), 'https://cdn.startinblox.com/owl#slug': dummy.slug}
# ]
# }
# response = self.client.patch('/ldpdummys/{}/'.format(parent.pk), data=json.dumps(data), content_type='application/ld+json')
# self.assertEqual(response.status_code, 404)
# variations on previous tests with an extra level of depth
# TODO
def test_post_nested_container_twice_nested_permission_denied(self):
pass
# TODO
def test_put_nested_container_twice_nested_permission_denied(self):
pass
# TODO: repeat of the above where it is authorized because I have permission through my Group
# https://git.startinblox.com/djangoldp-packages/djangoldp/issues/291
def test_put_request_change_urlid_rejected(self):
self.assertEqual(JobOffer.objects.count(), 1)
body = {'@id': "ishouldnotbeabletochangethis"}
response = self.client.put('/job-offers/{}/'.format(self.job.slug), data=json.dumps(body),
content_type='application/ld+json')
# TODO: this is failing quietly
# https://git.happy-dev.fr/startinblox/solid-spec/issues/14
self.assertEqual(response.status_code, 200)
self.assertEqual(JobOffer.objects.count(), 1)
self.assertFalse(JobOffer.objects.filter(urlid=body['@id']).exists())
def test_put_request_change_pk_rejected(self):
self.assertEqual(JobOffer.objects.count(), 1)
body = {'https://cdn.startinblox.com/owl#pk': 2}
response = self.client.put('/job-offers/{}/'.format(self.job.slug), data=json.dumps(body),
content_type='application/ld+json')
# TODO: this is failing quietly
# https://git.happy-dev.fr/startinblox/solid-spec/issues/14
self.assertEqual(response.status_code, 200)
self.assertEqual(JobOffer.objects.count(), 1)
self.assertFalse(JobOffer.objects.filter(pk=body['https://cdn.startinblox.com/owl#pk']).exists())
# tests that I receive a list of objects for which I am owner, filtering those for which I am not
def test_list_owned_resources(self):
my_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.get('/ownedresources/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
self.assertEqual(response.data['ldp:contains'][0]['@id'], my_resource.urlid)
# I do not have model permissions as an authenticated user, but I am the resources' owner
def test_get_owned_resource(self):
my_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.get('/ownedresources/{}/'.format(my_resource.pk))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['@id'], my_resource.urlid)
self.assertIn('delete', response.data['permissions'])
# I have permission to view this resource
response = self.client.patch('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 404)
def test_patch_owned_resource(self):
my_profile = UserProfile.objects.create(user=self.user, slug=self.user.username, description='about me')
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_profile = UserProfile.objects.create(user=another_user, slug=another_user.username, description='about')
response = self.client.patch('/userprofiles/{}/'.format(my_profile.slug))
self.assertEqual(response.status_code, 200)
response = self.client.patch('/userprofiles/{}/'.format(their_profile.slug))
self.assertEqual(response.status_code, 403)
def test_delete_owned_resource(self):
my_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.delete('/ownedresources/{}/'.format(my_resource.pk))
self.assertEqual(response.status_code, 204)
response = self.client.delete('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 404)
# test superuser permissions (configured on model)
def test_list_superuser_perms(self):
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.get('/ownedresources/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 0)
# now I'm superuser, I have the permissions
self._make_self_superuser()
response = self.client.get('/ownedresources/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
def test_get_superuser_perms(self):
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.patch('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 404)
self._make_self_superuser()
response = self.client.patch('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 200)
def test_put_superuser_perms(self):
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_profile = UserProfile.objects.create(user=another_user, slug=another_user.username, description='about')
response = self.client.patch('/userprofiles/{}/'.format(their_profile.slug))
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/336
self.assertEqual(response.status_code, 403)
self._make_self_superuser()
response = self.client.patch('/userprofiles/{}/'.format(their_profile.slug))
self.assertEqual(response.status_code, 200)
def test_delete_superuser_perms(self):
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
response = self.client.delete('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 404)
self._make_self_superuser()
response = self.client.delete('/ownedresources/{}/'.format(their_resource.pk))
self.assertEqual(response.status_code, 204)
# I have model (or object?) permissions. Attempt to make myself owner and thus upgrade my permissions
# TODO: https://git.startinblox.com/djangoldp-packages/djangoldp/issues/356/
'''
def test_hack_model_perms_privilege_escalation(self):
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
resource = OwnedResourceVariant.objects.create(description='another test', user=another_user)
# authenticated has 'change' permission but only owner's have 'control' permission, meaning that I should
# not be able to change my privilege level
body = {
'https://cdn.startinblox.com/owl#user': {'@id': self.user.urlid}
}
response = self.client.put('/ownedresourcevariants/{}/'.format(resource.pk), data=json.dumps(body),
content_type='application/ld+json')
self.assertEqual(response.status_code, 200)
resource = OwnedResourceVariant.objects.get(pk=resource.pk)
self.assertNotEqual(resource.user, self.user)
'''
class TestOwnerFieldUserPermissions(UserPermissionsTestCase):
restore_meta = None
def setUpTempOwnerFieldForModel(self, model, new_owner_field):
# store the old meta information for tearDown to cleanup after the test
if self.restore_meta is None:
self.restore_meta = []
self.restore_meta.append({
"model": model,
"owner_field": model._meta.owner_field
})
# replace the owner_field attribute for the test to run
model._meta.owner_field = new_owner_field
def tearDown(self):
# restore any previously changed owner_field attributes in the test
if self.restore_meta is not None:
for idx, model in enumerate(self.restore_meta):
model = self.restore_meta[idx]["model"]
model._meta.owner_field = self.restore_meta[idx]["owner_field"]
self.restore_meta = None
def test_list_owned_resources_nested(self):
my_resource = OwnedResource.objects.create(description='test', user=self.user)
my_second_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
my_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_resource)
my_second_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_second_resource)
their_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=their_resource)
response = self.client.get('/ownedresourcenestedownerships/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 2)
ids = [r['@id'] for r in response.data['ldp:contains']]
self.assertIn(my_nested.urlid, ids)
self.assertIn(my_second_nested.urlid, ids)
self.assertNotIn(their_nested.urlid, ids)
def test_list_owned_resources_nested_variation_urlid(self):
owner_field = OwnedResourceNestedOwnership._meta.owner_field
OwnedResourceNestedOwnership._meta.owner_field = None
OwnedResourceNestedOwnership._meta.owner_urlid_field = owner_field + "__urlid"
self.test_list_owned_resources_nested()
OwnedResourceNestedOwnership._meta.owner_urlid_field = None
OwnedResourceNestedOwnership._meta.owner_field = owner_field
def test_list_owned_resources_nested_variation_twice_nested(self):
my_resource = OwnedResource.objects.create(description='test', user=self.user)
my_second_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
my_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_resource)
my_second_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_second_resource)
their_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=their_resource)
my_twice_nested = OwnedResourceTwiceNestedOwnership.objects.create(description="test", parent=my_nested)
their_twice_nested = OwnedResourceTwiceNestedOwnership.objects.create(description="test", parent=their_nested)
response = self.client.get('/ownedresourcetwicenestedownerships/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['ldp:contains']), 1)
ids = [r['@id'] for r in response.data['ldp:contains']]
self.assertIn(my_twice_nested.urlid, ids)
self.assertNotIn(their_twice_nested.urlid, ids)
def test_list_owned_resources_nested_does_not_exist(self):
self.setUpTempOwnerFieldForModel(OwnedResourceNestedOwnership, "parent__doesnotexist")
my_resource = OwnedResource.objects.create(description='test', user=self.user)
my_second_resource = OwnedResource.objects.create(description='test', user=self.user)
another_user = get_user_model().objects.create_user(username='test', email='test@test.com', password='test')
their_resource = OwnedResource.objects.create(description='another test', user=another_user)
my_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_resource)
my_second_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=my_second_resource)
their_nested = OwnedResourceNestedOwnership.objects.create(description="test", parent=their_resource)
self.assertRaises(ValueError, self.client.get, '/ownedresourcenestedownerships/')
from importlib import import_module
from django.conf import settings
from django.conf.urls import re_path, include
from django.contrib.auth.models import Group
from django.urls import path, re_path, include
from djangoldp.models import LDPSource, Model
from djangoldp.permissions import LDPPermissions
from djangoldp.permissions import ReadOnly
from djangoldp.views import LDPSourceViewSet, WebFingerView, InboxView
from djangoldp.views import LDPViewSet
from djangoldp.views import LDPViewSet, serve_static_content
def __clean_path(path):
......@@ -18,36 +19,66 @@ def __clean_path(path):
return path
def get_all_non_abstract_subclasses(cls):
'''
returns a set of all subclasses for a given Python class (recursively calls cls.__subclasses__()). Ignores Abstract
classes
'''
def valid_subclass(sc):
'''returns True if the parameterised subclass is valid and should be returned'''
return not getattr(sc._meta, 'abstract', False)
return set(c for c in cls.__subclasses__() if valid_subclass(c)).union(
[subclass for c in cls.__subclasses__() for subclass in get_all_non_abstract_subclasses(c) if valid_subclass(subclass)])
urlpatterns = [
path('groups/', LDPViewSet.urls(model=Group)),
re_path(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'urlid'],
permission_classes=[LDPPermissions], )),
permission_classes=[ReadOnly], )),
re_path(r'^\.well-known/webfinger/?$', WebFingerView.as_view()),
re_path(r'^inbox/$', InboxView.as_view()),
path('inbox/', InboxView.as_view()),
re_path(r'^ssr/(?P<path>.*)$', serve_static_content, name='serve_static_content'),
]
for package in settings.LDP_PACKAGES:
if settings.ENABLE_SWAGGER_DOCUMENTATION:
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns.extend([
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path(
"docs/",
SpectacularSwaggerView.as_view(
template_name="swagger-ui.html", url_name="schema"
),
name="swagger-ui",
)
])
for package in settings.DJANGOLDP_PACKAGES:
try:
import_module('{}.models'.format(package))
urlpatterns.append(re_path(r'^', include('{}.djangoldp_urls'.format(package))))
except ModuleNotFoundError:
pass
if 'djangoldp_account' not in settings.DJANGOLDP_PACKAGES:
urlpatterns.append(re_path(r'^users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])))
# fetch a list of all models which subclass DjangoLDP Model
model_classes = {cls.__name__: cls for cls in Model.__subclasses__()}
try:
urlpatterns.append(path('', include('{}.djangoldp_urls'.format(package))))
except ModuleNotFoundError:
pass
# append urls for all DjangoLDP Model subclasses
for class_name in model_classes:
model_class = model_classes[class_name]
for model in get_all_non_abstract_subclasses(Model):
# the path is the url for this model
path = __clean_path(model_class.get_container_path())
model_path = __clean_path(model.get_container_path())
# urls_fct will be a method which generates urls for a ViewSet (defined in LDPViewSetGenerator)
urls_fct = model_class.get_view_set().urls
urlpatterns.append(re_path(r'^' + path,
urls_fct(model=model_class,
lookup_field=Model.get_meta(model_class, 'lookup_field', 'pk'),
permission_classes=Model.get_meta(model_class, 'permission_classes', [LDPPermissions]),
fields=Model.get_meta(model_class, 'serializer_fields', []),
nested_fields=model_class.nested.fields())))
urls_fct = getattr(model, 'view_set', LDPViewSet).urls
disable_url = getattr(model._meta, 'disable_url', False)
if not disable_url:
urlpatterns.append(path('' + model_path,
urls_fct(model=model,
lookup_field=getattr(model._meta, 'lookup_field', 'pk'),
permission_classes=getattr(model._meta, 'permission_classes', []),
fields=getattr(model._meta, 'serializer_fields', []),
nested_fields=getattr(model._meta, 'nested_fields', [])
)))
# NOTE: this route will be ignored if a custom (subclass of Model) user model is used, or it is registered by a package
# Django matches the first url it finds for a given path
urlpatterns.append(re_path('users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])))
\ No newline at end of file