Skip to content

Commit af8b5b9

Browse files
committed
Fixed 2FA being skipped on first login attempt
1 parent 86e597b commit af8b5b9

File tree

5 files changed

+28
-25
lines changed

5 files changed

+28
-25
lines changed

compose.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ services:
202202
# ================================================
203203
# Profile: `test`
204204
# Runs playwright tests against Ghost
205+
# Default services duplicated to keep full isolation between dev and test environments
205206
# ================================================
206207
mysql-test:
207208
image: mysql:8.4.5
@@ -214,6 +215,8 @@ services:
214215
restart: always
215216
networks:
216217
- test-network
218+
tmpfs:
219+
- /var/lib/mysql:rw,noexec,nosuid,size=1g
217220
healthcheck:
218221
test: mysql -uroot -proot ghost_test -e 'select 1'
219222
interval: 1s
@@ -338,4 +341,4 @@ volumes:
338341

339342
networks:
340343
test-network:
341-
driver: bridge
344+
driver: bridge

e2e/src/database/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import knex from 'knex';
2-
import logging from '@tryghost/logging';
2+
import type {Knex} from 'knex';
33

44
interface DatabaseConfig {
55
client: string;
@@ -25,11 +25,8 @@ function getDatabaseConfig(): DatabaseConfig {
2525

2626
export async function resetDb(): Promise<void> {
2727
const config = getDatabaseConfig();
28-
console.log('Resetting database', config);
2928
const db = knex(config);
3029

3130
await db('sessions').truncate();
32-
33-
const result = await db.raw('SELECT * FROM sessions');
34-
console.log('Result', result);
31+
await db('brute').truncate();
3532
}

e2e/src/playwright/global-setup.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import logging from '@tryghost/logging';
2+
import {resetDb} from '../database';
23

34
async function globalSetup() {
5+
logging.debug('globalSetup:begin');
46
const baseURL = process.env.BASE_URL || 'http://localhost:2368';
57
const adminUsername = process.env.ADMIN_USERNAME || '[email protected]';
68
const adminPassword = process.env.ADMIN_PASSWORD || 'testpassword123';
@@ -45,41 +47,38 @@ async function globalSetup() {
4547
}
4648

4749
logging.info('✅ Admin user created successfully');
48-
} else {
49-
logging.info('✅ Ghost setup already completed');
5050

51-
// Try to login to verify the admin user exists and credentials are correct
52-
const sessionResponse = await fetch(`${baseURL}/ghost/api/admin/session`, {
51+
// Perform initial login to mark user as having logged in
52+
// Otherwise 2FA is skipped because it's the first login
53+
logging.info('🔐 Performing initial login...');
54+
const loginResponse = await fetch(`${baseURL}/ghost/api/admin/session`, {
5355
method: 'POST',
5456
headers: {
5557
'Content-Type': 'application/json',
56-
'Accept-Version': 'v5.0',
57-
Origin: baseURL
58+
'Accept-Version': 'v5.0'
5859
},
5960
body: JSON.stringify({
6061
username: adminUsername,
6162
password: adminPassword
6263
})
6364
});
6465

65-
if (sessionResponse.status === 201) {
66-
logging.info('✅ Admin user credentials verified');
67-
} else if (sessionResponse.status === 403) {
68-
// This might be a 2FA requirement, which is fine
69-
const responseBody = await sessionResponse.json();
70-
if (responseBody.errors && responseBody.errors[0] && responseBody.errors[0].type === 'Needs2FAError') {
71-
logging.info('✅ Admin user exists (2FA required)');
72-
} else {
73-
logging.warn('⚠️ Could not verify admin credentials:', responseBody);
74-
}
66+
if (!loginResponse.ok) {
67+
const error = await loginResponse.text();
68+
logging.warn(`Initial login failed: ${loginResponse.status} - ${error}`);
7569
} else {
76-
logging.warn('⚠️ Could not verify admin credentials. Status:', sessionResponse.status);
70+
logging.info('✅ Initial login completed');
7771
}
72+
} else {
73+
throw new Error('Ghost setup already completed');
7874
}
7975
} catch (error) {
8076
logging.error('❌ Global setup failed:', error);
8177
throw error;
8278
}
79+
80+
await resetDb();
81+
logging.debug('globalSetup:end');
8382
}
8483

8584
export default globalSetup;

e2e/src/playwright/test-fixtures.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {EmailServiceFactory} from '../services/email/EmailServiceFactory';
66
import type {IEmailService} from '../services/email/IEmailService';
77
import {resetDb} from '../database';
88
import Errors from '@tryghost/errors';
9+
import logging from '@tryghost/logging';
910

1011
// Define types for our custom fixtures
1112
export type TestFixtures = {
@@ -85,8 +86,11 @@ export const test = baseTest.extend<TestFixtures>({
8586
});
8687

8788
// Global beforeEach hook to reset database sessions before each test
88-
test.beforeEach(async () => {
89+
test.beforeEach(async ({page}) => {
90+
logging.debug('globalBeforeEach:begin');
91+
await page.context().clearCookies();
8992
await resetDb();
93+
logging.debug('globalBeforeEach:end');
9094
});
9195

9296
export {expect} from '@playwright/test';

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"docker:test:unit": "COMPOSE_PROFILES=${COMPOSE_PROFILES:-ghost} NX_DAEMON=false docker compose run --rm --no-deps ghost yarn test:unit",
4747
"docker:test:browser": "COMPOSE_PROFILES=${COMPOSE_PROFILES:-ghost} docker compose run --rm ghost yarn test:browser",
4848
"docker:test:all": "COMPOSE_PROFILES=${COMPOSE_PROFILES:-ghost} NX_DAEMON=false docker compose run --rm ghost yarn nx run ghost:test:all",
49-
"docker:test:e2e": "COMPOSE_PROFILES=test,e2e docker compose up --exit-code-from tests-e2e --abort-on-container-exit --attach=tests-e2e",
49+
"docker:test:e2e": "COMPOSE_PROFILES=test,e2e docker compose up --force-recreate --exit-code-from tests-e2e --abort-on-container-exit --attach=tests-e2e --attach=e2e-server",
5050
"docker:reset": "docker compose down -v && docker compose up -d --wait",
5151
"docker:restart": "docker compose down && docker compose up -d --wait",
5252
"docker:down": "docker compose down",

0 commit comments

Comments
 (0)