Skip to content

Commit 2d0aff5

Browse files
author
Jussi Kukkonen
committed
tests: Add state dumping into RepositorySimulator
if state dumping is enabled with e.g. python3 test_updater_with_simulator.py --dump The repository state can be dumped at will. Modify the test so it dumps the state on every updater refresh if --dump is set. Add a root modifying case to test_refresh() Signed-off-by: Jussi Kukkonen <jkukkonen@vmware.com>
1 parent c71f16e commit 2d0aff5

2 files changed

Lines changed: 74 additions & 26 deletions

File tree

tests/repository_simulator.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
everything from memory.
1616
"""
1717

18-
import logging
1918
from collections import OrderedDict
2019
from datetime import datetime, timedelta
21-
from tuf.api.serialization.json import JSONSerializer
20+
import logging
21+
import os
22+
import tempfile
2223
from securesystemslib.keys import generate_ed25519_key
2324
from securesystemslib.signer import SSlibSigner
24-
from tuf.exceptions import FetcherHTTPError
2525
from typing import Dict, Iterator, List, Optional, Tuple
2626
from urllib import parse
2727

28+
from tuf.api.serialization.json import JSONSerializer
29+
from tuf.exceptions import FetcherHTTPError
2830
from tuf.api.metadata import(
2931
Key,
3032
Metadata,
@@ -57,6 +59,9 @@ def __init__(self):
5759
# signers are used on-demand at fetch time to sign metadata
5860
self.signers: Dict[str, List[SSlibSigner]] = {}
5961

62+
self.dump_dir = None
63+
self.dump_version = 0
64+
6065
self._initialize()
6166

6267
@property
@@ -179,3 +184,29 @@ def update_snapshot(self):
179184

180185
self.snapshot.version += 1
181186
self.update_timestamp()
187+
188+
def write(self):
189+
"""Dump current repository metadata to self.dump_dir
190+
191+
This is a debugging tool: dumping repository state before running
192+
Updater refresh may be useful while debugging a test.
193+
"""
194+
if self.dump_dir is None:
195+
self.dump_dir = tempfile.mkdtemp()
196+
print(f"Repository Simulator dumps in {self.dump_dir}")
197+
198+
self.dump_version += 1
199+
dir = os.path.join(self.dump_dir, str(self.dump_version))
200+
os.makedirs(dir)
201+
202+
for ver in range(1, len(self.signed_roots) + 1):
203+
with open(os.path.join(dir, f"{ver}.root.json"), "wb") as f:
204+
f.write(self._fetch_metadata("root", ver))
205+
206+
for role in ["timestamp", "snapshot", "targets"]:
207+
with open(os.path.join(dir, f"{role}.json"), "wb") as f:
208+
f.write(self._fetch_metadata(role))
209+
210+
for role in self.md_delegates.keys():
211+
with open(os.path.join(dir, f"{role}.json"), "wb") as f:
212+
f.write(self._fetch_metadata(role))

tests/test_updater_with_simulator.py

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
from tests.repository_simulator import RepositorySimulator
2020

2121
class TestUpdater(unittest.TestCase):
22+
# set dump_dir to trigger repository state dumps
23+
dump_dir:str = None
24+
2225
def setUp(self):
2326
self.client_dir = tempfile.TemporaryDirectory()
2427

@@ -28,81 +31,95 @@ def setUp(self):
2831
root = self.sim.download_bytes("https://example.com/metadata/1.root.json", 100000)
2932
f.write(root)
3033

31-
def _new_updater(self):
32-
return Updater(
34+
if self.dump_dir is not None:
35+
# create test specific dump directory
36+
name = self.id().split('.')[-1]
37+
self.sim.dump_dir = os.path.join(self.dump_dir, name)
38+
os.mkdir(self.sim.dump_dir)
39+
40+
def _run_refresh(self):
41+
if self.sim.dump_dir is not None:
42+
self.sim.write()
43+
44+
updater = Updater(
3345
self.client_dir.name,
3446
"https://example.com/metadata/",
3547
"https://example.com/targets/",
3648
self.sim
3749
)
50+
updater.refresh()
3851

3952
def test_refresh(self):
53+
# TODO: how do we ensure updater does what we expected it to?
54+
# TODO: how do we ensure updater state is correct after update()?
55+
4056
# Update top level metadata
41-
self._new_updater().refresh()
57+
self._run_refresh()
4258

43-
# New root (root needs to be explicitly published)
59+
# New root (root needs to be explicitly signed)
4460
self.sim.root.version += 1
4561
self.sim.publish_root()
4662

47-
# TODO compare file contents?
63+
self._run_refresh()
4864

49-
# New timestamp version
65+
# New timestamp
5066
self.sim.update_timestamp()
5167

52-
self._new_updater().refresh()
68+
self._run_refresh()
5369

54-
# TODO compare file contents?
55-
56-
# New targets version
70+
# New targets, snapshot, timestamp version
5771
self.sim.targets.version += 1
5872
self.sim.update_snapshot()
5973

60-
self._new_updater().refresh()
74+
self._run_refresh()
6175

62-
# TODO compare file contents?
76+
def test_keys_and_signatures(self):
77+
"""Example of the two trickiest test areas: keys and root updates"""
6378

64-
# this is just an example of testing different key/signature situations
65-
def test_targets_signatures(self):
6679
# Update top level metadata
67-
self._new_updater().refresh()
80+
self._run_refresh()
6881

69-
# New targets: signed by a new key that is not in roles keys
82+
# New targets: signed with a new key that is not in roles keys
7083
old_signer = self.sim.signers["targets"].pop()
7184
key, signer = self.sim.create_key()
7285
self.sim.signers["targets"] = [signer]
7386
self.sim.targets.version += 1
7487
self.sim.update_snapshot()
7588

7689
with self.assertRaises(UnsignedMetadataError):
77-
self._new_updater().refresh()
90+
self._run_refresh()
7891

7992
# New root: Add the new key as targets role key
8093
# (root changes require explicit publishing)
8194
self.sim.root.add_key("targets", key)
8295
self.sim.root.version += 1
8396
self.sim.publish_root()
8497

85-
self._new_updater().refresh()
98+
self._run_refresh()
8699

87100
# New root: Raise targets threshold to 2
88101
self.sim.root.roles["targets"].threshold = 2
89102
self.sim.root.version += 1
90103
self.sim.publish_root()
91104

92105
with self.assertRaises(UnsignedMetadataError):
93-
self._new_updater().refresh()
106+
self._run_refresh()
94107

95108
# New targets: sign with both new and old key
96109
self.sim.signers["targets"] = [signer, old_signer]
97110
self.sim.targets.version += 1
98111
self.sim.update_snapshot()
99112

100-
self._new_updater().refresh()
101-
113+
self._run_refresh()
102114

103115
def tearDown(self):
104116
self.client_dir.cleanup()
105117

106118
if __name__ == "__main__":
107-
utils.configure_test_logging(sys.argv)
108-
unittest.main()
119+
if "--dump" in sys.argv:
120+
TestUpdater.dump_dir = tempfile.mkdtemp()
121+
print(f"Repository Simulator dumps in {TestUpdater.dump_dir}")
122+
sys.argv.remove("--dump")
123+
124+
utils.configure_test_logging(sys.argv)
125+
unittest.main()

0 commit comments

Comments
 (0)