Web UI: Rework the Attach Device section to be universal (#1393)

* Correct German translation for Key

* Web UI: Rework the Attach Device section to be universal

* Web UI: Warn when working dirs are missing

* Refactor tests to use global endpoint constants

* Add fallback for unknown disk type devices

* Rearrange the index page sections

* Move Macproxy help text to admins page

* Remove image list exception for SCHD

* Show Settings button when auth is diabled

* Tweak CSS styles for both themes

* Move Eject action next to the file name, and improve UI labels
This commit is contained in:
Daniel Markstedt 2023-12-07 17:38:24 -08:00 committed by GitHub
parent 0f352396be
commit 05b9e0eb18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 455 additions and 428 deletions

View File

@ -58,6 +58,15 @@ div.footer div.theme-change-hint {
margin-bottom: 15px; margin-bottom: 15px;
} }
div.login-status {
text-align: right;
}
div.login-status a {
color: white;
text-decoration: underline;
}
div.logged-in { div.logged-in {
background-color: green; background-color: green;
} }

View File

@ -282,18 +282,6 @@ div.header div.login-form-title {
display: none; display: none;
} }
div.header div.authentication-disabled span.separator {
display: none;
}
div.header div.authentication-disabled span.wiki-help-text {
display: block;
}
div.header div.authentication-disabled a {
color: #fff;
}
@media (max-width: 900px) { @media (max-width: 900px) {
div.header { div.header {
flex-wrap: wrap; flex-wrap: wrap;
@ -663,10 +651,11 @@ table#attached-devices td.actions {
table#attached-devices td.parameters form { table#attached-devices td.parameters form {
display: flex; display: flex;
align-items: center;
} }
table#attached-devices td.parameters form label { table#attached-devices td.parameters form label {
display: none; padding: 0 0.5rem 0 0;
} }
table#attached-devices td.parameters form select { table#attached-devices td.parameters form select {
@ -775,6 +764,11 @@ section#files p {
margin-top: 1rem; margin-top: 1rem;
} }
section#files details.subdir {
padding-left: 1rem;
padding-right: 1rem;
}
section#files details.subdir summary.dirname { section#files details.subdir summary.dirname {
text-decoration: underline; text-decoration: underline;
font-family: monospace; font-family: monospace;
@ -864,7 +858,7 @@ section#upload a p {
/* /*
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
Index > Section: Attach peripheral devices Index > Section: Attach devices
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*/ */
section#attach-devices table th:last-child, section#attach-devices table th:last-child,
@ -876,6 +870,10 @@ section#attach-devices form {
display: block; display: block;
} }
section#attach-devices table form select.table-dropdown {
width: 16rem;
}
@media (max-width: 900px) { @media (max-width: 900px) {
section#attach-devices table tr th:nth-child(2), section#attach-devices table tr th:nth-child(2),
section#attach-devices table tr td:nth-child(2) { section#attach-devices table tr td:nth-child(2) {

View File

@ -115,6 +115,8 @@
<ul> <ul>
<li>{{ _("If you want to add a service, run the easyinstall.sh script and choose the one to install.") }}</li> <li>{{ _("If you want to add a service, run the easyinstall.sh script and choose the one to install.") }}</li>
<li>{{ _("In order to manage the services in the Web UI, you may install Webmin as well.") }}</li> <li>{{ _("In order to manage the services in the Web UI, you may install Webmin as well.") }}</li>
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/PiSCSI/piscsi/wiki/Vintage-Web-Proxy#macproxy") }}
</li>
</ul> </ul>
</details> </details>
<ul class="service_status"> <ul class="service_status">

View File

@ -27,37 +27,31 @@
<body class="{{ body_classes|join(' ') }}"> <body class="{{ body_classes|join(' ') }}">
<div class="header"> <div class="header">
{% if env["auth_active"] %} {% if env["logged_in"] or not env["auth_active"] %}
<div align="center" class="login-status logged-in">
{% if env["logged_in"] %} {% if env["logged_in"] %}
<div align="center" class="login-status logged-in"> <span class="logged-in-as-text">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }}</span>
<span class="logged-in-as-text">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }}</span> <span class="separator">-</span>
<span class="separator">-</span> <span class="log-out-button"><a href="/logout">{{ _("Log Out") }}</a></span>
<span class="log-out-button"><a href="/logout">{{ _("Log Out") }}</a></span> <span class="separator">-</span>
<span class="separator">-</span>
<span class="admin-button"><a href="/sys/admin">{{ _("Settings") }}</a></span>
</div>
{% else %}
<div align="center" class="login-status logged-out">
<form method="POST" action="/login">
<div class="login-form-title">{{ _("Log in to use Web Interface") }}</div>
<span>
<label for="username">{{ _("Username:") }}</label>
<input type="text" name="username" id="username">
</span>
<span>
<label for="password">{{ _("Password:") }}</label>
<input type="password" name="password" id="password">
</span>
<input type="submit" value="Login">
</form>
</div>
{% endif %} {% endif %}
<span class="admin-button"><a href="/sys/admin">{{ _("Settings") }}</a></span>
</div>
{% else %} {% else %}
<div align="center" class="login-status authentication-disabled"> <div align="center" class="login-status logged-out">
<span class="authentication-disabled-text">{{ _("Web Interface Authentication Disabled") }}</span> <form method="POST" action="/login">
<span class="separator">-</span> <div class="login-form-title">{{ _("Log in to use Web Interface") }}</div>
<span class="wiki-help-text">{{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/PiSCSI/piscsi/wiki/Web-Interface#enable-authentication") }}</span> <span>
</div> <label for="username">{{ _("Username:") }}</label>
<input type="text" name="username" id="username">
</span>
<span>
<label for="password">{{ _("Password:") }}</label>
<input type="password" name="password" id="password">
</span>
<input type="submit" value="Login">
</form>
</div>
{% endif %} {% endif %}
<div align="center" class="title"> <div align="center" class="title">

View File

@ -24,6 +24,7 @@
</ul> </ul>
</details> </details>
{% if env["cfg_dir_exists"] %}
<p> <p>
<form action="/config/action" method="post" id="config-actions"> <form action="/config/action" method="post" id="config-actions">
<label for="config_load_name">{{ _("File Name:") }}</label> <label for="config_load_name">{{ _("File Name:") }}</label>
@ -54,6 +55,13 @@
<input type="submit" value="{{ _("Save") }}"> <input type="submit" value="{{ _("Save") }}">
</form> </form>
</p> </p>
{% else %}
<div class="notice">
{{ _("Please create the PiSCSI configuration dir to use configurations:")}} {{ CFG_DIR }}
</div>
{% endif %}
<table id="attached-devices" border="black" cellpadding="3" summary="List of attached devices"> <table id="attached-devices" border="black" cellpadding="3" summary="List of attached devices">
<tbody> <tbody>
@ -82,13 +90,12 @@
{% endif %} {% endif %}
<td class="name" align="center">{{ device.device_name }}</td> <td class="name" align="center">{{ device.device_name }}</td>
<td class="parameters"> <td class="parameters">
{% if "No Media" in device.status %} {% if "No Media" in device.status %}
<form action="/scsi/attach" method="post"> <form action="/scsi/attach" method="post">
<label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File:") }}</label>
<input name="scsi_id" type="hidden" value="{{ device.id }}"> <input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}"> <input name="unit" type="hidden" value="{{ device.unit }}">
<input name="type" type="hidden" value="{{ device.device_type }}"> <input name="type" type="hidden" value="{{ device.device_type }}">
<input name="file_size" type="hidden" value="{{ device.size }}">
<label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File name") }}</label>
<select type="select" name="file_name" id="device_list_file_name_{{ device.id }}_{{ device.unit }}"> <select type="select" name="file_name" id="device_list_file_name_{{ device.id }}_{{ device.unit }}">
{% for f in files|sort(attribute='name') %} {% for f in files|sort(attribute='name') %}
{% if device.device_type == "SCCD" %} {% if device.device_type == "SCCD" %}
@ -106,7 +113,7 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</select> </select>
<input type="submit" value="{{ _("Attach") }}"> <input type="submit" value="{{ _("Insert") }}">
</form> </form>
{% else %} {% else %}
{% if device.params %} {% if device.params %}
@ -120,7 +127,14 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% elif device.file %} {% elif device.file %}
<span class="filename">{{ device.file }}</span> <form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
<label>{{ device.file }}</label>
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
<input type="submit" value="{{ _("Eject") }}">
{% endif %}
</form>
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>
@ -135,13 +149,6 @@
</td> </td>
<td class="actions" align="center"> <td class="actions" align="center">
{% if device.id in scsi_ids["occupied_ids"] %} {% if device.id in scsi_ids["occupied_ids"] %}
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
<input type="submit" value="{{ _("Eject") }}">
</form>
{% endif %}
<form action="/scsi/detach" method="post" onsubmit="return confirm('{{ _("Detach Device?") }}')"> <form action="/scsi/detach" method="post" onsubmit="return confirm('{{ _("Detach Device?") }}')">
<input name="scsi_id" type="hidden" value="{{ device.id }}"> <input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}"> <input name="unit" type="hidden" value="{{ device.unit }}">
@ -209,12 +216,8 @@
</ul> </ul>
</details> </details>
{% if not files|length: %} {% if env["image_dir_exists"] %}
<div class="notice"> {% if files|length %}
{{ _("The images directory is currently empty.") }}
</div>
{% else %}
<div> <div>
{% for subdir, group in formatted_image_files.items() %} {% for subdir, group in formatted_image_files.items() %}
@ -309,7 +312,6 @@
{% else %} {% else %}
<form action="/scsi/attach" method="post" class="file-attach"> <form action="/scsi/attach" method="post" class="file-attach">
<input name="file_name" type="hidden" value="{{ file['name'] }}"> <input name="file_name" type="hidden" value="{{ file['name'] }}">
<input name="file_size" type="hidden" value="{{ file['size'] }}">
<label for="image_list_scsi_id_{{ file["name"] }}">{{ _("ID") }}</label> <label for="image_list_scsi_id_{{ file["name"] }}">{{ _("ID") }}</label>
<select name="scsi_id" id="image_list_scsi_id_{{ file["name"] }}"> <select name="scsi_id" id="image_list_scsi_id_{{ file["name"] }}">
{% for id in scsi_ids["valid_ids"] %} {% for id in scsi_ids["valid_ids"] %}
@ -370,8 +372,170 @@
</details> </details>
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<div class="notice">
{{ _("The images directory is currently empty.") }}
</div>
{% endif %} {% endif %}
<p><small>{{ _("%(disk_space)s MiB disk space remaining for images", disk_space=env["free_disk_space"]) }}</small></p> <p>
<small>{{ _("%(disk_space)s MiB disk space remaining for images", disk_space=env["free_disk_space"]) }}</small>
</p>
{% else %}
<div class="notice">
{{ _("Please create the PiSCSI images dir to work with disk images:")}} {{ env["image_dir"] }}
</div>
{% endif %}
</section>
<hr/>
<section id="attach-devices">
<details>
<summary class="heading">
{{ _("Attach Device") }}
</summary>
<ul>
</li>
{% if bridge_configured %}
<li>{{ _("The <tt>piscsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %}
<li>{{ _("Please configure the <tt>piscsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %}
<li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types") }}
</li>
</ul>
</details>
<table border="black" cellpadding="3" summary="List of peripheral devices">
<tr>
<th scope="col">{{ _("Device") }}</th>
<th scope="col">{{ _("Key") }}</th>
<th scope="col">{{ _("Actions") }}</th>
</tr>
{% for type in device_types.keys() %}
<tr>
<td>
{% if device_types[type]["name"] == type %}
{% if type in REMOVABLE_DEVICE_TYPES %}
<div>{{ _("Unknown Removable Disk Drive") }}</div>
{% elif type in DISK_DEVICE_TYPES %}
<div>{{ _("Unknown Fixed Disk Drive") }}</div>
{% else %}
<div>{{ _("Unknown Device") }}</div>
{% endif %}
{% else %}
<div>{{ device_types[type]["name"] }}</div>
{% endif %}
</td>
<td>
<div>{{ type }}</div>
</td>
<td>
<form action="/scsi/attach" method="post" class="device-attach">
<input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"] | dictsort %}
<label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
{% elif key == "interface" %}
<select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
{% for if in netinfo["ifs"] %}
<option value="{{ if }}">
{{ if }}
</option>
{% endfor %}
</select>
{% else %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %}
{% endfor %}
{% if type in DISK_DEVICE_TYPES %}
<label for="{{ type }}_drive_name">{{ _("Identify as:") }}</label>
<select name="drive_name" id="{{ type }}_drive_name" class="table-dropdown">
<option value="">
{{ _("Generic device") }}
</option>
{% if type == "SCHD" %}
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% 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>
<label for="{{ type }}_image_file_name">{{ _("Image file:") }}</label>
<select name="file_name" id="{{ type }}_image_file_name" class="table-dropdown" {% if type not in REMOVABLE_DEVICE_TYPES %}required{% endif %}>
<option value="" selected {% if type not in REMOVABLE_DEVICE_TYPES %}disabled{% endif %}>
{% if type in REMOVABLE_DEVICE_TYPES %}
{{ _("None") }}
{% else %}
{{ _("Choose a file...") }}
{% endif %}
</option>
{% for f in files|sort(attribute='name') %}
{% if type == "SCHD" %}
{% if f["name"].lower().endswith(env['hd_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCCD" %}
{% if f["name"].lower().endswith(env['cd_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCRM" %}
{% if f["name"].lower().endswith(env['rm_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCMO" %}
{% if f["name"].lower().endswith(env['mo_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% else %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% endfor %}
</select>
{% endif %}
<label for="{{ type }}_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="{{ type }}_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<label for="{{ type }}_unit">{{ _("LUN") }}</label>
<input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
<input type="submit" value="{{ _("Attach") }}" title="{{ _("Attach") }}">
</form>
</td>
</tr>
{% endfor %}
</table>
</section> </section>
<hr/> <hr/>
@ -432,6 +596,69 @@
<hr/> <hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Identify as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("Generic device") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("Unformatted") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="create-iso"> <section id="create-iso">
<details> <details>
<summary class="heading"> <summary class="heading">
@ -507,166 +734,4 @@
<hr/> <hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("None") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("None") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="attach-devices">
<details>
<summary class="heading">
{{ _("Attach Peripheral Device") }}
</summary>
<ul>
</li>
{% if bridge_configured %}
<li>{{ _("The <tt>piscsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %}
<li>{{ _("Please configure the <tt>piscsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %}
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/PiSCSI/piscsi/wiki/Vintage-Web-Proxy#macproxy") }}</li>
</li>
<li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types") }}
</li>
</ul>
</details>
<table border="black" cellpadding="3" summary="List of peripheral devices">
<tr>
<th scope="col">{{ _("Device") }}</th>
<th scope="col">{{ _("Key") }}</th>
<th scope="col">{{ _("Parameters and Actions") }}</th>
</tr>
{% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %}
<tr>
<td>
<div>{{ device_types[type]["name"] }}</div>
</td>
<td>
<div>{{ type }}</div>
</td>
<td>
<form action="/scsi/attach_device" method="post" class="device-attach">
<input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"] | dictsort %}
<label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
{% elif key == "interface" %}
<select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
{% for if in netinfo["ifs"] %}
<option value="{{ if }}">
{{ if }}
</option>
{% endfor %}
</select>
{% else %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %}
{% endfor %}
{% if type in REMOVABLE_DEVICE_TYPES %}
<label for="{{ type }}_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="{{ type }}_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="{{ type }}_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="{{ type }}_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<label for="{{ type }}_unit">{{ _("LUN") }}</label>
<input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
<input type="submit" value="{{ _("Attach") }}" title="{{ _("Attach") }}">
</form>
</td>
</tr>
{% endfor %}
</table>
</section>
<hr/>
{% endblock content %} {% endblock content %}

View File

@ -1309,7 +1309,7 @@ msgstr ""
#: src/templates/index.html:594 #: src/templates/index.html:594
msgid "Key" msgid "Key"
msgstr "Taste" msgstr "Kürzel"
#: src/templates/index.html:595 #: src/templates/index.html:595
msgid "Parameters and Actions" msgid "Parameters and Actions"

View File

@ -46,7 +46,6 @@ from return_code_mapper import ReturnCodeMapper
from socket_cmds_flask import SocketCmdsFlask from socket_cmds_flask import SocketCmdsFlask
from web_utils import ( from web_utils import (
working_dirs_exist,
sort_and_format_devices, sort_and_format_devices,
get_valid_scsi_ids, get_valid_scsi_ids,
map_device_types_and_names, map_device_types_and_names,
@ -125,6 +124,9 @@ def get_env_info():
"image_dir": server_info["image_dir"], "image_dir": server_info["image_dir"],
"image_root_dir": Path(server_info["image_dir"]).name, "image_root_dir": Path(server_info["image_dir"]).name,
"shared_root_dir": Path(FILE_SERVER_DIR).name, "shared_root_dir": Path(FILE_SERVER_DIR).name,
"image_dir_exists": Path(server_info["image_dir"]).exists(),
"cfg_dir_exists": Path(CFG_DIR).exists(),
"hd_suffixes": tuple(server_info["schd"]),
"cd_suffixes": tuple(server_info["sccd"]), "cd_suffixes": tuple(server_info["sccd"]),
"rm_suffixes": tuple(server_info["scrm"]), "rm_suffixes": tuple(server_info["scrm"]),
"mo_suffixes": tuple(server_info["scmo"]), "mo_suffixes": tuple(server_info["scmo"]),
@ -219,7 +221,6 @@ def index():
Sets up data structures for and renders the index page Sets up data structures for and renders the index page
""" """
server_info = piscsi_cmd.get_server_info() server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
devices = piscsi_cmd.list_devices() devices = piscsi_cmd.list_devices()
device_types = map_device_types_and_names(piscsi_cmd.get_device_types()["device_types"]) device_types = map_device_types_and_names(piscsi_cmd.get_device_types()["device_types"])
@ -304,9 +305,6 @@ 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
""" """
server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
return response( return response(
template="drives.html", template="drives.html",
page_title=_("PiSCSI Create Drive"), page_title=_("PiSCSI Create Drive"),
@ -342,7 +340,6 @@ def upload_page():
Sets up the data structures and kicks off the rendering of the file uploading page Sets up the data structures and kicks off the rendering of the file uploading page
""" """
server_info = piscsi_cmd.get_server_info() server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
return response( return response(
template="upload.html", template="upload.html",
@ -544,7 +541,6 @@ def show_diskinfo():
if not safe_path["status"]: if not safe_path["status"]:
return response(error=True, message=safe_path["msg"]) return response(error=True, message=safe_path["msg"])
server_info = piscsi_cmd.get_server_info() server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
returncode, diskinfo = sys_cmd.get_diskinfo(Path(server_info["image_dir"]) / file_name) returncode, diskinfo = sys_cmd.get_diskinfo(Path(server_info["image_dir"]) / file_name)
if returncode == 0: if returncode == 0:
return response( return response(
@ -647,16 +643,17 @@ def log_level():
return response(error=True, message=process["msg"]) return response(error=True, message=process["msg"])
@APP.route("/scsi/attach_device", methods=["POST"]) @APP.route("/scsi/attach", methods=["POST"])
@login_required @login_required
def attach_device(): def attach_device():
""" """
Attaches a peripheral device that doesn't take an image file as argument Attaches device of any type
""" """
scsi_id = request.form.get("scsi_id") scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit") unit = request.form.get("unit")
device_type = request.form.get("type") device_type = request.form.get("type")
drive_name = request.form.get("drive_name") drive_name = request.form.get("drive_name")
file_name = request.form.get("file_name")
if not scsi_id: if not scsi_id:
return response(error=True, message=_("No SCSI ID specified")) return response(error=True, message=_("No SCSI ID specified"))
@ -690,11 +687,29 @@ def attach_device():
"device_type": device_type, "device_type": device_type,
"params": params, "params": params,
} }
if file_name:
kwargs["params"]["file"] = file_name
# If drive_props is defined use properies from this dict,
# otherwise fall back to the properties file if it exists
if drive_props: if drive_props:
kwargs["vendor"] = drive_props["vendor"] kwargs["vendor"] = drive_props["vendor"]
kwargs["product"] = drive_props["product"] kwargs["product"] = drive_props["product"]
kwargs["revision"] = drive_props["revision"] kwargs["revision"] = drive_props["revision"]
kwargs["block_size"] = drive_props["block_size"] kwargs["block_size"] = drive_props["block_size"]
else:
drive_properties = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
if drive_properties.is_file():
process = file_cmd.read_drive_properties(drive_properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
kwargs["vendor"] = conf["vendor"]
kwargs["product"] = conf["product"]
kwargs["revision"] = conf["revision"]
kwargs["block_size"] = conf["block_size"]
process = piscsi_cmd.attach_device(scsi_id, **kwargs) process = piscsi_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
@ -711,70 +726,6 @@ def attach_device():
return response(error=True, message=process["msg"]) return response(error=True, message=process["msg"])
@APP.route("/scsi/attach", methods=["POST"])
@login_required
def attach_image():
"""
Attaches a file image as a device
"""
file_name = request.form.get("file_name")
file_size = request.form.get("file_size")
scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit")
device_type = request.form.get("type")
if not scsi_id:
return response(error=True, message=_("No SCSI ID specified"))
if not file_name:
return response(error=True, message=_("No image file to insert"))
kwargs = {"unit": int(unit), "params": {"file": file_name}}
if device_type:
kwargs["device_type"] = device_type
device_types = piscsi_cmd.get_device_types()
expected_block_size = min(device_types["device_types"][device_type]["block_sizes"])
# Attempt to load the device properties file:
# same file name with PROPERTIES_SUFFIX appended
drive_properties = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
if drive_properties.is_file():
process = file_cmd.read_drive_properties(drive_properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
kwargs["vendor"] = conf["vendor"]
kwargs["product"] = conf["product"]
kwargs["revision"] = conf["revision"]
kwargs["block_size"] = conf["block_size"]
expected_block_size = conf["block_size"]
process = piscsi_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
if int(file_size) % int(expected_block_size):
logging.warning(
"The image file size %s bytes is not a multiple of %s. "
"PiSCSI will ignore the trailing data. "
"The image may be corrupted, so proceed with caution.",
file_size,
expected_block_size,
)
return response(
message=_(
"Attached %(file_name)s as %(device_type)s to "
"SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name,
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
)
)
return response(error=True, message=process["msg"])
@APP.route("/scsi/detach_all", methods=["POST"]) @APP.route("/scsi/detach_all", methods=["POST"])
@login_required @login_required
def detach_all_devices(): def detach_all_devices():

View File

@ -8,26 +8,13 @@ from pathlib import Path
from ua_parser import user_agent_parser from ua_parser import user_agent_parser
from re import findall from re import findall
from flask import request, abort from flask import request
from flask_babel import _ from flask_babel import _
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from piscsi.sys_cmds import SysCmds from piscsi.sys_cmds import SysCmds
def working_dirs_exist(working_dirs):
"""
Method for validating that working dirs exist.
Takes (tuple) of (str) working_dirs with paths to required dirs.
"""
for dir_path in working_dirs:
if not Path(dir_path).exists():
abort(
503,
_(f"Please create directory: {dir_path}"),
)
def get_valid_scsi_ids(devices, reserved_ids): def get_valid_scsi_ids(devices, reserved_ids):
""" """
Takes a list of (dict)s devices, and list of (int)s reserved_ids. Takes a list of (dict)s devices, and list of (int)s reserved_ids.

View File

@ -8,6 +8,41 @@ FILE_SIZE_1_MIB = 1048576
STATUS_SUCCESS = "success" STATUS_SUCCESS = "success"
STATUS_ERROR = "error" STATUS_ERROR = "error"
ENV_ENDPOINT = "/env"
HEALTHCHECK_ENDPOINT = "/healthcheck"
PWA_FAVICON_ENDPOINT = "/pwa/favicon.ico"
LOGIN_ENDPOINT = "/login"
LOGOUT_ENDPOINT = "/logout"
ATTACH_ENDPOINT = "/scsi/attach"
DETACH_ENDPOINT = "/scsi/detach"
DETACH_ALL_ENDPOINT = "/scsi/detach_all"
EJECT_ENDPOINT = "/scsi/eject"
RESERVE_ENDPOINT = "/scsi/reserve"
RELEASE_ENDPOINT = "/scsi/release"
INFO_ENDPOINT = "/scsi/info"
CREATE_ENDPOINT = "/files/create"
RENAME_ENDPOINT = "/files/rename"
COPY_ENDPOINT = "/files/copy"
DELETE_ENDPOINT = "/files/delete"
DOWNLOAD_URL_ENDPOINT = "/files/download_url"
DOWNLOAD_IMAGE_ENDPOINT = "/files/download_image"
DOWNLOAD_CONFIG_ENDPOINT = "/files/download_config"
EXTRACT_IMAGE_ENDPOINT = "/files/extract_image"
UPLOAD_ENDPOINT = "/files/upload"
CREATE_ISO_ENDPOINT = "/files/create_iso"
DISKINFO_ENDPOINT = "/files/diskinfo"
DRIVE_LIST_ENDPOINT = "/drive/list"
DRIVE_CREATE_ENDPOINT = "/drive/create"
DRIVE_CDROM_ENDPOINT = "/drive/cdrom"
MANPAGE_ENDPOINT = "/sys/manpage?app=piscsi"
LANGUAGE_ENDPOINT = "/language"
LOG_LEVEL_ENDPOINT = "/logs/level"
LOG_SHOW_ENDPOINT = "/logs/show"
CONFIG_SAVE_ENDPOINT = "/config/save"
CONFIG_ACTION_ENDPOINT = "/config/action"
THEME_ENDPOINT = "/theme"
SYS_RENAME_ENDPOINT = "/sys/rename"
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def create_test_image(request, http_client): def create_test_image(request, http_client):
@ -18,7 +53,7 @@ def create_test_image(request, http_client):
file_name = f"{file_prefix}.{image_type}" file_name = f"{file_prefix}.{image_type}"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": image_type, "type": image_type,
@ -42,7 +77,7 @@ def create_test_image(request, http_client):
def delete(): def delete():
for image in images: for image in images:
response = http_client.post("/files/delete", data={"file_name": image["file_name"]}) response = http_client.post(DELETE_ENDPOINT, data={"file_name": image["file_name"]})
if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS: if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS:
warnings.warn( warnings.warn(
f"Failed to auto-delete file created with create_test_image fixture: {image}" f"Failed to auto-delete file created with create_test_image fixture: {image}"
@ -71,7 +106,7 @@ def list_attached_images(http_client):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def delete_file(http_client): def delete_file(http_client):
def delete(file_name): def delete(file_name):
response = http_client.post("/files/delete", data={"file_name": file_name}) response = http_client.post(DELETE_ENDPOINT, data={"file_name": file_name})
if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS: if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS:
warnings.warn(f"Failed to delete file via delete_file fixture: {file_name}") warnings.warn(f"Failed to delete file via delete_file fixture: {file_name}")
@ -81,7 +116,7 @@ def delete_file(http_client):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def detach_devices(http_client): def detach_devices(http_client):
def detach(): def detach():
response = http_client.post("/scsi/detach_all") response = http_client.post(DETACH_ALL_ENDPOINT)
if response.json()["status"] == STATUS_SUCCESS: if response.json()["status"] == STATUS_SUCCESS:
return True return True
raise Exception("Failed to detach SCSI devices") raise Exception("Failed to detach SCSI devices")

View File

@ -1,11 +1,10 @@
from conftest import STATUS_SUCCESS, STATUS_ERROR from conftest import STATUS_SUCCESS, STATUS_ERROR, LOGIN_ENDPOINT, LOGOUT_ENDPOINT
# route("/login", methods=["POST"])
def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated): def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated):
# Note: This test depends on the piscsi group existing and 'username' a member the group # Note: This test depends on the piscsi group existing and 'username' a member the group
response = http_client_unauthenticated.post( response = http_client_unauthenticated.post(
"/login", LOGIN_ENDPOINT,
data={ data={
"username": pytestconfig.getoption("piscsi_username"), "username": pytestconfig.getoption("piscsi_username"),
"password": pytestconfig.getoption("piscsi_password"), "password": pytestconfig.getoption("piscsi_password"),
@ -19,10 +18,9 @@ def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated)
assert "env" in response_data["data"] assert "env" in response_data["data"]
# route("/login", methods=["POST"])
def test_login_with_invalid_credentials(http_client_unauthenticated): def test_login_with_invalid_credentials(http_client_unauthenticated):
response = http_client_unauthenticated.post( response = http_client_unauthenticated.post(
"/login", LOGIN_ENDPOINT,
data={ data={
"username": "__INVALID_USER__", "username": "__INVALID_USER__",
"password": "__INVALID_PASS__", "password": "__INVALID_PASS__",
@ -38,7 +36,6 @@ def test_login_with_invalid_credentials(http_client_unauthenticated):
) )
# route("/logout")
def test_logout(http_client): def test_logout(http_client):
response = http_client.get("/logout") response = http_client.get(LOGOUT_ENDPOINT)
assert response.status_code == 200 assert response.status_code == 200

View File

@ -2,20 +2,24 @@ import pytest
from conftest import ( from conftest import (
SCSI_ID, SCSI_ID,
FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
ATTACH_ENDPOINT,
DETACH_ENDPOINT,
DETACH_ALL_ENDPOINT,
EJECT_ENDPOINT,
RESERVE_ENDPOINT,
RELEASE_ENDPOINT,
INFO_ENDPOINT,
) )
# route("/scsi/attach", methods=["POST"]) def test_attach_device_with_image(http_client, create_test_image, detach_devices):
def test_attach_image(http_client, create_test_image, detach_devices):
test_image = create_test_image() test_image = create_test_image()
response = http_client.post( response = http_client.post(
"/scsi/attach", ATTACH_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
"type": "SCHD", "type": "SCHD",
@ -26,14 +30,13 @@ 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 Drive to SCSI ID {SCSI_ID} LUN 0" f"Attached Hard Disk Drive to SCSI ID {SCSI_ID} LUN 0"
) )
# Cleanup # Cleanup
detach_devices() detach_devices()
# route("/scsi/attach_device", methods=["POST"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device_name,device_config", "device_name,device_config",
[ [
@ -89,7 +92,7 @@ def test_attach_device(env, http_client, detach_devices, device_name, device_con
device_config["unit"] = 0 device_config["unit"] = 0
response = http_client.post( response = http_client.post(
"/scsi/attach_device", ATTACH_ENDPOINT,
data=device_config, data=device_config,
) )
@ -105,15 +108,13 @@ def test_attach_device(env, http_client, detach_devices, device_name, device_con
detach_devices() detach_devices()
# route("/scsi/detach", methods=["POST"])
def test_detach_device(http_client, create_test_image): def test_detach_device(http_client, create_test_image):
test_image = create_test_image() test_image = create_test_image()
http_client.post( http_client.post(
"/scsi/attach", ATTACH_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
"type": "SCHD", "type": "SCHD",
@ -121,7 +122,7 @@ def test_detach_device(http_client, create_test_image):
) )
response = http_client.post( response = http_client.post(
"/scsi/detach", DETACH_ENDPOINT,
data={ data={
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
@ -135,7 +136,6 @@ def test_detach_device(http_client, create_test_image):
assert response_data["messages"][0]["message"] == f"Detached SCSI ID {SCSI_ID} LUN 0" assert response_data["messages"][0]["message"] == f"Detached SCSI ID {SCSI_ID} LUN 0"
# route("/scsi/detach_all", methods=["POST"])
def test_detach_all_devices(http_client, create_test_image, list_attached_images): def test_detach_all_devices(http_client, create_test_image, list_attached_images):
test_images = [] test_images = []
scsi_ids = [4, 5, 6] scsi_ids = [4, 5, 6]
@ -145,10 +145,9 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
test_images.append(test_image) test_images.append(test_image)
http_client.post( http_client.post(
"/scsi/attach", ATTACH_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": scsi_id, "scsi_id": scsi_id,
"unit": 0, "unit": 0,
"type": "SCHD", "type": "SCHD",
@ -157,7 +156,7 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
assert list_attached_images() == test_images assert list_attached_images() == test_images
response = http_client.post("/scsi/detach_all") response = http_client.post(DETACH_ALL_ENDPOINT)
response_data = response.json() response_data = response.json()
assert response.status_code == 200 assert response.status_code == 200
@ -165,15 +164,13 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
assert list_attached_images() == [] assert list_attached_images() == []
# route("/scsi/eject", methods=["POST"])
def test_eject_device(http_client, create_test_image, detach_devices): def test_eject_device(http_client, create_test_image, detach_devices):
test_image = create_test_image() test_image = create_test_image()
http_client.post( http_client.post(
"/scsi/attach", ATTACH_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
"type": "SCCD", # CD-ROM "type": "SCCD", # CD-ROM
@ -181,7 +178,7 @@ def test_eject_device(http_client, create_test_image, detach_devices):
) )
response = http_client.post( response = http_client.post(
"/scsi/eject", EJECT_ENDPOINT,
data={ data={
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
@ -198,15 +195,13 @@ def test_eject_device(http_client, create_test_image, detach_devices):
detach_devices() detach_devices()
# route("/scsi/info", methods=["POST"])
def test_show_device_info(http_client, create_test_image, detach_devices): def test_show_device_info(http_client, create_test_image, detach_devices):
test_image = create_test_image() test_image = create_test_image()
http_client.post( http_client.post(
"/scsi/attach", ATTACH_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID, "scsi_id": SCSI_ID,
"unit": 0, "unit": 0,
"type": "SCHD", "type": "SCHD",
@ -214,7 +209,7 @@ def test_show_device_info(http_client, create_test_image, detach_devices):
) )
response = http_client.post( response = http_client.post(
"/scsi/info", INFO_ENDPOINT,
) )
response_data = response.json() response_data = response.json()
@ -228,13 +223,11 @@ def test_show_device_info(http_client, create_test_image, detach_devices):
detach_devices() detach_devices()
# route("/scsi/reserve", methods=["POST"])
# route("/scsi/release", methods=["POST"])
def test_reserve_and_release_device(http_client): def test_reserve_and_release_device(http_client):
scsi_id = 0 scsi_id = 0
response = http_client.post( response = http_client.post(
"/scsi/reserve", RESERVE_ENDPOINT,
data={ data={
"scsi_id": scsi_id, "scsi_id": scsi_id,
"memo": "TEST", "memo": "TEST",
@ -248,7 +241,7 @@ def test_reserve_and_release_device(http_client):
assert response_data["messages"][0]["message"] == f"Reserved SCSI ID {scsi_id}" assert response_data["messages"][0]["message"] == f"Reserved SCSI ID {scsi_id}"
response = http_client.post( response = http_client.post(
"/scsi/release", RELEASE_ENDPOINT,
data={ data={
"scsi_id": scsi_id, "scsi_id": scsi_id,
}, },

View File

@ -5,16 +5,26 @@ import os
from conftest import ( from conftest import (
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
CREATE_ENDPOINT,
RENAME_ENDPOINT,
COPY_ENDPOINT,
DELETE_ENDPOINT,
DOWNLOAD_URL_ENDPOINT,
DOWNLOAD_IMAGE_ENDPOINT,
DOWNLOAD_CONFIG_ENDPOINT,
EXTRACT_IMAGE_ENDPOINT,
UPLOAD_ENDPOINT,
CREATE_ISO_ENDPOINT,
DISKINFO_ENDPOINT,
) )
# route("/files/create", methods=["POST"])
def test_create_file(http_client, list_files, delete_file): def test_create_file(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4()) file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds" file_name = f"{file_prefix}.hds"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": "hds", "type": "hds",
@ -34,13 +44,12 @@ def test_create_file(http_client, list_files, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_with_properties(http_client, list_files, delete_file): def test_create_file_with_properties(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4()) file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds" file_name = f"{file_prefix}.hds"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": "hds", "type": "hds",
@ -64,13 +73,12 @@ def test_create_file_with_properties(http_client, list_files, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_and_format_hfs(http_client, list_files, delete_file): def test_create_file_and_format_hfs(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4()) file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hda" file_name = f"{file_prefix}.hda"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": "hda", "type": "hda",
@ -91,7 +99,6 @@ def test_create_file_and_format_hfs(http_client, list_files, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_and_format_fat(env, http_client, list_files, delete_file): def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
if env["is_docker"]: if env["is_docker"]:
pytest.skip("Test not supported in Docker environment.") pytest.skip("Test not supported in Docker environment.")
@ -99,7 +106,7 @@ def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
file_name = f"{file_prefix}.hdr" file_name = f"{file_prefix}.hdr"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": "hdr", "type": "hdr",
@ -120,13 +127,12 @@ def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/rename", methods=["POST"])
def test_rename_file(http_client, create_test_image, list_files, delete_file): def test_rename_file(http_client, create_test_image, list_files, delete_file):
original_file = create_test_image(auto_delete=False) original_file = create_test_image(auto_delete=False)
renamed_file = f"{uuid.uuid4()}.rename" renamed_file = f"{uuid.uuid4()}.rename"
response = http_client.post( response = http_client.post(
"/files/rename", RENAME_ENDPOINT,
data={"file_name": original_file, "new_file_name": renamed_file}, data={"file_name": original_file, "new_file_name": renamed_file},
) )
@ -141,13 +147,12 @@ def test_rename_file(http_client, create_test_image, list_files, delete_file):
delete_file(renamed_file) delete_file(renamed_file)
# route("/files/copy", methods=["POST"])
def test_copy_file(http_client, create_test_image, list_files, delete_file): def test_copy_file(http_client, create_test_image, list_files, delete_file):
original_file = create_test_image() original_file = create_test_image()
copy_file = f"{uuid.uuid4()}.copy" copy_file = f"{uuid.uuid4()}.copy"
response = http_client.post( response = http_client.post(
"/files/copy", COPY_ENDPOINT,
data={ data={
"file_name": original_file, "file_name": original_file,
"copy_file_name": copy_file, "copy_file_name": copy_file,
@ -167,11 +172,10 @@ def test_copy_file(http_client, create_test_image, list_files, delete_file):
delete_file(copy_file) delete_file(copy_file)
# route("/files/delete", methods=["POST"])
def test_delete_file(http_client, create_test_image, list_files): def test_delete_file(http_client, create_test_image, list_files):
file_name = create_test_image(auto_delete=False) file_name = create_test_image(auto_delete=False)
response = http_client.post("/files/delete", data={"file_name": file_name}) response = http_client.post(DELETE_ENDPOINT, data={"file_name": file_name})
response_data = response.json() response_data = response.json()
@ -181,7 +185,6 @@ def test_delete_file(http_client, create_test_image, list_files):
assert file_name not in list_files() assert file_name not in list_files()
# route("/files/extract_image", methods=["POST"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"archive_file_name,image_file_name", "archive_file_name,image_file_name",
[ [
@ -205,7 +208,7 @@ def test_extract_file(
) )
http_client.post( http_client.post(
"/files/download_url", DOWNLOAD_URL_ENDPOINT,
data={ data={
"destination": "disk_images", "destination": "disk_images",
"images_subdir": "", "images_subdir": "",
@ -214,7 +217,7 @@ def test_extract_file(
) )
response = http_client.post( response = http_client.post(
"/files/extract_image", EXTRACT_IMAGE_ENDPOINT,
data={ data={
"archive_file": archive_file_name, "archive_file": archive_file_name,
"archive_members": image_file_name, "archive_members": image_file_name,
@ -233,7 +236,6 @@ def test_extract_file(
delete_file(image_file_name) delete_file(image_file_name)
# route("/files/upload", methods=["POST"])
def test_upload_file(http_client, delete_file): def test_upload_file(http_client, delete_file):
file_name = f"{uuid.uuid4()}.test" file_name = f"{uuid.uuid4()}.test"
@ -267,7 +269,7 @@ def test_upload_file(http_client, delete_file):
file_data = {"file": (file_name, file.read(chunk_size))} file_data = {"file": (file_name, file.read(chunk_size))}
response = http_client.post( response = http_client.post(
"/files/upload", UPLOAD_ENDPOINT,
data=form_data, data=form_data,
files=file_data, files=file_data,
) )
@ -283,11 +285,10 @@ def test_upload_file(http_client, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/download_image", methods=["POST"])
def test_download_image(http_client, create_test_image): def test_download_image(http_client, create_test_image):
file_name = create_test_image() file_name = create_test_image()
response = http_client.post("/files/download_image", data={"file": file_name}) response = http_client.post(DOWNLOAD_IMAGE_ENDPOINT, data={"file": 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"
@ -295,13 +296,12 @@ def test_download_image(http_client, create_test_image):
assert response.headers["content-length"] == str(FILE_SIZE_1_MIB) assert response.headers["content-length"] == str(FILE_SIZE_1_MIB)
# route("/files/download_config", methods=["POST"])
def test_download_properties(http_client, list_files, delete_file): def test_download_properties(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4()) file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds" file_name = f"{file_prefix}.hds"
response = http_client.post( response = http_client.post(
"/files/create", CREATE_ENDPOINT,
data={ data={
"file_name": file_prefix, "file_name": file_prefix,
"type": "hds", "type": "hds",
@ -321,7 +321,7 @@ def test_download_properties(http_client, list_files, delete_file):
) )
assert file_name in list_files() assert file_name in list_files()
response = http_client.post("/files/download_config", data={"file": f"{file_name}.properties"}) response = http_client.post(DOWNLOAD_CONFIG_ENDPOINT, data={"file": f"{file_name}.properties"})
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"
@ -331,7 +331,6 @@ def test_download_properties(http_client, list_files, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/files/download_url", methods=["POST"])
def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file): def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file):
file_name = str(uuid.uuid4()) file_name = str(uuid.uuid4())
http_path = f"/images/{file_name}" http_path = f"/images/{file_name}"
@ -347,7 +346,7 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi
) )
response = http_client.post( response = http_client.post(
"/files/download_url", DOWNLOAD_URL_ENDPOINT,
data={ data={
"destination": "disk_images", "destination": "disk_images",
"images_subdir": subdir, "images_subdir": subdir,
@ -369,7 +368,6 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi
delete_file(file_name) delete_file(file_name)
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_url( def test_create_iso_from_url(
httpserver, httpserver,
http_client, http_client,
@ -392,7 +390,7 @@ def test_create_iso_from_url(
) )
response = http_client.post( response = http_client.post(
"/files/create_iso", CREATE_ISO_ENDPOINT,
data={ data={
"type": ISO_TYPE, "type": ISO_TYPE,
"url": url, "url": url,
@ -414,7 +412,6 @@ def test_create_iso_from_url(
delete_file(iso_file_name) delete_file(iso_file_name)
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_local_file( def test_create_iso_from_local_file(
http_client, http_client,
create_test_image, create_test_image,
@ -427,7 +424,7 @@ def test_create_iso_from_local_file(
ISO_TYPE = "HFS" ISO_TYPE = "HFS"
response = http_client.post( response = http_client.post(
"/files/create_iso", CREATE_ISO_ENDPOINT,
data={ data={
"type": ISO_TYPE, "type": ISO_TYPE,
"file": test_file_name, "file": test_file_name,
@ -449,12 +446,11 @@ def test_create_iso_from_local_file(
delete_file(iso_file_name) delete_file(iso_file_name)
# route("/files/diskinfo", methods=["POST"])
def test_show_diskinfo(http_client, create_test_image): def test_show_diskinfo(http_client, create_test_image):
test_image = create_test_image() test_image = create_test_image()
response = http_client.post( response = http_client.post(
"/files/diskinfo", DISKINFO_ENDPOINT,
data={ data={
"file_name": test_image, "file_name": test_image,
}, },

View File

@ -3,10 +3,16 @@ import uuid
from conftest import ( from conftest import (
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
ENV_ENDPOINT,
PWA_FAVICON_ENDPOINT,
HEALTHCHECK_ENDPOINT,
DRIVE_LIST_ENDPOINT,
DRIVE_CREATE_ENDPOINT,
DRIVE_CDROM_ENDPOINT,
MANPAGE_ENDPOINT,
) )
# route("/")
def test_index(http_client): def test_index(http_client):
response = http_client.get("/") response = http_client.get("/")
response_data = response.json() response_data = response.json()
@ -16,9 +22,8 @@ def test_index(http_client):
assert "devices" in response_data["data"] assert "devices" in response_data["data"]
# route("/env")
def test_get_env_info(http_client): def test_get_env_info(http_client):
response = http_client.get("/env") response = http_client.get(ENV_ENDPOINT)
response_data = response.json() response_data = response.json()
assert response.status_code == 200 assert response.status_code == 200
@ -26,17 +31,15 @@ def test_get_env_info(http_client):
assert "running_env" in response_data["data"] assert "running_env" in response_data["data"]
# route("/pwa/<path:pwa_path>")
def test_pwa_route(http_client): def test_pwa_route(http_client):
response = http_client.get("/pwa/favicon.ico") response = http_client.get(PWA_FAVICON_ENDPOINT)
assert response.status_code == 200 assert response.status_code == 200
assert response.headers["content-disposition"] == "inline; filename=favicon.ico" assert response.headers["content-disposition"] == "inline; filename=favicon.ico"
# route("/drive/list", methods=["GET"])
def test_show_named_drive_presets(http_client): def test_show_named_drive_presets(http_client):
response = http_client.get("/drive/list") response = http_client.get(DRIVE_LIST_ENDPOINT)
response_data = response.json() response_data = response.json()
prev_drive = {"name": ""} prev_drive = {"name": ""}
@ -57,12 +60,11 @@ def test_show_named_drive_presets(http_client):
assert "files" in response_data["data"] assert "files" in response_data["data"]
# route("/drive/cdrom", methods=["POST"])
def test_create_cdrom_properties_file(env, http_client): def test_create_cdrom_properties_file(env, http_client):
file_name = f"{uuid.uuid4()}.iso" file_name = f"{uuid.uuid4()}.iso"
response = http_client.post( response = http_client.post(
"/drive/cdrom", DRIVE_CDROM_ENDPOINT,
data={ data={
"drive_name": "Sony CDU-8012", "drive_name": "Sony CDU-8012",
"file_name": file_name, "file_name": file_name,
@ -78,13 +80,12 @@ def test_create_cdrom_properties_file(env, http_client):
) )
# route("/drive/create", methods=["POST"])
def test_create_image_with_properties_file(http_client, delete_file): def test_create_image_with_properties_file(http_client, delete_file):
file_prefix = str(uuid.uuid4()) file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds" file_name = f"{file_prefix}.hds"
response = http_client.post( response = http_client.post(
"/drive/create", DRIVE_CREATE_ENDPOINT,
data={ data={
"drive_name": "Miniscribe M8425", "drive_name": "Miniscribe M8425",
"size": FILE_SIZE_1_MIB, "size": FILE_SIZE_1_MIB,
@ -105,16 +106,14 @@ def test_create_image_with_properties_file(http_client, delete_file):
delete_file(file_name) delete_file(file_name)
# route("/sys/manpage", methods=["POST"])
def test_show_manpage(http_client): def test_show_manpage(http_client):
response = http_client.get("/sys/manpage?app=piscsi") response = http_client.get(MANPAGE_ENDPOINT)
response_data = response.json() response_data = response.json()
assert response.status_code == 200 assert response.status_code == 200
assert "piscsi" in response_data["data"]["manpage"] assert "piscsi" in response_data["data"]["manpage"]
# route("/healthcheck", methods=["GET"])
def test_healthcheck(http_client): def test_healthcheck(http_client):
response = http_client.get("/healthcheck") response = http_client.get(HEALTHCHECK_ENDPOINT)
assert response.status_code == 200 assert response.status_code == 200

View File

@ -1,10 +1,20 @@
import pytest import pytest
import uuid import uuid
from conftest import STATUS_SUCCESS from conftest import (
STATUS_SUCCESS,
ENV_ENDPOINT,
LANGUAGE_ENDPOINT,
LOG_LEVEL_ENDPOINT,
LOG_SHOW_ENDPOINT,
CONFIG_SAVE_ENDPOINT,
CONFIG_ACTION_ENDPOINT,
THEME_ENDPOINT,
SYS_RENAME_ENDPOINT,
RESERVE_ENDPOINT,
)
# route("/language", methods=["POST"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"locale,confirm_message", "locale,confirm_message",
[ [
@ -18,7 +28,7 @@ from conftest import STATUS_SUCCESS
) )
def test_set_language(http_client, locale, confirm_message): def test_set_language(http_client, locale, confirm_message):
response = http_client.post( response = http_client.post(
"/language", LANGUAGE_ENDPOINT,
data={ data={
"locale": locale, "locale": locale,
}, },
@ -31,11 +41,10 @@ def test_set_language(http_client, locale, confirm_message):
assert response_data["messages"][0]["message"] == confirm_message assert response_data["messages"][0]["message"] == confirm_message
# route("/logs/level", methods=["POST"])
@pytest.mark.parametrize("level", ["trace", "debug", "info", "warn", "err", "off"]) @pytest.mark.parametrize("level", ["trace", "debug", "info", "warn", "err", "off"])
def test_set_log_level(http_client, level): def test_set_log_level(http_client, level):
response = http_client.post( response = http_client.post(
"/logs/level", LOG_LEVEL_ENDPOINT,
data={ data={
"level": level, "level": level,
}, },
@ -49,17 +58,16 @@ def test_set_log_level(http_client, level):
# Cleanup # Cleanup
http_client.post( http_client.post(
"/logs/level", LOG_LEVEL_ENDPOINT,
data={ data={
"level": "debug", "level": "debug",
}, },
) )
# route("/logs/show", methods=["POST"])
def test_show_logs(http_client): def test_show_logs(http_client):
response = http_client.post( response = http_client.post(
"/logs/show", LOG_SHOW_ENDPOINT,
data={ data={
"lines": 100, "lines": 100,
"scope": "piscsi", "scope": "piscsi",
@ -73,8 +81,6 @@ def test_show_logs(http_client):
assert response_data["data"]["scope"] == "piscsi" assert response_data["data"]["scope"] == "piscsi"
# route("/config/save", methods=["POST"])
# route("/config/action", methods=["POST"])
def test_save_load_and_delete_configs(env, http_client): def test_save_load_and_delete_configs(env, http_client):
config_name = str(uuid.uuid4()) config_name = str(uuid.uuid4())
config_json_file = f"{config_name}.json" config_json_file = f"{config_name}.json"
@ -86,7 +92,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Save the initial state to a config # Save the initial state to a config
response = http_client.post( response = http_client.post(
"/config/save", CONFIG_SAVE_ENDPOINT,
data={ data={
"name": config_name, "name": config_name,
}, },
@ -104,7 +110,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Modify the state # Modify the state
http_client.post( http_client.post(
"/scsi/reserve", RESERVE_ENDPOINT,
data={ data={
"scsi_id": reserved_scsi_id, "scsi_id": reserved_scsi_id,
"memo": reservation_memo, "memo": reservation_memo,
@ -115,7 +121,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Load the saved config # Load the saved config
response = http_client.post( response = http_client.post(
"/config/action", CONFIG_ACTION_ENDPOINT,
data={ data={
"name": config_json_file, "name": config_json_file,
"load": True, "load": True,
@ -135,7 +141,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Delete the saved config # Delete the saved config
response = http_client.post( response = http_client.post(
"/config/action", CONFIG_ACTION_ENDPOINT,
data={ data={
"name": config_json_file, "name": config_json_file,
"delete": True, "delete": True,
@ -153,15 +159,13 @@ def test_save_load_and_delete_configs(env, http_client):
assert config_json_file not in http_client.get("/").json()["data"]["config_files"] assert config_json_file not in http_client.get("/").json()["data"]["config_files"]
# route("/config/save", methods=["POST"]) def test_download_configs(env, http_client):
# route("/config/action", methods=["POST"])
def test_download_configs(env, http_client, delete_file):
config_name = str(uuid.uuid4()) config_name = str(uuid.uuid4())
config_json_file = f"{config_name}.json" config_json_file = f"{config_name}.json"
# Save the initial state to a config # Save the initial state to a config
response = http_client.post( response = http_client.post(
"/config/save", CONFIG_SAVE_ENDPOINT,
data={ data={
"name": config_name, "name": config_name,
}, },
@ -172,7 +176,7 @@ def test_download_configs(env, http_client, delete_file):
# Download the saved config # Download the saved config
response = http_client.post( response = http_client.post(
"/config/action", CONFIG_ACTION_ENDPOINT,
data={ data={
"name": config_json_file, "name": config_json_file,
"send": True, "send": True,
@ -185,7 +189,7 @@ def test_download_configs(env, http_client, delete_file):
# Delete the saved config # Delete the saved config
response = http_client.post( response = http_client.post(
"/config/action", CONFIG_ACTION_ENDPOINT,
data={ data={
"name": config_json_file, "name": config_json_file,
"delete": True, "delete": True,
@ -196,7 +200,6 @@ def test_download_configs(env, http_client, delete_file):
assert config_json_file not in http_client.get("/").json()["data"]["config_files"] assert config_json_file not in http_client.get("/").json()["data"]["config_files"]
# route("/theme", methods=["POST"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"theme", "theme",
[ [
@ -206,7 +209,7 @@ def test_download_configs(env, http_client, delete_file):
) )
def test_set_theme(http_client, theme): def test_set_theme(http_client, theme):
response = http_client.post( response = http_client.post(
"/theme", THEME_ENDPOINT,
data={ data={
"name": theme, "name": theme,
}, },
@ -219,7 +222,6 @@ def test_set_theme(http_client, theme):
assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'." assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'."
# route("/theme", methods=["GET"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"theme", "theme",
[ [
@ -229,7 +231,7 @@ def test_set_theme(http_client, theme):
) )
def test_set_theme_via_query_string(http_client, theme): def test_set_theme_via_query_string(http_client, theme):
response = http_client.get( response = http_client.get(
"/theme", THEME_ENDPOINT,
params={ params={
"name": theme, "name": theme,
}, },
@ -242,17 +244,16 @@ def test_set_theme_via_query_string(http_client, theme):
assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'." assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'."
# route("/sys/rename", methods=["POST"])
def test_rename_system(env, http_client): def test_rename_system(env, http_client):
new_name = "SYSTEM NAME TEST" new_name = "SYSTEM NAME TEST"
response = http_client.get("/env") response = http_client.get(ENV_ENDPOINT)
response_data = response.json() response_data = response.json()
old_name = response_data["data"]["system_name"] old_name = response_data["data"]["system_name"]
response = http_client.post( response = http_client.post(
"/sys/rename", SYS_RENAME_ENDPOINT,
data={ data={
"system_name": new_name, "system_name": new_name,
}, },
@ -264,13 +265,13 @@ def test_rename_system(env, http_client):
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == f"System name changed to '{new_name}'." assert response_data["messages"][0]["message"] == f"System name changed to '{new_name}'."
response = http_client.get("/env") response = http_client.get(ENV_ENDPOINT)
response_data = response.json() response_data = response.json()
assert response_data["data"]["system_name"] == new_name assert response_data["data"]["system_name"] == new_name
response = http_client.post( response = http_client.post(
"/sys/rename", SYS_RENAME_ENDPOINT,
data={ data={
"system_name": old_name, "system_name": old_name,
}, },
@ -282,7 +283,7 @@ def test_rename_system(env, http_client):
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == f"System name changed to '{old_name}'." assert response_data["messages"][0]["message"] == f"System name changed to '{old_name}'."
response = http_client.get("/env") response = http_client.get(ENV_ENDPOINT)
response_data = response.json() response_data = response.json()
assert response_data["data"]["system_name"] == old_name assert response_data["data"]["system_name"] == old_name