Skip to content

Commit f7b8536

Browse files
committed
feat: add testing infrastructure for login MFA flow and E2E suite
Signed-off-by: romanetar <roman_ag@hotmail.com>
1 parent 3b111f5 commit f7b8536

6 files changed

Lines changed: 166 additions & 6 deletions

File tree

.github/workflows/pull_request_frontend_tests.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ jobs:
5454
PHP_VERSION: 8.3
5555
OTEL_SDK_DISABLED: true
5656
OTEL_SERVICE_ENABLED: false
57-
TURNSTILE_SITE_KEY: ''
58-
TURNSTILE_SECRET_KEY: ''
57+
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
58+
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
5959
services:
6060
mysql:
6161
image: mysql:8.0
@@ -97,7 +97,6 @@ jobs:
9797
run: yarn build
9898
- name: Prepare application
9999
run: |
100-
cp .env.example .env
101100
./update_doctrine.sh
102101
php artisan doctrine:migrations:migrate --no-interaction
103102
php artisan db:seed --force

.github/workflows/pull_request_unit_tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
PHP_VERSION: 8.3
3838
OTEL_SDK_DISABLED: true
3939
OTEL_SERVICE_ENABLED: false
40+
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
41+
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
42+
PLAYWRIGHT_WORKERS: 4
4043
services:
4144
mysql:
4245
image: mysql:8.0

.github/workflows/push.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ jobs:
3333
PHP_VERSION: 8.3
3434
OTEL_SDK_DISABLED: true
3535
OTEL_SERVICE_ENABLED: false
36-
TURNSTILE_SITE_KEY: '1x00000000000000000000AA'
37-
TURNSTILE_SECRET_KEY: '1x0000000000000000000000000000000AA'
36+
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
37+
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
3838
services:
3939
mysql:
4040
image: mysql:8.0
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: Front End Tests On Push
2+
3+
on: push
4+
5+
jobs:
6+
7+
js-unit-tests:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Check out repository code
11+
uses: actions/checkout@v4
12+
- name: Set up Node.js
13+
uses: actions/setup-node@v4
14+
with:
15+
node-version: '22'
16+
cache: 'yarn'
17+
- name: Install JS dependencies
18+
run: yarn install --frozen-lockfile
19+
- name: Run Jest unit tests
20+
run: yarn test:unit:ci
21+
- name: Upload Jest coverage
22+
uses: actions/upload-artifact@v4
23+
if: always()
24+
with:
25+
name: jest-coverage
26+
path: tests/js/coverage
27+
retention-days: 5
28+
29+
e2e-tests:
30+
runs-on: ubuntu-latest
31+
env:
32+
APP_ENV: testing
33+
APP_DEBUG: true
34+
APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4=
35+
APP_URL: http://localhost:8001
36+
DEV_EMAIL_TO: smarcet@gmail.com
37+
DB_CONNECTION: mysql
38+
DB_HOST: 127.0.0.1
39+
DB_PORT: 3306
40+
DB_DATABASE: idp_test
41+
DB_USERNAME: root
42+
DB_PASSWORD: 1qaz2wsx
43+
REDIS_HOST: 127.0.0.1
44+
REDIS_PORT: 6379
45+
REDIS_DB: 0
46+
REDIS_PASSWORD: 1qaz2wsx
47+
REDIS_DATABASES: 16
48+
SSL_ENABLED: false
49+
SESSION_DRIVER: redis
50+
SESSION_COOKIE_SECURE: false
51+
PHP_VERSION: 8.3
52+
OTEL_SDK_DISABLED: true
53+
OTEL_SERVICE_ENABLED: false
54+
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
55+
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
56+
PLAYWRIGHT_WORKERS: 4
57+
services:
58+
mysql:
59+
image: mysql:8.0
60+
env:
61+
MYSQL_ROOT_PASSWORD: 1qaz2wsx
62+
MYSQL_DATABASE: idp_test
63+
ports:
64+
- 3306:3306
65+
options: >-
66+
--health-cmd="mysqladmin ping"
67+
--health-interval=10s
68+
--health-timeout=5s
69+
--health-retries=3
70+
steps:
71+
- name: Create Redis
72+
uses: supercharge/redis-github-action@1.8.1
73+
with:
74+
redis-port: 6379
75+
redis-password: 1qaz2wsx
76+
- name: Check out repository code
77+
uses: actions/checkout@v4
78+
- name: Install PHP
79+
uses: shivammathur/setup-php@v2
80+
with:
81+
php-version: ${{ env.PHP_VERSION }}
82+
extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu
83+
- name: Install PHP dependencies
84+
uses: ramsey/composer-install@v3
85+
env:
86+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.PAT }}"} }'
87+
- name: Set up Node.js
88+
uses: actions/setup-node@v4
89+
with:
90+
node-version: '22'
91+
cache: 'yarn'
92+
- name: Install JS dependencies
93+
run: yarn install --frozen-lockfile
94+
- name: Build frontend assets
95+
run: yarn build
96+
- name: Prepare application
97+
run: |
98+
./update_doctrine.sh
99+
php artisan doctrine:migrations:migrate --no-interaction
100+
php artisan db:seed --force
101+
php artisan idp:create-super-admin test@test.com '1Qaz2wsx!'
102+
php artisan idp:create-raw-user e2e@test.com '1Qaz2wsx!'
103+
- name: Install Playwright Chromium
104+
run: npx playwright install --with-deps chromium
105+
- name: Start web server
106+
run: php artisan serve --host=127.0.0.1 --port=8001 &
107+
- name: Wait for server to be ready
108+
run: |
109+
for i in $(seq 1 20); do
110+
curl -sf http://localhost:8001 > /dev/null 2>&1 && echo "Server ready" && exit 0
111+
sleep 2
112+
done
113+
echo "Server did not start in time" && exit 1
114+
- name: Run E2E tests
115+
run: yarn test:e2e --reporter=list
116+
- name: Upload Playwright report
117+
uses: actions/upload-artifact@v4
118+
if: always()
119+
with:
120+
name: playwright-report
121+
path: tests/e2e/report
122+
retention-days: 7
123+
- name: Upload Playwright traces
124+
uses: actions/upload-artifact@v4
125+
if: failure()
126+
with:
127+
name: playwright-traces
128+
path: test-results/
129+
retention-days: 7

playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default defineConfig({
55
fullyParallel: true,
66
forbidOnly: !!process.env.CI,
77
retries: process.env.CI ? 2 : 0,
8-
workers: process.env.CI ? 1 : undefined,
8+
workers: process.env.CI ? parseInt(process.env.PLAYWRIGHT_WORKERS ?? '1') : undefined,
99
reporter: [['html', { outputFolder: 'tests/e2e/report' }]],
1010
use: {
1111
baseURL: process.env.APP_URL || 'http://localhost:8001',

tests/TwoFactorLoginFlowTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use App\libs\Auth\Models\TwoFactorAuditLog;
1717
use App\libs\Auth\Models\UserRecoveryCode;
1818
use App\libs\Auth\Models\UserTrustedDevice;
19+
use App\Mail\OAuth2PasswordlessOTPMail;
1920
use App\Services\Auth\IDeviceTrustService;
2021
use App\Services\Auth\ITwoFactorAuditService;
2122
use Auth\AuthHelper;
@@ -25,6 +26,7 @@
2526
use Illuminate\Support\Facades\Cache;
2627
use Illuminate\Support\Facades\Config;
2728
use Illuminate\Support\Facades\Hash;
29+
use Illuminate\Support\Facades\Mail;
2830
use Illuminate\Support\Facades\Session;
2931
use App\libs\OAuth2\Repositories\IOAuth2OTPRepository;
3032
use LaravelDoctrine\ORM\Facades\EntityManager;
@@ -90,6 +92,33 @@ public function testNonAdminWithoutMFALogsInNormally(): void
9092
$this->assertTrue(Auth::check(), 'a non-MFA user must get an authenticated session');
9193
}
9294

95+
// -------------------------------------------------------------------------
96+
// email delivery
97+
// -------------------------------------------------------------------------
98+
99+
public function testMFAChallengeQueuesEmailWithCorrectOTPCode(): void
100+
{
101+
$this->postLogin(self::ADMIN_EMAIL, self::SEED_PASSWORD);
102+
103+
$dbCode = $this->latestOtpCode(self::ADMIN_EMAIL);
104+
105+
Mail::assertQueued(
106+
OAuth2PasswordlessOTPMail::class,
107+
function (OAuth2PasswordlessOTPMail $mail) use ($dbCode): bool {
108+
return $mail->email === self::ADMIN_EMAIL
109+
&& $mail->otp === $dbCode;
110+
}
111+
);
112+
}
113+
114+
public function testResendMFAChallengeQueuesAdditionalEmail(): void
115+
{
116+
$this->postLogin(self::ADMIN_EMAIL, self::SEED_PASSWORD);
117+
$this->resend();
118+
119+
Mail::assertQueued(OAuth2PasswordlessOTPMail::class, 2);
120+
}
121+
93122
// -------------------------------------------------------------------------
94123
// verify2FA
95124
// -------------------------------------------------------------------------

0 commit comments

Comments
 (0)