idol-commons / tests /test_example_duplicates.py
fffiloni's picture
Complete bilingual responsive UI and per-asset licensing
0672639 verified
Raw History Blame Contribute Delete
5.64 kB
"""Cross-account and compare-and-swap safeguards for shared demo inputs."""
import copy,json,time,uuid,hashlib
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
from dataclasses import replace
from backend.app import create_app
from backend.duplicates import CATALOGUE,EXAMPLES,example_identity
from backend.store import canonical
from backend.repository import Conflict
from backend.validation import normalize_input
from test_api_publication import setup,client,fixture_generation,payload as base_payload,publish
def payload(identifier):
body=base_payload(identifier);body['rights']={'basis':'permission_for_cc_by','attribution':'Explicit fixture permission to share the source-derived result under CC BY.'};return body
EXAMPLE_SHA=next(k for k,v in EXAMPLES.items() if v['image']=='examples/obj-backpack.webp')
def demo_generation(app,owner,offset=0,seed=42,source=EXAMPLE_SHA,revision=None):
identifier=fixture_generation(app,owner,offset)
row=app.state.store.one('SELECT * FROM generations WHERE id=?',(identifier,))
result=json.loads(row['result']);params=json.loads(row['params']);params['seed']=seed
result['provenance']['source_image']={'kind':'uploaded','sha256':source}
if revision:result['provenance']['model']['revision']=revision
with app.state.store.transaction() as db:db.execute('UPDATE generations SET result=?,params=? WHERE id=?',(canonical(result),canonical(params),identifier))
return identifier
def test_pinned_catalogue_matches_actual_normalizer(tmp_path):
assert len(CATALOGUE['examples'])==94==len(EXAMPLES)
for path in Path('frontend/public/examples').glob('*.webp'):
entry=next(p for p in CATALOGUE['examples'] if p['image']=='examples/'+path.name)
assert hashlib.sha256(path.read_bytes()).hexdigest()==entry['raw_sha256']
assert normalize_input(path.read_bytes(),tmp_path/'normalized.png')['sha256']==entry['normalized_sha256']
def test_identical_file_between_accounts_links_to_original(setup):
app,repo=setup;a=client(app,'alice');b=client(app,'bob')
first=publish(a,payload(fixture_generation(app,'alice'))).json()
response=publish(b,payload(fixture_generation(app,'bob')))
assert response.status_code==200 and response.json()['status']=='already_exists'
assert response.json()['id']==first['id'] and response.json()['author']=='user-alice'
assert repo.version==1 and len(repo.data['records'])==1 and len(repo.files)==5
def test_example_other_seed_settings_title_and_account_is_one_asset(setup):
app,repo=setup;a=client(app,'alice');b=client(app,'bob')
first=publish(a,payload(demo_generation(app,'alice'))).json()
body=payload(demo_generation(app,'bob',.2,123));body['editorial']['title']='Another title'
response=publish(b,body);assert response.status_code==200,response.text
assert response.json()['status']=='already_exists' and response.json()['reason']=='demo_example'
assert response.json()['id']==first['id']
assert publish(b,body).json()['id']==first['id']
body['editorial']['title']='Changed again';assert publish(b,body).json()['id']==first['id']
assert repo.version==1 and len(repo.files)==5
asset=a.get('/api/catalogue').json()['assets'][0]
assert asset['demoExample']=='examples/obj-backpack.webp' and asset['author']=='user-alice'
def test_new_image_or_model_revision_is_not_false_duplicate(setup):
app,repo=setup;c=client(app,'alice')
ids=[demo_generation(app,'alice'),demo_generation(app,'alice',.1,source='e'*64),demo_generation(app,'alice',.2,revision='f'*40)]
for identifier in ids:assert publish(c,payload(identifier)).json()['status']=='published'
assert len(repo.data['records'])==3
def test_simultaneous_example_publications_commit_once(setup):
app,repo=setup
entries=[(client(app,owner),payload(demo_generation(app,owner,i*.1,10+i)),str(uuid.uuid4())) for i,owner in enumerate(['alice','bob'])]
def attempt(entry):
c,body,key=entry
for _ in range(50):
response=publish(c,body,key)
if response.status_code!=409:return response
time.sleep(.02)
return response
with ThreadPoolExecutor(max_workers=2) as pool:responses=list(pool.map(attempt,entries))
assert all(r.status_code==200 for r in responses),[r.text for r in responses]
assert {r.json()['status'] for r in responses}=={'published','already_exists'}
assert len({r.json()['id'] for r in responses})==1 and repo.version==1
def test_other_server_wins_cas_then_example_is_reconciled(setup,tmp_path):
app,repo=setup;other=create_app(replace(app.state.settings,state=tmp_path/'other'),repo)
a=client(app,'alice');b=client(other,'bob')
left=payload(demo_generation(app,'alice'));right=payload(demo_generation(other,'bob',.3,900))
original=repo.commit;winner=[]
def race(*args,**kwargs):
repo.commit=original
winner.append(publish(b,right).json())
raise Conflict()
repo.commit=race
response=publish(a,left)
assert response.status_code==200 and response.json()['status']=='already_exists'
assert response.json()['id']==winner[0]['id'] and repo.version==1 and len(repo.files)==5
def test_withdrawn_example_cannot_be_reintroduced_with_new_seed(setup):
app,repo=setup;c=client(app,'alice');first=publish(c,payload(demo_generation(app,'alice'))).json()
with app.state.store.transaction() as db:db.execute("UPDATE publications SET status='withdrawn' WHERE id=?",(first['id'],))
repo.data['records']=[]
assert publish(client(app,'bob'),payload(demo_generation(app,'bob',.1,777))).status_code==409