139 lines
7.9 KiB
Python
139 lines
7.9 KiB
Python
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
|