Tech Tools section with a code generator, toner report that names the part, toner forecasting, warranty proof of cover, application uploads
This commit is contained in:
165
tests/test_core/test_application_uploads.py
Normal file
165
tests/test_core/test_application_uploads.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""Tests for application image and installer uploads.
|
||||
|
||||
The form used to take an image FILENAME and expect someone to drop the file into
|
||||
the frontend's own directory by hand, and an install path typed from memory.
|
||||
Both are uploads now, following the model-image trio.
|
||||
|
||||
The two differ deliberately and that is most of what is pinned here: the image
|
||||
is public because application tiles render before anything is authenticated, the
|
||||
installer is not, because it is licensed vendor software.
|
||||
"""
|
||||
|
||||
import io
|
||||
|
||||
from shopdb.core.models import Application
|
||||
|
||||
|
||||
def _app(db, name='Test App'):
|
||||
app = Application(appname=name)
|
||||
db.session.add(app)
|
||||
db.session.commit()
|
||||
return app
|
||||
|
||||
|
||||
def _png():
|
||||
# 1x1 PNG; enough to exercise the path without a fixture file.
|
||||
return io.BytesIO(
|
||||
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01'
|
||||
b'\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\x00'
|
||||
b'\x01\x00\x00\x05\x00\x01\r\n-\xb4\x00\x00\x00\x00IEND\xaeB`\x82')
|
||||
|
||||
|
||||
def test_image_upload_sets_the_served_url(client, db, auth_headers):
|
||||
app = _app(db)
|
||||
|
||||
response = client.post(f'/api/applications/{app.appid}/image',
|
||||
data={'file': (_png(), 'logo.png')},
|
||||
content_type='multipart/form-data',
|
||||
headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200, response.get_json()
|
||||
assert response.get_json()['data']['image'] == \
|
||||
f'/api/applications/image/application-{app.appid}.png'
|
||||
|
||||
|
||||
def test_image_is_readable_without_a_token(client, db, auth_headers):
|
||||
"""Tiles and lists render the image before anything is authenticated."""
|
||||
app = _app(db)
|
||||
client.post(f'/api/applications/{app.appid}/image',
|
||||
data={'file': (_png(), 'logo.png')},
|
||||
content_type='multipart/form-data', headers=auth_headers)
|
||||
|
||||
anon = client.get(f'/api/applications/image/application-{app.appid}.png')
|
||||
|
||||
assert anon.status_code == 200
|
||||
assert anon.mimetype == 'image/png'
|
||||
|
||||
|
||||
def test_image_rejects_a_non_image(client, db, auth_headers):
|
||||
app = _app(db)
|
||||
|
||||
response = client.post(f'/api/applications/{app.appid}/image',
|
||||
data={'file': (io.BytesIO(b'MZ'), 'payload.exe')},
|
||||
content_type='multipart/form-data',
|
||||
headers=auth_headers)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert 'Unsupported image type' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_installer_upload_sets_the_install_path(client, db, auth_headers):
|
||||
app = _app(db)
|
||||
|
||||
response = client.post(f'/api/applications/{app.appid}/package',
|
||||
data={'file': (io.BytesIO(b'fake installer'), 'setup.msi')},
|
||||
content_type='multipart/form-data',
|
||||
headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200, response.get_json()
|
||||
body = response.get_json()['data']
|
||||
assert body['installpath'] == \
|
||||
f'/api/applications/package/application-{app.appid}.msi'
|
||||
# The name the vendor shipped is reported back, since the stored copy is
|
||||
# renamed and the uploader needs to recognise what they sent.
|
||||
assert body['uploadedfilename'] == 'setup.msi'
|
||||
|
||||
|
||||
def test_installer_download_needs_a_token(client, db, auth_headers):
|
||||
"""Licensed software: an open URL would publish it to the whole network."""
|
||||
app = _app(db)
|
||||
client.post(f'/api/applications/{app.appid}/package',
|
||||
data={'file': (io.BytesIO(b'fake installer'), 'setup.msi')},
|
||||
content_type='multipart/form-data', headers=auth_headers)
|
||||
path = f'/api/applications/package/application-{app.appid}.msi'
|
||||
|
||||
assert client.get(path).status_code == 401
|
||||
|
||||
authed = client.get(path, headers=auth_headers)
|
||||
assert authed.status_code == 200
|
||||
assert authed.data == b'fake installer'
|
||||
# Sent as an attachment so a browser saves it rather than rendering it.
|
||||
assert 'attachment' in authed.headers.get('Content-Disposition', '')
|
||||
|
||||
|
||||
def test_installer_rejects_an_unlisted_type(client, db, auth_headers):
|
||||
app = _app(db)
|
||||
|
||||
response = client.post(f'/api/applications/{app.appid}/package',
|
||||
data={'file': (io.BytesIO(b'x'), 'driver.dll')},
|
||||
content_type='multipart/form-data',
|
||||
headers=auth_headers)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert 'Unsupported installer type' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_installer_rejects_an_oversize_file(client, db, auth_headers, monkeypatch):
|
||||
"""The cap keeps an ISO out of the instance directory."""
|
||||
from shopdb.core.api import applications as applications_api
|
||||
monkeypatch.setattr(applications_api, 'MAX_PACKAGE_BYTES', 1024)
|
||||
app = _app(db)
|
||||
|
||||
response = client.post(f'/api/applications/{app.appid}/package',
|
||||
data={'file': (io.BytesIO(b'x' * 4096), 'big.msi')},
|
||||
content_type='multipart/form-data',
|
||||
headers=auth_headers)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert 'the limit is' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_removing_an_installer_clears_only_an_uploaded_path(client, db, auth_headers):
|
||||
"""A share path was typed by a person and is not ours to wipe."""
|
||||
app = _app(db)
|
||||
client.post(f'/api/applications/{app.appid}/package',
|
||||
data={'file': (io.BytesIO(b'installer'), 'setup.msi')},
|
||||
content_type='multipart/form-data', headers=auth_headers)
|
||||
|
||||
cleared = client.delete(f'/api/applications/{app.appid}/package',
|
||||
headers=auth_headers)
|
||||
assert cleared.get_json()['data']['installpath'] is None
|
||||
|
||||
app.installpath = r'\\share\installers\vendor.msi'
|
||||
db.session.commit()
|
||||
again = client.delete(f'/api/applications/{app.appid}/package',
|
||||
headers=auth_headers)
|
||||
assert again.get_json()['data']['installpath'] == r'\\share\installers\vendor.msi'
|
||||
|
||||
|
||||
def test_replacing_an_image_does_not_orphan_the_old_extension(client, db, auth_headers):
|
||||
"""One image per application, whatever the extension arrives as."""
|
||||
import glob
|
||||
import os
|
||||
from flask import current_app
|
||||
app = _app(db)
|
||||
|
||||
client.post(f'/api/applications/{app.appid}/image',
|
||||
data={'file': (_png(), 'logo.png')},
|
||||
content_type='multipart/form-data', headers=auth_headers)
|
||||
client.post(f'/api/applications/{app.appid}/image',
|
||||
data={'file': (io.BytesIO(b'<svg/>'), 'logo.svg')},
|
||||
content_type='multipart/form-data', headers=auth_headers)
|
||||
|
||||
imagedir = os.path.join(current_app.instance_path, 'applicationimages')
|
||||
files = glob.glob(os.path.join(imagedir, f'application-{app.appid}.*'))
|
||||
assert len(files) == 1 and files[0].endswith('.svg')
|
||||
Reference in New Issue
Block a user