fix(testy): awaria magazynu kont wymuszana podmianą, nie prawami pliku
build / build (push) Successful in 7s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 6s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
build / build (push) Successful in 7s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 6s
Testy / Kontrola składni wszystkich warstw (push) Successful in 5s
Dwa testy z PR #71 przechodziły lokalnie i padały w CI. Przyczyna nie miała nic wspólnego z badaną rzeczą: wymuszały awarię przez chmod 000 i chmod 555, a CI działa jako ROOT — root omija bity uprawnień w Linuksie, więc odczyt i zapis się udawały i asercje leciały na komunikat, którego nie było. Test zależny od tego, KTO go uruchamia, jest gorszy niż jego brak: daje fałszywe poczucie pokrycia i zapala się w miejscu niezwiązanym z tym, co sprawdza. Teraz awarię wymuszamy podmianą dokładnie tego punktu, w którym system plików mówi „nie": odczytu pliku kont (osłona przepuszcza wszystko inne, żeby szablony nadal się wczytywały) oraz mkstemp przy zapisie. To drugie jest celowe — mkstemp wywala się PIERWSZY przy katalogu tylko do odczytu, jeszcze zanim dojdzie do zapisu i podmiany, i właśnie tego dotyczyła naprawiana poprawka. Sprawdzian na PRAWDZIWYCH prawach zostaje, ale z pominięciem przy uruchomieniu z roota — jako kontrola, że podmiana odpowiada rzeczywistości, a nie tylko sama sobie. W CI się nie wykona i to jest w porządku: znaczyłby tam tyle co nic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit was merged in pull request #73.
This commit is contained in:
@@ -12,6 +12,7 @@ DWIE WŁASNOŚCI, KTÓRYCH PILNUJE TEN PLIK
|
||||
pozycji w menu, brak rysunków w źródle strony.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
@@ -315,40 +316,80 @@ def test_deleting_an_account_cannot_touch_the_administrator(env, monkeypatch):
|
||||
# Ekran kont to JEDYNE miejsce, z którego administrator może naprawić problem
|
||||
# z magazynem — więc musi na nim przeczytać, co i gdzie jest nie tak. Gołe 500
|
||||
# (tak było na pierwszym wdrożeniu) zostawia go z niczym.
|
||||
#
|
||||
# AWARIĘ WYMUSZAMY PODMIANĄ, NIE PRAWAMI PLIKU. Pierwsza wersja robiła chmod 000
|
||||
# i chmod 555 — i przechodziła lokalnie, a padała w CI, bo CI działa jako ROOT,
|
||||
# a root omija bity uprawnień w Linuksie. Test zależny od tego, kto go uruchamia,
|
||||
# jest gorszy niż jego brak: daje fałszywe poczucie pokrycia i zapala się
|
||||
# w miejscu, które z badaną rzeczą nie ma nic wspólnego.
|
||||
#
|
||||
# Podmiana jest przy okazji CELNIEJSZA: sprawdzamy dokładnie ten punkt, w którym
|
||||
# system plików mówi „nie", zamiast liczyć, że da się go do tego zmusić.
|
||||
|
||||
def _unreadable(tmp_path, monkeypatch, mode):
|
||||
d = tmp_path / "stan"
|
||||
d.mkdir()
|
||||
monkeypatch.setenv("ACCOUNTS_FILE", str(d / "accounts.json"))
|
||||
d.chmod(mode)
|
||||
return d
|
||||
def _fail_reading_accounts(monkeypatch, exc=PermissionError(13, "Permission denied")):
|
||||
"""Odczyt PLIKU KONT wywala się; wszystko inne (szablony!) czyta normalnie."""
|
||||
import builtins
|
||||
|
||||
from app import accounts as store
|
||||
|
||||
real_open, target = builtins.open, store.store_path()
|
||||
|
||||
def guarded(file, *a, **kw):
|
||||
if str(file) == target:
|
||||
raise exc
|
||||
return real_open(file, *a, **kw)
|
||||
|
||||
monkeypatch.setattr(builtins, "open", guarded)
|
||||
|
||||
|
||||
def test_unreadable_store_explains_itself_instead_of_500(env, tmp_path, monkeypatch):
|
||||
d = _unreadable(tmp_path, monkeypatch, 0o000)
|
||||
try:
|
||||
c = _client(monkeypatch)
|
||||
r = c.get("/accounts", headers=_auth("szef", "tajne-szefa"))
|
||||
assert r.status_code == 200, "problem z magazynem nie może wywalać strony"
|
||||
assert "Nie mogę odczytać pliku kont" in r.text
|
||||
assert str(d / "accounts.json") in r.text, "komunikat ma podać ŚCIEŻKĘ"
|
||||
finally:
|
||||
d.chmod(0o755)
|
||||
monkeypatch.setenv("ACCOUNTS_FILE", str(tmp_path / "stan" / "accounts.json"))
|
||||
_fail_reading_accounts(monkeypatch)
|
||||
|
||||
c = _client(monkeypatch)
|
||||
r = c.get("/accounts", headers=_auth("szef", "tajne-szefa"))
|
||||
assert r.status_code == 200, "problem z magazynem nie może wywalać strony"
|
||||
assert "Nie mogę odczytać pliku kont" in r.text
|
||||
assert "accounts.json" in r.text, "komunikat ma podać ŚCIEŻKĘ"
|
||||
|
||||
|
||||
def test_read_only_store_refuses_to_save_with_a_reason(env, tmp_path, monkeypatch):
|
||||
d = _unreadable(tmp_path, monkeypatch, 0o555)
|
||||
"""Podmieniamy mkstemp, bo to ON wywala się PIERWSZY przy katalogu tylko do
|
||||
odczytu — jeszcze zanim dojdzie do zapisu i podmiany pliku. Obudowanie samego
|
||||
os.replace niczego by nie dało i właśnie tego dotyczyła poprawka."""
|
||||
import tempfile
|
||||
|
||||
monkeypatch.setenv("ACCOUNTS_FILE", str(tmp_path / "stan" / "accounts.json"))
|
||||
monkeypatch.setattr(tempfile, "mkstemp",
|
||||
lambda *a, **kw: (_ for _ in ()).throw(
|
||||
PermissionError(13, "Read-only file system")))
|
||||
|
||||
c = _client(monkeypatch)
|
||||
r = c.post("/accounts/create", headers=_auth("szef", "tajne-szefa"),
|
||||
follow_redirects=False,
|
||||
data={"login": "ala", "password": "x", "granted": ["chart"]})
|
||||
assert r.status_code == 303
|
||||
# Komunikat jedzie w parametrze zapytania, więc jest zakodowany —
|
||||
# porównanie na surowym nagłówku sprawdzałoby procenty, nie treść.
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
assert "Nie mogę zapisać pliku kont" in unquote_plus(r.headers["location"])
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.geteuid() == 0,
|
||||
reason="root omija bity uprawnień — sprawdzian nic by nie znaczył")
|
||||
def test_real_file_permissions_produce_the_same_message(env, tmp_path, monkeypatch):
|
||||
"""Kontrola, że podmiana wyżej odpowiada RZECZYWISTOŚCI, a nie tylko sama
|
||||
sobie. Pomijana przy uruchomieniu z roota — czyli w CI."""
|
||||
d = tmp_path / "stan"
|
||||
d.mkdir()
|
||||
monkeypatch.setenv("ACCOUNTS_FILE", str(d / "accounts.json"))
|
||||
d.chmod(0o000)
|
||||
try:
|
||||
c = _client(monkeypatch)
|
||||
r = c.post("/accounts/create", headers=_auth("szef", "tajne-szefa"),
|
||||
follow_redirects=False,
|
||||
data={"login": "ala", "password": "x", "granted": ["chart"]})
|
||||
assert r.status_code == 303
|
||||
# Komunikat jedzie w parametrze zapytania, więc jest zakodowany —
|
||||
# porównanie na surowym nagłówku sprawdzałoby procenty, nie treść.
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
assert "Nie mogę zapisać pliku kont" in unquote_plus(r.headers["location"])
|
||||
r = c.get("/accounts", headers=_auth("szef", "tajne-szefa"))
|
||||
assert r.status_code == 200
|
||||
assert "Nie mogę odczytać pliku kont" in r.text
|
||||
finally:
|
||||
d.chmod(0o755)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user