# Test odbiorczy po wdrozeniu Lista do wykonania **w podanej kolejnosci** po uruchomieniu instancji u klienta. Kazdy punkt ma oczekiwany wynik: jesli sie nie zgadza, nie przechodz dalej, bo kolejne punkty na nim polegaja. Czas wykonania calosci: okolo 90 minut, plus czekanie na przypomnienia i na podsumowanie (do dwoch godzin, mozna w tle). **Wszystkie punkty wykonuje sie przy WLACZONYM trybie bezpiecznym wysylki**, poza sekcja I, ktora ten tryb celowo wylacza na koniec. Dzieki temu zadna wiadomosc z testow nie trafi do prawdziwych ludzi. --- ## A. Przed uruchomieniem | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | A1 | `cp .env.production.example .env.production` i uzupelnic | plik istnieje, `chmod 600` | | A2 | `./scripts/check-config.sh --skip-db` | `Konfiguracja w porzadku`, kod wyjscia 0 | | A3 | Sprawdzic DNS domeny: `dig +short webinary.klient.pl` | adres IP serwera | | A4 | Sprawdzic certyfikat SSL na reverse proxy klienta | waznosc powyzej 14 dni | | A5 | Porty otwarte na firewallu: 80, 443 | dostepne z internetu | | A6 | Przy `VIDEO_PROVIDER=LIVEKIT`: dodatkowo 7880, 7881 TCP oraz **port UDP 7882** (tryb mux, jeden port dla wszystkich) | dostepne z internetu | **A6 jest najczestszym przeoczeniem.** Bez otwartego zakresu UDP uczestnicy wchodza do pokoju i widza liste obecnych, ale **sie nie slysza**. Objaw wyglada jak awaria aplikacji, a jest regula firewalla. --- ## B. Uruchomienie | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | B1 | `docker compose -f docker-compose.prod.yml --env-file .env.production up -d` | wszystkie kontenery `Started` | | B2 | `docker compose -f docker-compose.prod.yml ps` | postgres i minio `healthy`, backend `healthy` (do 90 s) | | B3 | `docker compose logs backend \| grep "KONFIGURACJA INSTANCJI" -A 20` | wypis stanu, `Brak ostrzezen konfiguracyjnych` | | B4 | `curl -s https://webinary.klient.pl/api/health` | `{"status":"UP",...}` | | B5 | `./scripts/check-config.sh` (juz bez `--skip-db`) | kod wyjscia 0 | Jesli B3 pokazuje ostrzezenia, przeczytaj je: kazde nazywa skutek, nie ustawienie. Ostrzezenie o wylaczonym trybie bezpiecznym na tym etapie oznacza, ze **testy ponizej wysla prawdziwe wiadomosci**. | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | B6 | `docker compose logs backend \| grep -i "haslo administratora"` | jednorazowe haslo (gdy `ADMIN_PASSWORD` bylo puste) | | B7 | Zalogowac sie na `/logowanie` i **od razu zmienic haslo** | wejscie do `/panel`, haslo zmienione | --- ## C. Konfiguracja przez panel Poczta i bramka SMS **nie sa** w `.env`: ustawia sie je w panelu, bo zmiana nie moze wymagac dostepu do serwera. | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | C1 | Ustawienia -> E-mail: host, port, login, haslo, nazwa nadawcy | zapisane | | C2 | **Sprawdzic, ze `MAIL_SAFE_MODE` jest wlaczony i ma adres zastepczy** | wlaczony, adres wlasny | | C3 | Ustawienia -> E-mail -> Test wysylki | wiadomosc dociera na adres zastepczy | | C4 | Ustawienia -> Aplikacja: nazwa marki, adres publiczny, strefa czasowa | zapisane | | C5 | Ustawienia -> Grafika: logo i ikona | widoczne w panelu i na stronie glownej | | C6 | Ustawienia -> Zgody: tresc regulaminu i zgod marketingowych | zapisane, `CONSENT_VERSION` ustawiona | | C7 | Przy uzyciu SMS: Ustawienia -> SMS, klucz bramki, tryb bezpieczny **wlaczony** | test SMS dociera na numer zastepczy | --- ## D. Sciezka webinarowa | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | D1 | Panel -> Webinary -> nowy: tytul, termin **w przyszlosci**, limit miejsc | zapisany jako DRAFT | | D2 | Dodac tresc strony w edytorze, wgrac obrazek | obrazek widoczny w podgladzie | | D3 | Opublikowac webinar | status PUBLISHED | | D4 | Otworzyc `/w/{slug}` w oknie prywatnym | strona z trescia i formularzem zapisu | | D5 | Zapisac sie z wlasnego adresu, zaznaczyc zgody | ekran potwierdzenia | | D6 | Sprawdzic skrzynke | mail potwierdzajacy z **dzialajacym** linkiem i zalacznikiem `invite.ics` | | D7 | Otworzyc `invite.ics` w kalendarzu | wydarzenie z poprawna data i strefa | | D8 | Kliknac link z maila | strona wlasnego zapisu, widoczny przycisk rezygnacji | | D9 | Panel -> lista zapisanych | wpis widoczny, zgody z wersja i data | | D10 | Eksport CSV listy zapisanych | plik otwiera sie w Excelu, polskie znaki poprawne | | D11 | Dodac regule przypomnienia na **za 5 minut** i odczekac | przypomnienie dociera (na adres zastepczy) | | D12 | Wejsc na `/spotkanie/{token}` z linku | przedpokoj z tytulem i imieniem do potwierdzenia | | D13 | Wejsc na sale, wyslac wiadomosc na czacie | wiadomosc widoczna, autor jako imie i inicjal | | D14 | Zadac pytanie i zaglosowac na nie z drugiej przegladarki | licznik glosow rosnie o 1, drugi glos tej samej osoby odrzucony | | D15 | Panel prowadzenia: zatwierdzic pytanie | pytanie pojawia sie u uczestnika | | D16 | Zglosic sie do zabrania glosu, przyznac glos z panelu | mikrofon uczestnika dziala | | D17 | Zakonczyc webinar, zmienic termin na przeszly, odczekac `SUMMARY_DELAY_MINUTES` | podsumowanie dociera raz, nie dwa razy | | D18 | Wystawic certyfikat uczestnikowi z odnotowana obecnoscia | PDF z **polskimi znakami** w nazwisku | | D19 | Otworzyc `/certyfikat/{numer}` w oknie prywatnym | weryfikacja pokazuje numer, tytul, imie z inicjalem, **bez adresu e-mail** | **D18 jest krytyczny.** Certyfikat z polamanym nazwiskiem jest bezuzyteczny, a blad osadzania czcionki widac wylacznie na gotowym pliku. --- ## E. Sciezka Akademii | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | E1 | Otworzyc `/` w oknie prywatnym | strona glowna Akademii, przycisk `Zaloguj sie` | | E2 | Panel -> Kursy -> nowy kurs z modulem i lekcja wideo | zapisany jako DRAFT | | E3 | Wgrac nagranie lekcji, ustawic dlugosc | plik widoczny | | E4 | Opublikowac kurs, ustawic cene | widoczny na `/kursy` | | E5 | Zarejestrowac konto kursanta na `/rejestracja` | mail z linkiem potwierdzajacym | | E6 | Potwierdzic adres z linku | konto aktywne, wejscie na `/moje-konto` | | E7 | Zapisac sie na kurs **darmowy** | dostep od reki, kurs na pulpicie | | E8 | Otworzyc odtwarzacz, obejrzec lekcje do konca | lekcja oznaczona jako ukonczona, postep rosnie | | E9 | Przewinac nagranie w polowie | przewijanie dziala (naglowek Range) | | E10 | Dodac quiz do lekcji, rozwiazac go jako kursant | wynik policzony, **poprawne odpowiedzi nie sa widoczne w zrodle strony przed koncem** | | E11 | Ukonczyc wszystkie lekcje wymagane | kurs COMPLETED, certyfikat wystawiony automatycznie | | E12 | Sprawdzic mail o ukonczeniu | zalacznik PDF, dzialajacy link do pulpitu | | E13 | Panel -> Kursanci -> szczegoly | postep, zgody z historia, ostatnia aktywnosc | | E14 | Zalogowac sie adresem majacym konto w obu swiatach | ekran wyboru: `Moje konto` i `Panel` | --- ## F. Czego NIE DA SIE sprawdzic lokalnie Te punkty **musza** zostac wykonane po wdrozeniu, bo lokalnie nie ma jak ich przejsc. Kazdy z nich to droga, ktora dotad nie zostala przebyta end-to-end. ### F1. Notyfikacja ITN Autopay Operator laczy sie **z naszym serwerem**, wiec wymaga publicznej domeny z waznym certyfikatem. Na localhost nie da sie tego wywolac. | Krok | Oczekiwany wynik | | --- | --- | | Ustawic `AUTOPAY_MODE=TEST`, ServiceID i klucz ze srodowiska testowego operatora | Autopay: konfiguracja kompletna w wypisie startowym | | Zamowic kurs platny jako kursant | przekierowanie na bramke testowa | | Zaplacic karta testowa operatora | powrot na `/kurs/{slug}/platnosc` | | Odczekac na ITN (do kilkudziesieciu sekund) | strona sama pokazuje nadany dostep | | Panel -> Zamowienia | status PAID, w dzienniku wpis notyfikacji | | Powtorzyc notyfikacje z panelu operatora | **dostep nadany raz**, drugi komunikat potwierdzony bez zmiany | | Sprobowac zaplacic kwota inna niz w zamowieniu | notyfikacja odrzucona, wpis w dzienniku | **Dopiero po przejsciu wszystkich krokow** przelaczyc `AUTOPAY_MODE=PRODUCTION`. Wypis startowy pokaze wtedy ostrzezenie o prawdziwych pieniadzach: to jest oczekiwane i ma zostac zauwazone. ### F2. Nagrywanie przez Egress Lokalnie proces zatrzymywal sie na tym, ze szablon nie subskrybowal zadnej sciezki: do domkniecia potrzebny jest publikujacy uczestnik w tej samej sieci co Egress, czego nie da sie zestawic na Docker Desktop przy jednym `NODE_IP`. | Krok | Oczekiwany wynik | | --- | --- | | `docker compose ... --profile livekit up -d` | cztery kontenery wideo dzialaja | | `docker compose logs livekit-egress \| head -30` | polaczenie z Redisem, brak bledow ICE | | Zestawic spotkanie z **dwoma** uczestnikami, jeden publikuje obraz | obraz i dzwiek plyna w obie strony | | Uruchomic nagrywanie z panelu prowadzenia | wpis w `recordings` ze statusem DOWNLOADING | | Zakonczyc nagrywanie, odczekac | status READY, plik w MinIO | | Odtworzyc nagranie z panelu, przewinac | odtwarza sie i przewija | Gdy nagrywanie nie ruszy: **reczne wgranie pliku dziala i jest droga awaryjna**. Nie blokuje to wdrozenia. ### F3. Webhook dostawcy wideo | Krok | Oczekiwany wynik | | --- | --- | | Ustawic `VIDEO_WEBHOOK_SECRET` w panelu i ten sam sekret u dostawcy | zapisane | | Wywolac zdarzenie u dostawcy (zakonczenie nagrania) | wpis w logu backendu, odpowiedz 200 | | Wyslac zadanie z **blednym** sekretem: `curl -X POST .../api/webhooks/video/livekit -d '{}'` | **404**, nie 403 | Odpowiedz 404 jest celowa: 403 pozwalalaby sprawdzic, ktore webinary istnieja. ### F4. Zajawki w komunikatorach Wymagaja publicznego adresu: robot musi pobrac strone z internetu. | Krok | Oczekiwany wynik | | --- | --- | | Wkleic `https://webinary.klient.pl/w/{slug}` na Slacku albo Facebooku | zajawka z tytulem webinaru i opisem | | `curl -A "facebookexternalhit/1.1" https://webinary.klient.pl/w/{slug}` | HTML z `og:title` zawierajacym tytul | | To samo dla webinaru w stanie DRAFT | **404**, wersje robocze nie moga wyciekac | ### F5. Poczta wychodzaca do prawdziwych odbiorcow | Krok | Oczekiwany wynik | | --- | --- | | Sprawdzic rekordy SPF i DKIM domeny nadawcy | skonfigurowane | | Wyslac test na adres w Gmailu i w Outlooku | wiadomosc **nie trafia do spamu** | | Sprawdzic naglowki dostarczonej wiadomosci | SPF `pass`, DKIM `pass` | --- ## G. Kopia zapasowa | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | G1 | `./scripts/backup.sh` | katalog w `./backups`, pliki `baza.dump` i `minio.tar.gz` niepuste | | G2 | Sprawdzic `INFO.txt` w kopii | data, nazwa bazy, wersja obrazu | | G3 | Dodac wpis do `crontab` (patrz README) | `crontab -l` pokazuje wpis | | G4 | **Odtworzyc kopie na maszynie testowej**, nie na produkcji | dane zgodne ze stanem z chwili kopii | **G4 jest obowiazkowy.** Kopia, ktorej nigdy nie odtworzono, nie jest kopia zapasowa, tylko plikiem o nieznanej zawartosci. --- ## H. Bezpieczenstwo | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | H1 | `curl -s https://webinary.klient.pl/api/admin/webinars` | 401, nie lista | | H2 | `nmap -p 5432,9000,9001 webinary.klient.pl` | porty **zamkniete** z internetu | | H3 | Otworzyc `/swagger-ui.html` | 404 albo 403 (`SWAGGER_ENABLED=false`) | | H4 | Zalogowac sie 6 razy blednym haslem | blokada po 5 probie, komunikat **taki sam** jak przy zlym hasle | | H5 | Sprobowac pobrac certyfikat cudzym tokenem zapisu | 404 | | H6 | Sprawdzic `/api/public/config` | wylacznie nazwa marki, adres i strefa, **zadnych sekretow** | --- ## I. Przelaczenie na ruch produkcyjny **Dopiero po przejsciu wszystkich punktow powyzej.** | # | Czynnosc | Oczekiwany wynik | | --- | --- | --- | | I1 | Skasowac dane testowe: webinary, kursy, zamowienia, kontakty testowe | listy puste albo z prawdziwymi danymi | | I2 | **Nie kasowac** `app_settings` ani konta administratora | konfiguracja zachowana | | I3 | Ustawienia -> E-mail: **wylaczyc** `MAIL_SAFE_MODE` | zapisane | | I4 | Przy uzyciu SMS: wylaczyc `SMS_SAFE_MODE` | zapisane | | I5 | Zrestartowac backend i przeczytac wypis startowy | ostrzezenie o wylaczonym trybie bezpiecznym: **oczekiwane** | | I6 | Wyslac jedna prawdziwa wiadomosc testowa do siebie | dociera na wlasciwy adres | | I7 | `./scripts/backup.sh` na czysto | kopia stanu wyjsciowego | Po I3 kazda wysylka idzie do **prawdziwych odbiorcow**. Od tego momentu dane testowe z terminem w przeszlosci moga uruchomic scheduler i wyslac wiadomosci do osob z bazy: dlatego I1 jest **przed** I3, a nie po.