Skip to content

Commit 6d4eef7

Browse files
authored
Merge pull request #113 from jku/add-download-test-with-subdir
Add download test using targetpath with subdirs
2 parents 65b4af1 + 87cdf78 commit 6d4eef7

File tree

3 files changed

+78
-36
lines changed

3 files changed

+78
-36
lines changed

clients/go-tuf/cmd/download.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package cmd
1313

1414
import (
1515
"fmt"
16+
"net/url"
1617
"os"
1718
"path/filepath"
1819

@@ -103,7 +104,8 @@ func RefreshAndDownloadCmd(targetName string,
103104
}
104105

105106
// target is available, so let's see if the target is already present locally
106-
path, _, err := up.FindCachedTarget(targetInfo, "")
107+
localPath := filepath.Join(targetDownloadDir, url.QueryEscape(targetName))
108+
path, _, err := up.FindCachedTarget(targetInfo, localPath)
107109
if err != nil {
108110
return fmt.Errorf("failed while finding a cached target: %w", err)
109111
}
@@ -114,7 +116,8 @@ func RefreshAndDownloadCmd(targetName string,
114116
}
115117

116118
// target is not present locally, so let's try to download it
117-
path, _, err = up.DownloadTarget(targetInfo, filepath.Join(targetDownloadDir, targetName), targetBaseUrl)
119+
//
120+
path, _, err = up.DownloadTarget(targetInfo, localPath, targetBaseUrl)
118121
if err != nil {
119122
return fmt.Errorf("failed to download target file %s - %w", targetName, err)
120123
}

tuf_conformance/client_runner.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,24 @@ def __init__(
2323
self._server = server
2424
self._cmd = client_cmd.split(" ")
2525
self._tempdir = TemporaryDirectory()
26-
self._target_dir = TemporaryDirectory()
27-
self._remote_target_dir = TemporaryDirectory(dir=os.getcwd())
2826
# TODO: cleanup tempdir
2927
self.metadata_dir = os.path.join(self._tempdir.name, "metadata")
28+
self.artifact_dir = os.path.join(self._tempdir.name, "targets")
3029
os.mkdir(self.metadata_dir)
30+
os.mkdir(self.artifact_dir)
3131
self.test_name = test_name
3232

33-
def get_last_downloaded_target(self) -> str:
34-
list_of_files = glob.glob(self._target_dir.name + "/*")
35-
if len(list_of_files) == 0:
36-
return ""
37-
latest_file = max(list_of_files, key=os.path.getctime)
38-
return latest_file
33+
def get_downloaded_target_bytes(self) -> list[bytes]:
34+
"""Returns list of downloaded artifact contents in order of modification time"""
35+
artifacts = glob.glob(f"{self.artifact_dir}/**", recursive=True)
36+
artifact_bytes = []
37+
for artifact in sorted(artifacts, key=os.path.getmtime):
38+
if not os.path.isfile(artifact):
39+
continue
40+
with open(artifact, "rb") as f:
41+
artifact_bytes.append(f.read())
42+
43+
return artifact_bytes
3944

4045
def _run(self, cmd: list[str]) -> int:
4146
popen = subprocess.Popen(cmd)
@@ -77,7 +82,7 @@ def download_target(self, data: ClientInitData, target_name: str) -> int:
7782
"--target-name",
7883
target_name,
7984
"--target-dir",
80-
self._target_dir.name,
85+
self.artifact_dir,
8186
"--target-base-url",
8287
data.targets_url,
8388
"download",

tuf_conformance/test_file_download.py

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,40 @@ def test_client_downloads_expected_file(
2020
target_content = b"target file contents"
2121
repo.add_target(Targets.type, target_content, target_path)
2222

23-
# Client updates, sanity update that nothing was downloaded
23+
# Client updates, sanity check that nothing was downloaded
2424
assert client.refresh(init_data) == 0
25-
assert client.get_last_downloaded_target() == ""
25+
assert client.get_downloaded_target_bytes() == []
2626

2727
assert client.download_target(init_data, target_path) == 0
2828

2929
# assert that the downloaded file contents are correct
30-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
31-
assert last_download_file.read() == target_content
30+
assert client.get_downloaded_target_bytes() == [target_content]
31+
32+
33+
def test_client_downloads_expected_file_in_sub_dir(
34+
client: ClientRunner, server: SimulatorServer
35+
) -> None:
36+
"""This test adds a file to the repository targets metadata with a targetpath that
37+
contains a directory. Client then refreshes, and downloads the target. Finally,
38+
the test asserts that the client downloaded the file and that the contents of the
39+
file are correct
40+
"""
41+
init_data, repo = server.new_test(client.test_name)
42+
assert client.init_client(init_data) == 0
43+
44+
# Create a test artifact, add it to the repository
45+
target_path = "path/to/a/target_file.txt"
46+
target_content = b"target file contents"
47+
repo.add_target(Targets.type, target_content, target_path)
48+
49+
# Client updates, sanity check that nothing was downloaded
50+
assert client.refresh(init_data) == 0
51+
assert client.get_downloaded_target_bytes() == []
52+
53+
assert client.download_target(init_data, target_path) == 0
54+
55+
# assert that the downloaded file contents are correct
56+
assert client.get_downloaded_target_bytes() == [target_content]
3257

3358

3459
def test_repository_substitutes_target_file(
@@ -54,8 +79,7 @@ def test_repository_substitutes_target_file(
5479

5580
# Download one of the artifacts
5681
assert client.download_target(init_data, target_path_1) == 0
57-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
58-
assert last_download_file.read() == target_content_1
82+
assert client.get_downloaded_target_bytes() == [target_content_1]
5983

6084
# Change both artifact contents that repository serves
6185
malicious_content = b"malicious data - should not download"
@@ -68,15 +92,13 @@ def test_repository_substitutes_target_file(
6892
client.download_target(init_data, target_path_1)
6993

7094
# assert the client did not accept the malicious artifact
71-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
72-
assert last_download_file.read() == target_content_1
95+
assert client.get_downloaded_target_bytes() == [target_content_1]
7396

7497
# ask client to download the other artifact and expect failure
75-
assert client.download_target(init_data, target_path_1) == 1
98+
assert client.download_target(init_data, target_path_2) == 1
7699

77100
# assert the client did not store the malicious artifact
78-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
79-
assert last_download_file.read() == target_content_1
101+
assert client.get_downloaded_target_bytes() == [target_content_1]
80102

81103

82104
def test_multiple_changes_to_target(
@@ -109,18 +131,22 @@ def test_multiple_changes_to_target(
109131
# Client downloads the file
110132
client.download_target(init_data, target_path)
111133
# check file contents
112-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
113-
assert last_download_file.read() == target_content
134+
expected_downloads = [target_content]
135+
previous_content = target_content
136+
assert client.get_downloaded_target_bytes() == expected_downloads
114137

115138
# Now the repository makes 10 changes to the targets metadata.
116139
# It modifies the targets file in the targets metadata including
117140
# the hashes and length, and it also makes the corresponding
118-
# content changes in the file itself. As such, the client
119-
# should download the file
141+
# content changes in the file itself.
120142
for i in range(10):
121-
# Modify the artifact legitimately:
122-
new_file_contents = f"target file contents {i}".encode()
123-
repo.add_target(Targets.type, new_file_contents, target_path)
143+
# Modify the existing artifact legitimately:
144+
modified_contents = f"modified file contents {i}".encode()
145+
repo.add_target(Targets.type, modified_contents, target_path)
146+
# Add a completely new artifact
147+
new_file_contents = f"new file contents {i}".encode()
148+
new_target_path = f"new-target-{i}"
149+
repo.add_target(Targets.type, new_file_contents, new_target_path)
124150
repo.targets.version += 1
125151

126152
# Bump repo snapshot
@@ -129,13 +155,22 @@ def test_multiple_changes_to_target(
129155
# Client updates
130156
assert client.refresh(init_data) == 0
131157

132-
# Client downloads the new file
158+
# Client downloads the modified artifact
133159
assert client.download_target(init_data, target_path) == 0
134-
# check file contents
135-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
136-
assert last_download_file.read() == new_file_contents
160+
# the previous content is no longer there, modified content is there
161+
expected_downloads.remove(previous_content)
162+
expected_downloads.append(modified_contents)
163+
previous_content = modified_contents
164+
assert client.get_downloaded_target_bytes() == expected_downloads
165+
166+
# Client downloads the new artifact
167+
assert client.download_target(init_data, new_target_path) == 0
168+
expected_downloads.append(new_file_contents)
169+
170+
# check downloaded contents
171+
assert client.get_downloaded_target_bytes() == expected_downloads
137172

138-
# Modify the artifact content without updating the hashes/length in metadata.
173+
# Modify the original artifact content without updating the hashes/length in metadata.
139174
malicious_file_contents = f"malicious contents {i}".encode()
140175
repo.artifacts[target_path].data = malicious_file_contents
141176
repo.targets.version += 1
@@ -147,5 +182,4 @@ def test_multiple_changes_to_target(
147182
client.download_target(init_data, target_path)
148183

149184
# Check that the client did not download the malicious file
150-
with open(client.get_last_downloaded_target(), "rb") as last_download_file:
151-
assert new_file_contents == last_download_file.read()
185+
assert client.get_downloaded_target_bytes() == expected_downloads

0 commit comments

Comments
 (0)