Add required battery_size to devices, filter install panels by size
This commit is contained in:
+2
-2
@@ -42,8 +42,8 @@ def seeded_client(app):
|
||||
id=3 BrandX 002 (BrandX, retired)
|
||||
"""
|
||||
with app.test_client() as c:
|
||||
c.post("/device/add", data={"name": "Device A", "battery_slots": "2"})
|
||||
c.post("/device/add", data={"name": "Device B", "battery_slots": "1"})
|
||||
c.post("/device/add", data={"name": "Device A", "battery_slots": "2", "battery_size": "AA"})
|
||||
c.post("/device/add", data={"name": "Device B", "battery_slots": "1", "battery_size": "AA"})
|
||||
c.post("/battery/add", data={"brand": "BrandX", "count": "1"}) # id=1
|
||||
c.post("/battery/add", data={"brand": "BrandY", "count": "1"}) # id=2
|
||||
c.post("/battery/add", data={"brand": "BrandX", "count": "1"}) # id=3
|
||||
|
||||
+72
-21
@@ -235,7 +235,7 @@ def test_bulk_delete(client):
|
||||
|
||||
|
||||
def test_bulk_unassign(client):
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "TestBrand", "count": "2"})
|
||||
client.post("/device/1/install", data={"brand[]": "TestBrand", "qty[]": "2"})
|
||||
resp = client.post("/battery/bulk-action",
|
||||
@@ -269,7 +269,7 @@ def test_bulk_action_no_selection(client):
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_device_install_autoselects(client):
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "3"})
|
||||
resp = client.post("/device/1/install",
|
||||
data={"brand[]": "Eneloop", "qty[]": "2"},
|
||||
@@ -281,7 +281,7 @@ def test_device_install_autoselects(client):
|
||||
|
||||
|
||||
def test_device_install_mixed_brands(client):
|
||||
client.post("/device/add", data={"name": "Remote", "battery_slots": "4"})
|
||||
client.post("/device/add", data={"name": "Remote", "battery_slots": "4", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
|
||||
client.post("/battery/add", data={"brand": "Energizer", "count": "2"})
|
||||
resp = client.post("/device/1/install",
|
||||
@@ -293,7 +293,7 @@ def test_device_install_mixed_brands(client):
|
||||
|
||||
|
||||
def test_device_install_insufficient_batteries(client):
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "4"})
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "4", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
|
||||
resp = client.post("/device/1/install",
|
||||
data={"brand[]": "Eneloop", "qty[]": "2"},
|
||||
@@ -303,7 +303,7 @@ def test_device_install_insufficient_batteries(client):
|
||||
|
||||
|
||||
def test_device_install_over_capacity(client):
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "1", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "3"})
|
||||
resp = client.post("/device/1/install",
|
||||
data={"brand[]": "Eneloop", "qty[]": "3"},
|
||||
@@ -314,7 +314,7 @@ def test_device_install_over_capacity(client):
|
||||
|
||||
def test_bulk_install_device(client):
|
||||
"""Select multiple available batteries and install them into a device."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "3"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "3", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
|
||||
resp = client.post("/battery/bulk-action",
|
||||
data={"battery_ids": ["1", "2"], "action": "install_device",
|
||||
@@ -326,7 +326,7 @@ def test_bulk_install_device(client):
|
||||
|
||||
def test_bulk_install_device_over_capacity(client):
|
||||
"""Bulk install is blocked when device lacks free slots."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
|
||||
resp = client.post("/battery/bulk-action",
|
||||
data={"battery_ids": ["1", "2"], "action": "install_device",
|
||||
@@ -338,8 +338,8 @@ def test_bulk_install_device_over_capacity(client):
|
||||
|
||||
def test_bulk_install_device_moves_installed_battery(client):
|
||||
"""Bulk install moves a battery already installed in another device."""
|
||||
client.post("/device/add", data={"name": "Box A", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Box B", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Box A", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/device/add", data={"name": "Box B", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
|
||||
client.post("/battery/1/assign", data={"device_id": "1"})
|
||||
resp = client.post("/battery/bulk-action",
|
||||
@@ -357,7 +357,7 @@ def test_bulk_install_device_moves_installed_battery(client):
|
||||
|
||||
def test_dashboard_quick_assign(client):
|
||||
"""Quick-assign from dashboard (battery_assign POST) succeeds."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
|
||||
resp = client.post("/battery/1/assign", data={"device_id": "1"},
|
||||
follow_redirects=True)
|
||||
@@ -367,7 +367,7 @@ def test_dashboard_quick_assign(client):
|
||||
|
||||
def test_dashboard_quick_assign_full_device_blocked(client):
|
||||
"""Quick-assign from dashboard to a full device is blocked."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
|
||||
client.post("/battery/1/assign", data={"device_id": "1"}) # fills the slot
|
||||
resp = client.post("/battery/2/assign", data={"device_id": "1"},
|
||||
@@ -382,7 +382,7 @@ def test_dashboard_quick_assign_full_device_blocked(client):
|
||||
|
||||
def test_install_one_specific_battery(client):
|
||||
"""device_install_one installs a chosen battery."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "2"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
|
||||
resp = client.post("/device/1/install-one", data={"battery_id": "1"},
|
||||
follow_redirects=True)
|
||||
@@ -392,7 +392,7 @@ def test_install_one_specific_battery(client):
|
||||
|
||||
def test_install_one_over_capacity_blocked(client):
|
||||
"""device_install_one to a full device is blocked."""
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Box", "battery_slots": "1", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
|
||||
client.post("/device/1/install-one", data={"battery_id": "1"}) # fills slot
|
||||
resp = client.post("/device/1/install-one", data={"battery_id": "2"},
|
||||
@@ -418,7 +418,7 @@ def test_device_list(seeded_client):
|
||||
|
||||
def test_add_device(client):
|
||||
resp = client.post("/device/add",
|
||||
data={"name": "My Gadget", "battery_slots": "3"},
|
||||
data={"name": "My Gadget", "battery_slots": "3", "battery_size": "AA"},
|
||||
follow_redirects=True)
|
||||
assert resp.status_code == 200
|
||||
assert b"My Gadget" in resp.data
|
||||
@@ -426,7 +426,7 @@ def test_add_device(client):
|
||||
|
||||
def test_add_device_duplicate_name(seeded_client):
|
||||
resp = seeded_client.post("/device/add",
|
||||
data={"name": "Device A", "battery_slots": "2"})
|
||||
data={"name": "Device A", "battery_slots": "2", "battery_size": "AA"})
|
||||
assert resp.status_code == 400
|
||||
assert b"already exists" in resp.data
|
||||
|
||||
@@ -476,21 +476,22 @@ def test_delete_device_removed(seeded_client):
|
||||
|
||||
def test_add_device_with_type(client):
|
||||
resp = client.post("/device/add",
|
||||
data={"name": "TV Remote", "battery_slots": "2", "device_type": "Remote Control"},
|
||||
data={"name": "TV Remote", "battery_slots": "2", "battery_size": "AA",
|
||||
"device_type": "Remote Control"},
|
||||
follow_redirects=True)
|
||||
assert resp.status_code == 200
|
||||
assert b"TV Remote" in resp.data
|
||||
|
||||
|
||||
def test_device_detail_shows_type(client):
|
||||
client.post("/device/add", data={"name": "Torch", "battery_slots": "1", "device_type": "Flashlight"})
|
||||
client.post("/device/add", data={"name": "Torch", "battery_slots": "1", "battery_size": "AA", "device_type": "Flashlight"})
|
||||
resp = client.get("/device/1")
|
||||
assert resp.status_code == 200
|
||||
assert b"Flashlight" in resp.data
|
||||
|
||||
|
||||
def test_edit_device_type(client):
|
||||
client.post("/device/add", data={"name": "Torch", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Torch", "battery_slots": "1", "battery_size": "AA"})
|
||||
resp = client.post("/device/1/edit",
|
||||
data={"name": "Torch", "battery_slots": "1", "device_type": "Flashlight"},
|
||||
follow_redirects=True)
|
||||
@@ -500,7 +501,8 @@ def test_edit_device_type(client):
|
||||
|
||||
def test_add_device_with_location(client):
|
||||
resp = client.post("/device/add",
|
||||
data={"name": "Bedroom Remote", "battery_slots": "2", "location": "Bedroom"},
|
||||
data={"name": "Bedroom Remote", "battery_slots": "2", "battery_size": "AA",
|
||||
"location": "Bedroom"},
|
||||
follow_redirects=True)
|
||||
assert resp.status_code == 200
|
||||
assert b"Bedroom Remote" in resp.data
|
||||
@@ -509,7 +511,7 @@ def test_add_device_with_location(client):
|
||||
|
||||
|
||||
def test_edit_device_location(client):
|
||||
client.post("/device/add", data={"name": "Living Room Clock", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Living Room Clock", "battery_slots": "1", "battery_size": "AA"})
|
||||
resp = client.post("/device/1/edit",
|
||||
data={"name": "Living Room Clock", "battery_slots": "1", "location": "Living Room"},
|
||||
follow_redirects=True)
|
||||
@@ -545,7 +547,7 @@ def test_delete_capacity_test(client):
|
||||
|
||||
|
||||
def test_add_install_delete_battery(client):
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Gadget", "battery_slots": "1", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "AcmeBrand", "count": "1"})
|
||||
resp = client.post("/device/1/install",
|
||||
data={"brand[]": "AcmeBrand", "qty[]": "1"},
|
||||
@@ -553,3 +555,52 @@ def test_add_install_delete_battery(client):
|
||||
assert b"Gadget" in resp.data
|
||||
client.post("/battery/1/delete")
|
||||
assert client.get("/battery/1").status_code == 404
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Device — battery_size
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_add_device_requires_battery_size(client):
|
||||
"""POST without battery_size returns 400."""
|
||||
resp = client.post("/device/add", data={"name": "No Size Device", "battery_slots": "2"})
|
||||
assert resp.status_code == 400
|
||||
assert b"Battery size is required" in resp.data
|
||||
|
||||
|
||||
def test_add_device_with_battery_size(client):
|
||||
"""Device with battery_size shows it on detail page."""
|
||||
resp = client.post("/device/add",
|
||||
data={"name": "AA Remote", "battery_slots": "2", "battery_size": "AA"},
|
||||
follow_redirects=True)
|
||||
assert resp.status_code == 200
|
||||
resp2 = client.get("/device/1")
|
||||
assert b"AA" in resp2.data
|
||||
|
||||
|
||||
def test_install_one_filters_by_size(client):
|
||||
"""Single-battery install dropdown excludes batteries with wrong size."""
|
||||
client.post("/device/add", data={"name": "AA Device", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Eneloop", "count": "1"}) # id=1
|
||||
client.post("/battery/1/edit-details", data={"size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Energizer", "count": "1"}) # id=2
|
||||
client.post("/battery/2/edit-details", data={"size": "AAA"})
|
||||
resp = client.get("/device/1")
|
||||
assert resp.status_code == 200
|
||||
# AA battery should appear in the dropdown, AAA should not
|
||||
assert b"Eneloop 001" in resp.data
|
||||
assert b"Energizer 001" not in resp.data
|
||||
|
||||
|
||||
def test_bulk_install_filters_by_size(client):
|
||||
"""Bulk install with a brand that only has wrong-size batteries fails with 0 available."""
|
||||
client.post("/device/add", data={"name": "AA Device", "battery_slots": "2", "battery_size": "AA"})
|
||||
client.post("/battery/add", data={"brand": "Energizer", "count": "2"})
|
||||
# Set both batteries to AAA (explicitly incompatible)
|
||||
client.post("/battery/1/edit-details", data={"size": "AAA"})
|
||||
client.post("/battery/2/edit-details", data={"size": "AAA"})
|
||||
resp = client.post("/device/1/install",
|
||||
data={"brand[]": "Energizer", "qty[]": "1"},
|
||||
follow_redirects=True)
|
||||
assert resp.status_code == 200
|
||||
assert b"only 0 available" in resp.data
|
||||
|
||||
@@ -62,7 +62,7 @@ def test_ha_disabled_dashboard_no_ha_column(client):
|
||||
|
||||
|
||||
def test_ha_disabled_device_no_ha_field(client):
|
||||
client.post("/device/add", data={"name": "Dev A", "battery_slots": "1"})
|
||||
client.post("/device/add", data={"name": "Dev A", "battery_slots": "1", "battery_size": "AA"})
|
||||
resp = client.get("/device/1")
|
||||
assert resp.status_code == 200
|
||||
assert b"ha_entity_id" not in resp.data
|
||||
@@ -153,7 +153,7 @@ def _make_session_factory(ha_app):
|
||||
|
||||
def test_poll_updates_installed_batteries(ha_app, ha_client_f):
|
||||
"""Installed battery in a device with ha_entity_id gets battery_percentage updated."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev A", "battery_slots": "2"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev A", "battery_slots": "2", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
# Assign battery to device
|
||||
ha_client_f.post("/battery/1/assign", data={"device_id": "1"})
|
||||
@@ -184,7 +184,7 @@ def test_poll_updates_installed_batteries(ha_app, ha_client_f):
|
||||
|
||||
def test_poll_skips_uninstalled_batteries(ha_app, ha_client_f):
|
||||
"""Batteries that are available (not installed) are not updated by the poller."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev B", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev B", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
# Set entity on device but do NOT install the battery
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
@@ -211,7 +211,7 @@ def test_poll_skips_uninstalled_batteries(ha_app, ha_client_f):
|
||||
|
||||
def test_poll_skips_devices_without_entity_id(ha_app, ha_client_f):
|
||||
"""Devices with no ha_entity_id must never reach get_state."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev C", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev C", "battery_slots": "1", "battery_size": "AA"})
|
||||
|
||||
from ha_client import HomeAssistantClient
|
||||
from ha_poller import HaPoller
|
||||
@@ -228,7 +228,7 @@ def test_poll_skips_devices_without_entity_id(ha_app, ha_client_f):
|
||||
|
||||
def test_poll_handles_api_error_gracefully(ha_app, ha_client_f):
|
||||
"""When get_state returns None, no exception is raised and percentage stays None."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev D", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev D", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
ha_client_f.post("/battery/1/assign", data={"device_id": "1"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
@@ -343,14 +343,14 @@ def test_retired_battery_excluded_from_needs_attention_section(ha_app, ha_client
|
||||
|
||||
|
||||
def test_device_detail_shows_ha_field(ha_client_f):
|
||||
ha_client_f.post("/device/add", data={"name": "Dev E", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev E", "battery_slots": "1", "battery_size": "AA"})
|
||||
resp = ha_client_f.get("/device/1")
|
||||
assert resp.status_code == 200
|
||||
assert b"ha_entity_id" in resp.data
|
||||
|
||||
|
||||
def test_edit_device_ha_entity_id_saves(ha_client_f):
|
||||
ha_client_f.post("/device/add", data={"name": "Dev F", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev F", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
"name": "Dev F", "battery_slots": "1", "ha_entity_id": "sensor.my_remote"
|
||||
})
|
||||
@@ -359,7 +359,7 @@ def test_edit_device_ha_entity_id_saves(ha_client_f):
|
||||
|
||||
|
||||
def test_edit_device_ha_entity_id_clear(ha_client_f):
|
||||
ha_client_f.post("/device/add", data={"name": "Dev G", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev G", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
"name": "Dev G", "battery_slots": "1", "ha_entity_id": "sensor.foo"
|
||||
})
|
||||
@@ -415,7 +415,7 @@ def test_manual_battery_percentage_edit(ha_client_f):
|
||||
|
||||
def test_poll_skips_update_when_percentage_unchanged(ha_app, ha_client_f):
|
||||
"""No write and no pct_log entry when the polled value matches the stored value."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev NoCh", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev NoCh", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
ha_client_f.post("/battery/1/assign", data={"device_id": "1"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
@@ -446,7 +446,7 @@ def test_poll_skips_update_when_percentage_unchanged(ha_app, ha_client_f):
|
||||
|
||||
def test_poll_creates_pct_log_on_change(ha_app, ha_client_f):
|
||||
"""A pct_log entry with source='poll' is written when the value changes."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Chg", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Chg", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
ha_client_f.post("/battery/1/assign", data={"device_id": "1"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
@@ -519,7 +519,7 @@ def test_manual_edit_no_log_when_unchanged(ha_client_f):
|
||||
|
||||
def test_device_detail_shows_live_pct(ha_app, ha_client_f):
|
||||
"""Opening a device page fetches live % from HA and displays it."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Live", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Live", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
"name": "Dev Live", "battery_slots": "1", "ha_entity_id": "sensor.live_test"
|
||||
})
|
||||
@@ -537,7 +537,7 @@ def test_device_detail_shows_live_pct(ha_app, ha_client_f):
|
||||
|
||||
def test_device_detail_updates_battery_on_load(ha_app, ha_client_f):
|
||||
"""Battery percentage is updated (with pct_log) when device page is loaded and value changed."""
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Update", "battery_slots": "1"})
|
||||
ha_client_f.post("/device/add", data={"name": "Dev Update", "battery_slots": "1", "battery_size": "AA"})
|
||||
ha_client_f.post("/battery/add", data={"brand": "X", "count": "1"})
|
||||
ha_client_f.post("/battery/1/assign", data={"device_id": "1"})
|
||||
ha_client_f.post("/device/1/edit", data={
|
||||
|
||||
Reference in New Issue
Block a user