Skip to content

Commit 94a8ccf

Browse files
test: fix missing coverage for decompress_to_cache (#514)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 18ca738 commit 94a8ccf

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import io
2+
import tarfile
3+
from unittest.mock import MagicMock, patch
4+
5+
import pytest
6+
import requests
7+
from loguru import logger
8+
9+
from qwen3_embed.common.model_management import ModelManagement
10+
11+
12+
class TestModelManagementExtra:
13+
"""Extra tests to cover missing branches in model_management.py."""
14+
15+
def test_get_session_init(self):
16+
"""Test that _get_session initializes the session if it's None."""
17+
# Save original session to restore it later
18+
original_session = ModelManagement._session
19+
ModelManagement._session = None
20+
try:
21+
session = ModelManagement._get_session()
22+
assert isinstance(session, requests.Session)
23+
assert ModelManagement._session is session
24+
25+
# Second call should return the same session
26+
session2 = ModelManagement._get_session()
27+
assert session2 is session
28+
finally:
29+
ModelManagement._session = original_session
30+
31+
def test_decompress_absolute_path_mock(self, tmp_path):
32+
"""Mock getmembers to return an absolute path to trigger line 371."""
33+
tar_path = tmp_path / "test.tar.gz"
34+
# Create a dummy tar file
35+
with tarfile.open(tar_path, "w:gz") as tar:
36+
info = tarfile.TarInfo(name="safe.txt")
37+
info.size = 0
38+
tar.addfile(info, io.BytesIO(b""))
39+
40+
cache_dir = tmp_path / "cache"
41+
cache_dir.mkdir()
42+
43+
# Mock tarfile.open to return a tar with a malicious member
44+
mock_member = MagicMock()
45+
mock_member.name = "/absolute/path"
46+
47+
with patch("tarfile.open") as mock_open:
48+
mock_tar = mock_open.return_value.__enter__.return_value
49+
mock_tar.getmembers.return_value = [mock_member]
50+
51+
with pytest.raises(tarfile.TarError, match="Attempted path traversal"):
52+
ModelManagement.decompress_to_cache(str(tar_path), str(cache_dir))
53+
54+
def test_decompress_safe_symlink_and_hardlink(self, tmp_path):
55+
"""Test safe symlinks and hardlinks to cover safe branch for links (line 399)."""
56+
cache_dir = tmp_path / "cache_links"
57+
cache_dir.mkdir()
58+
59+
tar_path = tmp_path / "links.tar.gz"
60+
with tarfile.open(tar_path, "w:gz") as tar:
61+
# Regular file
62+
info = tarfile.TarInfo(name="file.txt")
63+
info.size = 4
64+
tar.addfile(info, io.BytesIO(b"data"))
65+
66+
# Safe symlink: points to a file within the same directory
67+
sym_info = tarfile.TarInfo(name="symlink.txt")
68+
sym_info.type = tarfile.SYMTYPE
69+
sym_info.linkname = "file.txt"
70+
tar.addfile(sym_info)
71+
72+
# Safe hardlink: points to a file within the same directory (relative to root)
73+
hard_info = tarfile.TarInfo(name="hardlink.txt")
74+
hard_info.type = tarfile.LNKTYPE
75+
hard_info.linkname = "file.txt"
76+
tar.addfile(hard_info)
77+
78+
result = ModelManagement.decompress_to_cache(str(tar_path), str(cache_dir))
79+
assert result == str(cache_dir)
80+
assert (cache_dir / "file.txt").exists()
81+
# On some systems/Python versions, symlink/hardlink might not be fully
82+
# supported or behaved differently in tests, but the logic should pass.
83+
assert (cache_dir / "symlink.txt").exists()
84+
assert (cache_dir / "hardlink.txt").exists()
85+
86+
def test_decompress_no_data_filter(self, tmp_path):
87+
"""Cover fallback line 412 by mocking tarfile to lack data_filter."""
88+
tar_path = tmp_path / "test.tar.gz"
89+
with tarfile.open(tar_path, "w:gz") as tar:
90+
info = tarfile.TarInfo(name="file.txt")
91+
info.size = 0
92+
tar.addfile(info, io.BytesIO(b""))
93+
94+
cache_dir = tmp_path / "cache_no_filter"
95+
cache_dir.mkdir()
96+
97+
# We patch the tarfile module in the model_management namespace
98+
with patch("qwen3_embed.common.model_management.tarfile") as mock_tarfile_mod:
99+
# Setup mock_tar
100+
mock_tar = MagicMock()
101+
mock_tarfile_mod.open.return_value.__enter__.return_value = mock_tar
102+
103+
# Mock getmembers to return a list of members
104+
member = MagicMock()
105+
member.name = "file.txt"
106+
member.issym.return_value = False
107+
member.islnk.return_value = False
108+
mock_tar.getmembers.return_value = [member]
109+
110+
# Ensure hasattr(tarfile, 'data_filter') returns False
111+
del mock_tarfile_mod.data_filter
112+
113+
ModelManagement.decompress_to_cache(str(tar_path), str(cache_dir))
114+
115+
# Verify extractall was called without filter keyword
116+
mock_tar.extractall.assert_called_once()
117+
args, kwargs = mock_tar.extractall.call_args
118+
assert "filter" not in kwargs
119+
120+
def test_decompress_logging_on_error(self, tmp_path):
121+
"""Verify logger.error is called on TarError."""
122+
tar_path = tmp_path / "corrupt.tar.gz"
123+
tar_path.write_text("not a tar file")
124+
125+
cache_dir = tmp_path / "cache_err"
126+
cache_dir.mkdir()
127+
128+
with patch.object(logger, "error") as mock_log_error:
129+
with pytest.raises(tarfile.TarError):
130+
ModelManagement.decompress_to_cache(str(tar_path), str(cache_dir))
131+
132+
mock_log_error.assert_called()
133+
# Verify the log message contains the filename
134+
args, _ = mock_log_error.call_args
135+
assert str(tar_path) in args[0]

0 commit comments

Comments
 (0)