Files
Web-Parser-Constructor/tests/test_admin_http.py
T
2026-10-01 00:28:16 +10:00

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