import json from pathlib import Path import sys from types import SimpleNamespace import unittest from unittest.mock import patch import urllib.error sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) import migrate_nextcloud_backup_account as migration class BackupAccountTests(unittest.TestCase): def test_requests_refuse_other_provider_origins(self): with patch.object(migration.urllib.request, 'build_opener') as opener: with self.assertRaises(migration.LaneError): migration.request('https://different.invalid/remote.php/dav', auth=('Backup','fixture')) opener.assert_not_called() def test_http_failure_exposes_only_status_not_credential_body(self): with patch.object(migration.urllib.request, 'build_opener') as opener: opener.return_value.open.side_effect = urllib.error.HTTPError('https://fixture.invalid/fixture-secret',403,'fixture-secret',{},None) self.assertEqual(migration.request(migration.HOST+'/probe'),(403,b'')) def test_wrong_quota_rejects_before_cutover(self): body=b'502' with patch.object(migration,'request',return_value=(207,body)): with self.assertRaisesRegex(migration.LaneError,'quota_contract_mismatch'): migration.quota(('Backup','fixture')) def test_expected_version_mismatch_stops_before_provider_mutation(self): responses=[{'data':{'policies':['platform-admin']}}, {'data':{'data':{'BACKUP_USERNAME':'Backup','BACKUP_PASSWORD':'fixture'}}}, {'data':{'metadata':{'version':2},'data':{}}}] with patch.object(migration,'bao',side_effect=[SimpleNamespace(stdout=json.dumps(x).encode()) for x in responses]), patch.object(migration,'quota',return_value={}), patch.object(migration,'request') as provider: with self.assertRaisesRegex(migration.LaneError,'kv_version_changed'): migration.run(SimpleNamespace(expected_version=99),{}) provider.assert_not_called() if __name__=='__main__': unittest.main()