# # # from unittest import TestCase from octodns.record.chunked import _ChunkedValue, _ChunkedValuesMixin from octodns.record.spf import SpfRecord from octodns.record.txt import TxtValue from octodns.zone import Zone class TestRecordChunked(TestCase): def test_chunked_value_rdata_text(self): for s in ( None, '', 'word', 42, 42.43, '1.2.3', 'some.words.that.here', '1.2.word.4', '1.2.3.4', # quotes are not removed '"Hello World!"', ): self.assertEqual(s, _ChunkedValue.parse_rdata_text(s)) # semi-colons are escaped self.assertEqual( 'Hello\\; World!', _ChunkedValue.parse_rdata_text('Hello; World!') ) # since we're always a string validate and __init__ don't # parse_rdata_text zone = Zone('unit.tests.', []) a = SpfRecord(zone, 'a', {'ttl': 42, 'value': 'some.target.'}) self.assertEqual('some.target.', a.values[0].rdata_text) class TestChunkedValue(TestCase): def test_validate(self): # valid stuff for data in ('a', 'ab', 'abcdefg', 'abc def', 'abc\\; def'): self.assertFalse(_ChunkedValue.validate(data, 'TXT')) self.assertFalse(_ChunkedValue.validate([data], 'TXT')) # missing for data in (None, []): self.assertEqual( ['missing value(s)'], _ChunkedValue.validate(data, 'TXT') ) # unescaped ; self.assertEqual( ['unescaped ; in "hello; world"'], _ChunkedValue.validate('hello; world', 'TXT'), ) # double escaped ; self.assertEqual( ['double escaped ; in "hello\\\\; world"'], _ChunkedValue.validate('hello\\\\; world', 'TXT'), ) # non-asci self.assertEqual( ['non ASCII character in "v=spf1 –all"'], _ChunkedValue.validate('v=spf1 –all', 'TXT'), ) self.assertEqual( ['non ASCII character in "Déjà vu"'], _ChunkedValue.validate('Déjà vu', 'TXT'), ) zone = Zone('unit.tests.', []) # some hacks to let us work with smaller sizes class Base: def __init__(self, *args, **kwargs): pass class SmallerChunkedMixin(_ChunkedValuesMixin, Base): CHUNK_SIZE = 8 _type = 'TXT' _value_type = TxtValue def __init__(self, values): super().__init__(None, None, {'values': values}) def test_splitting(self): for value, expected in ( # shorter ('0123', '"0123"'), # exact ('01234567', '"01234567"'), # simple ('0123456789', '"01234567" "89"'), # 1 extra ('012345678', '"01234567" "8"'), # escape in the middle ('01234\\;56789', '"01234\\;5" "6789"'), # escape before the boundary ('012345\\;6789', '"012345\\;" "6789"'), # escape after the boundary ('01234567\\;89', '"01234567" "\\;89"'), # escape spanning the boundary ('0123456\\;789', '"0123456" "\\;789"'), # multiple escapes at the boundary ('012345\\\\;6789', '"012345" "\\\\;6789"'), # exact size escape ('012345\\;', '"012345\\;"'), # spanning ending ('0123456\\;', '"0123456" "\\;"'), ): sc = self.SmallerChunkedMixin(value) self.assertEqual([expected], sc.chunked_values) sc = self.SmallerChunkedMixin(['0123456789']) self.assertEqual(['"01234567" "89"'], sc.chunked_values) def test_template(self): s = 'this.has.no.templating.' value = _ChunkedValue(s) got = value.template({'needle': 42}) self.assertIs(value, got) s = 'this.does.{needle}.have.templating.' value = _ChunkedValue(s) got = value.template({'needle': 42}) self.assertIsNot(value, got) self.assertEqual('this.does.42.have.templating.', got)