Skip to content

Commit

Permalink
Add lots of MetadataFile tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hkbakke committed Jan 19, 2017
1 parent 0320e13 commit 119dbbb
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 34 deletions.
94 changes: 74 additions & 20 deletions src/zfssnap.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def autotype(value):

return value


class MetadataFileException(Exception):
pass

class ReplicationException(Exception):
pass

Expand All @@ -61,12 +61,12 @@ class MetadataFile(object):
def __init__(self, path):
self.path = path
self.logger = logging.getLogger(__name__)
self.version = None
self._version = None
self._timestamp = None
self.label = None
self._label = None
self._snapshot = None
self._depends_on = None
self.segments = []
self._segments = []

@staticmethod
def _get_checksum(metadata):
Expand Down Expand Up @@ -108,32 +108,86 @@ def write(self):
metadata['depends_on'] = self.depends_on
metadata['segments'] = self.segments
metadata['checksum'] = self._get_checksum(metadata)

for key, value in metadata.items():
if key == 'depends_on':
continue
if not value:
raise MetadataFileException('\'%s\' attribute is not set' % key)

self._write_file(metadata)

def _validate_snapshot_name(self, name):
@staticmethod
def _validate_snapshot_name(name):
pattern = r'^zfssnap_[0-9]{8}T[0-9]{6}Z$'
if re.match(pattern, name):
return name
raise MetadataFileException('Invalid snapshot name %s' % name)

if not re.match(pattern, name):
raise MetadataFileException('Invalid snapshot name \'%s\'' % name)
return name

@property
def label(self):
return self._label

@label.setter
def label(self, label):
if not label:
raise MetadataFileException('empty label value')
if not isinstance(label, str):
raise MetadataFileException('label must be a str object')
self._label = label

@property
def version(self):
return self._version

@version.setter
def version(self, version):
if not version:
raise MetadataFileException('empty version value')
if not isinstance(version, str):
raise MetadataFileException('version must be a str object')
self._version = version

@property
def segments(self):
return self._segments

@segments.setter
def segments(self, segments):
if not segments:
raise MetadataFileException('empty segment list')
if not isinstance(segments, list):
raise MetadataFileException('segments must be a list object')
self._segments = segments

@property
def snapshot(self):
return self._snapshot

@snapshot.setter
def snapshot(self, name):
return



self._snapshot = self._validate_snapshot_name(name)

@property
def depends_on(self):
return self._depends_on


@depends_on.setter
def depends_on(self, name):
self._depends_on = self._validate_snapshot_name(name)

@property
def timestamp(self):
pattern = r'^.+@zfssnap_[0-9]{8}T[0-9]{6}Z$'
@property
return self._timestamp

@timestamp.setter
def timestamp(self, timestamp):
pattern = r'^[0-9]{8}T[0-9]{6}Z$'
if not re.match(pattern, timestamp):
raise MetadataFileException('Invalid timestamp \'%s\'' % timestamp)
self._timestamp = timestamp

@property
def datetime(self):
strptime_name = re.sub(r'Z$', '+0000', self.timestamp)
return datetime.strptime(strptime_name, '%Y%m%dT%H%M%S%z')
Expand Down Expand Up @@ -411,9 +465,9 @@ def _get_split_cmd(self, prefix, split_size, suffix_length):
]
return self.host.get_cmd('split', split_args)

def _get_segment_name(self, line, segments_log_re):
@staticmethod
def _get_segment_name(line, segments_log_re):
match = re.match(segments_log_re, line)

if match:
segment = match.group(1)
return os.path.basename(segment)
Expand Down
154 changes: 140 additions & 14 deletions tests/test_metadatafile.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,119 @@
import pytest
import datetime

from zfssnap import MetadataFile, ZFSSnapException
from zfssnap import MetadataFile, MetadataFileException

class TestMetadataFile(object):
@pytest.fixture
def metadata(self):
return MetadataFile('/tmp/test/abc_20170116T160746Z.json')

@pytest.fixture
def metadata_attr_tests(self, monkeypatch):
metadata = MetadataFile('/tmp/test/abc_20170116T160746Z.json')

def mock_write_metadata():
return True

monkeypatch.setattr(metadata, '_write_file', mock_write_metadata)
metadata.segments = [
'abc_20170116T160746Z-aaa',
'abc_20170116T160746Z-aab',
'abc_20170116T160746Z-aac',
'abc_20170116T160746Z-aad'
]
metadata.timestamp = '20170116T160746Z'
metadata.snapshot = 'zfssnap_20170116T160746Z'
metadata.depends_on = 'zfssnap_20170116T073154Z'
metadata.label = 'replicate-disconnected'
metadata.version = '3.5.3'
return metadata

def test_timestamp_property(self, metadata):
timestamp = '20170116T160746Z'
metadata.timestamp = timestamp
assert metadata.timestamp == timestamp

def test_snapshot_property(self, metadata):
snapshot = 'zfssnap_20170116T160746Z'
metadata.snapshot = snapshot
assert metadata.snapshot == snapshot

def test_depends_on_property(self, metadata):
snapshot = 'zfssnap_20170116T073154Z'
metadata.depends_on = snapshot
assert metadata.depends_on == snapshot

def test_version_property(self, metadata):
version = '3.5.3'
metadata.version = version
assert metadata.version == version

def test_label_property(self, metadata):
label = 'test'
metadata.label = label
assert metadata.label == label

def test_segments_property(self, metadata):
segments = [
'abc_20170116T160746Z-aaa',
'abc_20170116T160746Z-aab',
'abc_20170116T160746Z-aac',
'abc_20170116T160746Z-aad'
]
metadata.segments = segments
assert metadata.segments == segments

def test_metadata_datetime(self, metadata):
metadata.timestamp = '20170116T160746Z'
assert metadata.datetime == datetime.datetime(
2017, 1, 16, 16, 7, 46, tzinfo=datetime.timezone.utc)

def test_invalid_timestamp(self, metadata):
with pytest.raises(MetadataFileException):
# Replace Z with Y
metadata.timestamp = '20170116T160746Y'

def test_invalid_version(self, metadata):
with pytest.raises(MetadataFileException):
metadata.version = 1

def test_empty_version(self, metadata):
with pytest.raises(MetadataFileException):
metadata.version = None

def test_invalid_label(self, metadata):
with pytest.raises(MetadataFileException):
metadata.label = 1

def test_empty_label(self, metadata):
with pytest.raises(MetadataFileException):
metadata.label = None

def test_invalid_segments(self, metadata):
with pytest.raises(MetadataFileException):
metadata.segments = {}

def test_empty_segments(self, metadata):
with pytest.raises(MetadataFileException):
metadata.segments = []

def test_invalid_snapshot_name(self, metadata):
with pytest.raises(MetadataFileException):
metadata.snapshot = 'invalid@snapshot'

def test_invalid_depends_on_name(self, metadata):
with pytest.raises(MetadataFileException):
metadata.depends_on = 'invalid@snapshot'

def test_validate_snapshot_invalid_name(self, metadata):
with pytest.raises(MetadataFileException):
metadata._validate_snapshot_name('invalid_20170116T073154Z')

def test_validate_snapshot_name(self, metadata):
assert metadata._validate_snapshot_name(
'zfssnap_20170116T073154Z') == 'zfssnap_20170116T073154Z'

def test_get_metadata_checksum(self):
metadata = {
'segments': [
Expand All @@ -20,10 +130,6 @@ def test_get_metadata_checksum(self):
}
assert MetadataFile._get_checksum(metadata) == '79d539585d6e062f9592f475f8988f44'

@pytest.fixture
def metadata(self, monkeypatch):
return MetadataFile('/tmp/test/abc_20170116T160746Z.json')

def test_read_attrs(self, monkeypatch, metadata):
def mock_read_file():
return {
Expand Down Expand Up @@ -74,11 +180,11 @@ def mock_read_file_corrupted():
}

monkeypatch.setattr(metadata, '_read_file', mock_read_file_corrupted)
with pytest.raises(ZFSSnapException):
with pytest.raises(MetadataFileException):
metadata.read()

def test_read_invalid_checksum_attr(self, monkeypatch, metadata):
def mock_invalid_checksum():
def mock_invalid_file():
# Replace 44 with 33 at end of checksum to invalidate it
return {
'checksum': '79d539585d6e062f9592f475f8988f33',
Expand All @@ -95,8 +201,8 @@ def mock_invalid_checksum():
'version': '3.5.3'
}

monkeypatch.setattr(metadata, '_read_file', mock_invalid_checksum)
with pytest.raises(ZFSSnapException):
monkeypatch.setattr(metadata, '_read_file', mock_invalid_file)
with pytest.raises(MetadataFileException):
metadata.read()

def test_write_file(self, monkeypatch, metadata):
Expand Down Expand Up @@ -127,8 +233,28 @@ def mock_write_metadata(metadata):
metadata.label = 'replicate-disconnected'
metadata.version = '3.5.3'
metadata.write()

def test_metadata_datetime(self, metadata):
metadata.timestamp = '20170116T160746Z'
assert metadata.datetime == datetime.datetime(
2017, 1, 16, 16, 7, 46, tzinfo=datetime.timezone.utc)

def test_missing_version_on_write(self, monkeypatch, metadata_attr_tests):
monkeypatch.setattr(metadata_attr_tests, '_version', None)
with pytest.raises(MetadataFileException):
metadata_attr_tests.write()

def test_missing_timestamp_on_write(self, monkeypatch, metadata_attr_tests):
monkeypatch.setattr(metadata_attr_tests, '_timestamp', None)
with pytest.raises(MetadataFileException):
metadata_attr_tests.write()

def test_missing_snapshot_on_write(self, monkeypatch, metadata_attr_tests):
monkeypatch.setattr(metadata_attr_tests, '_snapshot', None)
with pytest.raises(MetadataFileException):
metadata_attr_tests.write()

def test_missing_segments_on_write(self, monkeypatch, metadata_attr_tests):
monkeypatch.setattr(metadata_attr_tests, '_segments', [])
with pytest.raises(MetadataFileException):
metadata_attr_tests.write()

def test_missing_label_on_write(self, monkeypatch, metadata_attr_tests):
monkeypatch.setattr(metadata_attr_tests, '_label', None)
with pytest.raises(MetadataFileException):
metadata_attr_tests.write()

0 comments on commit 119dbbb

Please sign in to comment.