Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #115 from a8/add_api

Add api - added ApiKeyAuthentication and DjangoAuthorization
  • Loading branch information...
commit 5b73c97ae7596115f16f1f20e4ea31f7ac6ac0f0 2 parents d9477b2 + e413dab
@tobami authored
View
116 codespeed/api.py
@@ -43,7 +43,7 @@
from tastypie.resources import ModelResource, Resource
from tastypie import fields
from tastypie.authorization import Authorization, DjangoAuthorization
-from tastypie.authentication import ApiKeyAuthentication
+from tastypie.authentication import Authentication, ApiKeyAuthentication, MultiAuthentication
from tastypie.models import create_api_key
from tastypie.utils.dict import dict_strip_unicode_keys
from codespeed.models import (Environment, Project, Result, Branch, Revision,
@@ -63,9 +63,8 @@ class Meta:
allowed_methods = ['get']
#excludes = ['email', 'password', 'is_superuser']
# Add it here.
- #authorization = DjangoAuthorization()
- authorization = Authorization()
- #authentication = ApiKeyAuthentication()
+ authorization = DjangoAuthorization()
+ authentication = ApiKeyAuthentication()
class ProjectResource(ModelResource):
@@ -73,7 +72,9 @@ class ProjectResource(ModelResource):
class Meta:
queryset = Project.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ # Note, the order for MultiAuthentication matters!
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class BranchResource(ModelResource):
@@ -83,7 +84,8 @@ class BranchResource(ModelResource):
class Meta:
queryset = Branch.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class RevisionResource(ModelResource):
@@ -94,7 +96,8 @@ class RevisionResource(ModelResource):
class Meta:
queryset = Revision.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class ExecutableResource(ModelResource):
@@ -104,7 +107,8 @@ class ExecutableResource(ModelResource):
class Meta:
queryset = Executable.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class BenchmarkResource(ModelResource):
@@ -112,7 +116,8 @@ class BenchmarkResource(ModelResource):
class Meta:
queryset = Benchmark.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class EnvironmentResource(ModelResource):
@@ -121,15 +126,22 @@ class EnvironmentResource(ModelResource):
class Meta:
queryset = Environment.objects.all()
resource_name = 'environment'
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = ApiKeyAuthentication()
+ #authentication = MultiAuthentication(Authentication(), ApiKeyAuthentication())
class ResultResource(ModelResource):
"""Resource for Result()"""
+ revision = fields.ToOneField(RevisionResource, 'revision')
+ executable = fields.ToOneField(ExecutableResource, 'executable')
+ benchmark = fields.ToOneField(BenchmarkResource, 'benchmark')
+ environment = fields.ToOneField(EnvironmentResource, 'environment')
class Meta:
queryset = Result.objects.all()
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class ReportResource(ModelResource):
@@ -142,7 +154,8 @@ class ReportResource(ModelResource):
class Meta:
queryset = Report.objects.all()
allowed_methods = ['get']
- authorization = Authorization()
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(), Authentication())
class ResultBundle(Bundle):
@@ -198,19 +211,18 @@ def _populate_obj_by_data(self):
get everything except the result, 2nd try reverse lookup
"""
def populate(key):
- return {
- 'project': lambda: Project.objects.get_or_create(
- name=self.data['project']),
- 'executable': lambda: Executable.objects.get_or_create(
- name=self.data['executable'], project=self.obj.project
- ),
- 'benchmark': lambda: Benchmark.objects.get_or_create(
- name=self.data['benchmark']),
- 'environment': lambda: (Environment.objects.get(
- name=self.data['environment']), False),
- 'branch': lambda: Branch.objects.get_or_create(
- name=self.data['branch'], project=self.obj.project),
- }.get(key, (None, None))()
+ return {'project': lambda: ProjectResource().get_via_uri(
+ self.data['project']),
+ 'executable': lambda: ExecutableResource().get_via_uri(
+ self.data['executable']),
+ 'benchmark': lambda: BenchmarkResource().get_via_uri(
+ self.data['benchmark']),
+ 'environment': lambda: EnvironmentResource().get_via_uri(
+ self.data['environment']),
+ 'branch': lambda: BranchResource().get_via_uri(
+ self.data['branch']),
+ 'revision': lambda: RevisionResource().get_via_uri(
+ self.data['commitid']),}.get(key, None)()
try:
self.obj.value = float(self.data['result_value'])
@@ -222,10 +234,10 @@ def populate(key):
raise ImmediateHttpResponse(
response=HttpBadRequest(u"Value needs to be a number"))
for key in [k for k in self.mandatory_keys \
- if k not in ('result_value', 'revision')]:
+ if k not in ('result_value',)]:
try:
#populate
- (item, created) = populate(key)
+ item = populate(key)
setattr(self.obj, key, item)
except Exception, error:
logging.error("Data for field %s: %s not found. %s" % (
@@ -234,13 +246,6 @@ def populate(key):
response=HttpBadRequest(u"Error finding: {0}={1}".format(
key, self.data[key]
)))
-
- # find the revision
- self.obj.revision, created = Revision.objects.get_or_create(
- commitid=self.data['commitid'],
- project=self.obj.project,
- branch=self.obj.branch,
- )
# populate optional data
for key in [k for k in self.optional_keys \
if k not in ('date')]:
@@ -261,10 +266,6 @@ def _populate_by_obj(self):
self.obj.branch = self.obj.revision.branch
#self.obj.result = self.obj
setattr(self.obj, 'result', self.obj)
- # TODO (a8): add user to models
- setattr(self.obj, 'user', User.objects.get(pk=1))
- #setattr(self.obj, 'user', None)
- setattr(self.obj, 'notify', None)
def _check_data(self):
"""See if all mandatory data is there"""
@@ -284,8 +285,8 @@ def _check_data(self):
# Check that the Environment exists
try:
- self.obj.environment = Environment.objects.get(
- name=self.data['environment'])
+ self.obj.environment = EnvironmentResource().get_via_uri(
+ self.data['environment'])
except Environment.DoesNotExist:
error_text = 'Environment: {0} not found in database.'.format(
self.data['environment'])
@@ -327,13 +328,13 @@ def _check_data(self):
raise ImmediateHttpResponse(
response=HttpBadRequest(error_text))
- def save(self):
- """Save self.obj which is an instance of Result()
+ def hydrate_and_save(self):
+ """Save self.obj which is an instance of Result()
- First populate the Result() instance with self.data
- """
- self._populate_obj_by_data()
- self.obj.save()
+ First populate the Result() instance with self.data
+ """
+ self._populate_obj_by_data()
+ self.obj.save()
class ResultBundleResource(Resource):
@@ -352,7 +353,7 @@ class ResultBundleResource(Resource):
not mandatory data
'notify' - Send notification to registered user if result varies from
- previous results
+ previous results, currently not implemented
"""
revision = fields.ToOneField(RevisionResource, 'revision')
@@ -362,12 +363,13 @@ class ResultBundleResource(Resource):
benchmark = fields.ToOneField(BenchmarkResource, 'benchmark')
environment = fields.ToOneField(EnvironmentResource, 'environment')
result = fields.ToOneField(ResultResource, 'result')
- user = fields.ToOneField(UserResource, 'user', null=True)
- notify = fields.CharField(attribute='notify', null=True)
class Meta:
resource_name = 'benchmark-result'
- authorization = Authorization()
+ object_class = Result
+ authorization = DjangoAuthorization()
+ authentication = MultiAuthentication(ApiKeyAuthentication(),
+ Authentication())
allowed_methods = ['get', 'post', 'put', 'delete']
def get_resource_uri(self, bundle_or_obj):
@@ -383,7 +385,6 @@ def get_resource_uri(self, bundle_or_obj):
if self._meta.api_name is not None:
kwargs['api_name'] = self._meta.api_name
- #FIXME (a8): reverse url should point to ResultResource()
return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
def get_object_list(self, request):
@@ -402,16 +403,14 @@ def obj_get(self, request=None, **kwargs):
result.project = result.executable.project
result.branch = result.revision.branch
setattr(result, 'result', result)
- # TODO (a8): add user to models
- #setattr(result, 'user', User.objects.get(pk=1))
- setattr(result, 'user', None)
- #setattr(result, 'notify', None)
return result
def obj_create(self, bundle, request=None, **kwargs):
- # FIXME (a8): Make full_hydrate work
+ # not calling hydrate here since bundle.save() has that functionality
+ # self.full_hydrate(bundle) will try to hydrate result which is not
+ # there yet
#bundle = self.full_hydrate(bundle)
- bundle.save()
+ bundle.hydrate_and_save()
return bundle
def obj_update(self, bundle, request=None, **kwargs):
@@ -457,3 +456,6 @@ def obj_delete(self, request=None, **kwargs):
def rollback(self, bundles):
pass
+
+ def detail_uri_kwargs(self):
+ pass
View
746 codespeed/tests/tests_api.py
@@ -15,11 +15,13 @@
from django.http import HttpRequest
from django.core.urlresolvers import reverse
from django.conf import settings
-from django.contrib.auth.models import User
+from django.contrib.auth.models import User, Permission
+from tastypie.authorization import Authorization, ReadOnlyAuthorization, DjangoAuthorization
from tastypie.exceptions import ImmediateHttpResponse
from tastypie.models import ApiKey, create_api_key
from tastypie.http import HttpUnauthorized
-from tastypie.authentication import Authentication, ApiKeyAuthentication
+from tastypie.authentication import ApiKeyAuthentication
+from codespeed.api import EnvironmentResource, ProjectResource
from codespeed.models import (Project, Benchmark, Revision, Branch,
Executable, Environment, Result, Report)
from codespeed.api import ResultBundle
@@ -34,12 +36,124 @@ def setUp(self):
self.api_user = User.objects.create_user(
username='apiuser', email='api@foo.bar', password='password')
self.api_user.save()
+ self.api_user.user_permissions.clear()
+ john_doe = User.objects.create_user(
+ username='johndoe', email='api@foo.bar', password='password')
+ john_doe.save()
+ authorization='ApiKey {0}:{1}'.format(self.api_user.username,
+ self.api_user.api_key.key)
+ self.post_auth = {'HTTP_AUTHORIZATION': authorization}
-class EnvironmentTest(FixtureTestCase):
+class ApiKeyAuthenticationTestCase(FixtureTestCase):
+
+ def setUp(self):
+ super(ApiKeyAuthenticationTestCase, self).setUp()
+ ApiKey.objects.all().delete()
+ self.auth = ApiKeyAuthentication()
+ self.request = HttpRequest()
+
+ # Simulate sending the signal.
+ user = User.objects.get(username='apiuser')
+ create_api_key(User, instance=user, created=True)
+
+ def test_is_not_authenticated(self):
+ """Should return HttpUnauthorized when incorrect credentials are given"""
+ # No username/api_key details
+ self.assertEqual(isinstance(
+ self.auth.is_authenticated(self.request), HttpUnauthorized), True)
+
+ # Wrong username details.
+ self.request.GET['username'] = 'foo'
+ self.assertEqual(isinstance(
+ self.auth.is_authenticated(self.request), HttpUnauthorized), True)
+
+ # No api_key.
+ self.request.GET['username'] = 'daniel'
+ self.assertEqual(isinstance(
+ self.auth.is_authenticated(self.request), HttpUnauthorized), True)
+
+ # Wrong user/api_key.
+ self.request.GET['username'] = 'daniel'
+ self.request.GET['api_key'] = 'foo'
+ self.assertEqual(isinstance(
+ self.auth.is_authenticated(self.request), HttpUnauthorized), True)
+
+ def test_is_authenticated(self):
+ """Should correctly authenticate when using an existing user and key"""
+ # Correct user/api_key.
+ user = User.objects.get(username='apiuser')
+ self.request.GET['username'] = 'apiuser'
+ self.request.GET['api_key'] = user.api_key.key
+ self.assertEqual(self.auth.is_authenticated(self.request), True)
+
+
+class UserTest(FixtureTestCase):
+ """Test api user related stuff"""
+
+ def test_has_apikey(self):
+ """User() should have an api key attr that was generated automatically."""
+ self.assertTrue(hasattr(self.api_user, 'api_key'))
+
+ def test_len_apikey(self):
+ """Should have api user key with a non-zero length."""
+ self.assertTrue(len(self.api_user.api_key.key) >= 1)
+
+ def test_is_authenticated_header(self):
+ """Taken from tastypie test suite to ensure api key is generated for
+ new users correctly and tastypie is installed correctly.
+ """
+ auth = ApiKeyAuthentication()
+ request = HttpRequest()
+
+ # Simulate sending the signal.
+ john_doe = User.objects.get(username='johndoe')
+
+ # No username/api_key details should fail.
+ self.assertEqual(isinstance(auth.is_authenticated(request), HttpUnauthorized), True)
+
+ # Wrong username details.
+ request.META['HTTP_AUTHORIZATION'] = 'foo'
+ self.assertEqual(isinstance(auth.is_authenticated(request), HttpUnauthorized), True)
+
+ # No api_key.
+ request.META['HTTP_AUTHORIZATION'] = 'ApiKey daniel'
+ self.assertEqual(isinstance(auth.is_authenticated(request), HttpUnauthorized), True)
+
+ # Wrong user/api_key.
+ request.META['HTTP_AUTHORIZATION'] = 'ApiKey daniel:pass'
+ self.assertEqual(isinstance(auth.is_authenticated(request), HttpUnauthorized), True)
+
+ # Correct user/api_key.
+ john_doe = User.objects.get(username='johndoe')
+ request.META['HTTP_AUTHORIZATION'] = 'ApiKey johndoe:%s' % john_doe.api_key.key
+ self.assertEqual(auth.is_authenticated(request), True)
+
+ # Capitalization shouldn't matter.
+ john_doe = User.objects.get(username='johndoe')
+ request.META['HTTP_AUTHORIZATION'] = 'aPiKeY johndoe:%s' % john_doe.api_key.key
+ self.assertEqual(auth.is_authenticated(request), True)
+
+ def test_api_key(self):
+ """User should be authenticated by it's api key."""
+ auth = ApiKeyAuthentication()
+ request = HttpRequest()
+ authorization='ApiKey %s:%s' % (self.api_user.username, self.api_user.api_key.key)
+ request.META['HTTP_AUTHORIZATION'] = authorization
+ self.assertEqual(auth.is_authenticated(request), True)
+
+
+class EnvironmentDjangoAuthorizationTestCase(FixtureTestCase):
"""Test Environment() API"""
def setUp(self):
+ super(EnvironmentDjangoAuthorizationTestCase, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_environment', 'codespeed', 'environment')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_environment', 'codespeed', 'environment')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_environment', 'codespeed', 'environment')
self.env1_data = dict(
name="env1",
cpu="cpu1",
@@ -61,17 +175,129 @@ def setUp(self):
[(k, getattr(env_db1, k)) for k in self.env1_data.keys()]
)
self.client = Client()
- super(EnvironmentTest, self).setUp()
+
+ def test_no_perms(self):
+ """User() should have only GET permission"""
+ # sanity check: user has no permissions
+ self.assertFalse(self.api_user.get_all_permissions())
+
+ request = HttpRequest()
+ request.method = 'GET'
+ request.user = self.api_user
+ # with no permissions, api is read-only
+ self.assertTrue(EnvironmentResource()._meta.authorization.is_authorized(
+ request))
+
+ for method in ('POST', 'PUT', 'DELETE'):
+ request.method = method
+ self.assertFalse(
+ EnvironmentResource()._meta.authorization.is_authorized(request)
+ )
+
+ def test_add_perm(self):
+ """User() should have add permission granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ # give add permission
+ request.user.user_permissions.add(self.add)
+ request.method = 'POST'
+ self.assertTrue(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ def test_change_perm(self):
+ """User() should have change permission granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ # give change permission
+ request.user.user_permissions.add(self.change)
+ request.method = 'PUT'
+ self.assertTrue(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ def test_delete_perm(self):
+ """User() should have delete permission granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ # give delete permission
+ request.user.user_permissions.add(self.delete)
+ request.method = 'DELETE'
+ self.assertTrue(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ def test_all(self):
+ """User() should have add, change, delete permissions granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
+ for method in ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'DELETE',
+ 'PATCH'):
+ request.method = method
+ self.assertTrue(
+ EnvironmentResource()._meta.authorization.is_authorized(request)
+ )
+
+ def test_patch_perms(self):
+ """User() should have patch (add, change, delete) permissions granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+ request.method = 'PATCH'
+
+ # Not enough.
+ request.user.user_permissions.add(self.add)
+ self.assertFalse(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ # Still not enough.
+ request.user.user_permissions.add(self.change)
+ self.assertFalse(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ # Much better.
+ request.user.user_permissions.add(self.delete)
+ # Nuke the perm cache. :/
+ del request.user._perm_cache
+ self.assertTrue(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
+
+ def test_unrecognized_method(self):
+ """User() should not have the permission to call non-existent method."""
+ request = HttpRequest()
+ self.api_user.user_permissions.clear()
+ request.user = self.api_user
+
+ # Check a non-existent HTTP method.
+ request.method = 'EXPLODE'
+ self.assertFalse(
+ EnvironmentResource()._meta.authorization.is_authorized(request))
def test_get_environment(self):
- """Should get an existing environment"""
- response = self.client.get('/api/v1/environment/1/')
+ """Should get an environment when given an existing ID"""
+ response = self.client.get(
+ '/api/v1/environment/1/?username={0}&api_key={1}'.format(
+ self.api_user.username, self.api_user.api_key.key))
self.assertEquals(response.status_code, 200)
self.assertEqual(json.loads(response.content)['name'], "Dual Core")
+ def test_get_non_existing_environment(self):
+ """Should return 404 when given a non existing environment ID"""
+ response = self.client.get(
+ '/api/v1/environment/999/?username={0}&api_key={1}'.format(
+ self.api_user.username, self.api_user.api_key.key))
+ self.assertEquals(response.status_code, 404)
+
def test_get_environment_all_fields(self):
"""Should get all fields for an environment"""
- response = self.client.get('/api/v1/environment/%s/' % (self.env1.id,))
+ response = self.client.get(
+ '/api/v1/environment/{0}/?username={1}&api_key={2}'.format(
+ self.env1.pk, self.api_user.username, self.api_user.api_key.key)
+ )
self.assertEquals(response.status_code, 200)
for k in self.env1_data.keys():
self.assertEqual(
@@ -79,17 +305,34 @@ def test_get_environment_all_fields(self):
def test_post(self):
"""Should save a new environment"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
response = self.client.post('/api/v1/environment/',
data=json.dumps(self.env2_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/environment/',
+ data=json.dumps(self.env2_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
- response = self.client.get('/api/v1/environment/{0}/'.format(id))
+ #response = self.client.get('/api/v1/environment/{0}/'.format(id))
+ response = self.client.get(
+ '/api/v1/environment/{0}/?username={1}&api_key={2}'.format(
+ id, self.api_user.username, self.api_user.api_key.key)
+ )
for k, v in self.env2_data.items():
self.assertEqual(
json.loads(response.content)[k], v)
response = self.client.delete('/api/v1/environment/{0}/'.format(id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
def test_put(self):
@@ -98,37 +341,71 @@ def test_put(self):
modified_data['name'] = "env2.2"
modified_data['memory'] = "128kB"
response = self.client.put('/api/v1/environment/1/',
- data=json.dumps(modified_data),
- content_type='application/json')
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
+ self.assertEquals(response.status_code, 401)
+
+ request = HttpRequest()
+ request.user = self.api_user
+ request.user.user_permissions.add(self.change)
+
+ response = self.client.put('/api/v1/environment/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/environment/1/')
+ response = self.client.get(
+ '/api/v1/environment/1/?username={0}&api_key={1}'.format(
+ self.api_user.username, self.api_user.api_key.key)
+ )
for k, v in modified_data.items():
self.assertEqual(
json.loads(response.content)[k], v)
def test_delete(self):
"""Should delete an environment"""
- response = self.client.get('/api/v1/environment/1/')
+ response = self.client.get(
+ '/api/v1/environment/1/?username={0}&api_key={1}'.format(
+ self.api_user.username, self.api_user.api_key.key))
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/environment/1/',
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
+ self.assertEquals(response.status_code, 401)
+
+ request = HttpRequest()
+ request.user = self.api_user
+ request.user.user_permissions.add(self.delete)
+ response = self.client.delete('/api/v1/environment/1/',
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
- response = self.client.get('/api/v1/environment/1/')
+ response = self.client.get(
+ '/api/v1/environment/1/?username={0}&api_key={1}'.format(
+ self.api_user.username, self.api_user.api_key.key))
self.assertEquals(response.status_code, 404)
# from just created data
response = self.client.get(
- '/api/v1/environment/{0}/'.format(self.env1.id))
+ '/api/v1/environment/{0}/?username={1}&api_key={2}'.format(
+ self.env1.pk, self.api_user.username, self.api_user.api_key.key)
+ )
self.assertEquals(response.status_code, 200)
response = self.client.delete(
'/api/v1/environment/{0}/'.format(self.env1.id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth
+ )
self.assertEquals(response.status_code, 204)
response = self.client.get(
- '/api/v1/environment/{0}/'.format(self.env1.id))
+ '/api/v1/environment/{0}/?username={1}&api_key={2}'.format(
+ self.env1.pk, self.api_user.username, self.api_user.api_key.key)
+ )
self.assertEquals(response.status_code, 404)
@@ -136,6 +413,13 @@ class ProjectTest(FixtureTestCase):
"""Test Project() API"""
def setUp(self):
+ super(ProjectTest, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_project', 'codespeed', 'project')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_project', 'codespeed', 'project')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_project', 'codespeed', 'project')
self.project_data = dict(
name="PyPy",
repo_type="M",
@@ -153,7 +437,22 @@ def setUp(self):
self.project = Project(**self.project_data)
self.project.save()
self.client = Client()
- super(ProjectTest, self).setUp()
+
+ def test_all(self):
+ """User should have all permissions granted."""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
+ for method in ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'DELETE',
+ 'PATCH'):
+ request.method = method
+ self.assertTrue(
+ ProjectResource()._meta.authorization.is_authorized(request)
+ )
def test_get_project(self):
"""Should get an existing project"""
@@ -173,12 +472,27 @@ def test_get_project_all_fields(self):
def test_post(self):
"""Should save a new project"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
response = self.client.post('/api/v1/project/',
data=json.dumps(self.project_data2),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/project/',
+ data=json.dumps(self.project_data2),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
- response = self.client.get('/api/v1/project/{0}/'.format(
- self.project.id))
+ id = response['Location'].rsplit('/', 2)[-2]
+ response = self.client.get(
+ '/api/v1/project/{0}/?username={1}&api_key={2}'.format(
+ self.project.id, self.api_user.username, self.api_user.api_key.key)
+ )
for k, v in self.project_data.items():
self.assertEqual(
json.loads(response.content)[k], v)
@@ -186,12 +500,21 @@ def test_post(self):
def test_delete(self):
"""Should delete an project"""
response = self.client.delete('/api/v1/project/{0}/'.format(
- self.project.id,),
- content_type='application/json')
- self.assertEquals(response.status_code, 204)
+ self.project.id,), content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ request = HttpRequest()
+ request.user = self.api_user
+ request.user.user_permissions.add(self.delete)
+ response = self.client.delete(
+ '/api/v1/project/{0}/'.format(self.project.id,),
+ content_type='application/json',
+ **self.post_auth
+ )
+ self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/project/{0}/'.format(
- self.project.id,))
+ self.project.id,)
+ )
self.assertEquals(response.status_code, 404)
@@ -263,6 +586,13 @@ class BranchTest(FixtureTestCase):
"""Test Branch() API"""
def setUp(self):
+ super(BranchTest, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_branch', 'codespeed', 'branch')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_branch', 'codespeed', 'branch')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_branch', 'codespeed', 'branch')
self.branch1 = Branch.objects.get(pk=1)
self.project_data = dict(
name="PyPy",
@@ -278,7 +608,6 @@ def setUp(self):
project='/api/v1/project/{0}/'.format(self.project.id)
)
self.client = Client()
- super(BranchTest, self).setUp()
def test_get_branch(self):
"""Should get an existing branch"""
@@ -301,10 +630,21 @@ def test_get_branch_all_fields(self):
def test_post(self):
"""Should save a new branch"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.branch2_data)
response = self.client.post('/api/v1/branch/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/branch/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
response = self.client.get('/api/v1/branch/{0}/'.format(id))
@@ -312,16 +652,26 @@ def test_post(self):
self.assertEqual(
json.loads(response.content)[k], v)
response = self.client.delete('/api/v1/branch/{0}/'.format(id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
def test_put(self):
"""Should modify an existing environment"""
+ request = HttpRequest()
+ request.user = self.api_user
+ request.user.user_permissions.add(self.change)
+
modified_data = copy.deepcopy(self.branch2_data)
modified_data['name'] = "tip"
response = self.client.put('/api/v1/branch/1/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.put('/api/v1/branch/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/branch/1/')
for k, v in modified_data.items():
@@ -330,11 +680,19 @@ def test_put(self):
def test_delete(self):
"""Should delete a branch"""
+ request = HttpRequest()
+ request.user = self.api_user
+ request.user.user_permissions.add(self.delete)
+
response = self.client.get('/api/v1/branch/1/')
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/branch/1/',
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.delete('/api/v1/branch/1/',
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/branch/1/')
@@ -345,6 +703,13 @@ class RevisionTest(FixtureTestCase):
"""Test Revision() API"""
def setUp(self):
+ super(RevisionTest, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_revision', 'codespeed', 'revision')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_revision', 'codespeed', 'revision')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_revision', 'codespeed', 'revision')
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
self.branch1 = Branch.objects.get(pk=1)
self.project1 = Project.objects.get(pk=1)
@@ -369,7 +734,6 @@ def setUp(self):
branch='/api/v1/branch/{0}/'.format(self.branch1.id),
)
self.client = Client()
- super(RevisionTest, self).setUp()
def test_get_revision(self):
"""Should get an existing revision"""
@@ -396,10 +760,22 @@ def test_get_revision_all_fields(self):
def test_post(self):
"""Should save a new revision"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.revision2_data)
response = self.client.post('/api/v1/revision/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/revision/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
response = self.client.get('/api/v1/revision/{0}/'.format(id))
@@ -407,16 +783,29 @@ def test_post(self):
self.assertEqual(
json.loads(response.content)[k], v)
response = self.client.delete('/api/v1/revision/{0}/'.format(id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
def test_put(self):
"""Should modify an existing revision"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.revision2_data)
modified_data['tag'] = "v0.9.1"
response = self.client.put('/api/v1/revision/1/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.put('/api/v1/revision/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/revision/1/')
for k, v in modified_data.items():
@@ -425,11 +814,20 @@ def test_put(self):
def test_delete(self):
"""Should delete a revision"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.delete)
+
response = self.client.get('/api/v1/revision/1/')
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/revision/1/',
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.delete('/api/v1/revision/1/',
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/revision/1/')
@@ -440,6 +838,15 @@ class ExecutableTest(FixtureTestCase):
"""Test Executable() API"""
def setUp(self):
+ super(ExecutableTest, self).setUp()
+
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_executable', 'codespeed', 'executable')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_executable', 'codespeed', 'executable')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_executable', 'codespeed', 'executable')
+
self.executable1 = Executable.objects.get(pk=1)
self.project1 = Project.objects.get(pk=1)
self.executable2_data = dict(
@@ -448,7 +855,6 @@ def setUp(self):
project= '/api/v1/project/{0}/'.format(self.project1.id),
)
self.client = Client()
- super(ExecutableTest, self).setUp()
def test_get_executable(self):
"""Should get an existing executable"""
@@ -473,27 +879,51 @@ def test_get_executable_all_fields(self):
def test_post(self):
"""Should save a new executable"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
modified_data = copy.deepcopy(self.executable2_data)
response = self.client.post('/api/v1/executable/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/executable/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
response = self.client.get('/api/v1/executable/{0}/'.format(id))
for k, v in self.executable2_data.items():
self.assertEqual(
json.loads(response.content)[k], v)
+ request.user.user_permissions.add(self.delete)
response = self.client.delete('/api/v1/executable/{0}/'.format(id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
def test_put(self):
"""Should modify an existing environment"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.executable2_data)
modified_data['name'] = "django"
response = self.client.put('/api/v1/executable/1/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.put('/api/v1/executable/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/executable/1/')
for k, v in modified_data.items():
@@ -502,11 +932,20 @@ def test_put(self):
def test_delete(self):
"""Should delete a executable"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.delete)
+
response = self.client.get('/api/v1/executable/1/')
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/executable/1/',
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.delete('/api/v1/executable/1/',
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/executable/1/')
@@ -517,6 +956,13 @@ class BenchmarkTest(FixtureTestCase):
"""Test Benchmark() API"""
def setUp(self):
+ super(BenchmarkTest, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_benchmark', 'codespeed', 'benchmark')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_benchmark', 'codespeed', 'benchmark')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_benchmark', 'codespeed', 'benchmark')
self.benchmark1 = Benchmark.objects.get(pk=1)
self.benchmark2_data = dict(
name="sleep",
@@ -530,7 +976,6 @@ def setUp(self):
self.benchmark2 = Benchmark(**self.benchmark2_data)
self.benchmark2.save()
self.client = Client()
- super(BenchmarkTest, self).setUp()
def test_get_benchmark(self):
"""Should get an existing benchmark"""
@@ -551,28 +996,53 @@ def test_get_benchmark_all_fields(self):
def test_post(self):
"""Should save a new benchmark"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
modified_data = copy.deepcopy(self.benchmark2_data)
modified_data['name'] = 'wake'
response = self.client.post('/api/v1/benchmark/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/benchmark/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth
+ )
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
response = self.client.get('/api/v1/benchmark/{0}/'.format(id))
for k, v in modified_data.items():
self.assertEqual(
json.loads(response.content)[k], v)
+ request.user.user_permissions.add(self.delete)
response = self.client.delete('/api/v1/benchmark/{0}/'.format(id),
- content_type='application/json')
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
def test_put(self):
"""Should modify an existing benchmark"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.benchmark2_data)
modified_data['name'] = "django"
response = self.client.put('/api/v1/benchmark/1/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.put('/api/v1/benchmark/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/benchmark/1/')
for k, v in modified_data.items():
@@ -581,11 +1051,19 @@ def test_put(self):
def test_delete(self):
"""Should delete a benchmark"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.delete)
response = self.client.get('/api/v1/benchmark/1/')
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/benchmark/1/',
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.delete('/api/v1/benchmark/1/',
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 204)
response = self.client.get('/api/v1/benchmark/1/')
@@ -596,6 +1074,14 @@ class ReportTest(FixtureTestCase):
"""Test Report() API"""
def setUp(self):
+ super(ReportTest, self).setUp()
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_report', 'codespeed', 'report')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_report', 'codespeed', 'report')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_report', 'codespeed', 'report')
+
self.report1 = Report.objects.get(pk=1)
self.revision1 = Revision.objects.get(pk=1)
self.executable1 = Executable.objects.get(pk=1)
@@ -621,7 +1107,6 @@ def setUp(self):
executable='/api/v1/executable/{0}/'.format(self.executable2.id),
)
self.client = Client()
- super(ReportTest, self).setUp()
def test_get_report(self):
"""Should get an existing report"""
@@ -641,92 +1126,77 @@ def test_get_report_all_fields(self):
self.assertEqual(json.loads(response.content)[k], v)
def test_post(self):
- """Should save a new report"""
+ """Should not save a new report"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
modified_data = copy.deepcopy(self.report2_data)
response = self.client.post('/api/v1/report/',
data=json.dumps(modified_data),
content_type='application/json')
+ self.assertEquals(response.status_code, 405)
+ # next has to be 405 (method not allowed),
+ # otherwise would raise IntegrityError
+ response = self.client.post('/api/v1/report/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
# next has to be 405, otherwise would raise IntegrityError
self.assertEquals(response.status_code, 405)
def test_put(self):
- """Should modify an existing report"""
+ """Should not modify an existing report"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+ request.user.user_permissions.add(self.change)
+ request.user.user_permissions.add(self.delete)
+
modified_data = copy.deepcopy(self.report2_data)
response = self.client.put('/api/v1/report/1/',
data=json.dumps(modified_data),
content_type='application/json')
self.assertEquals(response.status_code, 405)
+ response = self.client.put('/api/v1/report/1/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
+ self.assertEquals(response.status_code, 405)
def test_delete(self):
"""Should delete a report"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.delete)
+
response = self.client.get('/api/v1/report/1/')
self.assertEquals(response.status_code, 200)
# from fixture
response = self.client.delete('/api/v1/report/1/',
content_type='application/json')
self.assertEquals(response.status_code, 405)
-
-
-class UserTest(FixtureTestCase):
- """Test api user related stuff"""
-
- def test_has_apikey(self):
- self.assertTrue(hasattr(self.api_user, 'api_key'))
-
-
-class ApiKeyAuthenticationTestCase(FixtureTestCase):
-
- def setUp(self):
- super(ApiKeyAuthenticationTestCase, self).setUp()
- ApiKey.objects.all().delete()
- self.auth = ApiKeyAuthentication()
- self.request = HttpRequest()
-
- # Simulate sending the signal.
- user = User.objects.get(username='apiuser')
- create_api_key(User, instance=user, created=True)
-
- def test_is_not_authenticated(self):
- """Should return HttpUnauthorized when incorrect credentials are given"""
- # No username/api_key details
- self.assertEqual(isinstance(
- self.auth.is_authenticated(self.request), HttpUnauthorized), True)
-
- # Wrong username details.
- self.request.GET['username'] = 'foo'
- self.assertEqual(isinstance(
- self.auth.is_authenticated(self.request), HttpUnauthorized), True)
-
- # No api_key.
- self.request.GET['username'] = 'daniel'
- self.assertEqual(isinstance(
- self.auth.is_authenticated(self.request), HttpUnauthorized), True)
-
- # Wrong user/api_key.
- self.request.GET['username'] = 'daniel'
- self.request.GET['api_key'] = 'foo'
- self.assertEqual(isinstance(
- self.auth.is_authenticated(self.request), HttpUnauthorized), True)
-
- def test_is_authenticated(self):
- """Should correctly authenticate when using an existing user and key"""
- # Correct user/api_key.
- user = User.objects.get(username='apiuser')
- self.request.GET['username'] = 'apiuser'
- self.request.GET['api_key'] = user.api_key.key
- self.assertEqual(self.auth.is_authenticated(self.request), True)
+ response = self.client.delete('/api/v1/report/1/',
+ content_type='application/json',
+ **self.post_auth)
+ self.assertEquals(response.status_code, 405)
class ResultBundleTestCase(FixtureTestCase):
+ """Test CRUD of results via API"""
def setUp(self):
+ super(ResultBundleTestCase, self).setUp()
self.data1 = {
- 'commitid': '2',
- 'branch': 'default', # Always use default for trunk/master/tip
- 'project': 'MyProject',
- 'executable': 'myexe O3 64bits',
- 'benchmark': 'float',
- 'environment': "Bulldozer",
+ 'commitid': '/api/v1/revision/2/',
+ 'branch': '/api/v1/branch/1/', # Always use default for trunk/master/tip
+ 'project': '/api/v1/project/2/',
+ 'executable': '/api/v1/executable/1/',
+ 'benchmark': '/api/v1/benchmark/1/',
+ 'environment': '/api/v1/environment/2/',
'result_value': 4000,
}
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
@@ -749,26 +1219,28 @@ def setUp(self):
self.env1.save()
def test_populate_and_save(self):
+ """Should populate ResultBundle() with data"""
bundle = ResultBundle(**self.data1)
bundle._populate_obj_by_data()
# should raise exception if not OK
- bundle.save()
+ bundle.hydrate_and_save()
self.assert_(True)
def test_save_same_result_again(self):
"""Save a previously saved result. Expected is an IntegrityError"""
modified_data = copy.deepcopy(self.data1)
- modified_data['environment'] = "Dual Core"
+ modified_data['environment'] = '/api/v1/environment/1/'
+ modified_data['project'] = '/api/v1/project/1/'
bundle = ResultBundle(**modified_data)
bundle._populate_obj_by_data()
- self.assertRaises(IntegrityError, bundle.save)
+ self.assertRaises(IntegrityError, bundle.hydrate_and_save)
def test_for_nonexistent_environment(self):
"""Save data using non existing environment. Expected is an
ImmediateHttpResponse
"""
modified_data = copy.deepcopy(self.data1)
- modified_data['environment'] = "Foo the Bar"
+ modified_data['environment'] = '/api/v1/environment/3/'
self.assertRaises(ImmediateHttpResponse, ResultBundle, **modified_data)
def test_insufficient_data(self):
@@ -778,11 +1250,11 @@ def test_insufficient_data(self):
self.assertRaises(ImmediateHttpResponse, ResultBundle, **modified_data)
def test_date_attr_set(self):
- """Check if date attr of Result() is set if not given"""
+ """Should add date attr to Result() obj if date is not given"""
# date is set automatically
modified_data = copy.deepcopy(self.data1)
bundle = ResultBundle(**modified_data)
- bundle.save()
+ bundle.hydrate_and_save()
self.assertIsInstance(bundle.obj.date, datetime)
# date set by value
modified_data['date'] = '2011-05-05 03:01:45'
@@ -792,10 +1264,10 @@ def test_date_attr_set(self):
self.assertRaises(ImmediateHttpResponse, ResultBundle, **modified_data)
def test_optional_data(self):
- """Check handling of optional data"""
+ """Should save optional data."""
data = dict(self.data1.items() + self.data_optional.items())
bundle = ResultBundle(**data)
- bundle.save()
+ bundle.hydrate_and_save()
self.assertIsInstance(bundle.obj.date, datetime)
self.assertEqual(bundle.obj.std_dev,
float(self.data_optional['std_dev']))
@@ -804,35 +1276,29 @@ def test_optional_data(self):
self.assertEqual(bundle.obj.val_min,
float(self.data_optional['val_min']))
- def test_non_exiting_items(self):
- """Check handling of optional data"""
- modified_data = copy.deepcopy(self.data1)
- modified_data['commitid'] = '0b31bf33a469ac2cb1949666eea54d69a36c3724'
- modified_data['project'] = 'Cython'
- modified_data['benchmark'] = 'Django Template'
- modified_data['executable'] = 'pypy-jit'
- bundle = ResultBundle(**modified_data)
- bundle.save()
- self.assertEqual(bundle.obj.revision.commitid,
- modified_data['commitid'])
- self.assertEqual(bundle.obj.benchmark.name,
- modified_data['benchmark'])
- self.assertEqual(bundle.obj.project.name,
- modified_data['project'])
-
class ResultBundleResourceTestCase(FixtureTestCase):
"""Submitting new benchmark results"""
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
def setUp(self):
+
+ super(ResultBundleResourceTestCase, self).setUp()
+
+ self.add = Permission.objects.get_by_natural_key(
+ 'add_result', 'codespeed', 'result')
+ self.change = Permission.objects.get_by_natural_key(
+ 'change_result', 'codespeed', 'result')
+ self.delete = Permission.objects.get_by_natural_key(
+ 'delete_result', 'codespeed', 'result')
+
self.data1 = {
- 'commitid': '2',
- 'branch': 'default', # Always use default for trunk/master/tip
- 'project': 'MyProject',
- 'executable': 'myexe O3 64bits',
- 'benchmark': 'float',
- 'environment': "Bulldozer",
+ 'commitid': '/api/v1/revision/2/',
+ 'branch': '/api/v1/branch/1/', # Always use default for trunk/master/tip
+ 'project': '/api/v1/project/2/',
+ 'executable': '/api/v1/executable/1/',
+ 'benchmark': '/api/v1/benchmark/1/',
+ 'environment': '/api/v1/environment/2/',
'result_value': 4000,
}
self.data_optional = {
@@ -852,12 +1318,23 @@ def setUp(self):
self.project.save()
self.env1 = Environment(name='Bulldozer')
self.env1.save()
+ self.client = Client()
def test_post_mandatory(self):
"""Should save a new result with only mandatory data"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
response = self.client.post('/api/v1/benchmark-result/',
data=json.dumps(self.data1),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/benchmark-result/',
+ data=json.dumps(self.data1),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
id = response['Location'].rsplit('/', 2)[-2]
result = Result.objects.get(pk=int(id))
@@ -867,12 +1344,38 @@ def test_post_mandatory(self):
def test_post_all_data(self):
"""Should save a new result with mandatory and optional data"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
data = dict(self.data1, **self.data_optional)
response = self.client.post('/api/v1/benchmark-result/',
data=json.dumps(data),
content_type='application/json')
+ self.assertEquals(response.status_code, 401)
+ response = self.client.post('/api/v1/benchmark-result/',
+ data=json.dumps(data),
+ content_type='application/json',
+ **self.post_auth)
self.assertEquals(response.status_code, 201)
+ def test_post_invalid_data(self):
+ """Should save a new result with mandatory and optional data"""
+ request = HttpRequest()
+ request.user = self.api_user
+
+ request.user.user_permissions.add(self.add)
+
+ modified_data = copy.deepcopy(self.data1)
+ # environment does not exist
+ modified_data['environment'] = '/api/v1/environment/5/'
+ response = self.client.post('/api/v1/benchmark-result/',
+ data=json.dumps(modified_data),
+ content_type='application/json',
+ **self.post_auth)
+ self.assertEquals(response.status_code, 400)
+
def test_get_one(self):
"""Should get a result bundle"""
response = self.client.get('/api/v1/benchmark-result/1/',
@@ -890,4 +1393,3 @@ def test_get_one(self):
# suite = unittest.TestSuite()
# suite.addTest(EnvironmentTest())
# return suite
-
View
2  example/requirements.txt
@@ -1,4 +1,4 @@
Django>=1.3
South>=0.7.3
-django-tastypie>=0.9.9
+-e git://github.com/toastdriven/django-tastypie.git@d9c86299118b4a1db9f646018169c1c45fd065df#egg=django-tastypie
#-e git://github.com/robhudson/django-debug-toolbar.git#egg=django-debug-toolbar
View
7 example/settings.py
@@ -65,8 +65,15 @@
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
+ # 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+)
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
)
if DEBUG:
View
76 tools/create_environment.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+# -*- coding: utf-8 -*-
+
+"""
+Check if an environment exists, create otherwise
+"""
+import json
+import urllib2
+from optparse import OptionParser, OptionError
+from django.utils import simplejson
+
+CODESPEED_URL='http://speedcenter'
+
+def get_options():
+ """Get the options and arguments
+ """
+ parser = OptionParser()
+
+ parser.add_option("-e", "--environment", dest="environment",
+ help="name of the environment to create")
+
+ (options, args) = parser.parse_args()
+
+ if not options.environment:
+ parser.error("No environment given")
+
+ return options, args
+
+def is_environment(environment):
+ """check if environment does exist
+
+ return:
+ True if it exist
+ False if it doesn't exist
+ """
+ url = CODESPEED_URL + '/api/v1/environment/'
+ request = urllib2.Request(url)
+ opener = urllib2.build_opener()
+ try:
+ raw_data = opener.open(request)
+ except urllib2.HTTPError as e:
+ raise e
+ data = simplejson.load(raw_data)
+ if environment in [ env['name'] for env in data['objects']]:
+ return True
+ return False
+
+def create_environment(environment):
+ """create the environment
+
+ return:
+ True if success
+ False if not created
+ """
+ url = CODESPEED_URL + '/api/v1/environment/'
+ data = json.dumps({'name': environment})
+ request = urllib2.Request(url, data, {'Content-Type': 'application/json'})
+ try:
+ f = urllib2.urlopen(request)
+ response = f.read()
+ f.close()
+ except urllib2.HTTPError as e:
+ raise e
+ return response
+
+def main():
+ (options, args) = get_options()
+ if is_environment(options.environment):
+ print "Found environment, doing nothing."
+ else:
+ print create_environment(options.environment)
+
+if __name__ == "__main__":
+ main()
+
View
39 tools/save_single_result_via_api.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# -*- coding: utf-8 -*-
+
+"""
+Submit a single result via the RESTful API using requests
+
+Note, that is just an example. You need to add an user
+to get the apikey via the /admin
+All resources in the result_data dict need to exist.
+"""
+import json
+import requests
+
+def get_data():
+ result_data = {
+ 'commitid': '/api/v1/revision/2/',
+ 'branch': '/api/v1/branch/1/', # Always use default for trunk/master/tip
+ 'project': '/api/v1/project/2/',
+ 'executable': '/api/v1/executable/1/',
+ 'benchmark': '/api/v1/benchmark/1/',
+ 'environment': '/api/v1/environment/2/',
+ 'result_value': 4000,
+ }
+ headers = {'content-type': 'application/json',
+ 'Authorization': 'ApiKey apiuser2:2ee0fa1a175ccc3b88b245e799d70470e5d53430'}
+ url = 'http://localhost:8000/api/v1/benchmark-result/'
+ return(url, result_data, headers)
+
+
+def main():
+ url, result_data, headers = get_data()
+ print "{0}: {1}".format(url, result_data)
+ r = requests.post(url, data=json.dumps(result_data), headers=headers)
+ print r
+
+
+if __name__ == "__main__":
+ main()
Please sign in to comment.
Something went wrong with that request. Please try again.