test: add unit tests for stale-image checks to reach ≥90% diff-coverage
Covers StaleImageError / check_stale / check_stale_path, the BottleBackend.launch template method (skip_stale flag), stale_checks functions across docker / smolmachines / macos-container backends, _build_images cached paths in the macOS backend, image_created_at edge cases in both docker and container util modules, the CLI stale loop (headless die, interactive decline, interactive confirm + retry), and ConfigStore.cached_image_stale_warning_days fallback paths. Diff-coverage: 488/507 changed lines covered (96.3%).
This commit is contained in:
@@ -273,5 +273,71 @@ resolver #2
|
||||
)
|
||||
|
||||
|
||||
class TestMacosContainerImageCreatedAt(unittest.TestCase):
|
||||
def _ok(self, stdout: str) -> "util.subprocess.CompletedProcess": # type: ignore
|
||||
return util.subprocess.CompletedProcess(
|
||||
args=[], returncode=0, stdout=stdout, stderr="",
|
||||
)
|
||||
|
||||
def _fail(self, stderr: str = "no such image") -> "util.subprocess.CompletedProcess": # type: ignore
|
||||
return util.subprocess.CompletedProcess(
|
||||
args=[], returncode=1, stdout="", stderr=stderr,
|
||||
)
|
||||
|
||||
def test_parses_iso_timestamp_from_dict(self):
|
||||
payload = '[{"created": "2025-06-01T12:00:00"}]'
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok(payload)):
|
||||
dt = util.image_created_at("bot-bottle-agent:latest")
|
||||
self.assertEqual(2025, dt.year)
|
||||
self.assertEqual(6, dt.month)
|
||||
self.assertEqual(1, dt.day)
|
||||
|
||||
def test_accepts_list_or_dict_input(self):
|
||||
# Container CLI may return a list; we take the first element.
|
||||
payload = '[{"created": "2024-01-15T08:30:00"}]'
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok(payload)):
|
||||
dt = util.image_created_at("some-image:latest")
|
||||
self.assertEqual(2024, dt.year)
|
||||
|
||||
def test_accepts_uppercase_Created_field(self):
|
||||
payload = '[{"Created": "2024-03-20T10:00:00"}]'
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok(payload)):
|
||||
dt = util.image_created_at("some-image:latest")
|
||||
self.assertEqual(2024, dt.year)
|
||||
self.assertEqual(3, dt.month)
|
||||
|
||||
def test_dies_on_nonzero_returncode(self):
|
||||
with patch.object(util.subprocess, "run", return_value=self._fail("not found")), \
|
||||
patch.object(util, "die", side_effect=SystemExit("die")) as die:
|
||||
with self.assertRaises(SystemExit):
|
||||
util.image_created_at("missing:tag")
|
||||
die.assert_called_once()
|
||||
self.assertIn("missing:tag", die.call_args.args[0])
|
||||
|
||||
def test_dies_on_malformed_json(self):
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok("not-json {")), \
|
||||
patch.object(util, "die", side_effect=SystemExit("die")) as die:
|
||||
with self.assertRaises(SystemExit):
|
||||
util.image_created_at("some:tag")
|
||||
die.assert_called_once()
|
||||
|
||||
def test_dies_when_no_created_field(self):
|
||||
payload = '[{"id": "sha256:abc123"}]'
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok(payload)), \
|
||||
patch.object(util, "die", side_effect=SystemExit("die")) as die:
|
||||
with self.assertRaises(SystemExit):
|
||||
util.image_created_at("some:tag")
|
||||
die.assert_called_once()
|
||||
self.assertIn("creation timestamp", die.call_args.args[0])
|
||||
|
||||
def test_dies_on_invalid_timestamp_format(self):
|
||||
payload = '[{"created": "not-a-date"}]'
|
||||
with patch.object(util.subprocess, "run", return_value=self._ok(payload)), \
|
||||
patch.object(util, "die", side_effect=SystemExit("die")) as die:
|
||||
with self.assertRaises(SystemExit):
|
||||
util.image_created_at("some:tag")
|
||||
die.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user