def test_escaping(self): provider = Ns1Provider('test', 'api-key') record = { 'ttl': 31, 'short_answers': ['foo; bar baz; blip'] } self.assertEquals(['foo\; bar baz\; blip'], provider._data_for_SPF('SPF', record)['values']) record = { 'ttl': 31, 'short_answers': ['no', 'foo; bar baz; blip', 'yes'] } self.assertEquals(['no', 'foo\; bar baz\; blip', 'yes'], provider._data_for_TXT('TXT', record)['values']) zone = Zone('unit.tests.', []) record = Record.new(zone, 'spf', { 'ttl': 34, 'type': 'SPF', 'value': 'foo\; bar baz\; blip' }) self.assertEquals(['foo; bar baz; blip'], provider._params_for_SPF(record)['answers']) record = Record.new(zone, 'txt', { 'ttl': 35, 'type': 'TXT', 'value': 'foo\; bar baz\; blip' }) self.assertEquals(['foo; bar baz; blip'], provider._params_for_TXT(record)['answers'])
def test_data_for_CNAME(self): provider = Ns1Provider('test', 'api-key') # answers from nsone a_record = { 'ttl': 31, 'type': 'CNAME', 'short_answers': ['foo.unit.tests.'] } a_expected = { 'ttl': 31, 'type': 'CNAME', 'value': 'foo.unit.tests.' } self.assertEqual(a_expected, provider._data_for_CNAME(a_record['type'], a_record)) # no answers from nsone b_record = { 'ttl': 32, 'type': 'CNAME', 'short_answers': [] } b_expected = { 'ttl': 32, 'type': 'CNAME', 'value': None } self.assertEqual(b_expected, provider._data_for_CNAME(b_record['type'], b_record))
def test_populate(self, load_mock): provider = Ns1Provider('test', 'api-key') # Bad auth load_mock.side_effect = AuthException('unauthorized') zone = Zone('unit.tests.', []) with self.assertRaises(AuthException) as ctx: provider.populate(zone) self.assertEquals(load_mock.side_effect, ctx.exception) # General error load_mock.reset_mock() load_mock.side_effect = ResourceException('boom') zone = Zone('unit.tests.', []) with self.assertRaises(ResourceException) as ctx: provider.populate(zone) self.assertEquals(load_mock.side_effect, ctx.exception) self.assertEquals(('unit.tests', ), load_mock.call_args[0]) # Non-existant zone doesn't populate anything load_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(set(), zone.records) self.assertEquals(('unit.tests', ), load_mock.call_args[0]) # Existing zone w/o records load_mock.reset_mock() nsone_zone = DummyZone([]) load_mock.side_effect = [nsone_zone] zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(set(), zone.records) self.assertEquals(('unit.tests', ), load_mock.call_args[0]) # Existing zone w/records load_mock.reset_mock() nsone_zone = DummyZone(self.nsone_records) load_mock.side_effect = [nsone_zone] zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(self.expected, zone.records) self.assertEquals(('unit.tests', ), load_mock.call_args[0])
def test_sync(self, load_mock, create_mock): provider = Ns1Provider('test', 'api-key') desired = Zone('unit.tests.', []) desired.records.update(self.expected) plan = provider.plan(desired) # everything except the root NS expected_n = len(self.expected) - 1 self.assertEquals(expected_n, len(plan.changes)) # Fails, general error load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = ResourceException('boom') with self.assertRaises(ResourceException) as ctx: provider.apply(plan) self.assertEquals(load_mock.side_effect, ctx.exception) # Fails, bad auth load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') create_mock.side_effect = AuthException('unauthorized') with self.assertRaises(AuthException) as ctx: provider.apply(plan) self.assertEquals(create_mock.side_effect, ctx.exception) # non-existant zone, create load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') create_mock.side_effect = None got_n = provider.apply(plan) self.assertEquals(expected_n, got_n) # Update & delete load_mock.reset_mock() create_mock.reset_mock() nsone_zone = DummyZone(self.nsone_records + [{ 'type': 'A', 'ttl': 42, 'short_answers': ['9.9.9.9'], 'domain': 'delete-me.unit.tests.', }]) nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2' nsone_zone.loadRecord = Mock() load_mock.side_effect = [nsone_zone, nsone_zone] plan = provider.plan(desired) self.assertEquals(2, len(plan.changes)) self.assertIsInstance(plan.changes[0], Update) self.assertIsInstance(plan.changes[1], Delete) got_n = provider.apply(plan) self.assertEquals(2, got_n) nsone_zone.loadRecord.assert_has_calls([ call('unit.tests', u'A'), call().update(answers=[u'1.2.3.4'], ttl=32), call('delete-me', u'A'), call().delete() ])
def test_populate(self, load_mock): provider = Ns1Provider('test', 'api-key') # Bad auth load_mock.side_effect = AuthException('unauthorized') zone = Zone('unit.tests.', []) with self.assertRaises(AuthException) as ctx: provider.populate(zone) self.assertEquals(load_mock.side_effect, ctx.exception) # General error load_mock.reset_mock() load_mock.side_effect = ResourceException('boom') zone = Zone('unit.tests.', []) with self.assertRaises(ResourceException) as ctx: provider.populate(zone) self.assertEquals(load_mock.side_effect, ctx.exception) self.assertEquals(('unit.tests',), load_mock.call_args[0]) # Non-existant zone doesn't populate anything load_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(set(), zone.records) self.assertEquals(('unit.tests',), load_mock.call_args[0]) # Existing zone w/o records load_mock.reset_mock() nsone_zone = DummyZone([]) load_mock.side_effect = [nsone_zone] zone_search = Mock() zone_search.return_value = [ { "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ {'answer': ['1.1.1.1'], 'meta': {}}, {'answer': ['1.2.3.4'], 'meta': {'ca_province': ['ON']}}, {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}}, {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}}, {'answer': ['4.5.6.7'], 'meta': {'iso_region_code': ['NA-US-WA']}}, ], 'ttl': 34, }, ] nsone_zone.search = zone_search zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(1, len(zone.records)) self.assertEquals(('unit.tests',), load_mock.call_args[0]) # Existing zone w/records load_mock.reset_mock() nsone_zone = DummyZone(self.nsone_records) load_mock.side_effect = [nsone_zone] zone_search = Mock() zone_search.return_value = [ { "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ {'answer': ['1.1.1.1'], 'meta': {}}, {'answer': ['1.2.3.4'], 'meta': {'ca_province': ['ON']}}, {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}}, {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}}, {'answer': ['4.5.6.7'], 'meta': {'iso_region_code': ['NA-US-WA']}}, ], 'ttl': 34, }, ] nsone_zone.search = zone_search zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(self.expected, zone.records) self.assertEquals(('unit.tests',), load_mock.call_args[0])
def test_sync(self, load_mock, create_mock): provider = Ns1Provider('test', 'api-key') desired = Zone('unit.tests.', []) for r in self.expected: desired.add_record(r) plan = provider.plan(desired) # everything except the root NS expected_n = len(self.expected) - 1 self.assertEquals(expected_n, len(plan.changes)) # Fails, general error load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = ResourceException('boom') with self.assertRaises(ResourceException) as ctx: provider.apply(plan) self.assertEquals(load_mock.side_effect, ctx.exception) # Fails, bad auth load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') create_mock.side_effect = AuthException('unauthorized') with self.assertRaises(AuthException) as ctx: provider.apply(plan) self.assertEquals(create_mock.side_effect, ctx.exception) # non-existant zone, create load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') # ugh, need a mock zone with a mock prop since we're using getattr, we # can actually control side effects on `meth` with that. mock_zone = Mock() mock_zone.add_SRV = Mock() mock_zone.add_SRV.side_effect = [ RateLimitException('boo', period=0), None, ] create_mock.side_effect = [mock_zone] got_n = provider.apply(plan) self.assertEquals(expected_n, got_n) # Update & delete load_mock.reset_mock() create_mock.reset_mock() nsone_zone = DummyZone(self.nsone_records + [{ 'type': 'A', 'ttl': 42, 'short_answers': ['9.9.9.9'], 'domain': 'delete-me.unit.tests.', }]) nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2' nsone_zone.loadRecord = Mock() zone_search = Mock() zone_search.return_value = [ { "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ {'answer': ['1.1.1.1'], 'meta': {}}, {'answer': ['1.2.3.4'], 'meta': {'ca_province': ['ON']}}, {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}}, {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}}, {'answer': ['4.5.6.7'], 'meta': {'iso_region_code': ['NA-US-WA']}}, ], 'ttl': 34, }, ] nsone_zone.search = zone_search load_mock.side_effect = [nsone_zone, nsone_zone] plan = provider.plan(desired) self.assertEquals(3, len(plan.changes)) self.assertIsInstance(plan.changes[0], Update) self.assertIsInstance(plan.changes[2], Delete) # ugh, we need a mock record that can be returned from loadRecord for # the update and delete targets, we can add our side effects to that to # trigger rate limit handling mock_record = Mock() mock_record.update.side_effect = [ RateLimitException('one', period=0), None, None, ] mock_record.delete.side_effect = [ RateLimitException('two', period=0), None, None, ] nsone_zone.loadRecord.side_effect = [mock_record, mock_record, mock_record] got_n = provider.apply(plan) self.assertEquals(3, got_n) nsone_zone.loadRecord.assert_has_calls([ call('unit.tests', u'A'), call('geo', u'A'), call('delete-me', u'A'), ]) mock_record.assert_has_calls([ call.update(answers=[{'answer': [u'1.2.3.4'], 'meta': {}}], filters=[], ttl=32), call.update(answers=[{u'answer': [u'1.2.3.4'], u'meta': {}}], filters=[], ttl=32), call.update( answers=[ {u'answer': [u'101.102.103.104'], u'meta': {}}, {u'answer': [u'101.102.103.105'], u'meta': {}}, { u'answer': [u'201.202.203.204'], u'meta': { u'iso_region_code': [u'NA-US-NY'] }, }, ], filters=[ {u'filter': u'shuffle', u'config': {}}, {u'filter': u'geotarget_country', u'config': {}}, {u'filter': u'select_first_n', u'config': {u'N': 1}}, ], ttl=34), call.delete(), call.delete() ])
def test_sync(self, load_mock, create_mock): provider = Ns1Provider('test', 'api-key') desired = Zone('unit.tests.', []) for r in self.expected: desired.add_record(r) plan = provider.plan(desired) # everything except the root NS expected_n = len(self.expected) - 1 self.assertEquals(expected_n, len(plan.changes)) # Fails, general error load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = ResourceException('boom') with self.assertRaises(ResourceException) as ctx: provider.apply(plan) self.assertEquals(load_mock.side_effect, ctx.exception) # Fails, bad auth load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') create_mock.side_effect = AuthException('unauthorized') with self.assertRaises(AuthException) as ctx: provider.apply(plan) self.assertEquals(create_mock.side_effect, ctx.exception) # non-existant zone, create load_mock.reset_mock() create_mock.reset_mock() load_mock.side_effect = \ ResourceException('server error: zone not found') # ugh, need a mock zone with a mock prop since we're using getattr, we # can actually control side effects on `meth` with that. mock_zone = Mock() mock_zone.add_SRV = Mock() mock_zone.add_SRV.side_effect = [ RateLimitException('boo', period=0), None, ] create_mock.side_effect = [mock_zone] got_n = provider.apply(plan) self.assertEquals(expected_n, got_n) # Update & delete load_mock.reset_mock() create_mock.reset_mock() nsone_zone = DummyZone(self.nsone_records + [{ 'type': 'A', 'ttl': 42, 'short_answers': ['9.9.9.9'], 'domain': 'delete-me.unit.tests.', }]) nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2' nsone_zone.loadRecord = Mock() load_mock.side_effect = [nsone_zone, nsone_zone] plan = provider.plan(desired) self.assertEquals(2, len(plan.changes)) self.assertIsInstance(plan.changes[0], Update) self.assertIsInstance(plan.changes[1], Delete) # ugh, we need a mock record that can be returned from loadRecord for # the update and delete targets, we can add our side effects to that to # trigger rate limit handling mock_record = Mock() mock_record.update.side_effect = [ RateLimitException('one', period=0), None, ] mock_record.delete.side_effect = [ RateLimitException('two', period=0), None, ] nsone_zone.loadRecord.side_effect = [mock_record, mock_record] got_n = provider.apply(plan) self.assertEquals(2, got_n) nsone_zone.loadRecord.assert_has_calls([ call('unit.tests', u'A'), call('delete-me', u'A'), ]) mock_record.assert_has_calls( [call.update(answers=[u'1.2.3.4'], ttl=32), call.delete()])
def test_sync(self, zone_retrieve_mock, zone_create_mock, record_retrieve_mock, record_create_mock, record_update_mock, record_delete_mock): provider = Ns1Provider('test', 'api-key') desired = Zone('unit.tests.', []) for r in self.expected: desired.add_record(r) plan = provider.plan(desired) # everything except the root NS expected_n = len(self.expected) - 1 self.assertEquals(expected_n, len(plan.changes)) self.assertTrue(plan.exists) # Fails, general error zone_retrieve_mock.reset_mock() zone_create_mock.reset_mock() zone_retrieve_mock.side_effect = ResourceException('boom') with self.assertRaises(ResourceException) as ctx: provider.apply(plan) self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception) # Fails, bad auth zone_retrieve_mock.reset_mock() zone_create_mock.reset_mock() zone_retrieve_mock.side_effect = \ ResourceException('server error: zone not found') zone_create_mock.side_effect = AuthException('unauthorized') with self.assertRaises(AuthException) as ctx: provider.apply(plan) self.assertEquals(zone_create_mock.side_effect, ctx.exception) # non-existent zone, create zone_retrieve_mock.reset_mock() zone_create_mock.reset_mock() zone_retrieve_mock.side_effect = \ ResourceException('server error: zone not found') zone_create_mock.side_effect = ['foo'] # Test out the create rate-limit handling, then 9 successes record_create_mock.side_effect = [ RateLimitException('boo', period=0), ] + ([None] * 9) got_n = provider.apply(plan) self.assertEquals(expected_n, got_n) # Zone was created zone_create_mock.assert_has_calls([call('unit.tests')]) # Checking that we got some of the expected records too record_create_mock.assert_has_calls([ call('unit.tests', 'unit.tests', 'A', answers=[{ 'answer': ['1.2.3.4'], 'meta': {} }], filters=[], ttl=32), call('unit.tests', 'unit.tests', 'CAA', answers=[(0, 'issue', 'ca.unit.tests')], ttl=40), call('unit.tests', 'unit.tests', 'MX', answers=[(10, 'mx1.unit.tests.'), (20, 'mx2.unit.tests.')], ttl=35), ]) # Update & delete zone_retrieve_mock.reset_mock() zone_create_mock.reset_mock() ns1_zone = { 'records': self.ns1_records + [ { 'type': 'A', 'ttl': 42, 'short_answers': ['9.9.9.9'], 'domain': 'delete-me.unit.tests.', }, { "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "short_answers": [ '1.1.1.1', '1.2.3.4', '2.3.4.5', '3.4.5.6', '4.5.6.7', ], 'tier': 3, # This flags it as advacned, full load required 'ttl': 34, } ], } ns1_zone['records'][0]['short_answers'][0] = '2.2.2.2' record_retrieve_mock.side_effect = [{ "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ { 'answer': ['1.1.1.1'], 'meta': {} }, { 'answer': ['1.2.3.4'], 'meta': { 'ca_province': ['ON'] } }, { 'answer': ['2.3.4.5'], 'meta': { 'us_state': ['NY'] } }, { 'answer': ['3.4.5.6'], 'meta': { 'country': ['US'] } }, { 'answer': ['4.5.6.7'], 'meta': { 'iso_region_code': ['NA-US-WA'] } }, ], 'tier': 3, 'ttl': 34, }] zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone] plan = provider.plan(desired) self.assertEquals(3, len(plan.changes)) # Shouldn't rely on order so just count classes classes = defaultdict(lambda: 0) for change in plan.changes: classes[change.__class__] += 1 self.assertEquals(1, classes[Delete]) self.assertEquals(2, classes[Update]) record_update_mock.side_effect = [ RateLimitException('one', period=0), None, None, ] record_delete_mock.side_effect = [ RateLimitException('two', period=0), None, None, ] got_n = provider.apply(plan) self.assertEquals(3, got_n) record_update_mock.assert_has_calls([ call('unit.tests', 'unit.tests', 'A', answers=[{ 'answer': ['1.2.3.4'], 'meta': {} }], filters=[], ttl=32), call('unit.tests', 'unit.tests', 'A', answers=[{ 'answer': ['1.2.3.4'], 'meta': {} }], filters=[], ttl=32), call('unit.tests', 'geo.unit.tests', 'A', answers=[{ 'answer': ['101.102.103.104'], 'meta': {} }, { 'answer': ['101.102.103.105'], 'meta': {} }, { 'answer': ['201.202.203.204'], 'meta': { 'iso_region_code': ['NA-US-NY'] } }], filters=[{ 'filter': 'shuffle', 'config': {} }, { 'filter': 'geotarget_country', 'config': {} }, { 'filter': 'select_first_n', 'config': { 'N': 1 } }], ttl=34) ])
def test_populate(self, zone_retrieve_mock): provider = Ns1Provider('test', 'api-key') # Bad auth zone_retrieve_mock.side_effect = AuthException('unauthorized') zone = Zone('unit.tests.', []) with self.assertRaises(AuthException) as ctx: provider.populate(zone) self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception) # General error zone_retrieve_mock.reset_mock() zone_retrieve_mock.side_effect = ResourceException('boom') zone = Zone('unit.tests.', []) with self.assertRaises(ResourceException) as ctx: provider.populate(zone) self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception) self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0]) # Non-existent zone doesn't populate anything zone_retrieve_mock.reset_mock() zone_retrieve_mock.side_effect = \ ResourceException('server error: zone not found') zone = Zone('unit.tests.', []) exists = provider.populate(zone) self.assertEquals(set(), zone.records) self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0]) self.assertFalse(exists) # Existing zone w/o records zone_retrieve_mock.reset_mock() ns1_zone = { 'records': [{ "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ { 'answer': ['1.1.1.1'], 'meta': {} }, { 'answer': ['1.2.3.4'], 'meta': { 'ca_province': ['ON'] } }, { 'answer': ['2.3.4.5'], 'meta': { 'us_state': ['NY'] } }, { 'answer': ['3.4.5.6'], 'meta': { 'country': ['US'] } }, { 'answer': ['4.5.6.7'], 'meta': { 'iso_region_code': ['NA-US-WA'] } }, ], 'ttl': 34, }], } zone_retrieve_mock.side_effect = [ns1_zone] zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(1, len(zone.records)) self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0]) # Existing zone w/records zone_retrieve_mock.reset_mock() ns1_zone = { 'records': self.ns1_records + [{ "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ { 'answer': ['1.1.1.1'], 'meta': {} }, { 'answer': ['1.2.3.4'], 'meta': { 'ca_province': ['ON'] } }, { 'answer': ['2.3.4.5'], 'meta': { 'us_state': ['NY'] } }, { 'answer': ['3.4.5.6'], 'meta': { 'country': ['US'] } }, { 'answer': ['4.5.6.7'], 'meta': { 'iso_region_code': ['NA-US-WA'] } }, ], 'ttl': 34, }], } zone_retrieve_mock.side_effect = [ns1_zone] zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(self.expected, zone.records) self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0]) # Test skipping unsupported record type zone_retrieve_mock.reset_mock() ns1_zone = { 'records': self.ns1_records + [{ 'type': 'UNSUPPORTED', 'ttl': 42, 'short_answers': ['unsupported'], 'domain': 'unsupported.unit.tests.', }, { "domain": "geo.unit.tests", "zone": "unit.tests", "type": "A", "answers": [ { 'answer': ['1.1.1.1'], 'meta': {} }, { 'answer': ['1.2.3.4'], 'meta': { 'ca_province': ['ON'] } }, { 'answer': ['2.3.4.5'], 'meta': { 'us_state': ['NY'] } }, { 'answer': ['3.4.5.6'], 'meta': { 'country': ['US'] } }, { 'answer': ['4.5.6.7'], 'meta': { 'iso_region_code': ['NA-US-WA'] } }, ], 'ttl': 34, }], } zone_retrieve_mock.side_effect = [ns1_zone] zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(self.expected, zone.records) self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])
def test_populate(self): provider = Ns1Provider('test', 'api-key') unit_test_zone = octodns_test_zone() # General errors (bad request, forbidden, not found, internal, timeout) for code in [400, 403, 404, 500, 504]: with requests_mock() as mock: mock.get(ANY, status_code=code) with self.assertRaises(ResourceException): provider.populate(Zone('unit.tests.', [])) # Auth error with requests_mock() as mock: mock.get(ANY, status_code=401) with self.assertRaises(AuthException): provider.populate(Zone('unit.tests.', [])) # Zone not found, exception handled internally with requests_mock() as mock: mock.get(endpoint('/zones/unit.tests'), status_code=404, json={"message": "zone not found"}) provider.populate(Zone('unit.tests.', [])) # Ratelimit error with requests_mock() as mock: # First return a 429 (rate limit error) and then a 200 to check that # RateLimitExceptions are handled internally mock.get(ANY, [{ 'status_code': 429, 'json': {} }, { 'status_code': 200, 'json': {} }]) provider.populate(Zone('unit.tests.', [])) provider._reset_cache() # Existing NS1 zone with no records with requests_mock() as mock: mock.get(endpoint('/zones/unit.tests'), json={ 'zone': 'unit.tests', 'records': [], }) zone = Zone('unit.tests.', []) self.assertTrue(provider.populate(zone)) self.assertEquals(set(), zone.records) provider._reset_cache() # Test skipping unsupported record types with requests_mock() as mock: mock.get(endpoint('/zones/unit.tests'), json={ 'zone': 'unit.tests', 'records': [ { 'domain': 'unsupported.unit.tests', 'type': 'UNSUPPORTED', 'ttl': 30, 'short_answers': ['1.1.1.1'], 'tier': 1, }, ], }) zone = Zone('unit.tests.', []) self.assertTrue(provider.populate(zone)) self.assertEquals(set(), zone.records) provider._reset_cache() # Test populating full unit.tests zone with requests_mock() as mock: mock.get(endpoint('/zones/unit.tests'), json=self.ns1_zone) mock.get(endpoint('/zones/unit.tests/unit.tests/A'), json=self.ns1_geo_record) zone = Zone('unit.tests.', []) self.assertTrue(provider.populate(zone)) self.assertEquals(15, len(zone.records)) self.assertEquals(0, len(unit_test_zone.changes(zone, provider))) # Note: intentionally don't reset cache here # Test that repopulating the same zone just pulls from cache and makes # no actual calls. with requests_mock() as mock: mock.get(ANY, status_code=500) zone = Zone('unit.tests.', []) self.assertTrue(provider.populate(zone)) self.assertEquals(15, len(zone.records)) self.assertEquals(0, len(unit_test_zone.changes(zone, provider))) provider._reset_cache()
def test_apply(self): provider = Ns1Provider('test', 'api-key') unit_test_zone = octodns_test_zone() zones_mock = MagicMock(return_value=None) zones_mock.retrieve.return_value = self.ns1_zone provider._NS1Zones = zones_mock records_mock = MagicMock(return_value=None) records_mock.retrieve.return_value = self.ns1_geo_record provider._NS1Records = records_mock # Test creating the unit.tests zones from scratch. provider._zone_cache['unit.tests'] = None plan = provider.plan(unit_test_zone) provider._reset_cache() self.assertFalse(plan.exists) self.assertEquals(14, len(plan.changes)) self.assertEquals(14, provider.apply(plan)) self.assertEquals(0, zones_mock.retrieve.call_count) self.assertEquals(1, zones_mock.create.call_count) self.assertEquals(0, records_mock.retrieve.call_count) self.assertEquals(14, records_mock.create.call_count) self.assertEquals(0, records_mock.update.call_count) # Reset everything. provider._reset_cache() zones_mock.reset_mock() records_mock.reset_mock() unit_test_zone = octodns_test_zone() # Test adding a record. unit_test_zone.add_record( Record.new(unit_test_zone, 'ns1', { 'ttl': 3600, 'type': 'A', 'values': ['3.3.3.3'] })) plan = provider.plan(unit_test_zone) self.assertTrue(plan.exists) self.assertEquals(1, len(plan.changes)) self.assertEquals(1, provider.apply(plan)) self.assertEquals(1, zones_mock.retrieve.call_count) self.assertEquals(0, zones_mock.create.call_count) self.assertEquals(1, records_mock.retrieve.call_count) self.assertEquals(1, records_mock.create.call_count) self.assertEquals(0, records_mock.update.call_count) self.assertEquals(0, records_mock.delete.call_count) # Reset everything. provider._reset_cache() zones_mock.reset_mock() records_mock.reset_mock() unit_test_zone = octodns_test_zone() # Test changing a record. remove_octodns_record(unit_test_zone, '', 'A') rec = deepcopy(self.ns1_geo_record) rec['answers'].append({ "answer": ["100.100.100.100"], "meta": { "note": "octodns_region_code:NS-CA-BC", "ca_province": ["BC"] } }) args = provider._data_for('A', rec) unit_test_zone.add_record(Record.new(unit_test_zone, '', args)) plan = provider.plan(unit_test_zone) self.assertTrue(plan.exists) self.assertEquals(1, len(plan.changes)) self.assertEquals(1, provider.apply(plan)) self.assertEquals(1, zones_mock.retrieve.call_count) self.assertEquals(0, zones_mock.create.call_count) self.assertEquals(1, records_mock.retrieve.call_count) self.assertEquals(0, records_mock.create.call_count) self.assertEquals(1, records_mock.update.call_count) self.assertEquals(0, records_mock.delete.call_count) # Reset everything. provider._reset_cache() zones_mock.reset_mock() records_mock.reset_mock() unit_test_zone = octodns_test_zone() # Test removing a record. zones_mock.retrieve.return_value = self.ns1_zone records_mock.retrieve.return_value = self.ns1_geo_record remove_octodns_record(unit_test_zone, 'cname', 'CNAME') plan = provider.plan(unit_test_zone) self.assertTrue(plan.exists) self.assertEquals(1, len(plan.changes)) self.assertEquals(1, provider.apply(plan)) self.assertEquals(1, zones_mock.retrieve.call_count) self.assertEquals(0, zones_mock.create.call_count) self.assertEquals(1, records_mock.retrieve.call_count) self.assertEquals(0, records_mock.create.call_count) self.assertEquals(0, records_mock.update.call_count) self.assertEquals(1, records_mock.delete.call_count)