from bs4 import BeautifulSoup from test_web import env,login def headers(app,client): return {'origin':'http://testserver','x-csrf-token':app.state.auth.csrf(client.cookies['lab_session'])} def test_import_configure_preview_publish_and_conflict(env): app,c=env;login(c);h=headers(app,c) files={'file':('study.csv',b'name,score\nAlice,7\nBob,5','text/csv')} assert c.post('/api/admin/inspect',files=files).status_code==403 inspection=c.post('/api/admin/inspect',files=files,headers=h) assert inspection.status_code==200 and inspection.json()['count']==2 d=c.post('/api/admin/import',files=files,data={'title':'Study'},headers=h).json() from parser_lab.views import default_view config={'revision':d['revision'],'title':'Study','slug':d['slug'],'description':'', 'views':{'table':default_view('table',['/name','/score'])}} preview=c.post(f"/api/admin/datasets/{d['id']}/preview?kind=table",json=config,headers=h) assert preview.status_code==200 and 'Alice' in preview.json()['html'] assert app.state.repo.get(d['id'])['views']=={} saved=c.put(f"/api/admin/datasets/{d['id']}",json=config,headers=h) assert saved.status_code==200 assert c.put(f"/api/admin/datasets/{d['id']}",json=config,headers=h).status_code==409 published=c.post(f"/api/admin/datasets/{d['id']}/publish",json={'revision':saved.json()['revision']},headers=h) assert published.status_code==200 url=f"/interactive/{d['slug']}/v/1/table" page=c.get(url);assert page.status_code==200 and 'Alice' in page.text assert '/rio/' not in page.text assert c.get(url+'?fragment=1').json()['total']==2 def test_api_rejects_unauthorized_and_bad_config(env): app,c=env assert c.post('/api/admin/inspect',headers={'origin':'http://testserver'}).status_code==401 login(c);h=headers(app,c) d=app.state.repo.create('Test',b'{}','a.json',[{'name':'Alice'}]) response=c.put(f"/api/admin/datasets/{d['id']}",json={'views':{'table':{'columns':'bad'}},'revision':1},headers=h) assert response.status_code==400 assert app.state.repo.get(d['id'])['revision']==1 def test_replacement_preserves_published_snapshot(env): app,c=env;login(c);h=headers(app,c) from parser_lab.views import default_view d=app.state.repo.create('Test',b'name\nAlice','a.csv',[{'name':'Alice'}]) d['views']={'table':default_view('table',['/name'])};d=app.state.repo.save(d,1);p=app.state.repo.publish(d['id'],d['revision']) data={'replace_id':d['id'],'revision':str(d['revision'])} replaced=c.post('/api/admin/import',files={'file':('b.csv',b'name\nBob','text/csv')},data=data,headers=h) assert replaced.status_code==200 assert app.state.repo.get(d['id'])['views']=={} assert app.state.repo.publication(p['slug'],1)['records']==[{'name':'Alice'}] def test_publish_rejects_malformed_payload_and_client_cannot_replace_records(env): app,c=env;login(c);h=headers(app,c) d=app.state.repo.create('Test',b'{}','a.json',[{'name':'Alice'}]) for payload in ([],{'revision':None},{'revision':True}): assert c.post(f"/api/admin/datasets/{d['id']}/publish",json=payload,headers=h).status_code==400 payload={'revision':1,'views':{},'records':[{'name':'Injected'}],'id':'elsewhere'} assert c.put(f"/api/admin/datasets/{d['id']}",json=payload,headers=h).status_code==200 assert app.state.repo.get(d['id'])['records']==[{'name':'Alice'}] def test_example_questionnaires_import_without_manual_paths(env): from pathlib import Path app,c=env;login(c);h=headers(app,c) raw=(Path(__file__).parents[1]/'examples/data/questionnaires.json').read_bytes() files={'file':('anything.json',raw,'application/json')} inspected=c.post('/api/admin/inspect',files=files,headers=h).json() assert inspected['count']==2 assert inspected['record_pointer']=='/participants' assert inspected['choose_record_level'] is False response=c.post('/api/admin/import',files=files,headers=h) assert response.status_code==200 d=app.state.repo.get(response.json()['id']) assert [r['participant'] for r in d['records']]==['A01','A02'] assert d['views']['questionnaire']['questions_pointer']=='/answers' assert d['views']['questionnaire']['title_pointer']=='/participant' assert c.post(f"/api/admin/datasets/{d['id']}/publish",json={'revision':d['revision']},headers=h).status_code==200 soup=BeautifulSoup(c.get(f"/html/{d['slug']}/v/1/questionnaire").text,'html.parser') assert len(soup.select('.record'))==2 assert [x.text for x in soup.select('.field-pair dd')]==['8','Прогулка','true','5','null','false'] assert len(soup.select('.field-pair dt'))==6 explicit=c.post('/api/admin/import',files=files,data={'root_confirmed':'1'},headers=h) assert len(app.state.repo.get(explicit.json()['id'])['records'])==1 def test_existing_wrong_level_repair_preserves_publication_and_checks_revision(env): from pathlib import Path from parser_lab.importing import read_upload,ImportOptions from parser_lab.views import default_view app,c=env;login(c);h=headers(app,c) raw=(Path(__file__).parents[1]/'examples/data/questionnaires.json').read_bytes() d=app.state.repo.create('Original title',raw,'questionnaires.json',read_upload(raw,'q.json',ImportOptions()).records) d['views']={'table':default_view('table',['/description'])};d=app.state.repo.save(d,1) original=app.state.repo.publish(d['id'],d['revision']) url=f"/api/admin/datasets/{d['id']}/configure-questionnaire" assert c.post(url,json={'revision':1},headers=h).status_code==409 assert len(app.state.repo.get(d['id'])['records'])==1 response=c.post(url,json={'revision':d['revision']},headers=h) assert response.status_code==200 fixed=app.state.repo.get(d['id']) assert len(fixed['records'])==2 and fixed['title']=='Original title' assert fixed['views']['questionnaire']['question_pointer']=='/question' assert app.state.repo.publication(d['slug'],1)==original assert (app.state.repo.root/'sources'/fixed['source']).read_bytes()==raw response=c.post(f"/api/admin/datasets/{d['id']}/preview?kind=questionnaire",json=fixed,headers=h).json() assert response['errors']==[] and 'A01' in response['html'] and 'Прогулка' in response['html'] def test_ambiguous_questionnaire_wrapper_requires_choice(env): import json app,c=env;login(c);h=headers(app,c) rows=[{'participant':'A','answers':[{'question':'Q','answer':0}]}] raw=json.dumps({'first':rows,'second':rows}).encode() files={'file':('ambiguous.json',raw,'application/json')} inspected=c.post('/api/admin/inspect',files=files,headers=h).json() assert inspected['choose_record_level'] and inspected['count']==1 assert c.post('/api/admin/import',files=files,headers=h).status_code==400 d=c.post('/api/admin/import',files=files,data={'pointer':'/second'},headers=h).json() assert app.state.repo.get(d['id'])['views']['questionnaire']['answer_pointer']=='/answer' def test_questionnaire_rejects_array_as_question(env): from pathlib import Path from parser_lab.importing import read_upload,ImportOptions from parser_lab.views import default_view app,c=env;login(c);h=headers(app,c) raw=(Path(__file__).parents[1]/'examples/data/questionnaires.json').read_bytes() records=read_upload(raw,'q.json',ImportOptions()).records d=app.state.repo.create('Wrong level',raw,'q.json',records) view=default_view('questionnaire',['/description','/participants']) view.update(questionnaire_mode='pairs',questions_pointer='/participants',question_pointer='/answers',answer_pointer='/answers') config={'revision':1,'views':{'questionnaire':view}} preview=c.post(f"/api/admin/datasets/{d['id']}/preview?kind=questionnaire",json=config,headers=h).json() assert preview['errors'] and preview['html']=='' d['views']=config['views'];d=app.state.repo.save(d,1) assert c.post(f"/api/admin/datasets/{d['id']}/publish",json={'revision':d['revision']},headers=h).status_code==400