Files
VPSMaster/tests/test_bandwidth_sources.py

118 lines
5.5 KiB
Python

import io
import pytest
from proxydoctor import bandwidth as bw
class Response(io.BytesIO):
def __init__(self,body,status=206,headers=None):
super().__init__(body)
self.status=status
self.headers=headers or {}
def fake_response(monkeypatch,response):
requests=[]
class Opener:
def open(self,request,timeout):
requests.append(request)
return response
monkeypatch.setattr(bw,'opener',lambda:Opener())
return requests
@pytest.mark.parametrize('source',['hetzner-fsn','ovh-rbx'])
def test_official_download_sources_use_exact_validated_range(monkeypatch,source):
clock=iter(range(10))
monkeypatch.setattr(bw.time,'monotonic',lambda:next(clock))
requests=fake_response(monkeypatch,Response(b'x'*100,headers={'Content-Range':'bytes 0-99/104857600','Content-Length':'100'}))
sample=bw.transfer('download',100,source)
assert sample.mbps is not None and sample.error is None
assert sample.source==source
assert requests[0].get_header('Range')=='bytes=0-99'
assert requests[0].full_url==bw.SOURCES[source].download_url
def test_ignored_range_never_reads_full_large_file(monkeypatch):
class FullFile(Response):
def read1(self,*args): raise AssertionError('Must not read a full-file response')
def read(self,*args): raise AssertionError('Must not read a full-file response')
fake_response(monkeypatch,FullFile(b'',status=200))
sample=bw.transfer('download',100,'hetzner-fsn')
assert sample.error=='range_not_supported' and sample.mbps is None
@pytest.mark.parametrize('range_header,length',[
('bytes 1-100/104857600','100'),('bytes 0-100/104857600','100'),
('bytes 0-99/99','100'),('bytes 0-99/*','100'),('', '100'),
('bytes 0-99/104857600','101'),
])
def test_wrong_content_range_never_becomes_speed(monkeypatch,range_header,length):
fake_response(monkeypatch,Response(b'x'*100,headers={'Content-Range':range_header,'Content-Length':length}))
sample=bw.transfer('download',100,'ovh-rbx')
assert sample.mbps is None and sample.error=='invalid_content_range'
def test_partial_payload_and_html_cannot_be_valid_samples(monkeypatch):
fake_response(monkeypatch,Response(b'x'*99,headers={'Content-Range':'bytes 0-99/104857600'}))
assert bw.transfer('download',100,'ovh-rbx').error=='short_response'
fake_response(monkeypatch,Response(b'x'*100,headers={'Content-Range':'bytes 0-99/104857600','Content-Type':'text/html'}))
assert bw.transfer('download',100,'hetzner-fsn').error=='unexpected_content'
@pytest.mark.parametrize('source',['hetzner-fsn','ovh-rbx'])
def test_download_only_never_attempts_upload(monkeypatch,source):
monkeypatch.setattr(bw,'opener',lambda:pytest.fail('Unexpected upload request'))
assert bw.transfer('upload',100,source).error=='unsupported'
def test_multiple_sources_stay_separate_with_correct_budget(monkeypatch):
calls=[];events=[];samples=[]
def transfer(direction,size,source='cloudflare'):
calls.append((source,direction,size))
speed={'cloudflare':10,'hetzner-fsn':30,'ovh-rbx':50}.get(source,60)
return bw.Sample(direction,size,2,speed,source=source)
monkeypatch.setattr(bw,'transfer',transfer)
report=bw.measure_many(sources=tuple(bw.SOURCES),on_source=lambda *args:events.append(args[:2]),on_sample=lambda *args:samples.append(args))
assert report['payload_budget_bytes']==int(97*1024**2)
assert len(calls)==32 and len(samples)==24
assert report['sources']['hetzner-fsn']['directions']['download']['median_mbps']==30
assert report['sources']['ovh-rbx']['directions']['download']['median_mbps']==50
assert 'upload' not in report['sources']['ovh-rbx']['directions']
assert 'median_mbps' not in report and bw.is_complete(report)
assert events==[(s,event) for s in bw.SOURCES for event in ('start','complete')]
def test_failed_source_is_not_silently_replaced_and_next_source_runs(monkeypatch):
calls=[]
def transfer(direction,size,source='cloudflare'):
calls.append(source)
return bw.Sample(direction,size,2,20 if source=='ovh-rbx' else None,
error=None if source=='ovh-rbx' else 'range_not_supported',source=source)
monkeypatch.setattr(bw,'transfer',transfer)
report=bw.measure_many(sources=['hetzner-fsn','ovh-rbx'])
assert calls.count('hetzner-fsn')==1
assert report['sources']['hetzner-fsn']['directions']['download']['successful']==0
assert report['sources']['ovh-rbx']['directions']['download']['successful']==3
assert not bw.is_complete(report)
def test_source_selection_deduplicates_and_rejects_arbitrary_urls():
assert bw.source_ids()==('cloudflare','ovh-rbx')
assert bw.payload_budget(3,4)==int(36.375*1024**2)
assert bw.source_ids(['ovh-rbx','ovh-rbx'])==('ovh-rbx',)
assert bw.payload_budget(3,4,['ovh-rbx'])==int(12.125*1024**2)
for selected in ([],['https://example.com/'],['not-a-source']):
with pytest.raises(ValueError): bw.source_ids(selected)
def test_console_reports_download_only_without_calling_it_failed(monkeypatch,capsys):
monkeypatch.setattr(bw,'transfer',lambda direction,size,source='cloudflare':bw.Sample(direction,size,2,12,source=source))
bw.run_console('en','always',sources=['ovh-rbx'])
output=capsys.readouterr().out
assert 'OVHcloud' in output and 'FR / Roubaix' in output
assert 'Not supported (download-only source)' in output
assert 'Download summary' in output and 'Upload summary' not in output
assert '\x1b[32m' in output