Files
Investidor-V2/deploy/homologacao/test_webhook.py
T

42 lines
3.1 KiB
Python

"""Synthetic, loopback-only receiver rehearsal. Never calls the site's /deploy."""
import hashlib,hmac,json,os,socket,subprocess,tempfile,time,unittest,urllib.request,urllib.error
from pathlib import Path
class ReceiverTests(unittest.TestCase):
def test_signature_scope_replay_and_no_command_dispatch(self):
source=Path(__file__).with_name('webhook.php')
with tempfile.TemporaryDirectory(prefix='investmoz-webhook-QA-') as directory:
root=Path(directory);state=root/'state';(state/'requests').mkdir(parents=True);(state/'incoming').mkdir()
secret=root/'synthetic-secret';secret.write_text('SYNTHETIC-LOCAL-QA-ONLY')
config=root/'config.json';config.write_text(json.dumps({'webhook_secret_file':str(secret),'repository':'nala/Investidor-V2','branch':'homologacao','state_root':str(state)}))
env={**os.environ,'INVESTMOZ_DELIVERY_CONFIG':str(config)}
with socket.socket() as reserve:reserve.bind(('127.0.0.1',0));port=reserve.getsockname()[1]
with (root/'php.log').open('wb') as log:
proc=subprocess.Popen(['php8.4','-S','127.0.0.1:'+str(port),'-t',str(source.parent)],env=env,stdout=log,stderr=log)
try:
for _ in range(60):
if proc.poll() is not None:self.fail('Isolated PHP did not start')
with socket.socket() as probe:
if probe.connect_ex(('127.0.0.1',port))==0:break
time.sleep(0.05)
def post(payload,valid=True,event='push'):
data=json.dumps(payload).encode();signature=hmac.new(secret.read_bytes(),data,hashlib.sha256).hexdigest() if valid else '0'*64
req=urllib.request.Request('http://127.0.0.1:'+str(port)+'/webhook.php',data=data,headers={'Content-Type':'application/json','X-Gitea-Event':event,'X-Gitea-Signature':signature})
try:res=urllib.request.urlopen(req,timeout=5)
except urllib.error.HTTPError as error:res=error
return res.status
valid={'repository':{'full_name':'nala/Investidor-V2'},'ref':'refs/heads/homologacao','after':'a'*40}
self.assertEqual(post(valid,False),403)
self.assertEqual(post({**valid,'ref':'refs/heads/main'}),204)
self.assertEqual(post({**valid,'repository':{'full_name':'someone/other'}}),204)
self.assertEqual(post({**valid,'after':'a; touch /tmp/not-a-command'}),400)
self.assertEqual(list((state/'requests').iterdir()),[])
self.assertEqual(post(valid),202);self.assertEqual(post(valid),202)
files=list((state/'requests').glob('*.json'));self.assertEqual(len(files),1)
self.assertEqual(json.loads(files[0].read_text())['commit'],'a'*40)
self.assertEqual(list((state/'incoming').iterdir()),[])
finally:
proc.terminate();proc.wait(timeout=5)
if __name__=='__main__':unittest.main()