292 lines
11 KiB
Python
292 lines
11 KiB
Python
import urllib
|
|
|
|
from django.core.urlresolvers import reverse
|
|
from rest_framework.test import APITestCase
|
|
|
|
from experiments.factories import ExperimentDataFactory, ExperimentKeyValueFactory
|
|
from experiments.models import ExperimentData, ExperimentKeyValue
|
|
from experiments.serializers import ExperimentDataSerializer
|
|
from student.tests.factories import UserFactory
|
|
|
|
|
|
class ExperimentDataViewSetTests(APITestCase):
|
|
def assert_data_created_for_user(self, user, method='post', status=201):
|
|
url = reverse('api_experiments:v0:data-list')
|
|
data = {
|
|
'experiment_id': 1,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
}
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = getattr(self.client, method)(url, data)
|
|
self.assertEqual(response.status_code, status)
|
|
|
|
# This will raise an exception if no data exists
|
|
ExperimentData.objects.get(user=user)
|
|
|
|
data['user'] = user.username
|
|
self.assertDictContainsSubset(data, response.data)
|
|
|
|
def test_list_permissions(self):
|
|
""" Users should only be able to list their own data. """
|
|
url = reverse('api_experiments:v0:data-list')
|
|
user = UserFactory()
|
|
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
ExperimentDataFactory()
|
|
datum = ExperimentDataFactory(user=user)
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.data['results'], ExperimentDataSerializer([datum], many=True).data)
|
|
|
|
def test_list_filtering(self):
|
|
""" Users should be able to filter by the experiment_id and key fields. """
|
|
url = reverse('api_experiments:v0:data-list')
|
|
user = UserFactory()
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
experiment_id = 1
|
|
ExperimentDataFactory()
|
|
ExperimentDataFactory(user=user)
|
|
data = ExperimentDataFactory.create_batch(3, user=user, experiment_id=experiment_id)
|
|
|
|
qs = urllib.urlencode({'experiment_id': experiment_id})
|
|
response = self.client.get('{url}?{qs}'.format(url=url, qs=qs))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.data['results'], ExperimentDataSerializer(data, many=True).data)
|
|
|
|
datum = data[0]
|
|
qs = urllib.urlencode({'key': datum.key})
|
|
response = self.client.get('{url}?{qs}'.format(url=url, qs=qs))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.data['results'], ExperimentDataSerializer([datum], many=True).data)
|
|
|
|
qs = urllib.urlencode({'experiment_id': experiment_id, 'key': datum.key})
|
|
response = self.client.get('{url}?{qs}'.format(url=url, qs=qs))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.data['results'], ExperimentDataSerializer([datum], many=True).data)
|
|
|
|
def test_read_permissions(self):
|
|
""" Users should only be allowed to read their own data. """
|
|
user = UserFactory()
|
|
datum = ExperimentDataFactory(user=user)
|
|
url = reverse('api_experiments:v0:data-detail', kwargs={'pk': datum.id})
|
|
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
other_user = UserFactory()
|
|
self.client.login(username=other_user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_create_permissions(self):
|
|
""" Users should only be allowed to create data for themselves. """
|
|
url = reverse('api_experiments:v0:data-list')
|
|
|
|
# Authentication is required
|
|
response = self.client.post(url, {})
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
user = UserFactory()
|
|
data = {
|
|
'experiment_id': 1,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
}
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
# Users can create data for themselves
|
|
response = self.client.post(url, data)
|
|
self.assertEqual(response.status_code, 201)
|
|
ExperimentData.objects.get(user=user)
|
|
|
|
# A non-staff user cannot create data for another user
|
|
other_user = UserFactory()
|
|
data['user'] = other_user.username
|
|
response = self.client.post(url, data)
|
|
self.assertEqual(response.status_code, 403)
|
|
self.assertFalse(ExperimentData.objects.filter(user=other_user).exists())
|
|
|
|
# A staff user can create data for other users
|
|
user.is_staff = True
|
|
user.save()
|
|
response = self.client.post(url, data)
|
|
self.assertEqual(response.status_code, 201)
|
|
ExperimentData.objects.get(user=other_user)
|
|
|
|
def test_put_as_create(self):
|
|
""" Users should be able to use PUT to create new data. """
|
|
user = UserFactory()
|
|
self.assert_data_created_for_user(user, 'put')
|
|
|
|
# Subsequent requests should update the data
|
|
self.assert_data_created_for_user(user, 'put', 200)
|
|
|
|
def test_update_permissions(self):
|
|
""" Users should only be allowed to update their own data. """
|
|
user = UserFactory()
|
|
other_user = UserFactory()
|
|
datum = ExperimentDataFactory(user=user)
|
|
url = reverse('api_experiments:v0:data-detail', kwargs={'pk': datum.id})
|
|
data = {}
|
|
|
|
response = self.client.patch(url, data)
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = self.client.patch(url, data)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.client.login(username=other_user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = self.client.patch(url, data)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_bulk_upsert_permissions(self):
|
|
""" Only staff users can access the bulk upsert endpoint. """
|
|
url = reverse('api_experiments:v0:data-bulk-upsert')
|
|
data = []
|
|
|
|
# Authentication is required
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
user = UserFactory()
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
# No access to non-staff users
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
user.is_staff = True
|
|
user.save()
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_bulk_upsert(self):
|
|
""" The endpoint should support creating/updating multiple ExperimentData objects with a single call. """
|
|
url = reverse('api_experiments:v0:data-bulk-upsert')
|
|
experiment_id = 1
|
|
user = UserFactory(is_staff=True)
|
|
other_user = UserFactory()
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
data = [
|
|
{
|
|
'experiment_id': experiment_id,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
'user': user.username,
|
|
},
|
|
{
|
|
'experiment_id': experiment_id,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
'user': other_user.username,
|
|
},
|
|
]
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 200)
|
|
kwargs = {
|
|
'experiment_id': experiment_id,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
}
|
|
ExperimentData.objects.get(user=user, **kwargs)
|
|
ExperimentData.objects.get(user=other_user, **kwargs)
|
|
|
|
# Subsequent calls should update the existing data rather than create more
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 200)
|
|
ExperimentData.objects.get(user=user, **kwargs)
|
|
ExperimentData.objects.get(user=other_user, **kwargs)
|
|
|
|
|
|
class ExperimentKeyValueViewSetTests(APITestCase):
|
|
def test_permissions(self):
|
|
""" Staff access is required for write operations. """
|
|
url = reverse('api_experiments:v0:key_value-list')
|
|
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
response = self.client.post(url, {})
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
instance = ExperimentKeyValueFactory()
|
|
url = reverse('api_experiments:v0:key_value-detail', kwargs={'pk': instance.id})
|
|
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
user = UserFactory(is_staff=False)
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
response = self.client.put(url, {})
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
response = self.client.patch(url, {})
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
response = self.client.delete(url)
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
def test_bulk_upsert_permissions(self):
|
|
""" Non-staff users should not be allowed to access the endpoint. """
|
|
data = []
|
|
url = reverse('api_experiments:v0:key_value-bulk-upsert')
|
|
user = UserFactory(is_staff=False)
|
|
|
|
# Authentication required
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 401)
|
|
|
|
# Staff permission required
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
def test_bulk_upsert(self):
|
|
""" The endpoint should support creating/updating multiple ExperimentData objects with a single call. """
|
|
url = reverse('api_experiments:v0:key_value-bulk-upsert')
|
|
experiment_id = 1
|
|
user = UserFactory(is_staff=True)
|
|
data = [
|
|
{
|
|
'experiment_id': experiment_id,
|
|
'key': 'foo',
|
|
'value': 'bar',
|
|
},
|
|
{
|
|
'experiment_id': experiment_id,
|
|
'key': 'foo1',
|
|
'value': 'bar',
|
|
},
|
|
]
|
|
|
|
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
|
|
|
# New data should be created
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 200)
|
|
kwargs = {
|
|
'experiment_id': experiment_id,
|
|
'value': 'bar',
|
|
}
|
|
ExperimentKeyValue.objects.get(key='foo', **kwargs)
|
|
ExperimentKeyValue.objects.get(key='foo1', **kwargs)
|
|
|
|
# Subsequent calls should update the existing data rather than create more
|
|
response = self.client.put(url, data, format='json')
|
|
self.assertEqual(response.status_code, 200)
|
|
ExperimentKeyValue.objects.get(key='foo', **kwargs)
|
|
ExperimentKeyValue.objects.get(key='foo1', **kwargs)
|