fix(trigger): tolerate unsupported directory fsync
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import errno
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
@@ -402,7 +403,11 @@ class LocalTriggerStore:
|
|||||||
with suppress(PermissionError):
|
with suppress(PermissionError):
|
||||||
fd = os.open(str(path.parent), os.O_RDONLY)
|
fd = os.open(str(path.parent), os.O_RDONLY)
|
||||||
try:
|
try:
|
||||||
os.fsync(fd)
|
try:
|
||||||
|
os.fsync(fd)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EINVAL:
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import errno
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -60,6 +62,40 @@ def test_trigger_store_allows_multiple_triggers_per_session(tmp_path: Path) -> N
|
|||||||
assert first.id != second.id
|
assert first.id != second.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_trigger_store_atomic_write_ignores_unsupported_directory_fsync(
|
||||||
|
tmp_path: Path,
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""Shared folders may allow opening directories but reject directory fsync."""
|
||||||
|
store = LocalTriggerStore(tmp_path)
|
||||||
|
real_open = os.open
|
||||||
|
real_fsync = os.fsync
|
||||||
|
directory_fds: set[int] = set()
|
||||||
|
|
||||||
|
def fake_open(path: str, flags: int, *args: object, **kwargs: object) -> int:
|
||||||
|
fd = real_open(path, flags, *args, **kwargs)
|
||||||
|
if Path(path).name == "triggers":
|
||||||
|
directory_fds.add(fd)
|
||||||
|
return fd
|
||||||
|
|
||||||
|
def fake_fsync(fd: int) -> None:
|
||||||
|
if fd in directory_fds:
|
||||||
|
raise OSError(errno.EINVAL, "Invalid argument")
|
||||||
|
real_fsync(fd)
|
||||||
|
|
||||||
|
monkeypatch.setattr(os, "open", fake_open)
|
||||||
|
monkeypatch.setattr(os, "fsync", fake_fsync)
|
||||||
|
|
||||||
|
trigger = store.create(
|
||||||
|
name="Shared folder safe",
|
||||||
|
channel="websocket",
|
||||||
|
chat_id="chat-1",
|
||||||
|
session_key="websocket:chat-1",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert store.get(trigger.id) is not None
|
||||||
|
|
||||||
|
|
||||||
def test_enqueue_rejects_disabled_trigger(tmp_path: Path) -> None:
|
def test_enqueue_rejects_disabled_trigger(tmp_path: Path) -> None:
|
||||||
store = LocalTriggerStore(tmp_path)
|
store = LocalTriggerStore(tmp_path)
|
||||||
trigger = store.create(
|
trigger = store.create(
|
||||||
|
|||||||
Reference in New Issue
Block a user