Camera permission and review fixes (openpanel C3, C4)
C4: a device's `viewers` (superusers by default, or members) covers the image and video messages its ports publish. Anyone else gets none of their values on the socket, in the catalogue or a flow's state, 403 for their history, for the frames they hold (the last 8 included) and for TURN credentials, no bytes when asking for them, and `bye: forbidden` for a live view. A device credential stays bounded by its dashboards. The device page sets it in its Camera section, and a tile the viewer may not see says "No access to this camera" and asks the camera for nothing. C3: - A volatile upload is held to MAX_FRAME_BYTES (4 MiB, 413 above it, read without extra copies), and so is a frame on a device's socket. - A frame the engine holds in memory is written to the ring directory before a local worker is handed it, so load_artifact finds it. - A socket the bus dropped events for catches up with a snapshot, demand included, and a device's credential is checked again per frame, cached by the flow store revision and its nonce, so a lost event never keeps an unpaired device's frames coming. - A report is at most 64 KB once its named config_schemas are put in place. - A device away for 60 s has its true bool messages set to false. - Refused frames are logged by fixed keys, a frame is named after its message, `fluksio serve` waits 5 s for open connections, a tile with a stream URL creates no demand, the 8-name cap keeps the newest names. - Every container's log rotates (json-file, 3 x 10 MB).
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from fluksio.flow.alerts import Alert, AlertManager, AlertsConfig, Channel, Rule
|
||||
from fluksio.flow.controller import HEALTH_FLOOR, FlowController
|
||||
from fluksio.flow.devices import presence
|
||||
@@ -165,3 +167,41 @@ def test_a_device_away_after_a_restart_alerts_nobody(tmp_path: Path) -> None:
|
||||
]
|
||||
assert sent == []
|
||||
presence.forget("wall")
|
||||
|
||||
|
||||
def test_a_device_gone_for_the_grace_reads_false(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""A flag such as `person` stops saying someone is there once the device
|
||||
has been away for the grace, and a device back in time keeps it."""
|
||||
from fluksio.flow.nodes import device as device_node
|
||||
|
||||
monkeypatch.setattr(device_node, "OFFLINE_GRACE_S", 0.1)
|
||||
controller = _controller(tmp_path)
|
||||
|
||||
def bell() -> object:
|
||||
return controller.values("hall")["hall.bell"]["value"]
|
||||
|
||||
async def scenario() -> list[object]:
|
||||
await controller.reload()
|
||||
presence.opened("wall")
|
||||
controller.publish_message("hall.bell", True)
|
||||
seen = [bell()]
|
||||
# Back within the grace: nothing changes.
|
||||
presence.closed("wall")
|
||||
presence.opened("wall")
|
||||
await asyncio.sleep(0.25)
|
||||
seen.append(bell())
|
||||
# Away for good.
|
||||
presence.closed("wall")
|
||||
await asyncio.sleep(0.05)
|
||||
seen.append(bell())
|
||||
await asyncio.sleep(0.25)
|
||||
seen.append(bell())
|
||||
# What reads it hears so too.
|
||||
seen.append(controller.values("hall")["hall.rung"]["value"])
|
||||
await controller._teardown()
|
||||
presence.forget("wall")
|
||||
return seen
|
||||
|
||||
assert asyncio.run(scenario()) == [True, True, True, False, False]
|
||||
|
||||
Reference in New Issue
Block a user