Przejdź do treści

Plan testów — wszystkie kroki + warianty

Kompletny plan testów E2E z konkretnymi krokami, expected behavior i metodą weryfikacji. Szczególnie szczegółowe warianty Kroku 5 (otwarcie skrytki) i Kroku 6 (zamknięcie skrytki).

Setup testowy

Pole Wartość
Maszyna LOCKER-SZB-SDI-001 (Szubin SDI dev, real Modbus hardware)
Kiosk version v1.1.21 (lub nowszy)
Bridge hid-reader.ps1 + SmartBoxHidReader task musi działać
E2E Kurier PIN 261438 — nadawca, używa "Kod nadania"
E2E Pracownik PIN 517796 — odbiorca

Przed każdym testem sprawdź panel: https://smartbox.ergoflow.app/dashboard/system/machines/{id} — status ONLINE, currentVersion 1.1.21.

Mapa testów

flowchart LR
    A[Test 1-3<br/>Login / PIN / Profile] --> B[Test 4 Happy<br/>full deposit cycle]
    B --> C[Test 5 KROK 5<br/>otwarcie skrytki<br/>warianty 5.A → 5.F]
    C --> D[Test 6 KROK 6<br/>zamknięcie skrytki<br/>warianty 6.A → 6.H]
    D --> E[Test 7-11<br/>backend guards]
    E --> F[Test 12-20<br/>evil paths + cleanup]

Test 1 — Login PIN

Setup: świeży kiosk na ekranie login.

Kroki: 1. Kliknij zakładkę PIN 2. Wpisz 517796 3. Tap ✓

Expected: ekran menu główne usera "E2E Pracownik".

Weryfikacja: Credential.lastUsedAt zaktualizowane w DB.


Test 2 — Wrong PIN (buffer clear)

Kroki: 1. PIN tab → wpisz 000000 → ✓ 2. Pokaże się: 🔴 "Nie rozpoznano PIN-u" 3. Wpisz drugi PIN 517796 → ✓

Expected: PinPad wyzerowany przed drugim wpisaniem (operator nie powinien widzieć żadnych pozostałych kropek po pierwszej próbie). Drugi login OK.


Test 3 — Profile (edycja telefonu + locale)

Kroki: 1. Login PIN 517796 2. Menu → "Mój profil" 3. Zmień telefon na +48 600 100 200 4. Zmień język na EN 5. Tap "Zapisz"

Expected: Toast sukcesu. Wyloguj się i zaloguj ponownie — interfejs w EN.


Test 4 — Deposit happy path (full cycle)

Setup: w panelu utwórz świeżą paczkę dla E2E Pracownik (lub użyj jednej z 5 pre-seeded: 995354 / 969031 / 915274 / 146795 / 462257).

Kroki: 1. Login PIN 261438 (Kurier) 2. Menu → "Nadaj paczkę" → "Kod nadania" 3. Wpisz ostatnie 6 cyfr trackingId (np. 995354) 4. Wybierz rozmiar M 5. Pre-confirm → "Otwórz teraz" 6. Drzwi się otwierają → włóż coś (kartka A4) 7. Zamknij drzwi 8. Reopen panel → "Zakończ"

Expected: 🟢 DepositSuccessScreen "Paczka nadana" z QR kodem.

Weryfikacja panel: - shipment status DEPOSITED - lockerId ustawiony, depositedAt ≈ NOW - Timeline: ✅ Utworzono ✅ Przypisano ✅ Złożono w skrytce - Locker status w panelu: OCCUPIED


Test 5 — KROK 5: Otwarcie skrytki (wszystkie warianty)

Krok 5 = moment od tapnięcia "Otwórz teraz" do osiągnięcia stanu "drzwi otwarte, kiosk czeka na zamknięcie". Testujemy każdy realny scenariusz hardware'owy.

Test 5.A — Happy open (sanity)

Kroki: Standardowy flow z Test 4 do momentu otwarcia drzwi.

Expected: Pulse Modbus → drzwi otwierają się w <2s → ekran "Skrytka otwarta, włóż paczkę i zamknij" → kiosk czeka.

Weryfikacja: Locker.status = RESERVED (do momentu close).

Test 5.B — Drzwi już otwarte przed pulse (pre-flight detection)

Setup: Ręcznie pootwieraj drzwi wybranej skrytki PRZED rozpoczęciem flow.

Kroki: 1. Pełen flow do "Otwórz teraz" 2. _runOpen wywoła hardware.isClosed(N) → zwróci false 3. Kiosk SKIP unlock pulse, przechodzi od razu do phase=open 4. Ekran "Drzwi już otwarte — zamknij gdy gotowe"

Expected: Żadnego E07 z mastera (bo pulse nigdy nie poszedł). Operator zamyka drzwi normalnie → happy path.

Weryfikacja: Brak fault reportu, shipment idzie do DEPOSITED.

Test 5.C — Master Modbus offline (firewall)

Setup: Na kiosku jako Admin: netsh advfirewall firewall add rule name="block-modbus-test" dir=out action=block protocol=TCP remoteport=502

Kroki: 1. Deposit flow do "Otwórz teraz" 2. Driver próbuje wysłać Modbus command → po ~12s timeout

Expected: 🟡 Fault dialog "Wystąpił problem techniczny" z _phasePriorToFault=opening → przyciski [🔄 Spróbuj ponownie] [Anuluj] (oba widoczne — retry MA SENS bo drzwi się nie otworzyły).

Weryfikacja: - W panelu Zgłoszenia: MACHINE-reported, faultCode: E07-DRIVER, currentPhase: opening - Po Anuluj → shipment CREATED, locker BROKEN

Cleanup: netsh advfirewall firewall delete rule name="block-modbus-test" + admin reset locker'a w panelu.

Test 5.D — Door open timeout E07 (drzwi mechanicznie zacięte)

Setup: Wybierz lockera który mechanicznie się nie otwiera, lub trzymaj drzwi siłą zamknięte w momencie pulse.

Kroki: 1. Deposit flow → "Otwórz teraz" 2. Master wyśle pulse, ale sensor nie wykryje otwarcia 3. Master emituje E07 po ~30s

Expected: Fault dialog "Wystąpił problem techniczny" + przyciski [🔄 Spróbuj ponownie] [Anuluj].

Weryfikacja: - Zgłoszenia: MACHINE-reported, faultCode: E07, sensorByte + masterMW100 zapisane - Spróbuj ponownie → force re-fire pulse (forceCommand=true) - Anuluj → locker BROKEN, shipment CREATED

Test 5.E — Driver timeout (brak ACK z mastera, sieć cool)

Setup: Trudne do zasymulowania bez modyfikacji hardware. W praktyce dzieje się przy network glitchu Modbus TCP.

Kroki: 1. Deposit flow → "Otwórz teraz" 2. hardware.open() rzuca TimeoutException (12s default)

Expected: Identyczny UI jak 5.D, ale faultCode: E07-DRIVER (rozróżnienie w panel).

Test 5.F — "Zgłoś obsłudze" w opening phase

Kroki: 1. Deposit flow → "Otwórz teraz" 2. W trakcie animacji opening, jeśli pojawi się userConfirmOpened dialog (20s bez opened event) — tap "Zgłoś obsłudze"

Expected: USER-reported fault, phase=faultReported, _phasePriorToFault=userConfirmOpened.

Weryfikacja: - v1.1.21: przyciski TYLKO [Anuluj] (BRAK Spróbuj ponownie — drzwi już są fizycznie w jakimś dziwnym stanie) - Po Anuluj → shipment CREATED, locker BROKEN, USER fault report z currentPhase: opening


Test 6 — KROK 6: Zamknięcie skrytki (wszystkie warianty)

Krok 6 = moment od momentu kiedy drzwi są fizycznie otwarte, do osiągnięcia stanu DEPOSITED/PICKED_UP po reopen panel. Testujemy wszystkie scenariusze close + reopen + Anuluj.

Test 6.A — Happy close: zamknij + "Zakończ"

Kroki: 1. Drzwi otwarte → wkładasz coś (deposit) lub wyjmujesz (pickup) → zamknij drzwi 2. Reopen panel pojawia się z 3 przyciskami (deposit) lub 2 (pickup) 3. Tap "Zakończ"

Expected: 🟢 DepositSuccessScreen / "Paczka odebrana".

Weryfikacja: status DEPOSITED/PICKED_UP, locker OCCUPIED/FREE, audit log notification.

Test 6.B — Happy close + auto-pop countdown (10s)

Kroki: 1. Zamknij drzwi → reopen panel 2. Nie tap żadnego przycisku przez 10s

Expected: Countdown z 10 do 0, auto-pop, identyczny stan jak Test 6.A.

Test 6.C — "Otwórz ponownie" normalny

Kroki: 1. Zamknij drzwi → reopen panel 2. Tap 🔵 "Otwórz ponownie" 3. Drzwi otwierają się znów → zamknij ponownie → reopen panel pojawia się znowu 4. Tap "Zakończ"

Expected: - Drugi open pulse poszedł do hardware (drzwi się otworzyły) - Backend: drugi markDeposited zwrócił 409 ConflictException (już DEPOSITED), kiosk zignorował silent - Audit log: dokładnie JEDEN system.notification_shipment.deposited (nie dwa!) - Finalny stan: DEPOSITED + OCCUPIED bez korupcji

Test 6.D — Race: "Otwórz ponownie" w trakcie blokowania

Cel: Bug operator'a — klikał Otwórz ponownie zaraz po pierwszym kliknięciu (w trakcie animacji blokowania) → komunikat o błędzie z hardware.

Kroki: 1. Zamknij drzwi → reopen panel 2. Tap "Otwórz ponownie" 3. Bardzo szybko (200ms-1s) tap "Otwórz ponownie" ponownie 3-4 razy

Expected (v1.1.14+): Tylko PIERWSZY tap się wykonuje. Kolejne tap'y w trakcie phase=opening są dropowane przez early-return guard if (_phase != _Phase.done) return;.

Weryfikacja: - agent.log na kiosku: tylko jeden modbus.command.sent - Brak komunikatów o błędzie z hardware - Brak E16 z mastera

Test 6.E — "Anuluj — paczki nie ma w skrytce" (czerwony, deposit only)

Cel: v1.1.11 NEW endpoint — operator zamknął drzwi, ale po refleksji zorientował się że nie włożył paczki.

Kroki: 1. Deposit flow → zamknij drzwi → reopen panel 2. Tap 🔴 "Anuluj — paczki nie ma w skrytce"

Expected: Kiosk wywoła POST /deposit/cancel-after-deposit → 🔴 DepositFailureScreen.

Weryfikacja krytyczna: - shipment status CREATED (rollback) - lockerId: null, depositedAt: null, assignedAt: null, machineId: null - Locker status FREE (NIE BROKEN! operator EXPLICITNIE potwierdził że pusty)

❌ FAIL jeśli: locker zostaje OCCUPIED, lub shipment dalej DEPOSITED, lub depositedAt nadal ustawione.

Test 6.F — Reopen + Anuluj-paczki-nie-ma (multi-cycle rollback)

Kroki: 1. Deposit flow → zamknij drzwi → reopen panel 2. Tap "Otwórz ponownie" → drzwi otwierają się 3. Zamknij ponownie → reopen panel 4. Tap "Anuluj — paczki nie ma w skrytce"

Expected: Identyczne jak 6.E — kompletny rollback nawet po reopen cycle.

Test 6.G — Drzwi przytrzymane (E07 podczas close phase) — krytyczny test

Cel: Bug Kevina + Test 3 operator'a — drzwi otwarte, operator nie zamyka, system pokazuje czerwony screen ALE backend marked DEPOSITED.

Setup: Świeża paczka, deposit flow.

Kroki: 1. Login Kurier 261438 → "Nadaj paczkę" → kod nadania (np. 995354) → rozmiar M → "Otwórz teraz" 2. Drzwi się otwierają 3. Trzymaj drzwi otwarte 60s+ — nie zamykaj 4. Po ~20s pojawi się userConfirmClosed dialog "Czy skrytka zamknięta?" 5. Tap "Zgłoś obsłudze" 6. Pojawi się żółty card

Expected (v1.1.21): - Tytuł: "Drzwi nie zostały zamknięte" (NIE "Wystąpił problem techniczny") - Subtitle: "Zamknij drzwi skrytki, aby zakończyć operację. Jeśli drzwi się nie zamykają — anuluj i poproś o pomoc." - Przyciski: TYLKO [Anuluj] (BRAK Spróbuj ponownie!)

  1. Tap Anuluj

Expected: 🔴 DepositFailureScreen.

Weryfikacja krytyczna w panelu (v1.1.20 fix): - shipment status CREATED (NIE DEPOSITED!) - lockerId: null, depositedAt: nullKLUCZOWE - Locker: BROKEN (przez abandon-locker) - Panel Zgłoszenia: USER-reported, currentPhase: userConfirmClosed

❌ FAIL jeśli: shipment DEPOSITED mimo czerwonego screena. To wskazuje że sensor flicker wciąż triggeruje _onCloseDetected w wadliwy sposób.

Test 6.H — Sensor flicker (sztuczne — wymaga modu hardware)

Cel: Sprawdzić v1.1.20 guard w _onCloseDetected — gdy phase != open, close event jest SUPPRESSED.

Trudne do zasymulowania bez wstrzyknięcia false-positive close event w driver. Pominąć jeśli brak dostępu.

Test 6.I — Master comm loss mid-cycle (po pulse, przed close)

Setup: PO unlock pulse (drzwi otwarte), ZANIM zamkniesz drzwi: netsh advfirewall firewall add rule name="block-modbus-mid" dir=out action=block protocol=TCP remoteport=502

Kroki: 1. Deposit flow → drzwi otwarte 2. Włącz firewall block 3. Zamknij drzwi

Expected: - Driver event 'closed' nie dochodzi (master nie odpowiada) - Po 2s fallback poll (UI-side) próbuje isClosed() bezpośrednio → też timeout - Po 20s (_openPhaseDeadline) → userConfirmClosed dialog

Weryfikacja: - Tap "DOCIŚNIJ" lub "TAK - sprawdź" → re-poll - Po unblock firewall: re-poll wykryje closed → onClosed → DEPOSITED OK - Cleanup: netsh advfirewall firewall delete rule name="block-modbus-mid"


Test 7 — Inactivity overlay → bounce do login

Kroki: 1. Login PIN 517796 → wejdź w "Moje przesyłki" lub menu 2. Nie tap przez 5+ sekund (inactivityTimeoutSec) 3. Pojawi się overlay "Wylogowanie za 10s..." 4. Nie tap przez kolejne 10s

Expected: Bounce do LoginScreen, PIN pad wyczyszczony, tab snap na KARTA.

Weryfikacja: Jeśli operator tap w trakcie 10s countdown — countdown anulowany, wraca do operacji.


Test 8 — Pickup happy path (sanity)

Setup: Świeżo zdeponowana paczka dla E2E Pracownik.

Kroki: 1. Login PIN 517796 2. Lista DEPOSITED → tap paczka → pre-confirm "Otworzymy skrytkę N" 3. "Otwórz teraz" → drzwi otwierają się 4. Wyjmij paczkę → zamknij 5. Reopen panel → "Zakończ"

Expected: 🟢 "Paczka odebrana".

Weryfikacja: shipment PICKED_UP, locker FREE.


Test 9 — Backend duplicate PIN guard (cross-user)

Cel: Sprawdzić że dwóch użytkowników nie może mieć tego samego PIN-u.

```bash

Wewnątrz backend container, test przez NestFactory:

docker exec smartbox-backend-1 node /tmp/test-guards.js ```

Expected: creds.create(kurier, { PIN, 517796 })409 ConflictException ("PIN already in use")


Test 10 — Backend duplicate RFID guard (cross-user, case-insensitive)

Kroki: 1. Seed RFID abc1234567 dla E2E Pracownik 2. Próba dodać ten sam UID dla E2E Kurier → 409 3. Próba dodać ABC1234567 (uppercase) → 409 (lookup hash jest lowercase)


Test 11 — Reactivation guard

Kroki: 1. Dezaktywuj poprzedni RFID Pracownika (set active: false) 2. Dodaj ten sam UID dla Kuriera (succeeds bo unique constraint patrzy tylko na active) 3. Próbuj reaktywować poprzedni Pracownika RFID → 409 ("would create duplicate active")


Test 12 — Kiosk evil-path: short PIN, slow PIN, backspace

Kroki: 1. Login → PIN tab → wpisz 123 → ✓ button disabled (canSubmit=false) 2. Wpisz dalej 456 → ✓ aktywne 3. Backspace → znów disabled 4. Skasuj wszystko → tap ✓ — nie powinno nic robić

Expected: Submit dostępny tylko przy pin.length == 6.


Test 13 — Inactive user PIN

Setup: sql UPDATE "User" SET active=false WHERE email='e2e-employee@test.local';

Kroki: Login PIN 517796

Expected: Backend /pickup/verify zwraca 404 (lub 403). Kiosk: "Nie rozpoznano".

Cleanup: UPDATE "User" SET active=true WHERE email='e2e-employee@test.local';


Test 14 — Deposit lookup: bad tracking number

Kroki: 1. Login Kurier → Nadaj paczkę → Kod nadania 2. Próby: - 000000 (nieistniejący) → 404 - last6 paczki która jest już DEPOSITED gdzie indziej → 409 - last6 paczki CANCELLED → 404 (nie pokazuje terminal status)

Expected: Każdy przypadek pokazuje błąd, operator może spróbować ponownie.


Test 15 — Deposit: brak wolnego lockera

Setup: Zajmij wszystkie M lockery (BROKEN lub OCCUPIED w panelu).

Kroki: Nadaj paczkę → kod nadania → rozmiar M → "Otwórz teraz"

Expected: Backend /deposit/begin-existing zwraca 409 "no free locker of size M". Kiosk pokazuje "Brak wolnych skrytek tego rozmiaru".


Test 16 — Double-click race: "Nadaj" 3× szybko

Cel: Sprawdzić że szybki potrójny tap nie tworzy 3 orders.

Kroki: Po wpisaniu kodu nadania, tap "Otwórz teraz" 3× szybko (200ms apart).

Expected: Tylko JEDEN assign + jeden open. Następne tap'y są no-op (Flutter setState idempotent + backend transakcja FOR UPDATE).

Weryfikacja: Audit log = jeden shipment.assign_locker event.


Test 17 — Anuluj deposit po pre-flight (drzwi already open z poprzedniej sesji)

Setup: Symuluj crash poprzedniej sesji — ręcznie otwórz drzwi.

Kroki: 1. Deposit flow → pre-confirm 2. Tap "Otwórz teraz" → isClosed() zwraca false 3. Caller (deposit_lookup_screen) wywołuje /deposit/abandon-locker automatycznie

Expected: 🔴 DepositFailureScreen "Wybrana skrytka nie spełnia warunków".

Weryfikacja: locker BROKEN, shipment CREATED (cofnięte).


Test 18 — Brak komunikacji z masterem — kill ModbusClient TCP, spróbuj open

Setup: Firewall block port 502 PRZED rozpoczęciem flow.

Kroki: Deposit flow → "Otwórz teraz"

Expected: Po ~12s driver timeout → E07-DRIVER fault report → przyciski [Spróbuj ponownie] [Anuluj].


Test 19 — Locale switch w trakcie sesji

Kroki: 1. Login (PL UI) 2. W headerze tap flag UK 3. Cały UI przeładowuje się w EN 4. Wyloguj się, zaloguj ponownie

Expected: Po reloginie języka jest zapamiętany jako preferredLocale w User row, kiosk podstawia od razu.


Test 20 — Cleanup (po wszystkich testach)

Cel: Po sesji testów pozostawić system w czystym stanie.

bash ssh smartbox 'docker exec smartbox-postgres-1 psql -U smartbox -d smartbox -c " -- Cancel all test shipments UPDATE \"Shipment\" SET status='CANCELLED', \"cancelledAt\"=NOW(), \"lockerId\"=NULL WHERE \"recipientId\"=(SELECT id FROM \"User\" WHERE email='e2e-employee@test.local') AND status NOT IN ('PICKED_UP', 'CANCELLED'); -- Reset BROKEN lockers to FREE UPDATE \"Locker\" SET status='FREE' WHERE \"machineId\"=(SELECT id FROM \"Machine\" WHERE code='LOCKER-SZB-SDI-001') AND status IN ('BROKEN','RESERVED','OCCUPIED'); "'


Macierz pokrycia wariantów

Etap Test Wariant Stan testu
Login 1 PIN happy
Login 2 wrong PIN
Login 12 short/incomplete PIN ⚪ TODO
Login 13 inactive user ⚪ TODO
Krok 5 (open) 5.A happy
Krok 5 5.B drzwi już otwarte (pre-flight)
Krok 5 5.C master comm loss (firewall)
Krok 5 5.D E07 mechaniczny ✓ (D zaliczone wcześniej)
Krok 5 5.E driver timeout
Krok 5 5.F Zgłoś obsłudze podczas opening
Krok 6 (close) 6.A happy + Zakończ
Krok 6 6.B auto-pop 10s
Krok 6 6.C Otwórz ponownie + Zakończ
Krok 6 6.D race Otwórz ponownie w trakcie blokowania
Krok 6 6.E NEW Anuluj-paczki-nie-ma ✓ (v1.1.20 fix verified)
Krok 6 6.F reopen + Anuluj-paczki-nie-ma
Krok 6 6.G drzwi trzymane otwarte → status korupcja ✓ (v1.1.20 fix verified)
Krok 6 6.H sensor flicker 🚧 wymaga mocka
Krok 6 6.I master comm loss mid-cycle
Backend 9 duplicate PIN guard
Backend 10 duplicate RFID guard
Backend 11 reactivation guard
Edge 14 bad lookup
Edge 15 no free locker
Edge 16 double-click race
Edge 17 abandon po pre-flight
Edge 18 brak Modbus
UX 7 inactivity bounce
UX 19 locale switch
Cleanup 20 DELETE testowych

Legenda: ✓ = zaliczone, ⚪ = pending, 🚧 = blokada (wymaga modu hardware/test infra)

Diagram drzewa decyzyjnego dla testów Krok 5 + 6

flowchart TD
    A[Tap Otwórz teraz] --> B{Drzwi otwierają się?}
    B -- TAK --> C{Drzwi się zamykają?}
    B -- NIE master E07 --> D[Test 5.D]
    B -- NIE driver timeout --> E[Test 5.E]
    B -- NIE comm loss --> F[Test 5.C]
    B -- już otwarte --> G[Test 5.B]

    C -- TAK happy --> H[Test 6.A / 6.B]
    C -- nie zamyka --> I{User reakcja?}
    I -- Zgłoś obsłudze --> J[Test 6.G - drzwi przytrzymane]
    I -- DOCIŚNIJ --> K[re-pulse + czeka]
    I -- nic / 20s --> L[userConfirmClosed dialog]

    H --> M{Reopen panel akcja?}
    M -- Zakończ --> N[🟢 success]
    M -- Otwórz ponownie --> O[Test 6.C / 6.D]
    M -- Anuluj-paczki-nie-ma --> P[Test 6.E / 6.F]
    M -- auto 10s --> N

    O --> H
    P --> Q[🔴 failure + rollback]
    J --> Q

Co notować per test

Per test, w issue / ticket: - PASS / FAIL + numer + nazwa - Tracking ID paczki testowej - Screenshot panel detalu (timeline + status) - Czy pojawił się fault report w System → Zgłoszenia (faultCode, reportedBy, timeline długość) - flutter-keys.log tail (jeśli problem z input/RFID) - agent.log tail (jeśli problem z OTA / heartbeat) - Stan locker'a w panelu (FREE/RESERVED/OCCUPIED/BROKEN)

Szybkie sprawdzanie stanu z bash (per test)

bash ssh smartbox 'docker exec smartbox-postgres-1 psql -U smartbox -d smartbox -c " SELECT \"trackingId\", status, \"lockerId\" IS NOT NULL AS has_locker, \"depositedAt\" IS NOT NULL AS deposited, \"pickedUpAt\" IS NOT NULL AS picked_up FROM \"Shipment\" WHERE \"updatedAt\" > NOW() - INTERVAL '\''10 minutes'\'' ORDER BY \"updatedAt\" DESC LIMIT 8; SELECT number, status FROM \"Locker\" WHERE \"machineId\" = (SELECT id FROM \"Machine\" WHERE code='\''LOCKER-SZB-SDI-001'\'') ORDER BY number; "'