Selaa lähdekoodia

fix(api): decouple django tests from name servers

As a side effect, tests can now be run without docker by using a temporary
sqlite database, like so:

python3 manage.py test -v 3 --noinput --settings=api.settings_quick_test

This usually requires python3 to be run inside a virtual environment. Once
way to create it is

	cd api/

	# create virtual environment and enter it
	virtualenv -p python3 venv
	source venv/bin/activate

	# install requirements
	pip install -r requirements.txt

	# make environment available
	set -a && source ../.env && set +a

Both source commands must be run before the tests are started in a fresh
shell. PyCharm can be configured to run the tests in an analogous way.

Closes #47
Nils Wisiol 6 vuotta sitten
vanhempi
commit
52e77231eb

+ 2 - 1
.gitignore

@@ -1,6 +1,7 @@
-# python virtualenv
+# python virtualenv and test files
 .env
 api/venv
+api/desecapi.sqlite
 
 # IDE files
 .idea

+ 17 - 0
api/api/settings_quick_test.py

@@ -0,0 +1,17 @@
+from api.settings import *
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': 'desecapi.sqlite',
+        'TEST': {
+            'CHARSET': 'utf8mb4',
+        },
+    },
+
+}
+
+# avoid computationally expensive password hashing for tests
+PASSWORD_HASHERS = [
+    'django.contrib.auth.hashers.MD5PasswordHasher',
+]

+ 26 - 22
api/desecapi/tests/testdomains.py

@@ -55,7 +55,7 @@ class AuthenticatedDomainTests(APITestCase):
         self.assertEqual(response.data[1]['name'], self.ownedDomains[1].name)
 
     def testCanDeleteOwnedDomain(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
         httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name+ '.')
 
@@ -74,7 +74,8 @@ class AuthenticatedDomainTests(APITestCase):
         self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
 
     def testCantDeleteOtherDomains(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
+        httpretty.reset()
         httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.otherDomains[1].name + '.')
         httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.otherDomains[1].name+ '.')
 
@@ -85,7 +86,7 @@ class AuthenticatedDomainTests(APITestCase):
         self.assertTrue(Domain.objects.filter(pk=self.otherDomains[1].pk).exists())
 
     def testCanGetOwnedDomains(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.GET,
                                settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + './cryptokeys',
                                body='[]',
@@ -121,6 +122,7 @@ class AuthenticatedDomainTests(APITestCase):
     def testCanPostDomains(self):
         url = reverse('v1:domain-list')
         data = {'name': utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         self.assertEqual(len(mail.outbox), 0)
@@ -128,17 +130,7 @@ class AuthenticatedDomainTests(APITestCase):
     def testCanPostReverseDomains(self):
         name = '0.8.0.0.0.1.c.a.2.4.6.0.c.e.e.d.4.4.0.1.a.0.1.0.8.f.4.0.1.0.a.2.ip6.arpa'
 
-        httpretty.enable()
-        httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones', status=201)
-        httpretty.register_uri(httpretty.GET,
-                               settings.NSLORD_PDNS_API + '/zones/' + name + '.',
-                               body='{"rrsets": []}',
-                               content_type="application/json")
-        httpretty.register_uri(httpretty.GET,
-                               settings.NSLORD_PDNS_API + '/zones/' + name + './cryptokeys',
-                               body='[]',
-                               content_type="application/json")
-        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify', status=200)
+        utils.httpretty_for_pdns_domain_creation(name)
 
         url = reverse('v1:domain-list')
         data = {'name': name}
@@ -150,12 +142,14 @@ class AuthenticatedDomainTests(APITestCase):
         url = reverse('v1:domain-list')
 
         data = {'name': utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
 
         data = {'name': 'www.' + self.ownedDomains[0].name}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
@@ -166,7 +160,7 @@ class AuthenticatedDomainTests(APITestCase):
     def testCantPostDomainAlreadyTakenInPdns(self):
         name = utils.generateDomainname()
 
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones',
                                body='{"error": "Domain \'' + name + '.\' already exists"}', status=422)
 
@@ -179,6 +173,7 @@ class AuthenticatedDomainTests(APITestCase):
         url = reverse('v1:domain-list')
 
         data = {'name': '*.' + utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertTrue("does not match the required pattern." in response.data['name'][0])
@@ -190,6 +185,7 @@ class AuthenticatedDomainTests(APITestCase):
 
         url = reverse('v1:domain-list')
         data = {'name': utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
@@ -197,6 +193,7 @@ class AuthenticatedDomainTests(APITestCase):
         name = utils.generateDomainname()
         data = {'name': name}
         url = reverse('v1:domain-list')
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         self.client.post(url, data)
 
         # Lock user
@@ -221,13 +218,14 @@ class AuthenticatedDomainTests(APITestCase):
     def testCanPostComplicatedDomains(self):
         url = reverse('v1:domain-list')
         data = {'name': 'very.long.domain.name.' + utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
     def testPostingCausesPdnsAPICalls(self):
         name = utils.generateDomainname()
 
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
         httpretty.register_uri(httpretty.GET,
                                settings.NSLORD_PDNS_API + '/zones/' + name + '.',
@@ -256,7 +254,7 @@ class AuthenticatedDomainTests(APITestCase):
     def testRollback(self):
         name = utils.generateDomainname()
 
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones', body="some error", status=500)
 
         url = reverse('v1:domain-list')
@@ -280,7 +278,7 @@ class AuthenticatedDynDomainTests(APITestCase):
         httpretty.disable()
 
     def testCanDeleteOwnedDynDomain(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
         httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
 
@@ -301,7 +299,8 @@ class AuthenticatedDynDomainTests(APITestCase):
         self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
 
     def testCantDeleteOtherDynDomains(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
+        httpretty.reset()
         httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.otherDomains[1].name + '.')
         httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.otherDomains[1].name+ '.')
 
@@ -314,6 +313,7 @@ class AuthenticatedDynDomainTests(APITestCase):
     def testCanPostDynDomains(self):
         url = reverse('v1:domain-list')
         data = {'name': utils.generateDynDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         email = str(mail.outbox[0].message())
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
@@ -329,18 +329,20 @@ class AuthenticatedDynDomainTests(APITestCase):
         url = reverse('v1:domain-list')
 
         data = {'name': utils.generateDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
         self.assertEqual(response.data['code'], 'domain-illformed')
 
         data = {'name': 'very.long.domain.' + utils.generateDynDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
         self.assertEqual(response.data['code'], 'domain-illformed')
 
 
     def testLimitDynDomains(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
 
         outboxlen = len(mail.outbox)
@@ -366,12 +368,13 @@ class AuthenticatedDynDomainTests(APITestCase):
             self.assertEqual(len(mail.outbox), outboxlen+i+1)
 
         data = {'name': utils.generateDynDomainname()}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
         self.assertEqual(len(mail.outbox), outboxlen + settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT-2)
 
     def testCantUseInvalidCharactersInDomainNamePDNS(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
 
         outboxlen = len(mail.outbox)
@@ -392,6 +395,7 @@ class AuthenticatedDynDomainTests(APITestCase):
         url = reverse('v1:domain-list')
         for domainname in invalidnames:
             data = {'name': domainname}
+            utils.httpretty_for_pdns_domain_creation(data['name'])
             response = self.client.post(url, data)
             self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
             self.assertEqual(len(mail.outbox), outboxlen)
@@ -404,7 +408,7 @@ class AuthenticatedDynDomainTests(APITestCase):
         newdomain = utils.generateDynDomainname()
         data = {'name': newdomain}
         self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.GET,
                                settings.NSLORD_PDNS_API + '/zones/' + newdomain + './cryptokeys',
                                body='[]',

+ 2 - 1
api/desecapi/tests/testdyndns12update.py

@@ -22,6 +22,7 @@ class DynDNS12UpdateTest(APITestCase):
 
         url = reverse('v1:domain-list')
         data = {'name': self.domain}
+        utils.httpretty_for_pdns_domain_creation(data['name'])
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
@@ -29,7 +30,7 @@ class DynDNS12UpdateTest(APITestCase):
         self.password = self.token
         self.client.credentials(HTTP_AUTHORIZATION='Basic ' + base64.b64encode((self.username + ':' + self.password).encode()).decode())
 
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.HTTPretty.allow_net_connect = False
         self.httpretty_reset_uris()
 

+ 2 - 1
api/desecapi/tests/testdynupdateauthentication.py

@@ -26,10 +26,11 @@ class DynUpdateAuthenticationTests(APITestCase):
             self.domain = utils.generateDynDomainname()
             url = reverse('v1:domain-list')
             data = {'name': self.domain}
+            utils.httpretty_for_pdns_domain_creation(data['name'])
             response = self.client.post(url, data)
             self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
-            httpretty.enable()
+            httpretty.enable(allow_net_connect=False)
             httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
             httpretty.register_uri(httpretty.GET,
                                    settings.NSLORD_PDNS_API + '/zones/' + self.domain + '.',

+ 20 - 11
api/desecapi/tests/testrrsets.py

@@ -36,9 +36,6 @@ class AuthenticatedRRsetTests(APITestCase):
     restricted_types = ('SOA', 'RRSIG', 'DNSKEY', 'NSEC3PARAM')
 
     def setUp(self):
-        httpretty.reset()
-        httpretty.disable()
-
         if not hasattr(self, 'owner'):
             self.owner = utils.createUser()
             self.ownedDomains = [utils.createDomain(self.owner), utils.createDomain(self.owner)]
@@ -49,11 +46,18 @@ class AuthenticatedRRsetTests(APITestCase):
             self.otherDomains = [utils.createDomain(self.otherOwner), utils.createDomain()]
             self.otherToken = utils.createToken(user=self.otherOwner)
 
+            httpretty.reset()
+            httpretty.enable(allow_net_connect=False)
+            for domain in self.ownedDomains + self.otherDomains:
+                httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + domain.name + '.')
+                httpretty.register_uri(httpretty.PUT,
+                                       settings.NSLORD_PDNS_API + '/zones/' + domain.name + './notify')
+
     def testCanGetOwnRRsets(self):
         url = reverse('v1:rrsets', args=(self.ownedDomains[1].name,))
         response = self.client.get(url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 0)  # NS RRset unavailable in mock pdns environment
 
     def testCantGetForeignRRsets(self):
         url = reverse('v1:rrsets', args=(self.otherDomains[1].name,))
@@ -64,7 +68,7 @@ class AuthenticatedRRsetTests(APITestCase):
         url = reverse('v1:rrsets', args=(self.ownedDomains[1].name,))
         response = self.client.get(url + '?subname=')
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 0)  # NS RRset unavailable in mock pdns environment
 
     def testCanGetOwnRRsetsFromSubname(self):
         url = reverse('v1:rrsets', args=(self.ownedDomains[1].name,))
@@ -83,7 +87,7 @@ class AuthenticatedRRsetTests(APITestCase):
 
         response = self.client.get(url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 3 + 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 3)  # NS RRset unavailable in mock pdns environment
 
         response = self.client.get(url + '?subname=test')
         self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -111,7 +115,7 @@ class AuthenticatedRRsetTests(APITestCase):
 
         response = self.client.get(url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 3 + 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 3)  # NS RRset unavailable in mock pdns environment
 
         response = self.client.get(url + '?type=A')
         self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -130,7 +134,7 @@ class AuthenticatedRRsetTests(APITestCase):
 
         response = self.client.get(url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 1 + 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 1)  # NS RRset unavailable in mock pdns environment
 
         url = reverse('v1:rrset', args=(self.ownedDomains[1].name, '', 'A',))
         response = self.client.get(url)
@@ -199,6 +203,8 @@ class AuthenticatedRRsetTests(APITestCase):
         # Unknown type is a semantical error --> 422
         url = reverse('v1:rrsets', args=(self.ownedDomains[1].name,))
         data = {'records': ['123456'], 'ttl': 60, 'type': 'AA'}
+        httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.',
+                               body='', status=422)
         response = self.client.post(url, json.dumps(data), content_type='application/json')
         self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY)
 
@@ -265,7 +271,7 @@ class AuthenticatedRRsetTests(APITestCase):
 
         response = self.client.get(url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(len(response.data), 3 + 1) # don't forget NS RRset
+        self.assertEqual(len(response.data), 3, response.data)  # NS RRset unavailable in mock pdns environment
 
         url = reverse('v1:rrset', args=(self.ownedDomains[1].name, 'test', 'A',))
         response = self.client.get(url)
@@ -615,7 +621,7 @@ class AuthenticatedRRsetTests(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
     def testPostCausesPdnsAPICall(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
         httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + './notify')
 
@@ -629,7 +635,7 @@ class AuthenticatedRRsetTests(APITestCase):
         self.assertEqual(httpretty.last_request().method, 'PUT')
 
     def testDeleteCausesPdnsAPICall(self):
-        httpretty.enable()
+        httpretty.enable(allow_net_connect=False)
         httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
         httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + './notify')
 
@@ -662,4 +668,7 @@ class AuthenticatedRRsetTests(APITestCase):
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
         # Not checking anything here; errors will raise an exception
+        httpretty.register_uri(httpretty.GET, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.',
+                               status=200, body='{"rrsets":[{"name":"asdf","type":"A",' +
+                                                '"records":[{"content":"1.1.1.1"},{"content":"2.2.2.2"}],"ttl":20}]}')
         call_command('sync-from-pdns', self.ownedDomains[1].name)

+ 19 - 0
api/desecapi/tests/utils.py

@@ -1,6 +1,9 @@
 import random
 import string
 
+from httpretty import httpretty
+
+from api import settings
 from desecapi.models import Domain, User, Token
 
 
@@ -54,9 +57,25 @@ class utils(object):
         else:
             name = cls.generateDomainname()
         domain = Domain(name=name, owner=owner)
+        cls.httpretty_for_pdns_domain_creation(name)
         domain.save()
         return domain
 
+    @classmethod
+    def httpretty_for_pdns_domain_creation(cls, name):
+        httpretty.enable(allow_net_connect=False)
+        httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
+        httpretty.register_uri(httpretty.GET,
+                               settings.NSLORD_PDNS_API + '/zones/' + name + '.',
+                               body='{"rrsets": []}',
+                               content_type="application/json")
+        httpretty.register_uri(httpretty.GET,
+                               settings.NSLORD_PDNS_API + '/zones/' + name + './cryptokeys',
+                               body='[]',
+                               content_type="application/json")
+        httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify',
+                               status=200)
+
     @classmethod
     def createToken(cls, user):
         token = Token.objects.create(user=user)