Browse Source

Add support for LOC records

pull/635/head
Mark Tearle 5 years ago
parent
commit
9d4bd0aaec
15 changed files with 730 additions and 26 deletions
  1. +1
    -0
      docs/records.md
  2. +1
    -1
      octodns/provider/yaml.py
  3. +190
    -0
      octodns/record/__init__.py
  4. +28
    -0
      tests/config/unit.tests.yaml
  5. +7
    -7
      tests/test_octodns_manager.py
  6. +1
    -1
      tests/test_octodns_provider_constellix.py
  7. +1
    -1
      tests/test_octodns_provider_digitalocean.py
  8. +2
    -2
      tests/test_octodns_provider_dnsimple.py
  9. +1
    -1
      tests/test_octodns_provider_dnsmadeeasy.py
  10. +1
    -1
      tests/test_octodns_provider_easydns.py
  11. +2
    -2
      tests/test_octodns_provider_gandi.py
  12. +2
    -2
      tests/test_octodns_provider_powerdns.py
  13. +5
    -4
      tests/test_octodns_provider_yaml.py
  14. +484
    -4
      tests/test_octodns_record.py
  15. +4
    -0
      tests/zones/unit.tests.tst

+ 1
- 0
docs/records.md View File

@ -10,6 +10,7 @@ OctoDNS supports the following record types:
* `CAA`
* `CNAME`
* `DNAME`
* `LOC`
* `MX`
* `NAPTR`
* `NS`


+ 1
- 1
octodns/provider/yaml.py View File

@ -104,7 +104,7 @@ class YamlProvider(BaseProvider):
'''
SUPPORTS_GEO = True
SUPPORTS_DYNAMIC = True
SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'DNAME', 'MX',
SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'DNAME', 'LOC', 'MX',
'NAPTR', 'NS', 'PTR', 'SSHFP', 'SPF', 'SRV', 'TXT'))
def __init__(self, id, directory, default_ttl=3600, enforce_order=True,


+ 190
- 0
octodns/record/__init__.py View File

@ -97,6 +97,7 @@ class Record(EqualityTupleMixin):
'CAA': CaaRecord,
'CNAME': CnameRecord,
'DNAME': DnameRecord,
'LOC': LocRecord,
'MX': MxRecord,
'NAPTR': NaptrRecord,
'NS': NsRecord,
@ -879,6 +880,195 @@ class DnameRecord(_DynamicMixin, _ValueMixin, Record):
_value_type = DnameValue
class LocValue(EqualityTupleMixin):
# TODO: work out how to do defaults per RFC
@classmethod
def validate(cls, data, _type):
int_keys = [
'lat_degrees',
'lat_minutes',
'long_degrees',
'long_minutes',
]
float_keys = [
'lat_seconds',
'long_seconds',
'altitude',
'size',
'precision_horz',
'precision_vert',
]
direction_keys = [
'lat_direction',
'long_direction',
]
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
for key in int_keys:
try:
int(value[key])
if (
(
key == 'lat_degrees' and
not 0 <= int(value[key]) <= 90
) or (
key == 'long_degrees' and
not 0 <= int(value[key]) <= 180
) or (
key in ['lat_minutes', 'long_minutes'] and
not 0 <= int(value[key]) <= 59
)
):
reasons.append('invalid value for {} "{}"'
.format(key, value[key]))
except KeyError:
reasons.append('missing {}'.format(key))
except ValueError:
reasons.append('invalid {} "{}"'
.format(key, value[key]))
for key in float_keys:
try:
float(value[key])
if (
(
key in ['lat_seconds', 'long_seconds'] and
not 0 <= float(value[key]) <= 59.999
) or (
key == 'altitude' and
not -100000.00 <= float(value[key]) <= 42849672.95
) or (
key in ['size',
'precision_horz',
'precision_vert'] and
not 0 <= float(value[key]) <= 90000000.00
)
):
reasons.append('invalid value for {} "{}"'
.format(key, value[key]))
except KeyError:
reasons.append('missing {}'.format(key))
except ValueError:
reasons.append('invalid {} "{}"'
.format(key, value[key]))
for key in direction_keys:
try:
str(value[key])
if (
key == 'lat_direction' and
value[key] not in ['N', 'S']
):
reasons.append('invalid direction for {} "{}"'
.format(key, value[key]))
if (
key == 'long_direction' and
value[key] not in ['E', 'W']
):
reasons.append('invalid direction for {} "{}"'
.format(key, value[key]))
except KeyError:
reasons.append('missing {}'.format(key))
return reasons
@classmethod
def process(cls, values):
return [LocValue(v) for v in values]
def __init__(self, value):
self.lat_degrees = int(value['lat_degrees'])
self.lat_minutes = int(value['lat_minutes'])
self.lat_seconds = float(value['lat_seconds'])
self.lat_direction = value['lat_direction'].upper()
self.long_degrees = int(value['long_degrees'])
self.long_minutes = int(value['long_minutes'])
self.long_seconds = float(value['long_seconds'])
self.long_direction = value['long_direction'].upper()
self.altitude = float(value['altitude'])
self.size = float(value['size'])
self.precision_horz = float(value['precision_horz'])
self.precision_vert = float(value['precision_vert'])
@property
def data(self):
return {
'lat_degrees': self.lat_degrees,
'lat_minutes': self.lat_minutes,
'lat_seconds': self.lat_seconds,
'lat_direction': self.lat_direction,
'long_degrees': self.long_degrees,
'long_minutes': self.long_minutes,
'long_seconds': self.long_seconds,
'long_direction': self.long_direction,
'altitude': self.altitude,
'size': self.size,
'precision_horz': self.precision_horz,
'precision_vert': self.precision_vert,
}
def __hash__(self):
return hash((
self.lat_degrees,
self.lat_minutes,
self.lat_seconds,
self.lat_direction,
self.long_degrees,
self.long_minutes,
self.long_seconds,
self.long_direction,
self.altitude,
self.size,
self.precision_horz,
self.precision_vert,
))
def _equality_tuple(self):
return (
self.lat_degrees,
self.lat_minutes,
self.lat_seconds,
self.lat_direction,
self.long_degrees,
self.long_minutes,
self.long_seconds,
self.long_direction,
self.altitude,
self.size,
self.precision_horz,
self.precision_vert,
)
def __repr__(self):
loc_format = "'{0} {1} {2:.3f} {3} " + \
"{4} {5} {6:.3f} {7} " + \
"{8:.2f}m {9:.2f}m {10:.2f}m {11:.2f}m'"
return loc_format.format(
self.lat_degrees,
self.lat_minutes,
self.lat_seconds,
self.lat_direction,
self.long_degrees,
self.long_minutes,
self.long_seconds,
self.long_direction,
self.altitude,
self.size,
self.precision_horz,
self.precision_vert,
)
class LocRecord(_ValuesMixin, Record):
_type = 'LOC'
_value_type = LocValue
class MxValue(EqualityTupleMixin):
@classmethod


+ 28
- 0
tests/config/unit.tests.yaml View File

@ -77,6 +77,34 @@ included:
- test
type: CNAME
value: unit.tests.
loc:
ttl: 300
type: LOC
values:
- altitude: 20
lat_degrees: 31
lat_direction: S
lat_minutes: 58
lat_seconds: 52.1
long_degrees: 115
long_direction: E
long_minutes: 49
long_seconds: 11.7
precision_horz: 10
precision_vert: 2
size: 10
- altitude: 20
lat_degrees: 53
lat_direction: N
lat_minutes: 13
lat_seconds: 10
long_degrees: 2
long_direction: W
long_minutes: 18
long_seconds: 26
precision_horz: 1000
precision_vert: 2
size: 10
mx:
ttl: 300
type: MX


+ 7
- 7
tests/test_octodns_manager.py View File

@ -118,12 +118,12 @@ class TestManager(TestCase):
environ['YAML_TMP_DIR'] = tmpdir.dirname
tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False)
self.assertEquals(22, tc)
self.assertEquals(23, tc)
# try with just one of the zones
tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False, eligible_zones=['unit.tests.'])
self.assertEquals(16, tc)
self.assertEquals(17, tc)
# the subzone, with 2 targets
tc = Manager(get_config_filename('simple.yaml')) \
@ -138,18 +138,18 @@ class TestManager(TestCase):
# Again with force
tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False, force=True)
self.assertEquals(22, tc)
self.assertEquals(23, tc)
# Again with max_workers = 1
tc = Manager(get_config_filename('simple.yaml'), max_workers=1) \
.sync(dry_run=False, force=True)
self.assertEquals(22, tc)
self.assertEquals(23, tc)
# Include meta
tc = Manager(get_config_filename('simple.yaml'), max_workers=1,
include_meta=True) \
.sync(dry_run=False, force=True)
self.assertEquals(26, tc)
self.assertEquals(27, tc)
def test_eligible_sources(self):
with TemporaryDirectory() as tmpdir:
@ -215,13 +215,13 @@ class TestManager(TestCase):
fh.write('---\n{}')
changes = manager.compare(['in'], ['dump'], 'unit.tests.')
self.assertEquals(16, len(changes))
self.assertEquals(17, len(changes))
# Compound sources with varying support
changes = manager.compare(['in', 'nosshfp'],
['dump'],
'unit.tests.')
self.assertEquals(15, len(changes))
self.assertEquals(16, len(changes))
with self.assertRaises(ManagerException) as ctx:
manager.compare(['nope'], ['dump'], 'unit.tests.')


+ 1
- 1
tests/test_octodns_provider_constellix.py View File

@ -132,7 +132,7 @@ class TestConstellixProvider(TestCase):
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded, no unsupported
n = len(self.expected.records) - 6
n = len(self.expected.records) - 7
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))


+ 1
- 1
tests/test_octodns_provider_digitalocean.py View File

@ -163,7 +163,7 @@ class TestDigitalOceanProvider(TestCase):
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded, no unsupported
n = len(self.expected.records) - 8
n = len(self.expected.records) - 9
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))
self.assertFalse(plan.exists)


+ 2
- 2
tests/test_octodns_provider_dnsimple.py View File

@ -136,8 +136,8 @@ class TestDnsimpleProvider(TestCase):
]
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded
n = len(self.expected.records) - 4
# No root NS, no ignored, no excluded, no unsupported
n = len(self.expected.records) - 5
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))
self.assertFalse(plan.exists)


+ 1
- 1
tests/test_octodns_provider_dnsmadeeasy.py View File

@ -134,7 +134,7 @@ class TestDnsMadeEasyProvider(TestCase):
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded, no unsupported
n = len(self.expected.records) - 6
n = len(self.expected.records) - 7
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))


+ 1
- 1
tests/test_octodns_provider_easydns.py View File

@ -374,7 +374,7 @@ class TestEasyDNSProvider(TestCase):
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded, no unsupported
n = len(self.expected.records) - 7
n = len(self.expected.records) - 8
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))
self.assertFalse(plan.exists)


+ 2
- 2
tests/test_octodns_provider_gandi.py View File

@ -192,8 +192,8 @@ class TestGandiProvider(TestCase):
]
plan = provider.plan(self.expected)
# No root NS, no ignored, no excluded
n = len(self.expected.records) - 4
# No root NS, no ignored, no excluded, no LOC
n = len(self.expected.records) - 5
self.assertEquals(n, len(plan.changes))
self.assertEquals(n, provider.apply(plan))
self.assertFalse(plan.exists)


+ 2
- 2
tests/test_octodns_provider_powerdns.py View File

@ -185,7 +185,7 @@ class TestPowerDnsProvider(TestCase):
expected = Zone('unit.tests.', [])
source = YamlProvider('test', join(dirname(__file__), 'config'))
source.populate(expected)
expected_n = len(expected.records) - 3
expected_n = len(expected.records) - 4
self.assertEquals(16, expected_n)
# No diffs == no changes
@ -291,7 +291,7 @@ class TestPowerDnsProvider(TestCase):
expected = Zone('unit.tests.', [])
source = YamlProvider('test', join(dirname(__file__), 'config'))
source.populate(expected)
self.assertEquals(19, len(expected.records))
self.assertEquals(20, len(expected.records))
# A small change to a single record
with requests_mock() as mock:


+ 5
- 4
tests/test_octodns_provider_yaml.py View File

@ -35,7 +35,7 @@ class TestYamlProvider(TestCase):
# without it we see everything
source.populate(zone)
self.assertEquals(19, len(zone.records))
self.assertEquals(20, len(zone.records))
source.populate(dynamic_zone)
self.assertEquals(5, len(dynamic_zone.records))
@ -58,12 +58,12 @@ class TestYamlProvider(TestCase):
# We add everything
plan = target.plan(zone)
self.assertEquals(16, len([c for c in plan.changes
self.assertEquals(17, len([c for c in plan.changes
if isinstance(c, Create)]))
self.assertFalse(isfile(yaml_file))
# Now actually do it
self.assertEquals(16, target.apply(plan))
self.assertEquals(17, target.apply(plan))
self.assertTrue(isfile(yaml_file))
# Dynamic plan
@ -87,7 +87,7 @@ class TestYamlProvider(TestCase):
# A 2nd sync should still create everything
plan = target.plan(zone)
self.assertEquals(16, len([c for c in plan.changes
self.assertEquals(17, len([c for c in plan.changes
if isinstance(c, Create)]))
with open(yaml_file) as fh:
@ -106,6 +106,7 @@ class TestYamlProvider(TestCase):
self.assertTrue('values' in data.pop('naptr'))
self.assertTrue('values' in data.pop('sub'))
self.assertTrue('values' in data.pop('txt'))
self.assertTrue('values' in data.pop('loc'))
# these are stored as singular 'value'
self.assertTrue('value' in data.pop('aaaa'))
self.assertTrue('value' in data.pop('cname'))


+ 484
- 4
tests/test_octodns_record.py View File

@ -9,10 +9,11 @@ from six import text_type
from unittest import TestCase
from octodns.record import ARecord, AaaaRecord, AliasRecord, CaaRecord, \
CaaValue, CnameRecord, DnameRecord, Create, Delete, GeoValue, MxRecord, \
MxValue, NaptrRecord, NaptrValue, NsRecord, PtrRecord, Record, \
SshfpRecord, SshfpValue, SpfRecord, SrvRecord, SrvValue, TxtRecord, \
Update, ValidationError, _Dynamic, _DynamicPool, _DynamicRule
CaaValue, CnameRecord, DnameRecord, Create, Delete, GeoValue, LocRecord, \
LocValue, MxRecord, MxValue, NaptrRecord, NaptrValue, NsRecord, \
PtrRecord, Record, SshfpRecord, SshfpValue, SpfRecord, SrvRecord, \
SrvValue, TxtRecord, Update, ValidationError, _Dynamic, _DynamicPool, \
_DynamicRule
from octodns.zone import Zone
from helpers import DynamicProvider, GeoProvider, SimpleProvider
@ -379,6 +380,98 @@ class TestRecord(TestCase):
self.assertSingleValue(DnameRecord, 'target.foo.com.',
'other.foo.com.')
def test_loc(self):
a_values = [{
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}]
a_data = {'ttl': 30, 'values': a_values}
a = LocRecord(self.zone, 'a', a_data)
self.assertEquals('a', a.name)
self.assertEquals('a.unit.tests.', a.fqdn)
self.assertEquals(30, a.ttl)
self.assertEquals(a_values[0]['lat_degrees'], a.values[0].lat_degrees)
self.assertEquals(a_values[0]['lat_minutes'], a.values[0].lat_minutes)
self.assertEquals(a_values[0]['lat_seconds'], a.values[0].lat_seconds)
self.assertEquals(a_values[0]['lat_direction'],
a.values[0].lat_direction)
self.assertEquals(a_values[0]['long_degrees'],
a.values[0].long_degrees)
self.assertEquals(a_values[0]['long_minutes'],
a.values[0].long_minutes)
self.assertEquals(a_values[0]['long_seconds'],
a.values[0].long_seconds)
self.assertEquals(a_values[0]['long_direction'],
a.values[0].long_direction)
self.assertEquals(a_values[0]['altitude'], a.values[0].altitude)
self.assertEquals(a_values[0]['size'], a.values[0].size)
self.assertEquals(a_values[0]['precision_horz'],
a.values[0].precision_horz)
self.assertEquals(a_values[0]['precision_vert'],
a.values[0].precision_vert)
b_value = {
'lat_degrees': 32,
'lat_minutes': 7,
'lat_seconds': 19,
'lat_direction': 'S',
'long_degrees': 116,
'long_minutes': 2,
'long_seconds': 25,
'long_direction': 'E',
'altitude': 10,
'size': 1,
'precision_horz': 10000,
'precision_vert': 10,
}
b_data = {'ttl': 30, 'value': b_value}
b = LocRecord(self.zone, 'b', b_data)
self.assertEquals(b_value['lat_degrees'], b.values[0].lat_degrees)
self.assertEquals(b_value['lat_minutes'], b.values[0].lat_minutes)
self.assertEquals(b_value['lat_seconds'], b.values[0].lat_seconds)
self.assertEquals(b_value['lat_direction'], b.values[0].lat_direction)
self.assertEquals(b_value['long_degrees'], b.values[0].long_degrees)
self.assertEquals(b_value['long_minutes'], b.values[0].long_minutes)
self.assertEquals(b_value['long_seconds'], b.values[0].long_seconds)
self.assertEquals(b_value['long_direction'],
b.values[0].long_direction)
self.assertEquals(b_value['altitude'], b.values[0].altitude)
self.assertEquals(b_value['size'], b.values[0].size)
self.assertEquals(b_value['precision_horz'],
b.values[0].precision_horz)
self.assertEquals(b_value['precision_vert'],
b.values[0].precision_vert)
self.assertEquals(b_data, b.data)
target = SimpleProvider()
# No changes with self
self.assertFalse(a.changes(a, target))
# Diff in lat_direction causes change
other = LocRecord(self.zone, 'a', {'ttl': 30, 'values': a_values})
other.values[0].lat_direction = 'N'
change = a.changes(other, target)
self.assertEqual(change.existing, a)
self.assertEqual(change.new, other)
# Diff in altitude causes change
other.values[0].altitude = a.values[0].altitude
other.values[0].altitude = -10
change = a.changes(other, target)
self.assertEqual(change.existing, a)
self.assertEqual(change.new, other)
# __repr__ doesn't blow up
a.__repr__()
def test_mx(self):
a_values = [{
'preference': 10,
@ -1127,6 +1220,93 @@ class TestRecord(TestCase):
self.assertTrue(d >= d)
self.assertTrue(d <= d)
def test_loc_value(self):
a = LocValue({
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
})
b = LocValue({
'lat_degrees': 32,
'lat_minutes': 7,
'lat_seconds': 19,
'lat_direction': 'S',
'long_degrees': 116,
'long_minutes': 2,
'long_seconds': 25,
'long_direction': 'E',
'altitude': 10,
'size': 1,
'precision_horz': 10000,
'precision_vert': 10,
})
c = LocValue({
'lat_degrees': 53,
'lat_minutes': 14,
'lat_seconds': 10,
'lat_direction': 'N',
'long_degrees': 2,
'long_minutes': 18,
'long_seconds': 26,
'long_direction': 'W',
'altitude': 10,
'size': 1,
'precision_horz': 1000,
'precision_vert': 10,
})
self.assertEqual(a, a)
self.assertEqual(b, b)
self.assertEqual(c, c)
self.assertNotEqual(a, b)
self.assertNotEqual(a, c)
self.assertNotEqual(b, a)
self.assertNotEqual(b, c)
self.assertNotEqual(c, a)
self.assertNotEqual(c, b)
self.assertTrue(a < b)
self.assertTrue(a < c)
self.assertTrue(b > a)
self.assertTrue(b < c)
self.assertTrue(c > a)
self.assertTrue(c > b)
self.assertTrue(a <= b)
self.assertTrue(a <= c)
self.assertTrue(a <= a)
self.assertTrue(a >= a)
self.assertTrue(b >= a)
self.assertTrue(b <= c)
self.assertTrue(b >= b)
self.assertTrue(b <= b)
self.assertTrue(c >= a)
self.assertTrue(c >= b)
self.assertTrue(c >= c)
self.assertTrue(c <= c)
# Hash
values = set()
values.add(a)
self.assertTrue(a in values)
self.assertFalse(b in values)
values.add(b)
self.assertTrue(b in values)
def test_mx_value(self):
a = MxValue({'preference': 0, 'priority': 'a', 'exchange': 'v',
'value': '1'})
@ -1960,6 +2140,306 @@ class TestRecordValidation(TestCase):
self.assertEquals(['DNAME value "foo.bar.com" missing trailing .'],
ctx.exception.reasons)
def test_LOC(self):
# doesn't blow up
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
# missing int key
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['missing lat_degrees'], ctx.exception.reasons)
# missing float key
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['missing lat_seconds'], ctx.exception.reasons)
# missing text key
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['missing lat_direction'], ctx.exception.reasons)
# invalid direction
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'U',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid direction for lat_direction "U"'],
ctx.exception.reasons)
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'N',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid direction for long_direction "N"'],
ctx.exception.reasons)
# invalid degrees
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 360,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid value for lat_degrees "360"'],
ctx.exception.reasons)
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 'nope',
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid lat_degrees "nope"'],
ctx.exception.reasons)
# invalid minutes
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 60,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid value for lat_minutes "60"'],
ctx.exception.reasons)
# invalid seconds
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 60,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid value for lat_seconds "60"'],
ctx.exception.reasons)
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 'nope',
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid lat_seconds "nope"'],
ctx.exception.reasons)
# invalid altitude
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': -666666,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid value for altitude "-666666"'],
ctx.exception.reasons)
# invalid size
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'LOC',
'ttl': 600,
'value': {
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 99999999.99,
'precision_horz': 10,
'precision_vert': 2,
}
})
self.assertEquals(['invalid value for size "99999999.99"'],
ctx.exception.reasons)
def test_MX(self):
# doesn't blow up
Record.new(self.zone, '', {


+ 4
- 0
tests/zones/unit.tests.tst View File

@ -32,6 +32,10 @@ mx 300 IN MX 20 smtp-2.unit.tests.
mx 300 IN MX 30 smtp-3.unit.tests.
mx 300 IN MX 40 smtp-1.unit.tests.
; LOC Records
loc 300 IN LOC 31 58 52.1 S 115 49 11.7 E 20m 10m 10m 2m
loc 300 IN LOC 53 14 10 N 2 18 26 W 20m 10m 1000m 2m
; A Records
@ 300 IN A 1.2.3.4
@ 300 IN A 1.2.3.5


Loading…
Cancel
Save