Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
53 lines
2.5 KiB
Python
53 lines
2.5 KiB
Python
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
|
|
import cleanup_nextcloud_recovery_fixture as cleanup
|
|
|
|
|
|
class RecoveryFixtureCleanup(unittest.TestCase):
|
|
def proof(self, directory, status='restored'):
|
|
path = Path(directory) / 'proof.json'
|
|
path.write_text(json.dumps({'status': status, 'cleanup': True,
|
|
'offsite_artifact': 'wp0029-recovery-forgejo-dump-20260905-verified.zip.age'}))
|
|
return path
|
|
|
|
def test_no_credentials_before_successful_recovery(self):
|
|
with tempfile.TemporaryDirectory() as directory, patch.object(cleanup, 'bao') as bao:
|
|
with self.assertRaises(Exception):
|
|
cleanup.run(self.proof(directory, 'failed'), {})
|
|
bao.assert_not_called()
|
|
|
|
def exercise(self, size):
|
|
opener = MagicMock()
|
|
head = MagicMock(status=200, headers={'Content-Length': str(size), 'ETag': '"fixture-version"'})
|
|
delete = MagicMock(status=204)
|
|
opener.open.side_effect = [MagicMock(__enter__=lambda _: head, __exit__=lambda *a: False),
|
|
MagicMock(__enter__=lambda _: delete, __exit__=lambda *a: False)]
|
|
with tempfile.TemporaryDirectory() as directory, \
|
|
patch.object(cleanup, 'bao'), \
|
|
patch.object(cleanup, 'data', return_value={'data': {'data': {'BACKUP_USERNAME': 'Backup', 'BACKUP_PASSWORD': 'fixture'}}}), \
|
|
patch.object(cleanup, 'quota', return_value={}), \
|
|
patch.object(cleanup.urllib.request, 'build_opener', return_value=opener):
|
|
receipt = {}
|
|
if size != cleanup.SIZE:
|
|
with self.assertRaises(Exception):
|
|
cleanup.run(self.proof(directory), receipt)
|
|
self.assertEqual(opener.open.call_count, 1)
|
|
else:
|
|
cleanup.run(self.proof(directory), receipt)
|
|
request = opener.open.call_args_list[1].args[0]
|
|
self.assertEqual(request.method, 'DELETE')
|
|
self.assertTrue(request.full_url.endswith('/forgejo/' + cleanup.ARTIFACT))
|
|
self.assertEqual(request.get_header('If-match'), '"fixture-version"')
|
|
self.assertEqual(receipt['status'], 'removed')
|
|
|
|
def test_mismatched_object_is_preserved(self):
|
|
self.exercise(1)
|
|
|
|
def test_exact_object_has_conditional_delete(self):
|
|
self.exercise(cleanup.SIZE)
|