Skip to content

Commit b57b53b

Browse files
committed
New API: Comprehensive serialization testing
The idea of this commit is to separate (de)serialization testing outside test_api.py and make sure we are testing from_dict/to_dict for all possible valid data for all classes. Jussi in his comment here: #1391 (comment) proposed using decorators when creating comprehensive testing for metadata serialization. The main problems he pointed out is that: 1) there is a lot of code needed to generate the data for each case 2) the test implementation scales badly when you want to add new cases for your tests, then you would have to add code as well 3) the dictionary format is not visible - we are loading external files and assuming they are not changed and valid In this change, I am using a decorator with an argument that complicates the implementation of the decorator and requires three nested functions, but the advantages are that we are resolving the above three problems: 1) we don't need new code when adding a new test case 2) a small amount of hardcoded data is required for each new test 3) the dictionaries are all in the test module without the need of creating new directories and copying data. Signed-off-by: Martin Vrachev <[email protected]>
1 parent 39ed706 commit b57b53b

File tree

2 files changed

+204
-167
lines changed

2 files changed

+204
-167
lines changed

tests/test_api.py

Lines changed: 1 addition & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -243,49 +243,6 @@ def test_metadata_base(self):
243243
Metadata.from_dict(data)
244244

245245

246-
def test_metafile_class(self):
247-
# Test from_dict and to_dict with all attributes.
248-
data = {
249-
"hashes": {
250-
"sha256": "8f88e2ba48b412c3843e9bb26e1b6f8fc9e98aceb0fbaa97ba37b4c98717d7ab"
251-
},
252-
"length": 515,
253-
"version": 1
254-
}
255-
metafile_obj = MetaFile.from_dict(copy.copy(data))
256-
self.assertEqual(metafile_obj.to_dict(), data)
257-
258-
# Test from_dict and to_dict without length.
259-
del data["length"]
260-
metafile_obj = MetaFile.from_dict(copy.copy(data))
261-
self.assertEqual(metafile_obj.to_dict(), data)
262-
263-
# Test from_dict and to_dict without length and hashes.
264-
del data["hashes"]
265-
metafile_obj = MetaFile.from_dict(copy.copy(data))
266-
self.assertEqual(metafile_obj.to_dict(), data)
267-
268-
269-
def test_targetfile_class(self):
270-
# Test from_dict and to_dict with all attributes.
271-
data = {
272-
"custom": {
273-
"file_permissions": "0644"
274-
},
275-
"hashes": {
276-
"sha256": "65b8c67f51c993d898250f40aa57a317d854900b3a04895464313e48785440da",
277-
"sha512": "467430a68afae8e9f9c0771ea5d78bf0b3a0d79a2d3d3b40c69fde4dd42c461448aef76fcef4f5284931a1ffd0ac096d138ba3a0d6ca83fa8d7285a47a296f77"
278-
},
279-
"length": 31
280-
}
281-
targetfile_obj = TargetFile.from_dict(copy.copy(data))
282-
self.assertEqual(targetfile_obj.to_dict(), data)
283-
284-
# Test from_dict and to_dict without custom.
285-
del data["custom"]
286-
targetfile_obj = TargetFile.from_dict(copy.copy(data))
287-
self.assertEqual(targetfile_obj.to_dict(), data)
288-
289246
def test_metadata_snapshot(self):
290247
snapshot_path = os.path.join(
291248
self.repo_dir, 'metadata', 'snapshot.json')
@@ -304,13 +261,6 @@ def test_metadata_snapshot(self):
304261
snapshot.signed.meta['role1.json'].to_dict(), fileinfo.to_dict()
305262
)
306263

307-
# Test from_dict and to_dict without hashes and length.
308-
snapshot_dict = snapshot.to_dict()
309-
del snapshot_dict['signed']['meta']['role1.json']['length']
310-
del snapshot_dict['signed']['meta']['role1.json']['hashes']
311-
test_dict = copy.deepcopy(snapshot_dict['signed'])
312-
snapshot = Snapshot.from_dict(test_dict)
313-
self.assertEqual(snapshot_dict['signed'], snapshot.to_dict())
314264

315265
def test_metadata_timestamp(self):
316266
timestamp_path = os.path.join(
@@ -349,13 +299,6 @@ def test_metadata_timestamp(self):
349299
timestamp.signed.meta['snapshot.json'].to_dict(), fileinfo.to_dict()
350300
)
351301

352-
# Test from_dict and to_dict without hashes and length.
353-
timestamp_dict = timestamp.to_dict()
354-
del timestamp_dict['signed']['meta']['snapshot.json']['length']
355-
del timestamp_dict['signed']['meta']['snapshot.json']['hashes']
356-
test_dict = copy.deepcopy(timestamp_dict['signed'])
357-
timestamp_test = Timestamp.from_dict(test_dict)
358-
self.assertEqual(timestamp_dict['signed'], timestamp_test.to_dict())
359302

360303

361304
def test_key_class(self):
@@ -369,21 +312,12 @@ def test_key_class(self):
369312
},
370313
}
371314
for key_dict in keys.values():
372-
# Testing that the workflow of deserializing and serializing
373-
# a key dictionary doesn't change the content.
374-
test_key_dict = key_dict.copy()
375-
key_obj = Key.from_dict("id", test_key_dict)
376-
self.assertEqual(key_dict, key_obj.to_dict())
377315
# Test creating an instance without a required attribute.
378316
for key in key_dict.keys():
379317
test_key_dict = key_dict.copy()
380318
del test_key_dict[key]
381319
with self.assertRaises(KeyError):
382320
Key.from_dict("id", test_key_dict)
383-
# Test creating a Key instance with wrong keyval format.
384-
key_dict["keyval"] = {}
385-
with self.assertRaises(KeyError):
386-
Key.from_dict("id", key_dict)
387321

388322

389323
def test_role_class(self):
@@ -402,23 +336,12 @@ def test_role_class(self):
402336
},
403337
}
404338
for role_dict in roles.values():
405-
# Testing that the workflow of deserializing and serializing
406-
# a role dictionary doesn't change the content.
407-
test_role_dict = role_dict.copy()
408-
role_obj = Role.from_dict(test_role_dict)
409-
self.assertEqual(role_dict, role_obj.to_dict())
410339
# Test creating an instance without a required attribute.
411340
for role_attr in role_dict.keys():
412341
test_role_dict = role_dict.copy()
413342
del test_role_dict[role_attr]
414343
with self.assertRaises(KeyError):
415-
Key.from_dict("id", test_role_dict)
416-
# Test creating a Role instance with keyid dublicates.
417-
# for keyid in role_dict["keyids"]:
418-
role_dict["keyids"].append(role_dict["keyids"][0])
419-
test_role_dict = role_dict.copy()
420-
with self.assertRaises(ValueError):
421-
Role.from_dict(test_role_dict)
344+
Role.from_dict(test_role_dict)
422345

423346

424347
def test_metadata_root(self):
@@ -465,84 +388,8 @@ def test_metadata_root(self):
465388
with self.assertRaises(KeyError):
466389
root.signed.remove_key('root', 'nosuchkey')
467390

468-
# Test serializing and deserializing without consistent_snapshot.
469-
root_dict = root.to_dict()
470-
del root_dict["signed"]["consistent_snapshot"]
471-
root = Root.from_dict(copy.deepcopy(root_dict["signed"]))
472-
self.assertEqual(root_dict["signed"], root.to_dict())
473-
474-
def test_delegated_role_class(self):
475-
roles = [
476-
{
477-
"keyids": [
478-
"c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a"
479-
],
480-
"name": "role1",
481-
"paths": [
482-
"file3.txt"
483-
],
484-
"terminating": False,
485-
"threshold": 1
486-
}
487-
]
488-
for role in roles:
489-
# Testing that the workflow of deserializing and serializing
490-
# a delegation role dictionary doesn't change the content.
491-
key_obj = DelegatedRole.from_dict(role.copy())
492-
self.assertEqual(role, key_obj.to_dict())
493-
494-
# Test creating a DelegatedRole object with both "paths" and
495-
# "path_hash_prefixes" set.
496-
role["path_hash_prefixes"] = "foo"
497-
with self.assertRaises(ValueError):
498-
DelegatedRole.from_dict(role.copy())
499-
500-
# Test creating DelegatedRole only with "path_hash_prefixes" (an empty one)
501-
del role["paths"]
502-
role["path_hash_prefixes"] = []
503-
role_obj = DelegatedRole.from_dict(role.copy())
504-
self.assertEqual(role_obj.to_dict(), role)
505-
506-
# Test creating DelegatedRole only with "paths" (now an empty one)
507-
del role["path_hash_prefixes"]
508-
role["paths"] = []
509-
role_obj = DelegatedRole.from_dict(role.copy())
510-
self.assertEqual(role_obj.to_dict(), role)
511-
512-
# Test creating DelegatedRole without "paths" and
513-
# "path_hash_prefixes" set
514-
del role["paths"]
515-
role_obj = DelegatedRole.from_dict(role.copy())
516-
self.assertEqual(role_obj.to_dict(), role)
517-
518391

519392
def test_delegation_class(self):
520-
roles = [
521-
{
522-
"keyids": [
523-
"c8022fa1e9b9cb239a6b362bbdffa9649e61ad2cb699d2e4bc4fdf7930a0e64a"
524-
],
525-
"name": "role1",
526-
"paths": [
527-
"file3.txt"
528-
],
529-
"terminating": False,
530-
"threshold": 1
531-
}
532-
]
533-
keys = {
534-
"59a4df8af818e9ed7abe0764c0b47b4240952aa0d179b5b78346c470ac30278d":{
535-
"keytype": "ed25519",
536-
"keyval": {
537-
"public": "edcd0a32a07dce33f7c7873aaffbff36d20ea30787574ead335eefd337e4dacd"
538-
},
539-
"scheme": "ed25519"
540-
},
541-
}
542-
delegations_dict = {"keys": keys, "roles": roles}
543-
delegations = Delegations.from_dict(copy.deepcopy(delegations_dict))
544-
self.assertEqual(delegations_dict, delegations.to_dict())
545-
546393
# empty keys and roles
547394
delegations_dict = {"keys":{}, "roles":[]}
548395
delegations = Delegations.from_dict(delegations_dict.copy())
@@ -584,19 +431,6 @@ def test_metadata_targets(self):
584431
targets.signed.targets[filename].to_dict(), fileinfo.to_dict()
585432
)
586433

587-
# Test from_dict/to_dict Targets with empty targets.
588-
targets_dict = copy.deepcopy(targets.to_dict())
589-
targets_dict["signed"]["targets"] = {}
590-
tmp_dict = copy.deepcopy(targets_dict["signed"])
591-
targets_obj = Targets.from_dict(tmp_dict)
592-
self.assertEqual(targets_dict["signed"], targets_obj.to_dict())
593-
594-
# Test from_dict/to_dict Targets without delegations
595-
targets_dict = targets.to_dict()
596-
del targets_dict["signed"]["delegations"]
597-
tmp_dict = copy.deepcopy(targets_dict["signed"])
598-
targets_obj = Targets.from_dict(tmp_dict)
599-
self.assertEqual(targets_dict["signed"], targets_obj.to_dict())
600434

601435
def setup_dict_with_unrecognized_field(self, file_path, field, value):
602436
json_dict = {}

0 commit comments

Comments
 (0)