enable agent to run e2e tests agaisnt fresh db
This commit is contained in:
parent
d3ccae9c75
commit
446568963f
38
AGENTS.md
38
AGENTS.md
@ -21,3 +21,41 @@
|
|||||||
## Tests
|
## Tests
|
||||||
Use JUnit 5 for backend unit test
|
Use JUnit 5 for backend unit test
|
||||||
Use Playwright for frontend end-to-end tests
|
Use Playwright for frontend end-to-end tests
|
||||||
|
|
||||||
|
## Full-stack end-to-end tests
|
||||||
|
|
||||||
|
Agents running end-to-end tests must use free ports so they do not interfere with
|
||||||
|
an existing development server. Find two currently available ports with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -e "const net=require('net');let left=2,ports=[];for(let i=0;i<2;i++){const s=net.createServer();s.listen(0,'127.0.0.1',()=>{ports.push(s.address().port);s.close(()=>{if(!--left)console.log(ports.join(' '))})})}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the backend from the repository root, substituting the first free port and
|
||||||
|
the normal local database credentials:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DB_URL=jdbc:postgresql://localhost:5432/postgres \
|
||||||
|
DB_USERNAME=root \
|
||||||
|
DB_PASSWORD=root \
|
||||||
|
DB_REINITIALIZE=true \
|
||||||
|
SERVER_PORT=<backend-port> \
|
||||||
|
mvn exec:java
|
||||||
|
```
|
||||||
|
|
||||||
|
`DB_REINITIALIZE=true` drops and recreates the application's tables at startup,
|
||||||
|
so only use it for a disposable test database. `SERVER_PORT` defaults to `7070`
|
||||||
|
when omitted.
|
||||||
|
|
||||||
|
From `frontend/`, run Playwright using the second free port. Playwright starts
|
||||||
|
Vite on that port, and Vite proxies API requests to the backend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VITE_API_TARGET=http://127.0.0.1:<backend-port> \
|
||||||
|
PLAYWRIGHT_BASE_URL=http://127.0.0.1:<frontend-port> \
|
||||||
|
npm run test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the backend running until Playwright finishes, then stop it. The free-port
|
||||||
|
check and server startup should happen close together because another process
|
||||||
|
can claim a released port in between.
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test'
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
|
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000'
|
||||||
|
const frontendPort = new URL(baseURL).port || (baseURL.startsWith('https:') ? '443' : '80')
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests',
|
testDir: './tests',
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
@ -8,7 +11,7 @@ export default defineConfig({
|
|||||||
workers: process.env.CI ? 1 : undefined,
|
workers: process.env.CI ? 1 : undefined,
|
||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
use: {
|
use: {
|
||||||
baseURL: 'http://localhost:3000',
|
baseURL,
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
@ -18,8 +21,8 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'npm run dev',
|
command: `npm run dev -- --port ${frontendPort}`,
|
||||||
url: 'http://localhost:3000',
|
url: baseURL,
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import io.javalin.http.HttpStatus
|
|||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val database = Database(DatabaseConfig.fromEnvironment())
|
val database = Database(DatabaseConfig.fromEnvironment())
|
||||||
database.initialize()
|
database.initialize(reinitialize = System.getenv("DB_REINITIALIZE").toBoolean())
|
||||||
val users = UserRepository(database)
|
val users = UserRepository(database)
|
||||||
val loginService = LoginService(users, SessionRepository(database))
|
val loginService = LoginService(users, SessionRepository(database))
|
||||||
val entities = EntityDefinitionRepository(database)
|
val entities = EntityDefinitionRepository(database)
|
||||||
|
|||||||
@ -35,9 +35,15 @@ class Database(private val config: DatabaseConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize(reinitialize: Boolean = false) {
|
||||||
getConnection().use { connection ->
|
getConnection().use { connection ->
|
||||||
connection.createStatement().use { statement ->
|
connection.createStatement().use { statement ->
|
||||||
|
if (reinitialize) {
|
||||||
|
statement.execute(DROP_ENTITY_FIELDS_TABLE)
|
||||||
|
statement.execute(DROP_ENTITY_DEFINITIONS_TABLE)
|
||||||
|
statement.execute(DROP_SESSIONS_TABLE)
|
||||||
|
statement.execute(DROP_USERS_TABLE)
|
||||||
|
}
|
||||||
statement.execute(CREATE_USERS_TABLE)
|
statement.execute(CREATE_USERS_TABLE)
|
||||||
statement.execute(ADD_USER_ROLE)
|
statement.execute(ADD_USER_ROLE)
|
||||||
statement.execute(CREATE_SESSIONS_TABLE)
|
statement.execute(CREATE_SESSIONS_TABLE)
|
||||||
@ -67,6 +73,11 @@ class Database(private val config: DatabaseConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
const val DROP_ENTITY_FIELDS_TABLE = "DROP TABLE IF EXISTS entity_fields"
|
||||||
|
const val DROP_ENTITY_DEFINITIONS_TABLE = "DROP TABLE IF EXISTS entity_definitions"
|
||||||
|
const val DROP_SESSIONS_TABLE = "DROP TABLE IF EXISTS sessions"
|
||||||
|
const val DROP_USERS_TABLE = "DROP TABLE IF EXISTS users"
|
||||||
|
|
||||||
const val CREATE_USERS_TABLE = """
|
const val CREATE_USERS_TABLE = """
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user