201 lines
9.6 KiB
Python
201 lines
9.6 KiB
Python
import io
|
|
import json
|
|
import urllib.error
|
|
|
|
import pytest
|
|
|
|
from proxydoctor import bandwidth as bw, ipquality as iq, media, net
|
|
|
|
|
|
def test_bandwidth_median_excludes_failures_and_marks_insufficient():
|
|
samples=[bw.Sample('download',1024,2,10),bw.Sample('download',1024,3,30),
|
|
bw.Sample('download',1024,error='request_failed')]
|
|
result=bw.summarize(samples)
|
|
assert result['median_mbps']==20 and result['successful']==2
|
|
assert result['quality']=='insufficient'
|
|
assert result['min_mbps']==10 and result['max_mbps']==30
|
|
|
|
|
|
def test_fast_and_variable_samples_are_not_claimed_consistent():
|
|
assert bw.summarize([bw.Sample('download',1024,.1,10)]*3)['quality']=='short_samples'
|
|
assert bw.summarize([bw.Sample('download',1024,2,n) for n in (10,20,100)])['quality']=='variable'
|
|
assert bw.summarize([bw.Sample('download',1024,2,n) for n in (10,11,12)])['quality']=='consistent'
|
|
|
|
|
|
def test_warmups_are_excluded_and_each_round_streamed(monkeypatch):
|
|
calls=[];emitted=[]
|
|
def transfer(direction,size):
|
|
calls.append((direction,size))
|
|
return bw.Sample(direction,size,2,999 if size==131072 else 12)
|
|
monkeypatch.setattr(bw,'transfer',transfer)
|
|
result=bw.measure(rounds=3,sample_mib=4,on_sample=lambda *args:emitted.append(args))
|
|
assert len(calls)==8 and len(emitted)==6
|
|
assert result['directions']['download']['median_mbps']==12
|
|
assert result['payload_budget_bytes']==int(24.25*1024**2)
|
|
|
|
|
|
def test_provider_refusal_stops_further_transfers(monkeypatch):
|
|
calls=[]
|
|
def transfer(direction,size):
|
|
calls.append(direction)
|
|
return bw.Sample(direction,size,error='http_429')
|
|
monkeypatch.setattr(bw,'transfer',transfer)
|
|
result=bw.measure()
|
|
assert calls==['download','upload']
|
|
assert result['directions']['download']['successful']==0
|
|
|
|
|
|
@pytest.mark.parametrize('rounds,size',[(2,4),(9,4),(3,0),(3,21)])
|
|
def test_traffic_limits_validated(rounds,size):
|
|
with pytest.raises(ValueError): bw.measure(rounds,size)
|
|
|
|
|
|
def test_anonymous_missing_flags_never_become_false(monkeypatch):
|
|
monkeypatch.delenv('PROXYDOCTOR_IPAPI_KEY',raising=False)
|
|
monkeypatch.delenv('PROXYDOCTOR_ABUSEIPDB_KEY',raising=False)
|
|
monkeypatch.setattr(iq,'request_json',lambda *a,**k:({'ip':'8.8.8.8','company':'Example','country':'US'},'ok'))
|
|
result=iq.collect()
|
|
assert result['values']['is_proxy'] is None
|
|
assert result['sources']['AbuseIPDB']['state']=='key_required'
|
|
assert result['coverage']['complete'] is False
|
|
|
|
|
|
def test_keys_are_never_in_result_or_urls_and_abuse_ip_must_match(monkeypatch):
|
|
monkeypatch.setenv('PROXYDOCTOR_IPAPI_KEY','secret-one')
|
|
monkeypatch.setenv('PROXYDOCTOR_ABUSEIPDB_KEY','secret-two')
|
|
calls=[]
|
|
def request(url,**kwargs):
|
|
calls.append((url,kwargs))
|
|
return ({'ip':'8.8.8.8','is_vpn':'false'},'ok') if 'ipapi.is' in url else ({'data':{'ipAddress':'1.1.1.1','abuseConfidenceScore':0}},'ok')
|
|
monkeypatch.setattr(iq,'request_json',request)
|
|
result=iq.collect()
|
|
assert calls[0][1]['payload']=={'key':'secret-one'}
|
|
assert calls[1][1]['headers']=={'Key':'secret-two'}
|
|
assert all('secret' not in url for url,_ in calls)
|
|
assert 'secret' not in json.dumps(result)
|
|
assert result['values']['is_vpn'] is None
|
|
assert result['reputation']['score'] is None
|
|
assert result['sources']['AbuseIPDB']['state']=='invalid_response'
|
|
|
|
|
|
def test_full_ip_and_abuse_response_with_zero_reports_is_preserved(monkeypatch):
|
|
monkeypatch.setenv('PROXYDOCTOR_IPAPI_KEY','key')
|
|
monkeypatch.setenv('PROXYDOCTOR_ABUSEIPDB_KEY','key')
|
|
fixture={'ip':'8.8.8.8','company':{'name':'Example','type':'business','abuser_score':'0 (Very Low)'},
|
|
'asn':{'asn':15169,'org':'Example'},'location':{'country':'US','region':'CA','city':'Test','timezone':'Etc/UTC'},
|
|
**dict.fromkeys(iq.FLAGS,False)}
|
|
def request(url,**kwargs):
|
|
return (fixture,'ok') if 'ipapi.is' in url else ({'data':{'ipAddress':'8.8.8.8','abuseConfidenceScore':0,
|
|
'totalReports':0,'numDistinctUsers':0,'lastReportedAt':None}},'ok')
|
|
monkeypatch.setattr(iq,'request_json',request)
|
|
result=iq.collect()
|
|
assert result['coverage']['complete'] is True
|
|
assert result['reputation']['score']==0
|
|
assert result['reputation']['last_reported_at'] is None
|
|
|
|
|
|
def test_private_egress_does_not_trigger_abuse_lookup(monkeypatch):
|
|
monkeypatch.setenv('PROXYDOCTOR_ABUSEIPDB_KEY','key')
|
|
calls=[]
|
|
monkeypatch.setattr(iq,'request_json',lambda url,**kw:calls.append(url) or ({'ip':'127.0.0.1'},'ok'))
|
|
result=iq.collect()
|
|
assert result['sources']['ipapi.is']['state']=='invalid_response'
|
|
assert len(calls)==1
|
|
|
|
|
|
def test_region_needs_repeat_confirmation(monkeypatch):
|
|
responses=iter([media.Page(200,'Netflix is not available in your country'),media.Page(200,'Welcome')])
|
|
monkeypatch.setattr(media,'fetch_page',lambda url:next(responses))
|
|
result=media.check('Netflix')
|
|
assert result['state']=='unknown' and result['evidence']=='inconsistent_results'
|
|
assert result['playback']=='not_tested' and len(result['probes'])==2
|
|
|
|
|
|
def test_priced_offer_needs_visible_specific_evidence_and_is_not_playback(monkeypatch):
|
|
page=media.Page(200,'<h1>Get YouTube Premium</h1><p>$13.99 / month</p>')
|
|
monkeypatch.setattr(media,'fetch_page',lambda url:page)
|
|
result=media.check('YouTube Premium')
|
|
assert result['state']=='offer_available' and result['playback']=='not_tested'
|
|
assert result['evidence']=='confirmed_visible_priced_subscription'
|
|
assert media.classify('YouTube Premium',media.Page(200,'YouTube Premium'))['state']=='unknown'
|
|
assert media.classify('YouTube Premium',media.Page(200,'<script>Get YouTube Premium $13.99</script>'))['state']=='unknown'
|
|
|
|
|
|
def test_waf_never_becomes_region_or_offer():
|
|
assert media.classify('Netflix',media.Page(403,'Netflix is not available in your country'))['state']=='unknown'
|
|
assert media.classify('YouTube Premium',media.Page(200,'Verify you are human. Get YouTube Premium $10'))['state']=='unknown'
|
|
|
|
|
|
def test_netflix_structured_title_is_catalog_only(monkeypatch):
|
|
def page(url):
|
|
if '/title/' not in url: return media.Page(200,'Welcome')
|
|
body=json.dumps({'@context':'https://schema.org','@type':'TVSeries','name':'Example','url':url})
|
|
return media.Page(200,'<script type="application/ld+json">'+body+'</script>')
|
|
monkeypatch.setattr(media,'fetch_page',page)
|
|
result=media.check('Netflix')
|
|
assert result['state']=='catalog_visible' and result['catalog_titles_observed']==2
|
|
assert result['playback']=='not_tested'
|
|
assert not media.title_evidence(media.Page(200,'80057281 TVSeries'), '80057281')
|
|
assert not media.title_evidence(media.Page(200,'<script type="application/ld+json">{"@type":"TVSeries","name":"X","url":"https://evil.com/title/80057281"}</script>'),'80057281')
|
|
|
|
|
|
def test_json_transport_error_does_not_echo_provider_body_or_key(monkeypatch):
|
|
class Opener:
|
|
def open(self,*args,**kwargs):
|
|
raise urllib.error.HTTPError('https://api.example/?secret',429,'secret',{},io.BytesIO(b'secret'))
|
|
monkeypatch.setattr(net,'opener',lambda:Opener())
|
|
assert net.request_json('https://api.example',headers={'Key':'secret'})==(None,'rate_limited')
|
|
|
|
|
|
def test_bounded_read_rejects_oversize_and_deadline():
|
|
with pytest.raises(ValueError): net.read_limited(io.BytesIO(b'12345'),4,float('inf'))
|
|
with pytest.raises(TimeoutError): net.read_limited(io.BytesIO(b'1'),4,0)
|
|
|
|
|
|
def test_media_redirects_only_allow_explicit_https_canonical_hosts(monkeypatch):
|
|
import urllib.request
|
|
seen=[]
|
|
class Response(io.BytesIO):
|
|
code=200
|
|
def geturl(self): return 'https://www.max.com/'
|
|
class Opener:
|
|
def open(self,request,timeout): return Response(b'<p>Welcome</p>')
|
|
def build(*handlers):
|
|
seen.extend(handlers)
|
|
return Opener()
|
|
monkeypatch.setattr(media.urllib.request,'build_opener',build)
|
|
media.fetch_page('https://www.max.com/')
|
|
handler=next(h for h in seen if isinstance(h,urllib.request.HTTPRedirectHandler))
|
|
request=urllib.request.Request('https://www.max.com/')
|
|
for url in ('http://www.max.com/','https://www.max.com.evil.test/','https://evil.test/',
|
|
'https://www.max.com:444/','https://name:password@www.max.com/'):
|
|
assert handler.redirect_request(request,None,302,'',{},url) is None
|
|
redirected=handler.redirect_request(request,None,302,'',{},'https://www.hbomax.com/')
|
|
assert redirected.full_url=='https://www.hbomax.com/'
|
|
|
|
|
|
def test_media_console_failure_does_not_abort_other_services(monkeypatch,capsys):
|
|
monkeypatch.setattr(media,'SERVICES',{'Netflix':None,'Spotify':None})
|
|
def check(name):
|
|
if name=='Netflix': raise RuntimeError('private response data')
|
|
return {'state':'access_denied','http':403,'evidence':'http_response'}
|
|
monkeypatch.setattr(media,'check_reachability',check)
|
|
results=media.run_console('en','always')
|
|
output=capsys.readouterr().out
|
|
assert len(results)==2 and '\x1b[33m' in output
|
|
assert 'Access denied' in output
|
|
assert 'private response data' not in output
|
|
|
|
|
|
def test_offline_json_stays_local_and_cannot_overwrite(tmp_path):
|
|
from proxydoctor.suite import run_suite
|
|
path=tmp_path/'result.json'
|
|
import platform
|
|
assert run_suite(offline=True,json_output=path)==(0 if platform.system()=='Linux' else 1)
|
|
result=json.loads(path.read_text(encoding='utf-8'))
|
|
assert result['offline'] and result['bandwidth'] is None and result['media']=={}
|
|
assert result['result']==('local_execution_complete' if platform.system()=='Linux' else 'incomplete')
|
|
original=path.read_bytes()
|
|
with pytest.raises(FileExistsError): run_suite(offline=True,json_output=path)
|
|
assert path.read_bytes()==original
|