147 lines
5.1 KiB
Python
147 lines
5.1 KiB
Python
from io import BytesIO
|
|||
|
|
|
||
|
|
from PIL import Image
|
||
|
|
|
||
|
|
from pigway_frame_flow.providers.photo_sources import PhotoSourceInput
|
||
|
|
|
||
|
|
|
||
|
|
def setup_photos(service, settings):
|
||
|
|
root = settings.data_dir.parent / "photos"
|
||
|
|
root.mkdir()
|
||
|
|
settings.photo_root = root
|
||
|
|
Image.new("RGB", (300, 200)).save(root / "a.jpg")
|
||
|
|
service.photo_sources.save(
|
||
|
|
"family", PhotoSourceInput(name="Family", kind="local", location=".")
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def make_job(client, name):
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/jobs",
|
||
|
|
json={
|
||
|
|
"name": name,
|
||
|
|
"enabled": False,
|
||
|
|
"schedule": {"type": "daily", "time": "07:00"},
|
||
|
|
"source": {"type": "photo_source", "source_id": "family"},
|
||
|
|
},
|
||
|
|
)
|
||
|
|
assert r.status_code == 201
|
||
|
|
return r.json()["id"]
|
||
|
|
|
||
|
|
|
||
|
|
def run(client, service, job, confirm=True):
|
||
|
|
r = client.post(f"/api/v1/jobs/{job}/run")
|
||
|
|
assert r.status_code == 202, r.text
|
||
|
|
id = r.json()["operation_id"]
|
||
|
|
service.process_one()
|
||
|
|
if confirm:
|
||
|
|
service.sync()
|
||
|
|
op = client.get("/api/v1/operations/" + id).json()
|
||
|
|
return op["result"]["image_id"], op["result"]["rendered_asset_id"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_task_origin_filter_and_star_bulk(client, service, settings):
|
||
|
|
setup_photos(service, settings)
|
||
|
|
job = make_job(client, "Family random")
|
||
|
|
image, asset = run(client, service, job)
|
||
|
|
rows = client.get("/api/v1/assets", params={"origin": "task", "job_id": job}).json()
|
||
|
|
assert len(rows) == 1 and rows[0]["job_name"] == "Family random"
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/assets/batch", json={"action": "star", "asset_ids": [asset, asset]}
|
||
|
|
)
|
||
|
|
assert len(r.json()["results"]) == 1
|
||
|
|
assert client.get("/api/v1/assets").json()[0]["starred"]
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/assets/batch",
|
||
|
|
json={"action": "delete", "asset_ids": [asset, "missing"]},
|
||
|
|
)
|
||
|
|
assert [x["status"] for x in r.json()["results"]] == ["completed", "failed"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_each_task_keeps_one_after_confirmation(client, service, settings):
|
||
|
|
setup_photos(service, settings)
|
||
|
|
job = make_job(client, "A")
|
||
|
|
first, _ = run(client, service, job)
|
||
|
|
second, _ = run(client, service, job)
|
||
|
|
service.cloud_library.sweep()
|
||
|
|
assert service.process_one()
|
||
|
|
assert first not in {
|
||
|
|
x.image_id for x in service.registry.get("living-room").images()
|
||
|
|
}
|
||
|
|
assert second in {x.image_id for x in service.registry.get("living-room").images()}
|
||
|
|
other = make_job(client, "B")
|
||
|
|
third, _ = run(client, service, other)
|
||
|
|
service.cloud_library.checked = 0
|
||
|
|
service.cloud_library.sweep()
|
||
|
|
assert second in {x.image_id for x in service.registry.get("living-room").images()}
|
||
|
|
assert third in {x.image_id for x in service.registry.get("living-room").images()}
|
||
|
|
|
||
|
|
|
||
|
|
def test_unconfirmed_replacement_never_evicts_previous(client, service, settings):
|
||
|
|
setup_photos(service, settings)
|
||
|
|
job = make_job(client, "A")
|
||
|
|
first, _ = run(client, service, job)
|
||
|
|
second, _ = run(client, service, job, confirm=False)
|
||
|
|
service.cloud_library.sweep()
|
||
|
|
assert not service.process_one()
|
||
|
|
assert first in {x.image_id for x in service.registry.get("living-room").images()}
|
||
|
|
assert not service.db.one(
|
||
|
|
"SELECT cleanup_operation FROM managed_cloud_uploads WHERE image_id=?", (first,)
|
||
|
|
)["cleanup_operation"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_starred_old_upload_survives_then_unstar_cleans(client, service, settings):
|
||
|
|
setup_photos(service, settings)
|
||
|
|
job = make_job(client, "A")
|
||
|
|
first, asset = run(client, service, job)
|
||
|
|
assert (
|
||
|
|
client.put(f"/api/v1/assets/{asset}/star", json={"starred": True}).status_code
|
||
|
|
== 200
|
||
|
|
)
|
||
|
|
run(client, service, job)
|
||
|
|
service.cloud_library.sweep()
|
||
|
|
assert not service.process_one()
|
||
|
|
assert (
|
||
|
|
client.put(f"/api/v1/assets/{asset}/star", json={"starred": False}).status_code
|
||
|
|
== 200
|
||
|
|
)
|
||
|
|
service.cloud_library.checked = 0
|
||
|
|
service.cloud_library.sweep()
|
||
|
|
assert service.process_one()
|
||
|
|
assert first not in {
|
||
|
|
x.image_id for x in service.registry.get("living-room").images()
|
||
|
|
}
|
||
|
|
assert service.assets.path(asset).exists()
|
||
|
|
|
||
|
|
|
||
|
|
def test_manual_upload_origin(client, service):
|
||
|
|
buf = BytesIO()
|
||
|
|
Image.new("RGB", (30, 20)).save(buf, "JPEG")
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/preview", files={"file": ("manual.jpg", buf.getvalue(), "image/jpeg")}
|
||
|
|
)
|
||
|
|
id = r.json()["asset_id"]
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/devices/living-room/upload", json={"type": "image", "asset_id": id}
|
||
|
|
)
|
||
|
|
service.process_one()
|
||
|
|
rows = client.get("/api/v1/assets", params={"origin": "manual"}).json()
|
||
|
|
assert len(rows) == 1 and rows[0]["asset_id"] == id
|
||
|
|
|
||
|
|
|
||
|
|
def test_clear_cloud_requires_confirmation_and_clears_current(client, service):
|
||
|
|
before = len(service.registry.get("living-room").images())
|
||
|
|
assert (
|
||
|
|
client.post(
|
||
|
|
"/api/v1/devices/living-room/images/clear", json={"confirm_current": False}
|
||
|
|
).status_code
|
||
|
|
== 400
|
||
|
|
)
|
||
|
|
r = client.post(
|
||
|
|
"/api/v1/devices/living-room/images/clear", json={"confirm_current": True}
|
||
|
|
)
|
||
|
|
assert r.status_code == 200 and len(r.json()["operations"]) == before
|
||
|
|
for _ in range(before):
|
||
|
|
assert service.process_one()
|
||
|
|
assert not service.registry.get("living-room").images()
|