prj-forgejo-org-refactor/tests/test_inventory.py
tegwick 4a690f9e27 Finish local collector preparation and block remaining coordination work
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11beb-4eef-7fc1-94a7-597130a01ae9
2026-10-09 00:38:03 +02:00

141 lines
7.7 KiB
Python

import importlib.util
import io
import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
from unittest.mock import Mock, patch
import urllib.error
import urllib.request
import zipfile
def module(name):
spec=importlib.util.spec_from_file_location(name,Path(__file__).parents[1]/'tools'/f'{name}.py')
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m);return m
inv=module('inventory');verify=module('verify_inventory')
class InventoryTests(unittest.TestCase):
def test_scan_emits_reference_only_and_skips_sensitive_files(self):
with tempfile.TemporaryDirectory() as d:
p=Path(d)
subprocess.run(['git','init','-q',d],check=True)
(p/'values.yaml').write_text('image: https://user:do-not-emit@forgejo.example/coulomb/example:tag?token=also-private\n')
(p/'.env').write_text('SECRET=coulomb/private-secret\n')
(p/'link').symlink_to(p/'values.yaml')
subprocess.run(['git','-C',d,'add','.'],check=True)
subprocess.run(['git','-C',d,'-c','user.name=Test','-c','user.email=test@example.invalid','commit','-qm','fixture'],check=True)
result=inv.scan(p)
self.assertEqual([r['reference'] for r in result['references']],['coulomb/example'])
self.assertNotIn('do-not-emit',str(result));self.assertNotIn('also-private',str(result))
self.assertNotIn('private-secret',str(result))
self.assertEqual({e['reason'] for e in result['exclusions']},{'sensitive-or-encrypted','symlink-not-followed'})
self.assertEqual(result,inv.scan(p))
def test_paginated_server_read_does_not_stop_at_first_page(self):
original=inv.get
try:
inv.get=lambda url: [{'id':1}] if 'page=1' in url else [{'id':2}] if 'page=2' in url else []
self.assertEqual(inv.pages('/repos'),[{'id':1},{'id':2}])
inv.get=lambda url:[{'id':1}]
with self.assertRaises(RuntimeError):inv.pages('/repos')
finally:inv.get=original
def test_empty_partial_inventory_cannot_pass(self):
result=verify.check({'g1_complete':False,'local_repositories':[]},{'approved':False,'assignments':[]})
self.assertFalse(result['clean'])
def test_retained_org_and_stale_moved_path(self):
i={'g1_complete':True,'local_repositories':[{'name':'consumer','references':[{'reference':'coulomb/example','categories':['historical-or-planning']}]}]}
m={'approved':True,'assignments':[{'repository':'example','proposed_org':'coulomb'}]}
self.assertTrue(verify.check(i,m)['clean'])
m['assignments'][0]['proposed_org']='railiance'
self.assertFalse(verify.check(i,m)['clean'])
def test_authenticated_reads_use_get_and_are_scoped_to_forge(self):
response = Mock()
response.read.return_value = b'{"id": 42}'
opener = Mock()
opener.open.return_value.__enter__ = Mock(return_value=response)
opener.open.return_value.__exit__ = Mock(return_value=False)
with patch.object(inv.urllib.request, 'build_opener', return_value=opener):
self.assertEqual(inv.get(inv.BASE + '/api/v1/repos/coulomb/private', token='fixture-only'), {'id':42})
request = opener.open.call_args.args[0]
self.assertEqual(request.get_method(), 'GET')
self.assertEqual(request.get_header('Authorization'), 'token fixture-only')
for url in ['http://127.0.0.1:8000/repos/', 'https://other.invalid/repos/',
'http://forgejo.coulomb.social/repos/']:
with self.assertRaises(RuntimeError):
inv.get(url, token='fixture-only')
inv.get('http://127.0.0.1:8000/repos/')
self.assertIsNone(opener.open.call_args.args[0].get_header('Authorization'))
def test_authenticated_redirects_cannot_leak_token_or_downgrade(self):
req = urllib.request.Request(inv.BASE + '/start', headers={'Authorization':'token fixture-only'})
handler = inv.ScopedRedirects(True)
redirect = handler.redirect_request(req, None, 302, 'Found', {}, inv.BASE + '/next')
self.assertEqual(redirect.get_header('Authorization'), 'token fixture-only')
for url in ['https://other.invalid/next', 'http://forgejo.coulomb.social/next',
'https://user@forgejo.coulomb.social/next']:
with self.assertRaisesRegex(RuntimeError, '^Authenticated forge redirect refused$'):
handler.redirect_request(req, None, 302, 'Found', {}, url)
def test_transport_errors_do_not_emit_credential_or_raw_response(self):
for error, message in [
(urllib.error.URLError('fixture-only raw error'), 'Inventory read failed'),
(urllib.error.HTTPError(inv.BASE, 403, 'fixture-only raw response', {}, None), 'HTTP 403')]:
opener = Mock()
opener.open.side_effect = error
with patch.object(inv.urllib.request, 'build_opener', return_value=opener):
with self.assertRaises(RuntimeError) as caught:
inv.get(inv.BASE + '/api/v1/repos', token='fixture-only')
self.assertEqual(str(caught.exception), message)
def test_token_is_forwarded_to_all_metadata_reads_but_not_hub(self):
calls = []
def fake_get(url, *, token=None):
calls.append((url,token))
if url.endswith('/repos/'):
return []
if '/orgs/' in url and 'page=1' in url:
return [{'id':42, 'name':inv.SELF.name, 'full_name':'coulomb/'+inv.SELF.name, 'private':True}]
if '/orgs/' in url or '/packages/' in url:
return []
return {'version':'fixture'}
with tempfile.TemporaryDirectory() as root, patch.object(inv,'get',side_effect=fake_get):
result=inv.collect(Path(root),token='fixture-only')
self.assertFalse(result['g1_complete'])
self.assertEqual(result['forge']['authentication'],'authenticated-read')
self.assertTrue(result['server_repositories'][0]['private'])
for url,token in calls:
self.assertEqual(token, 'fixture-only' if url.startswith(inv.BASE) else None)
self.assertNotIn('fixture-only',json.dumps(result))
def test_missing_requested_credential_refuses_before_collection(self):
with tempfile.TemporaryDirectory() as root:
output=Path(root)/'snapshot.json'
env=dict(os.environ)
env.pop('ORGREF_TEST_MISSING_TOKEN',None)
result=subprocess.run([
'python3',str(Path(__file__).parents[1]/'tools/inventory.py'),
'--output',str(output),'--forge-token-env','ORGREF_TEST_MISSING_TOKEN'],
env=env,capture_output=True,text=True)
self.assertEqual(result.returncode,2)
self.assertFalse(output.exists())
def test_private_archive_uses_the_same_scoped_read_credential(self):
raw=io.BytesIO()
with zipfile.ZipFile(raw,'w') as archive:
archive.writestr('private/values.yaml','image: forgejo.example/coulomb/example:tag\n')
with patch.object(inv,'get',return_value={'commit':{'id':'abc'}}) as metadata, \
patch.object(inv,'read_bytes',return_value=raw.getvalue()) as archive_read:
result=inv.scan_archive({'name':'private','default_branch':'main'},token='fixture-only')
self.assertEqual(metadata.call_args.kwargs['token'],'fixture-only')
self.assertEqual(archive_read.call_args.kwargs,{'token':'fixture-only','limit':50_000_001})
self.assertEqual([r['reference'] for r in result['references']],['coulomb/example'])
self.assertNotIn('fixture-only',json.dumps(result))
if __name__=='__main__':unittest.main()