|
12 | 12 | import unittest
|
13 | 13 | import copy
|
14 | 14 |
|
15 |
| -from typing import Dict, Callable |
| 15 | +from typing import Dict, Callable, Optional, Mapping, Any |
| 16 | +from datetime import datetime |
16 | 17 |
|
17 | 18 | from tests import utils
|
18 | 19 |
|
19 | 20 | from tuf.api.metadata import (
|
| 21 | + Signed, |
20 | 22 | Root,
|
21 | 23 | Snapshot,
|
22 | 24 | Timestamp,
|
@@ -50,18 +52,89 @@ def wrapper(test_cls: "TestSerialization"):
|
50 | 52 |
|
51 | 53 | class TestSerialization(unittest.TestCase):
|
52 | 54 |
|
| 55 | + # Snapshot instances with meta = {} are valid, but for a full valid |
| 56 | + # repository it's required that meta has at least one element inside it. |
| 57 | + invalid_signed: DataSet = { |
| 58 | + "no _type": '{"spec_version": "1.0.0", "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 59 | + "no spec_version": '{"_type": "signed", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 60 | + "no version": '{"_type": "signed", "spec_version": "1.0.0", "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 61 | + "no expires": '{"_type": "signed", "spec_version": "1.0.0", "version": 1, "meta": {}}', |
| 62 | + "empty str _type": |
| 63 | + '{"_type": "", "spec_version": "1.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 64 | + "empty str spec_version": |
| 65 | + '{"_type": "signed", "spec_version": "", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 66 | + "_type wrong type": |
| 67 | + '{"_type": "foo", "spec_version": "1.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 68 | + "version wrong type": |
| 69 | + '{"_type": "signed", "spec_version": "1.0.0", "version": "a", "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 70 | + "invalid spec_version str": |
| 71 | + '{"_type": "signed", "spec_version": "abc", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 72 | + "two digit spec_version": |
| 73 | + '{"_type": "signed", "spec_version": "1.2.a", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 74 | + "no digit spec_version": |
| 75 | + '{"_type": "signed", "spec_version": "a.b.c", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 76 | + "different major spec_version": |
| 77 | + '{"_type": "signed", "spec_version": "0.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 78 | + "version 0": |
| 79 | + '{"_type": "signed", "spec_version": "1.0.0", "version": 0, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 80 | + "version below 0": |
| 81 | + '{"_type": "signed", "spec_version": "1.0.0", "version": -1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', |
| 82 | + "wrong datetime string": |
| 83 | + '{"_type": "signed", "spec_version": "1.0.0", "version": 1, "expires": "abc", "meta": {}}', |
| 84 | + } |
| 85 | + |
| 86 | + @run_sub_tests_with_dataset(invalid_signed) |
| 87 | + def test_invalid_signed_serialization(self, test_case_data: Dict[str, str]): |
| 88 | + case_dict = json.loads(test_case_data) |
| 89 | + with self.assertRaises((KeyError, ValueError, TypeError)): |
| 90 | + Snapshot.from_dict(copy.deepcopy(case_dict)) |
| 91 | + |
| 92 | + |
53 | 93 | valid_keys: DataSet = {
|
54 | 94 | "all": '{"keytype": "rsa", "scheme": "rsassa-pss-sha256", \
|
55 | 95 | "keyval": {"public": "foo"}}',
|
56 | 96 | }
|
57 | 97 |
|
58 | 98 | @run_sub_tests_with_dataset(valid_keys)
|
59 |
| - def test_key_serialization(self, test_case_data: str): |
| 99 | + def test_valid_key_serialization(self, test_case_data: str): |
60 | 100 | case_dict = json.loads(test_case_data)
|
61 | 101 | key = Key.from_dict("id", copy.copy(case_dict))
|
62 | 102 | self.assertDictEqual(case_dict, key.to_dict())
|
63 | 103 |
|
64 | 104 |
|
| 105 | + invalid_keys: DataSet = { |
| 106 | + "no keyid": '{"keytype": "rsa", "scheme": "rsassa-pss-sha256", "keyval": {"public": "abc"}}', |
| 107 | + "no keytype": '{"keyid": "id", "scheme": "rsassa-pss-sha256", "keyval": {"public": "foo"}}', |
| 108 | + "no scheme": '{"keyid": "id", "keytype": "rsa", "keyval": {"public": "foo"}}', |
| 109 | + "no keyval": '{"keyid": "id", "keytype": "rsa", "scheme": "rsassa-pss-sha256"}', |
| 110 | + "keyid wrong type": '{"keyid": 1, "keytype": "rsa", "scheme": "rsassa-pss-sha256", "keyval": {"public": "abc"}}', |
| 111 | + "keytype wrong type": '{"keyid": "id", "keytype": 1, "scheme": "rsassa-pss-sha256", "keyval": {"public": "abc"}}', |
| 112 | + "scheme wrong type": '{"keyid": "id", "keytype": "rsa", "scheme": 1, "keyval": {"public": "abc"}}', |
| 113 | + "keyval wrong type": '{"keyid": "id", "keytype": "rsa", "scheme": "rsassa-pss-sha256", "keyval": 1}', |
| 114 | + } |
| 115 | + |
| 116 | + @run_sub_tests_with_dataset(invalid_keys) |
| 117 | + def test_invalid_key_serialization(self, test_case_data: Dict[str, str]): |
| 118 | + case_dict = json.loads(test_case_data) |
| 119 | + with self.assertRaises((TypeError, KeyError)): |
| 120 | + keyid = case_dict.pop("keyid") |
| 121 | + Key.from_dict(keyid, copy.copy(case_dict)) |
| 122 | + |
| 123 | + invalid_roles: DataSet = { |
| 124 | + "no threshold": '{"keyids": ["keyid"]}', |
| 125 | + "no keyids": '{"threshold": 3}', |
| 126 | + "wrong threshold type": '{"keyids": ["keyid"], "threshold": "a"}', |
| 127 | + "threshold below 1": '{"keyids": ["keyid"], "threshold": 0}', |
| 128 | + "duplicate keyids": '{"keyids": ["keyid", "keyid"], "threshold": 3}', |
| 129 | + } |
| 130 | + |
| 131 | + @run_sub_tests_with_dataset(invalid_roles) |
| 132 | + def test_invalid_role_serialization(self, test_case_data: Dict[str, str]): |
| 133 | + case_dict = json.loads(test_case_data) |
| 134 | + with self.assertRaises((KeyError, TypeError, ValueError)): |
| 135 | + Role.from_dict(copy.deepcopy(case_dict)) |
| 136 | + |
| 137 | + |
65 | 138 | valid_roles: DataSet = {
|
66 | 139 | "all": '{"keyids": ["keyid"], "threshold": 3}'
|
67 | 140 | }
|
@@ -92,9 +165,26 @@ def test_root_serialization(self, test_case_data: str):
|
92 | 165 | root = Root.from_dict(copy.deepcopy(case_dict))
|
93 | 166 | self.assertDictEqual(case_dict, root.to_dict())
|
94 | 167 |
|
| 168 | + |
| 169 | + invalid_metafiles: DataSet = { |
| 170 | + "wrong length type": '{"version": 1, "length": "a", "hashes": {"sha256" : "abc"}}', |
| 171 | + "length 0": '{"version": 1, "length": 0, "hashes": {"sha256" : "abc"}}', |
| 172 | + "length below 0": '{"version": 1, "length": -1, "hashes": {"sha256" : "abc"}}', |
| 173 | + "empty hashes dict": '{"version": 1, "length": 1, "hashes": {}}', |
| 174 | + "hashes wrong type": '{"version": 1, "length": 1, "hashes": 1}', |
| 175 | + "hashes values wrong type": '{"version": 1, "length": 1, "hashes": {"sha256": 1}}', |
| 176 | + } |
| 177 | + |
| 178 | + @run_sub_tests_with_dataset(invalid_metafiles) |
| 179 | + def test_invalid_metafile_serialization(self, test_case_data: Dict[str, str]): |
| 180 | + case_dict = json.loads(test_case_data) |
| 181 | + with self.assertRaises((TypeError, ValueError, AttributeError)): |
| 182 | + MetaFile.from_dict(copy.deepcopy(case_dict)) |
| 183 | + |
| 184 | + |
95 | 185 | valid_metafiles: DataSet = {
|
96 | 186 | "all": '{"hashes": {"sha256" : "abc"}, "length": 12, "version": 1}',
|
97 |
| - "no length": '{"hashes": {"sha256" : "abc"}, "version": 1 }', |
| 187 | + "no length": '{"hashes": {"sha256" : "abc"}, "version": 1}', |
98 | 188 | "no hashes": '{"length": 12, "version": 1}'
|
99 | 189 | }
|
100 | 190 |
|
@@ -159,6 +249,20 @@ def test_delegation_serialization(self, test_case_data: str):
|
159 | 249 | self.assertDictEqual(case_dict, delegation.to_dict())
|
160 | 250 |
|
161 | 251 |
|
| 252 | + invalid_targetfiles: DataSet = { |
| 253 | + "no hashes": '{"length": 1}', |
| 254 | + "no length": '{"hashes": {"sha256": "abc"}}' |
| 255 | + # The remaining cases are the same as for invalid_hashes and |
| 256 | + # invalid_length datasets. |
| 257 | + } |
| 258 | + |
| 259 | + @run_sub_tests_with_dataset(invalid_targetfiles) |
| 260 | + def test_invalid_targetfile_serialization(self, test_case_data: Dict[str, str]): |
| 261 | + case_dict = json.loads(test_case_data) |
| 262 | + with self.assertRaises(KeyError): |
| 263 | + TargetFile.from_dict(copy.deepcopy(case_dict)) |
| 264 | + |
| 265 | + |
162 | 266 | valid_targetfiles: DataSet = {
|
163 | 267 | "all": '{"length": 12, "hashes": {"sha256" : "abc"}, \
|
164 | 268 | "custom" : {"foo": "bar"} }',
|
|
0 commit comments