fix(dane): bazy zastane na udziale zostają w użyciu po przejściu na rejestr
build / build (push) Successful in 7s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m35s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Failing after 4m52s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
build / build (push) Successful in 7s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m35s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Failing after 4m52s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
Bez tego wdrożenie DAN-27 WYŁĄCZYŁOBY WYSZUKIWANIE. Dotąd bazy działały domyślnie (wyłączało się je jawnie przez DISABLED_BASES). Po przejściu na rejestr plik bez wpisu w stanie dostaje `ready`, czyli NIE w użyciu — a stan po wdrożeniu jest pusty. Efekt: żadna baza nie jest aktywna i program nagle niczego nie znajduje. Ta cicha zmiana zachowania byłaby gorsza od awarii, bo wygląda jak pusta baza, a nie jak zepsuty deploy — i szukałoby się jej w warstwie danych albo w indeksie. Teraz brak PLIKU stanu oznacza pierwsze uruchomienie i bazy zastane są przyjmowane jako aktywne. Pusty słownik przy ISTNIEJĄCYM pliku to co innego: ktoś świadomie wszystko odstawił, więc nie wskrzeszamy — osobny test tego pilnuje. Rozróżnienie jest celowe i też pod testem: `ready` dotyczy plików WGRANYCH przez ekran (te ktoś musi świadomie włączyć), a nie zastanych przy przejściu na rejestr. Inaczej nowa baza wchodziłaby do wyników sama, bez niczyjej decyzji. Przyjęcie działa też na udziale tylko do odczytu: zapis stanu wtedy nie przechodzi, więc powtórzy się przy każdym uruchomieniu — zachowanie to samo, koszt żaden. Dwa testy opisujące STARE zachowanie zostały poprawione, bo to one były błędne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit was merged in pull request #69.
This commit is contained in:
@@ -202,6 +202,30 @@ def _scan(root: Path) -> list[Path]:
|
||||
if p.is_file() and not p.name.startswith((".", "~$"))]
|
||||
|
||||
|
||||
def _adopt_existing(root: Path) -> dict:
|
||||
"""Pierwsze uruchomienie: bazy zastane na udziale są OD RAZU w użyciu.
|
||||
|
||||
Bez tego wdrożenie DAN-27 wyłączyłoby wyszukiwanie. Dotąd bazy działały
|
||||
domyślnie (wyłączało się je jawnie przez DISABLED_BASES); po przejściu na
|
||||
rejestr plik bez wpisu dostaje `ready`, czyli NIE w użyciu — więc pusty stan
|
||||
po wdrożeniu oznaczałby, że program nagle niczego nie znajduje. Ta cicha
|
||||
zmiana zachowania byłaby gorsza od awarii, bo wygląda jak pusta baza.
|
||||
|
||||
Rozróżnienie jest celowe: `ready` dotyczy plików WGRANYCH przez ekran (te
|
||||
ktoś musi świadomie włączyć), a nie zastanych przy przejściu na rejestr.
|
||||
|
||||
Zapis stanu może się nie udać (udział read-only) — wtedy trudno, przy każdym
|
||||
uruchomieniu przyjmiemy je na nowo. Zachowanie jest to samo, koszt żaden."""
|
||||
files = {str(p.relative_to(root)): {"status": ACTIVE, "adopted_at": _now()}
|
||||
for p in _scan(root)}
|
||||
data = {"files": files, "rules": {**DEFAULT_RULES}}
|
||||
try:
|
||||
_write_state(root, data)
|
||||
except OSError:
|
||||
pass
|
||||
return data
|
||||
|
||||
|
||||
def registry(root: Path | str, *, for_admin: bool = False) -> list[dict]:
|
||||
"""Pliki na udziale wraz ze stanem. `for_admin` odsłania kwarantannę i powody.
|
||||
|
||||
@@ -209,7 +233,9 @@ def registry(root: Path | str, *, for_admin: bool = False) -> list[dict]:
|
||||
dochodziły do przeglądarki i były tylko ukrywane stylem, wystarczyłby podgląd
|
||||
źródła strony, żeby poznać reguły walidacji."""
|
||||
root = Path(root)
|
||||
data = _read_state(root)
|
||||
# Brak PLIKU stanu = pierwsze uruchomienie. Pusty słownik przy istniejącym
|
||||
# pliku to co innego: ktoś świadomie wszystko odstawił, więc nie wskrzeszamy.
|
||||
data = _read_state(root) if state_path(root).exists() else _adopt_existing(root)
|
||||
out: list[dict] = []
|
||||
for p in _scan(root):
|
||||
rel = str(p.relative_to(root))
|
||||
|
||||
@@ -32,19 +32,24 @@ def _xlsx(path, rows=3, header=("id", "opis")):
|
||||
|
||||
# ── rejestr i stany ──────────────────────────────────────────────────────
|
||||
|
||||
def test_new_file_is_visible_but_not_in_use(root):
|
||||
def test_a_file_dropped_on_the_share_is_adopted_as_active(root):
|
||||
"""Baza położona na udziale poza aplikacją (np. przez NFS) ma działać —
|
||||
tak było przed DAN-27 i tak ma zostać. Plik WGRANY EKRANEM to inna sprawa:
|
||||
ten wymaga świadomego włączenia (patrz test niżej)."""
|
||||
_xlsx(root / "baza.xlsx")
|
||||
entry = files.registry(root)[0]
|
||||
assert entry["status"] == files.READY
|
||||
assert entry["in_use"] is False, "nowy plik nie może sam wejść do wyszukiwania"
|
||||
assert entry["status"] == files.ACTIVE
|
||||
assert entry["in_use"] is True
|
||||
|
||||
|
||||
def test_only_active_files_reach_the_search(root):
|
||||
_xlsx(root / "a.xlsx")
|
||||
_xlsx(root / "b.xlsx")
|
||||
assert files.usable_paths(root) == []
|
||||
files.set_status(root, "a.xlsx", files.ACTIVE)
|
||||
files.registry(root) # przyjęcie zastanych
|
||||
files.set_status(root, "b.xlsx", files.READY) # świadome odstawienie
|
||||
assert [pathlib.Path(p).name for p in files.usable_paths(root)] == ["a.xlsx"]
|
||||
files.set_status(root, "b.xlsx", files.ACTIVE)
|
||||
assert len(files.usable_paths(root)) == 2
|
||||
|
||||
|
||||
def test_state_survives_a_restart(root):
|
||||
@@ -160,3 +165,50 @@ def test_delete_removes_the_file_and_its_entry(root):
|
||||
assert not (root / "a.xlsx").exists()
|
||||
assert files.registry(root, for_admin=True) == []
|
||||
assert files.usable_paths(root) == []
|
||||
|
||||
|
||||
# ── przejście na rejestr nie może wyłączyć wyszukiwania ─────────────────
|
||||
|
||||
def test_bases_already_on_the_share_stay_in_use_after_the_switch(root):
|
||||
"""Dotąd bazy działały domyślnie (wyłączało się je przez DISABLED_BASES).
|
||||
Po przejściu na rejestr pusty stan oznaczałby, że program nagle niczego nie
|
||||
znajduje — cicha zmiana zachowania gorsza od awarii, bo wygląda jak pusta baza."""
|
||||
_xlsx(root / "main_base.xlsx")
|
||||
_xlsx(root / "zodiac_pl.xlsx")
|
||||
assert len(files.usable_paths(root)) == 2, "zastane bazy wypadły z wyszukiwania"
|
||||
assert all(e["in_use"] for e in files.registry(root))
|
||||
|
||||
|
||||
def test_adoption_happens_once_and_respects_later_decisions(root):
|
||||
"""Po przyjęciu stan jest zapisany, więc świadome odstawienie bazy ZOSTAJE —
|
||||
kolejny odczyt nie może jej wskrzesić."""
|
||||
_xlsx(root / "a.xlsx")
|
||||
_xlsx(root / "b.xlsx")
|
||||
files.registry(root) # przyjęcie
|
||||
files.set_status(root, "a.xlsx", files.READY) # świadome odstawienie
|
||||
assert [pathlib.Path(p).name for p in files.usable_paths(root)] == ["b.xlsx"]
|
||||
files.set_status(root, "b.xlsx", files.READY) # odstawiamy wszystko
|
||||
assert files.usable_paths(root) == [], "pusty wybór został wskrzeszony"
|
||||
|
||||
|
||||
def test_uploaded_files_still_need_an_explicit_switch_on(root):
|
||||
"""Przyjęcie dotyczy TYLKO baz zastanych. Plik wgrany ekranem ktoś musi
|
||||
świadomie włączyć — inaczej nowa baza wchodziłaby do wyników sama."""
|
||||
_xlsx(root / "zastana.xlsx")
|
||||
files.registry(root)
|
||||
out = files.store_upload(root, "nowa.xlsx", _xlsx(root / "tmp.xlsx").read_bytes())
|
||||
assert out["accepted"] is True
|
||||
names = [pathlib.Path(p).name for p in files.usable_paths(root)]
|
||||
assert "nowa.xlsx" not in names, "wgrana baza weszła do wyników bez decyzji"
|
||||
|
||||
|
||||
def test_adoption_survives_a_read_only_share(root, monkeypatch):
|
||||
"""Na udziale tylko do odczytu stanu nie da się zapisać — zachowanie ma
|
||||
zostać to samo, tylko przyjęcie powtórzy się przy każdym uruchomieniu."""
|
||||
_xlsx(root / "a.xlsx")
|
||||
|
||||
def boom(*a, **kw):
|
||||
raise OSError("read-only file system")
|
||||
|
||||
monkeypatch.setattr(files, "_write_state", boom)
|
||||
assert len(files.usable_paths(root)) == 1
|
||||
|
||||
Reference in New Issue
Block a user