''' Tests the validation functions. ''' import pytest from simplezfs.exceptions import ValidationError from simplezfs.validation import ( validate_dataset_name, validate_dataset_path, validate_metadata_property_name, validate_native_property_name, validate_pool_name, validate_property_value, ) class TestPoolName: ''' Tests the function ``validate_pool_name``. ''' @pytest.mark.parametrize('name', ['a', 'aa', 'aaa', 'a' * 20, 'a123', 'a ', 'a 1 2 _ : .. ::', 'a:a']) def test_valid_name(self, name): ''' Tests a set of known good combinations. ''' validate_pool_name(name) @pytest.mark.parametrize('name', [' ', ' a', 'ä', 'aä', 'aaaaaa→', '\0', '\n', '\t']) def test_invalid_name(self, name): ''' Tests a set of known bad combinations. ''' with pytest.raises(ValidationError) as excinfo: validate_pool_name(name) assert 'malformed name' in str(excinfo.value) @pytest.mark.parametrize('name', ['mirror', 'raidz', 'spare', 'log']) def test_invalid_reserved_keyword(self, name): ''' Tests with reserved keywords. ''' with pytest.raises(ValidationError) as excinfo: validate_pool_name(name) assert 'reserved name' in str(excinfo.value) @pytest.mark.parametrize('name', ['mirrored', 'mirror:ed', 'spared', 'spare:d', 'raidzfun', 'raidz fun']) def test_invalid_begins_with_reserved_keyword(self, name): ''' Tests with strings that are known to be reserved starts of the name. ''' with pytest.raises(ValidationError) as excinfo: validate_pool_name(name) assert 'starts with invalid token' in str(excinfo.value) def test_valid_keyword_begin_extra(self): ''' Of the reserved keywords, 'log' is allowed as beginning of the pool name, check that it is allowed. ''' validate_pool_name('logger') @pytest.mark.parametrize('name', ['c0', 'c1', 'c0 ', 'c0d0', 'c0t0', 'c999', 'c9 9asd .-:']) def test_invalid_solaris_disk_names_begin(self, name): ''' Test with solaris disk names and similar names. ''' with pytest.raises(ValidationError) as excinfo: validate_pool_name(name) assert 'begins with reserved sequence' in str(excinfo.value) def test_too_short(self): ''' Tests with a name that we know is too short. ''' with pytest.raises(ValidationError) as excinfo: validate_pool_name('') assert 'too short' in str(excinfo.value) # TODO test too long class TestDatasetName: ''' Tests the function ``validate_dataset_name``. ''' @pytest.mark.parametrize('name', ['a', 'aa', '0', '0a', 'A', 'A0', 'qwertzuiop', 'a-', '-a', 'a.a', '.', 'a:', ':a', 'a:a', 'a::a', '842bf5a29bd55c12c20a8d1e73bdb5790e8ab804d857885e35e55be025acb6b2', '842bf5a29bd55c12c20a8d1e73bdb5790e8ab804d857885e35e55be025acb6b2-init', 'towel@20190525', 'page#42']) def test_valid_name(self, name): ''' Tests a set of known good combinations. ''' validate_dataset_name(name) @pytest.mark.parametrize('name', ['/a', '/', 'a/a', 'a/', 'a+', 'ä', '→', '\0', '\n', 'towel@@20190525', 'towel@#42', 'page##42', 'page#@20190525']) def test_invalid_name(self, name): ''' Tests a set of known invalid combinations. ''' with pytest.raises(ValidationError) as excinfo: validate_dataset_name(name) assert 'disallowed characters' in str(excinfo.value) @pytest.mark.parametrize('name', ['a@a', 'aasdf@1234', 'a#a', 'a#123']) def test_invalid_name_strict(self, name): ''' Tests with strict=True, which disallows snapshot or bookmark identifiers. ''' with pytest.raises(ValidationError) as excinfo: validate_dataset_name(name, strict=True) assert 'not allowed in strict' in str(excinfo.value) # TODO trigger UnicodeEncodeError! def test_invalid_name_length_short(self): ''' Tests the behaviour if the name is too short ''' with pytest.raises(ValidationError) as excinfo: validate_dataset_name('') assert 'too short' in str(excinfo.value) def test_invalid_length_long(self): ''' Providing a very long name, it should bail out. ''' with pytest.raises(ValidationError) as excinfo: validate_dataset_name('a' * 1024) assert 'length >' in str(excinfo.value) class TestDatasetPath: ''' Tests the function ``validate_dataset_path``. ''' @pytest.mark.parametrize('path', ['a/a', 'a/a/a', 'a/b/c', 'asdf/qwer/yxcv', 'a/a@a', 'a/a#a']) def test_valid_path(self, path): ''' Tests a set of known good combinations. ''' validate_dataset_path(path) @pytest.mark.parametrize('path', ['a', '/a', 'a/', '/a/', '/aaaaa/', 'a/a a', 'a@a/a', 'a/a#a/a', 'a/a@a/a#a']) def test_invalid_path(self, path): ''' Tests a set of known bad combinations. ''' with pytest.raises(ValidationError): validate_dataset_path(path) @pytest.mark.parametrize('path', ['asdf', 'asdf@yesterday', 'asdf#tomorrow']) def test_invalid_path_no_slash(self, path): ''' Tests the behaviour if no slash is found, making it a dataset name. ''' with pytest.raises(ValidationError) as excinfo: validate_dataset_path(path) assert 'Not a path' in str(excinfo.value) # TODO tests for specific errors passed from the validation functions for pool and dataset name class TestNativePropertyName: ''' Tests the function ``validate_native_property_name``. ''' @pytest.mark.parametrize('name', ['a', 'aa', 'a0', 'a0a', 'asdfghjkl']) def test_valid_name(self, name): ''' Tests a set of known good combinations. ''' validate_native_property_name(name) @pytest.mark.parametrize('name', ['0', '0a', 'A', 'AA', '-', 'a-', 'a-a', '-a', '_', 'a_', 'a_a', '_a', ':', 'a:', 'a:a', ':a', '\0']) def test_invalid_name(self, name): ''' Tests a set of known invalid combinations. ''' with pytest.raises(ValidationError) as excinfo: validate_native_property_name(name) assert 'does not match' in str(excinfo.value) # TODO trigger UnicodeEncodeError! def test_invalid_length_short(self): ''' Tests the behaviour if the name is too short. ''' with pytest.raises(ValidationError) as excinfo: validate_native_property_name('') assert 'too short' in str(excinfo.value) def test_invalid_length_long(self): ''' Provided a very long name, it should bail out. ''' with pytest.raises(ValidationError) as excinfo: validate_native_property_name('a' * 1024) assert 'length >' in str(excinfo.value) class TestMetadataPropertyName: ''' Tests the function ``validate_metadata_property_name``. ''' @pytest.mark.parametrize('name', [':a', 'a:a', 'a:0', 'a:0:a', ':a:s:d:f:g:h:j:k::l', ':-', 'a-:-a']) def test_valid_name(self, name): ''' Tests a set of known good combinations. ''' validate_metadata_property_name(name) @pytest.mark.parametrize('name', ['0', '0a', 'A', 'AA', '-', 'a-', 'a-a', '-a', '_', 'a_', 'a_a', '_a', ':', 'a:', '\0', 'a+:a']) def test_invalid_name(self, name): ''' Tests a set of known invalid combinations. ''' with pytest.raises(ValidationError) as excinfo: validate_metadata_property_name(name) assert 'does not match' in str(excinfo.value) # TODO trigger UnicodeEncodeError! def test_invalid_length_short(self): ''' Tests the behaviour if the name is too short. ''' with pytest.raises(ValidationError) as excinfo: validate_metadata_property_name('') assert 'too short' in str(excinfo.value) def test_invalid_length_long(self): ''' Provided a very long name, it should bail out. ''' with pytest.raises(ValidationError) as excinfo: validate_metadata_property_name('a' * 1024) assert 'length >' in str(excinfo.value) class TestPropertyValue: ''' Tests the function ``validate_property_value``. ''' @pytest.mark.parametrize('value', ['a', '1', '1TB', '1ZB', '99 red baloons', 'asd 123']) def test_value_valid(self, value): ''' Tests a set of known good combinations. ''' validate_property_value(value) def test_value_valid_long(self): ''' Tests with a value that is long, but still valid. ''' validate_property_value('x' * 8191) def test_value_too_long(self): ''' Tests with a value that is too long ''' with pytest.raises(ValidationError) as excinfo: validate_property_value('x' * 8192) assert 'length >' in str(excinfo.value)