Attach empty removable drives in the Web UI (#877)

* Read the drive properties file once and store it in the Flask app config. Spin out the drive properties formatting to a helper method.

* Add empty removable disk drives to the attach peripherals UI

* Refinement of UI labels and help text, moving some context to the wiki
This commit is contained in:
Daniel Markstedt 2022-10-01 16:51:30 -07:00 committed by GitHub
parent 255a6e139f
commit d969fbdcce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 187 additions and 84 deletions

View File

@ -426,9 +426,9 @@
"revision": "1.0k", "revision": "1.0k",
"block_size": 2048, "block_size": 2048,
"size": null, "size": null,
"name": "Apple CD 600e", "name": "AppleCD 600e (Matsushita CR-8005)",
"file_type": null, "file_type": null,
"description": "Emulates Apple CD ROM drive for use with Macintosh computers.", "description": "Emulates an Apple CD-ROM drive for use with Macintosh computers.",
"url": "" "url": ""
}, },
{ {
@ -438,7 +438,7 @@
"revision": null, "revision": null,
"block_size": 512, "block_size": 512,
"size": null, "size": null,
"name": "Generic CD-ROM 512 block size", "name": "Generic CD-ROM block size 512",
"file_type": null, "file_type": null,
"description": "For use with host systems that expect the non-standard 512 byte block size for CD-ROM drives, such as Akai samplers.", "description": "For use with host systems that expect the non-standard 512 byte block size for CD-ROM drives, such as Akai samplers.",
"url": "" "url": ""

View File

@ -3,7 +3,7 @@
{% block content %} {% block content %}
<h2>{{ _("Disclaimer") }}</h2> <h2>{{ _("Disclaimer") }}</h2>
<p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p> <p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p>
<h2>{{ _("Hard Drives") }}</h2> <h2>{{ _("Hard Disk Drives") }}</h2>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black">
<tbody> <tbody>
@ -14,7 +14,7 @@
<td><b>{{ _("Ref.") }}</b></td> <td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td> <td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for hd in hd_conf|sort(attribute='name') %} {% for hd in drive_properties['hd_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ hd.name }}</td> <td style="text-align:center">{{ hd.name }}</td>
<td style="text-align:center">{{ hd.size_mb }}</td> <td style="text-align:center">{{ hd.size_mb }}</td>
@ -47,8 +47,8 @@
<hr/> <hr/>
<h2>{{ _("CD-ROM Drives") }}</h2> <h2>{{ _("CD/DVD Drives") }}</h2>
<p><em>{{ _("This will create a properties file for the given CD-ROM image. No new image file will be created.") }}</em></p> <p><em>{{ _("This will create a properties file for the given CD-ROM or DVD image. No new image file will be created.") }}</em></p>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black">
<tbody> <tbody>
<tr> <tr>
@ -58,7 +58,7 @@
<td><b>{{ _("Ref.") }}</b></td> <td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td> <td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for cd in cd_conf|sort(attribute='name') %} {% for cd in drive_properties['cd_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ cd.name }}</td> <td style="text-align:center">{{ cd.name }}</td>
<td style="text-align:center">{{ cd.size_mb }}</td> <td style="text-align:center">{{ cd.size_mb }}</td>
@ -94,7 +94,7 @@
<hr/> <hr/>
<h2>{{ _("Removable Drives") }}</h2> <h2>{{ _("Removable Disk Drives") }}</h2>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black">
<tbody> <tbody>
<tr> <tr>
@ -104,7 +104,7 @@
<td><b>{{ _("Ref.") }}</b></td> <td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td> <td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for rm in rm_conf|sort(attribute='name') %} {% for rm in drive_properties['rm_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ rm.name }}</td> <td style="text-align:center">{{ rm.name }}</td>
<td style="text-align:center">{{ rm.size_mb }}</td> <td style="text-align:center">{{ rm.size_mb }}</td>

View File

@ -326,31 +326,36 @@
{{ _("Attach Peripheral Device") }} {{ _("Attach Peripheral Device") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("<a href=\"%(url1)s\">DaynaPORT SCSI/Link</a> and <a href=\"%(url2)s\">X68000 Host Bridge</a> are network devices.", url1="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link", url2="https://github.com/akuker/RASCSI/wiki/X68000#Host_File_System_driver") }} <li>{{ _("Before using a networking device, it is recommended to run easyinstall.sh from the command line to configure your Raspberry Pi.") }}
</li> </li>
<ul> <ul>
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the inet field when attaching.") }}</li>
<li>{{ _("Configure the network bridge by running easyinstall.sh, or follow the <a href=\"%(url)s\">manual steps in the wiki</a>.", url="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link#manual-setup") }}
{% if bridge_configured %} {% if bridge_configured %}
<li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li> <li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %}
<li>{{ _("Please configure the <tt>rascsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %} {% endif %}
<li>{{ _("To browse the modern web, install a vintage web proxy like <a href=\"%(url)s\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li> <li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the inet field when attaching.") }}</li>
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li>
</li> </li>
</ul> </ul>
<li>{{ _("The Printer and Host Services device are currently supported on compatible Atari systems, and require <a href=\"%(url)s\">driver software</a> to be installed on the host system.", url="https://www.hddriver.net/en/rascsi_tools.html") }} <li>{{ _("Read more about <a href=\"%(url)s\">supported device types</a> on the wiki.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types") }}
</li> </li>
</ul> </ul>
</details> </details>
<table border="black" cellpadding="3"> <table border="black" cellpadding="3">
<tr style="font-weight: bold;"> <tr style="font-weight: bold;">
<td>{{ _("Peripheral") }}</td> <td>{{ _("Device") }}</td>
<td>{{ _("Code") }}</td>
<td>{{ _("Parameters and Actions") }}</td> <td>{{ _("Parameters and Actions") }}</td>
</tr> </tr>
{% for type in PERIPHERAL_DEVICE_TYPES %} {% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %}
<tr> <tr>
<td> <td>
<div>{{ device_types[type]["name"] }}</div> <div>{{ device_types[type]["name"] }}</div>
</td> </td>
<td>
<div>{{ type }}</div>
</td>
<td> <td>
<form action="/scsi/attach_device" method="post"> <form action="/scsi/attach_device" method="post">
<input name="type" type="hidden" value="{{ type }}"> <input name="type" type="hidden" value="{{ type }}">
@ -370,6 +375,35 @@
<input name="{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}"> <input name="{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if type in REMOVABLE_DEVICE_TYPES %}
<label for="drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name">
<option value="">
{{ _("None") }}
</option>
{% if type == "SCCD" %}
{% for drive in drive_properties["cd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCRM" %}
{% for drive in drive_properties["rm_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCMO" %}
{% for drive in drive_properties["mo_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
</select>
{% endif %}
<label for="scsi_id">{{ _("SCSI ID:") }}</label> <label for="scsi_id">{{ _("SCSI ID:") }}</label>
<select name="scsi_id"> <select name="scsi_id">
{% for id in scsi_ids %} {% for id in scsi_ids %}
@ -481,7 +515,6 @@
<ul> <ul>
<li>{{ _("Create an ISO file system CD-ROM image with the downloaded file, and mount it on the given SCSI ID.") }}</li> <li>{{ _("Create an ISO file system CD-ROM image with the downloaded file, and mount it on the given SCSI ID.") }}</li>
<li>{{ _("HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.") }}</li> <li>{{ _("HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.") }}</li>
<li>{{ _("On Mac OS, a <a href=\"%(url)s\">compatible CD-ROM driver</a> is required.", url="https://github.com/akuker/RASCSI/wiki/Drive-Setup#Mounting_CD_ISO_or_MO_images") }}</li>
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li> <li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
</ul> </ul>
</details> </details>

View File

@ -51,6 +51,7 @@ from web_utils import (
map_device_types_and_names, map_device_types_and_names,
get_device_name, get_device_name,
map_image_file_descriptions, map_image_file_descriptions,
format_drive_properties,
auth_active, auth_active,
is_bridge_configured, is_bridge_configured,
upload_with_dropzonejs, upload_with_dropzonejs,
@ -164,7 +165,7 @@ def index():
""" """
Sets up data structures for and renders the index page Sets up data structures for and renders the index page
""" """
if not ractl_cmd.is_token_auth()["status"] and not APP.config["TOKEN"]: if not ractl_cmd.is_token_auth()["status"] and not APP.config["RASCSI_TOKEN"]:
abort( abort(
403, 403,
_( _(
@ -217,6 +218,16 @@ def index():
server_info["sccd"] server_info["sccd"]
) )
try:
drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
except:
drive_properties = {
"hd_conf": [],
"cd_conf": [],
"rm_conf": [],
"mo_conf": [],
}
return response( return response(
template="index.html", template="index.html",
locales=get_supported_locales(), locales=get_supported_locales(),
@ -247,6 +258,7 @@ def index():
cdrom_file_suffix=tuple(server_info["sccd"]), cdrom_file_suffix=tuple(server_info["sccd"]),
removable_file_suffix=tuple(server_info["scrm"]), removable_file_suffix=tuple(server_info["scrm"]),
mo_file_suffix=tuple(server_info["scmo"]), mo_file_suffix=tuple(server_info["scmo"]),
drive_properties=drive_properties,
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX, PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES, ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(), REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(),
@ -268,39 +280,15 @@ def drive_list():
""" """
Sets up the data structures and kicks off the rendering of the drive list page Sets up the data structures and kicks off the rendering of the drive list page
""" """
# Reads the canonical drive properties into a dict try:
# The file resides in the current dir of the web ui process drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
drive_properties = Path(DRIVE_PROPERTIES_FILE) except:
if not drive_properties.is_file(): drive_properties = {
return response( "hd_conf": [],
error=True, "cd_conf": [],
message=_("Could not read drive properties from %(properties_file)s", "rm_conf": [],
properties_file=drive_properties), "mo_conf": [],
) }
process = file_cmd.read_drive_properties(str(drive_properties))
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
hd_conf = []
cd_conf = []
rm_conf = []
for device in conf:
if device["device_type"] == "SCHD":
device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = "{:,.2f}".format(device["size"] / 1024 / 1024)
hd_conf.append(device)
elif device["device_type"] == "SCCD":
device["size_mb"] = "N/A"
cd_conf.append(device)
elif device["device_type"] == "SCRM":
device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = "{:,.2f}".format(device["size"] / 1024 / 1024)
rm_conf.append(device)
server_info = ractl_cmd.get_server_info() server_info = ractl_cmd.get_server_info()
@ -308,9 +296,7 @@ def drive_list():
template="drives.html", template="drives.html",
files=file_cmd.list_images()["files"], files=file_cmd.list_images()["files"],
base_dir=server_info["image_dir"], base_dir=server_info["image_dir"],
hd_conf=hd_conf, drive_properties=drive_properties,
cd_conf=cd_conf,
rm_conf=rm_conf,
version=server_info["version"], version=server_info["version"],
cdrom_file_suffix=tuple(server_info["sccd"]), cdrom_file_suffix=tuple(server_info["sccd"]),
) )
@ -548,6 +534,7 @@ def attach_device():
Attaches a peripheral device that doesn't take an image file as argument Attaches a peripheral device that doesn't take an image file as argument
""" """
params = {} params = {}
drive_props = None
for item in request.form: for item in request.form:
if item == "scsi_id": if item == "scsi_id":
scsi_id = request.form.get(item) scsi_id = request.form.get(item)
@ -555,6 +542,12 @@ def attach_device():
unit = request.form.get(item) unit = request.form.get(item)
elif item == "type": elif item == "type":
device_type = request.form.get(item) device_type = request.form.get(item)
elif item == "drive_name":
drive_name = request.form.get(item)
for drive in APP.config["RASCSI_DRIVE_PROPERTIES"]:
if drive["name"] == drive_name:
drive_props = drive
break
else: else:
param = request.form.get(item) param = request.form.get(item)
if param: if param:
@ -577,6 +570,12 @@ def attach_device():
"device_type": device_type, "device_type": device_type,
"params": params, "params": params,
} }
if drive_props:
kwargs["vendor"] = drive_props["vendor"]
kwargs["product"] = drive_props["product"]
kwargs["revision"] = drive_props["revision"]
kwargs["block_size"] = drive_props["block_size"]
process = ractl_cmd.attach_device(scsi_id, **kwargs) process = ractl_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
@ -1099,15 +1098,23 @@ if __name__ == "__main__":
) )
arguments = parser.parse_args() arguments = parser.parse_args()
APP.config["TOKEN"] = arguments.password APP.config["RASCSI_TOKEN"] = arguments.password
sock_cmd = SocketCmdsFlask(host=arguments.rascsi_host, port=arguments.rascsi_port) sock_cmd = SocketCmdsFlask(host=arguments.rascsi_host, port=arguments.rascsi_port)
ractl_cmd = RaCtlCmds(sock_cmd=sock_cmd, token=APP.config["TOKEN"]) ractl_cmd = RaCtlCmds(sock_cmd=sock_cmd, token=APP.config["RASCSI_TOKEN"])
file_cmd = FileCmds(sock_cmd=sock_cmd, ractl=ractl_cmd, token=APP.config["TOKEN"]) file_cmd = FileCmds(sock_cmd=sock_cmd, ractl=ractl_cmd, token=APP.config["RASCSI_TOKEN"])
sys_cmd = SysCmds() sys_cmd = SysCmds()
if Path(f"{CFG_DIR}/{DEFAULT_CONFIG}").is_file(): if Path(f"{CFG_DIR}/{DEFAULT_CONFIG}").is_file():
file_cmd.read_config(DEFAULT_CONFIG) file_cmd.read_config(DEFAULT_CONFIG)
if Path(f"{DRIVE_PROPERTIES_FILE}").is_file():
process = file_cmd.read_drive_properties(DRIVE_PROPERTIES_FILE)
if process["status"]:
APP.config["RASCSI_DRIVE_PROPERTIES"] = process["conf"]
else:
logging.error(process["msg"])
else:
logging.warning("Could not read drive properties from %s", DRIVE_PROPERTIES_FILE)
logging.basicConfig(stream=sys.stdout, logging.basicConfig(stream=sys.stdout,
format="%(asctime)s %(levelname)s %(filename)s:%(lineno)s %(message)s", format="%(asctime)s %(levelname)s %(filename)s:%(lineno)s %(message)s",

View File

@ -81,15 +81,15 @@ def get_device_name(device_type):
Returns the human-readable name for the device type. Returns the human-readable name for the device type.
""" """
if device_type == "SCHD": if device_type == "SCHD":
return _("Hard Disk") return _("Hard Disk Drive")
if device_type == "SCRM": if device_type == "SCRM":
return _("Removable Disk") return _("Removable Disk Drive")
if device_type == "SCMO": if device_type == "SCMO":
return _("Magneto-Optical Disk") return _("Magneto-Optical Drive")
if device_type == "SCCD": if device_type == "SCCD":
return _("CD / DVD") return _("CD/DVD Drive")
if device_type == "SCBR": if device_type == "SCBR":
return _("X68000 Host Bridge") return _("Host Bridge")
if device_type == "SCDP": if device_type == "SCDP":
return _("DaynaPORT SCSI/Link") return _("DaynaPORT SCSI/Link")
if device_type == "SCLP": if device_type == "SCLP":
@ -132,6 +132,41 @@ def get_image_description(file_suffix):
return file_suffix return file_suffix
def format_drive_properties(drive_properties):
"""
Takes a (dict) with structured drive properties data
Returns a (dict) with the formatted properties, one (list) per device type
"""
hd_conf = []
cd_conf = []
rm_conf = []
mo_conf = []
FORMAT_FILTER = "{:,.2f}"
for device in drive_properties:
if device["device_type"] == "SCHD":
device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024)
hd_conf.append(device)
elif device["device_type"] == "SCCD":
device["size_mb"] = _("N/A")
cd_conf.append(device)
elif device["device_type"] == "SCRM":
device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024)
rm_conf.append(device)
elif device["device_type"] == "SCMO":
device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024)
mo_conf.append(device)
return {
"hd_conf": hd_conf,
"cd_conf": cd_conf,
"rm_conf": rm_conf,
"mo_conf": mo_conf,
}
def auth_active(group): def auth_active(group):
""" """
Inspects if the group defined in (str) group exists on the system. Inspects if the group defined in (str) group exists on the system.

View File

@ -1,7 +1,6 @@
import pytest import pytest
from conftest import ( from conftest import (
IMAGES_DIR,
SCSI_ID, SCSI_ID,
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
@ -27,7 +26,7 @@ def test_attach_image(http_client, create_test_image, detach_devices):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == ( assert response_data["messages"][0]["message"] == (
f"Attached {test_image} as Hard Disk to SCSI ID {SCSI_ID} LUN 0" f"Attached {test_image} as Hard Disk Drive to SCSI ID {SCSI_ID} LUN 0"
) )
# Cleanup # Cleanup
@ -38,8 +37,43 @@ def test_attach_image(http_client, create_test_image, detach_devices):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device_name,device_config", "device_name,device_config",
[ [
# TODO: Fix networking in container, else SCBR attachment fails (
# ("X68000 Host Bridge", {"type": "SCBR", "interface": "eth0", "inet": "10.10.20.1/24"}), "Removable Disk Drive",
{
"type": "SCRM",
"drive_props": {
"vendor": "VENDOR",
"product": "PRODUCT",
"revision": "0123",
"block_size": "512",
},
},
),
(
"Magneto-Optical Drive",
{
"type": "SCMO",
"drive_props": {
"vendor": "VENDOR",
"product": "PRODUCT",
"revision": "0123",
"block_size": "512",
},
},
),
(
"CD/DVD Drive",
{
"type": "SCCD",
"drive_props": {
"vendor": "VENDOR",
"product": "PRODUCT",
"revision": "0123",
"block_size": "512",
},
},
),
("Host Bridge", {"type": "SCBR", "interface": "eth0", "inet": "10.10.20.1/24"}),
("DaynaPORT SCSI/Link", {"type": "SCDP", "interface": "eth0", "inet": "10.10.20.1/24"}), ("DaynaPORT SCSI/Link", {"type": "SCDP", "interface": "eth0", "inet": "10.10.20.1/24"}),
("Host Services", {"type": "SCHS"}), ("Host Services", {"type": "SCHS"}),
("Printer", {"type": "SCLP", "timeout": 30, "cmd": "lp -oraw %f"}), ("Printer", {"type": "SCLP", "timeout": 30, "cmd": "lp -oraw %f"}),

View File

@ -4,7 +4,6 @@ import os
from conftest import ( from conftest import (
IMAGES_DIR, IMAGES_DIR,
AFP_DIR,
SCSI_ID, SCSI_ID,
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
@ -201,10 +200,7 @@ def test_upload_file(http_client, delete_file):
def test_download_file(http_client, create_test_image): def test_download_file(http_client, create_test_image):
file_name = create_test_image() file_name = create_test_image()
response = http_client.post( response = http_client.post("/files/download", data={"file": f"{IMAGES_DIR}/{file_name}"})
"/files/download",
data={"file": f"{IMAGES_DIR}/{file_name}"}
)
assert response.status_code == 200 assert response.status_code == 200
assert response.headers["content-type"] == "application/octet-stream" assert response.headers["content-type"] == "application/octet-stream"

View File

@ -42,9 +42,7 @@ def test_show_named_drive_presets(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert "cd_conf" in response_data["data"] assert "drive_properties" in response_data["data"]
assert "hd_conf" in response_data["data"]
assert "rm_conf" in response_data["data"]
# route("/drive/cdrom", methods=["POST"]) # route("/drive/cdrom", methods=["POST"])