123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- from django.core.urlresolvers import reverse
- from rest_framework import status
- from rest_framework.test import APITestCase
- from .utils import utils
- from desecapi.models import Domain
- from django.core import mail
- import httpretty
- from django.conf import settings
- import json
- class UnauthenticatedDomainTests(APITestCase):
- def testExpectUnauthorizedOnGet(self):
- url = reverse('domain-list')
- response = self.client.get(url, format='json')
- self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
- def testExpectUnauthorizedOnPost(self):
- url = reverse('domain-list')
- response = self.client.post(url, format='json')
- self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
- def testExpectUnauthorizedOnPut(self):
- url = reverse('domain-detail', args=(1,))
- response = self.client.put(url, format='json')
- self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
- def testExpectUnauthorizedOnDelete(self):
- url = reverse('domain-detail', args=(1,))
- response = self.client.delete(url, format='json')
- self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
- class AuthenticatedDomainTests(APITestCase):
- def setUp(self):
- if not hasattr(self, 'owner'):
- self.owner = utils.createUser()
- self.ownedDomains = [utils.createDomain(self.owner), utils.createDomain(self.owner)]
- self.otherDomains = [utils.createDomain(), utils.createDomain()]
- self.token = utils.createToken(user=self.owner)
- self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
- def tearDown(self):
- httpretty.reset()
- httpretty.disable()
- def testExpectOnlyOwnedDomains(self):
- url = reverse('domain-list')
- response = self.client.get(url, format='json')
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data), 2)
- self.assertEqual(response.data[0]['name'], self.ownedDomains[0].name)
- self.assertEqual(response.data[1]['name'], self.ownedDomains[1].name)
- def testCanDeleteOwnedDomain(self):
- httpretty.enable()
- 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+ '.')
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.delete(url)
- self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
- self.assertEqual(httpretty.last_request().method, 'DELETE')
- self.assertEqual(httpretty.last_request().headers['Host'], 'nsmaster:8081')
- httpretty.reset()
- 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+ '.')
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
- self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
- def testCantDeleteOtherDomains(self):
- httpretty.enable()
- 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+ '.')
- url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
- response = self.client.delete(url)
- self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
- self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
- self.assertTrue(Domain.objects.filter(pk=self.otherDomains[1].pk).exists())
- def testCanGetOwnedDomains(self):
- httpretty.enable()
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + './cryptokeys',
- body='[]',
- content_type="application/json")
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['name'], self.ownedDomains[1].name)
- self.assertTrue(isinstance(response.data['keys'], list))
- def testCantGetOtherDomains(self):
- url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
- def testCanPutOwnedDomain(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- response.data['arecord'] = '1.2.3.4'
- response = self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['arecord'], '1.2.3.4')
- def testCantChangeDomainName(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- newname = utils.generateDomainname()
- response.data['name'] = newname
- response.data['arecord'] = None
- response.data['aaaarecord'] = None
- response = self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['name'], self.ownedDomains[1].name)
- def testCantPutOtherDomains(self):
- url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
- response = self.client.put(url, json.dumps({}), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
- def testCanPostDomains(self):
- url = reverse('domain-list')
- data = {'name': utils.generateDomainname()}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(len(mail.outbox), 0)
- 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")
- url = reverse('domain-list')
- data = {'name': name}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(len(mail.outbox), 0)
- def testCantPostDomainAlreadyTakenInAPI(self):
- url = reverse('domain-list')
- data = {'name': utils.generateDomainname()}
- 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}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- data = {'name': 'www.' + self.otherDomains[0].name}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
- def testCantPostDomainAlreadyTakenInPdns(self):
- name = utils.generateDomainname()
- httpretty.enable()
- httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones',
- body='{"error": "Domain \'' + name + '.\' already exists"}', status=422)
- url = reverse('domain-list')
- data = {'name': name}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
- def testCantPostDomainsViolatingPolicy(self):
- url = reverse('domain-list')
- data = {'name': '*.' + utils.generateDomainname()}
- 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])
- def testCanPostComplicatedDomains(self):
- url = reverse('domain-list')
- data = {'name': 'very.long.domain.name.' + utils.generateDomainname()}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- def testCanUpdateARecord(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- response.data['arecord'] = '10.13.3.7'
- response.data['aaaarecord'] = None
- response = self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['arecord'], '10.13.3.7')
- def testCanUpdateAAAARecord(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- response.data['arecord'] = None
- response.data['aaaarecord'] = 'fe80::a11:10ff:fee0:ff77'
- response = self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['aaaarecord'], 'fe80::a11:10ff:fee0:ff77')
- def testCanUpdateAcmeChallenge(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- response.data['acme_challenge'] = 'test_challenge'
- response = self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['acme_challenge'], 'test_challenge')
- def testPostingCausesPdnsAPICalls(self):
- name = utils.generateDomainname()
- httpretty.enable()
- 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")
- url = reverse('domain-list')
- self.client.post(url, {'name': name})
- self.assertEqual(httpretty.httpretty.latest_requests[-3].method, 'POST')
- self.assertTrue(name in httpretty.httpretty.latest_requests[-3].parsed_body)
- self.assertTrue('ns1.desec.io' in httpretty.httpretty.latest_requests[-3].parsed_body)
- self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'GET')
- self.assertTrue((settings.NSLORD_PDNS_API + '/zones/' + name + '.').endswith(httpretty.httpretty.latest_requests[-2].path))
- def testPostingWithRecordsCausesPdnsAPIPatch(self):
- name = utils.generateDomainname()
- httpretty.enable()
- httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
- httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + name + '.',
- body='{"rrsets": []}',
- content_type="application/json")
- httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + name + './cryptokeys',
- body='[]',
- content_type="application/json")
- url = reverse('domain-list')
- data = {'name': name, 'arecord': '1.3.3.7', 'aaaarecord': 'dead::beef', 'acme_challenge': 'letsencrypt_ftw'}
- self.client.post(url, data)
- self.assertEqual(httpretty.httpretty.latest_requests[-4].method, 'PATCH')
- self.assertTrue(data['name'] in httpretty.httpretty.latest_requests[-4].parsed_body)
- self.assertTrue('1.3.3.7' in httpretty.httpretty.latest_requests[-4].parsed_body)
- self.assertTrue('dead::beef' in httpretty.httpretty.latest_requests[-4].parsed_body)
- self.assertTrue('letsencrypt_ftw' in httpretty.httpretty.latest_requests[-4].parsed_body)
- def testPostDomainCausesPdnsAPIPatch(self):
- name = utils.generateDomainname()
- httpretty.enable()
- 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.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
- httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + name + './notify')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + name + './cryptokeys',
- body='[]',
- content_type="application/json")
- url = reverse('domain-list')
- data = {'name': name, 'acme_challenge': 'letsencrypt_ftw'}
- self.client.post(url, data)
- self.assertEqual(httpretty.httpretty.latest_requests[-4].method, 'PATCH')
- self.assertTrue(data['name'] in httpretty.httpretty.latest_requests[-4].parsed_body)
- self.assertTrue('letsencrypt_ftw' in httpretty.httpretty.latest_requests[-4].parsed_body)
- def testUpdateingCausesPdnsAPIPatchCall(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- httpretty.enable()
- httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.',
- body='{"rrsets": []}',
- content_type="application/json")
- httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './notify')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './cryptokeys',
- body='[]',
- content_type="application/json")
- response.data['arecord'] = '10.13.3.7'
- self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertTrue('10.13.3.7' in httpretty.httpretty.latest_requests[-4].parsed_body)
- def testUpdateingCausesPdnsAPINotifyCall(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.get(url)
- httpretty.enable()
- httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.',
- body='{"rrsets": []}',
- content_type="application/json")
- httpretty.register_uri(httpretty.PUT, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './notify')
- httpretty.register_uri(httpretty.GET,
- settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + './cryptokeys',
- body='[]',
- content_type="application/json")
- response.data['arecord'] = '10.13.3.10'
- self.client.put(url, json.dumps(response.data), content_type='application/json')
- self.assertEqual(httpretty.httpretty.latest_requests[-4].method, 'PATCH')
- self.assertTrue('10.13.3.10' in httpretty.httpretty.latest_requests[-4].parsed_body)
- self.assertEqual(httpretty.httpretty.latest_requests[-2].method, 'PUT')
- def testDomainDetailURL(self):
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- urlByName = reverse('domain-detail/byName', args=(self.ownedDomains[1].name,))
- self.assertTrue(("/%d" % self.ownedDomains[1].pk) in url)
- self.assertTrue("/" + self.ownedDomains[1].name in urlByName)
- def testRollback(self):
- name = utils.generateDomainname()
- httpretty.enable()
- httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones', body="some error", status=500)
- url = reverse('domain-list')
- data = {'name': name}
- self.client.post(url, data)
- self.assertFalse(Domain.objects.filter(name=name).exists())
- class AuthenticatedDynDomainTests(APITestCase):
- def setUp(self):
- if not hasattr(self, 'owner'):
- self.owner = utils.createUser(dyn=True)
- self.ownedDomains = [utils.createDomain(self.owner, dyn=True), utils.createDomain(self.owner, dyn=True)]
- self.otherDomains = [utils.createDomain(), utils.createDomain()]
- self.token = utils.createToken(user=self.owner)
- self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
- def tearDown(self):
- httpretty.reset()
- httpretty.disable()
- def testCanDeleteOwnedDynDomain(self):
- httpretty.enable()
- 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+ '.')
- httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/dedyn.io.')
- url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
- response = self.client.delete(url)
- self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
- self.assertEqual(httpretty.last_request().method, 'PATCH')
- self.assertEqual(httpretty.last_request().headers['Host'], 'nslord:8081')
- self.assertTrue('"NS"' in httpretty.last_request().parsed_body)
- self.assertTrue('"' + self.ownedDomains[1].name + '."' in httpretty.last_request().parsed_body)
- self.assertTrue('"DELETE"' in httpretty.last_request().parsed_body)
- httpretty.reset()
- 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+ '.')
- response = self.client.get(url)
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
- self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
- def testCantDeleteOtherDynDomains(self):
- httpretty.enable()
- 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+ '.')
- url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
- response = self.client.delete(url)
- self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
- self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
- self.assertTrue(Domain.objects.filter(pk=self.otherDomains[1].pk).exists())
- def testCanPostDynDomains(self):
- url = reverse('domain-list')
- data = {'name': utils.generateDynDomainname()}
- response = self.client.post(url, data)
- email = str(mail.outbox[0].message())
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(len(mail.outbox), 1)
- self.assertTrue(data['name'] in email)
- self.assertTrue(self.token in email)
- def testCantPostNonDynDomains(self):
- url = reverse('domain-list')
- data = {'name': utils.generateDomainname()}
- 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()}
- 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.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
- outboxlen = len(mail.outbox)
- url = reverse('domain-list')
- for i in range(settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT-2):
- name = utils.generateDynDomainname()
- 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")
- response = self.client.post(url, {'name': name})
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(len(mail.outbox), outboxlen+i+1)
- data = {'name': utils.generateDynDomainname()}
- 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.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
- outboxlen = len(mail.outbox)
- invalidnames = [
- 'with space.dedyn.io',
- 'another space.de',
- ' spaceatthebeginning.com',
- 'percentage%sign.com',
- '%percentagesign.dedyn.io',
- 'slash/desec.io',
- '/slashatthebeginning.dedyn.io',
- '\\backslashatthebeginning.dedyn.io',
- 'backslash\\inthemiddle.at',
- '@atsign.com',
- 'at@sign.com',
- ]
- url = reverse('domain-list')
- for domainname in invalidnames:
- data = {'name': domainname}
- response = self.client.post(url, data)
- self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
- self.assertEqual(len(mail.outbox), outboxlen)
|