Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ jobs:
with:
working-directory: apps/jobnik-manager

e2e:
name: Run E2E Suite
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
with:
fetch-depth: 0
filter: 'blob:none'

- name: Init nodejs
uses: ./.github/actions/init-pnpm

- name: Run e2e suite
run: pnpm exec turbo run e2e --affected

helm-lint:
name: Run Helm lint Check
runs-on: ubuntu-latest
Expand Down
16 changes: 13 additions & 3 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ npm ci
npm test
```

**For Docker setup**: Ensure the `jobnik-manager` repository is cloned alongside this repo, then run:
**For Docker setup**: the manager image is built from this monorepo's own checkout, so no other repository needs to be cloned. From the repository root:

```bash
docker compose up -d
pnpm --filter jobnik-e2e run e2e
```

or, to manage the services directly:

```bash
docker compose -f e2e/docker-compose.yaml up -d --build
```

## Usage
Expand All @@ -64,7 +70,7 @@ npm test -- -t "retry"

## Docker Services

The test suite uses Docker Compose to orchestrate services. **Note**: Docker setup requires the `jobnik-manager` repository to be cloned in the same directory.
The test suite uses Docker Compose to orchestrate services. The `manager` and `migrator` services build the manager image from this repository's own checkout (`docker/backend.Dockerfile`), so no other repository needs to be cloned.

**Services:**

Expand All @@ -88,6 +94,10 @@ docker compose down -v

**Alternative**: You can also run the jobnik-manager server locally instead of using Docker. See the [jobnik-manager repository](https://github.com/MapColonies/jobnik-manager) for local development setup instructions.

### Turbo wiring

`package.json` lists `jobnik-manager` as a devDependency even though nothing here imports it. It's not dead weight: turbo derives which packages a change "affects" from the workspace dependency graph, and this suite's only real link to the manager is the Docker image built above, which turbo can't see. The unused dependency gives turbo that edge, so `turbo run e2e --affected` (and CI's e2e job) actually run when the manager changes.

## Test Suites

| Test File | Description |
Expand Down
14 changes: 11 additions & 3 deletions e2e/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ version: '3.8'

services:
manager:
image: jobnik-manager:e2e
build:
context: ./jobnik-manager
context: ..
dockerfile: docker/backend.Dockerfile
args:
APP_NAME: jobnik-manager
ports:
- '8080:8080'
environment:
Expand All @@ -28,15 +32,19 @@ services:

migrator:
restart: 'no'
image: jobnik-manager:e2e
build:
context: ./jobnik-manager
context: ..
dockerfile: docker/backend.Dockerfile
args:
APP_NAME: jobnik-manager
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/jobnik?schema=job_manager
entrypoint: ['npx']
command: ['prisma', 'migrate', 'deploy', '--schema', './db/prisma/schema.prisma']
command: ['prisma@6', 'migrate', 'deploy', '--schema', './db/prisma/schema.prisma']

db:
image: postgres:14
Expand Down
17 changes: 17 additions & 0 deletions e2e/infrastructure/globalSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from 'node:path';
import { upAll, downAll } from 'docker-compose';
import isCI from 'is-ci';

const composeOptions = { cwd: path.join(__dirname, '..'), log: true };

export async function setup(): Promise<void> {
// Always rebuild: the suite must run against an image built from the current checkout,
// never a stale one left over from a previous invocation.
await upAll({ ...composeOptions, commandOptions: ['--build', '--wait'] });
}

export async function teardown(): Promise<void> {
if (isCI) {
await downAll({ ...composeOptions, commandOptions: ['-v'] });
}
}
8 changes: 6 additions & 2 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"scripts": {
"e2e": "vitest run",
"e2e:watch": "vitest watch",
"e2e:ui": "vitest --ui"
"e2e:ui": "vitest --ui",
"type-check": "tsc --noEmit"
},
"repository": {
"type": "git",
Expand All @@ -32,6 +33,9 @@
},
"devDependencies": {
"@faker-js/faker": "^10.0.0",
"@vitest/ui": "^3.2.4"
"@vitest/ui": "^3.2.4",
"docker-compose": "^1.3.0",
"is-ci": "^4.1.0",
"jobnik-manager": "workspace:*"
}
}
8 changes: 3 additions & 5 deletions e2e/tests/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ describe('pause test', () => {
});

const taskSampleData = createTaskData();
const task = await producer.createTasks(stage.id, stage.type, [taskSampleData]);
await api.PUT('/v1/tasks/{taskId}/status', {
body: { status: 'PENDING' },
params: { path: { taskId: task[0]!.id } },
});
// Tasks are created PENDING already; the status endpoint only accepts COMPLETED/FAILED, so
// there is no user-facing way to set PENDING here (and none is needed).
await producer.createTasks(stage.id, stage.type, [taskSampleData]);
//#endregion

const dequeueResult = await consumer.dequeueTask(stage.type);
Expand Down
14 changes: 5 additions & 9 deletions e2e/tests/wait.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,14 @@ describe('wait test', () => {

//#region create stage
const stageSampleData = createStageData();
// startAsWaiting creates the stage as WAITING directly; the status endpoint only accepts
// PENDING (WAITING is system-managed), so there is no user-facing way to set it here.
const stage = await producer.createStage(job.id, stageSampleData, true);
await api.PUT('/v1/stages/{stageId}/status', {
body: { status: 'WAITING' },
params: { path: { stageId: stage.id } },
});

const taskSampleData = createTaskData();
const task = await producer.createTasks(stage.id, stage.type, [taskSampleData]);
await api.PUT('/v1/tasks/{taskId}/status', {
body: { status: 'PENDING' },
params: { path: { taskId: task[0]!.id } },
});
// Tasks are created PENDING already, even under a WAITING stage; the status endpoint only
// accepts COMPLETED/FAILED, so there is no user-facing way to set PENDING here.
await producer.createTasks(stage.id, stage.type, [taskSampleData]);
//#endregion
const dequeueResult = await consumer.dequeueTask(stage.type);

Expand Down
1 change: 1 addition & 0 deletions e2e/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineConfig({
test: {
include: ['tests/*.spec.ts'],
environment: 'node',
globalSetup: ['./infrastructure/globalSetup.ts'],

reporters,
},
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading