Skip to content

Commit 4c665c9

Browse files
Fix seed endpoint URL, auth requirement, and request format in 5.1 Playwright examples
1 parent 9983dc7 commit 4c665c9

1 file changed

Lines changed: 43 additions & 18 deletions

File tree

blogs/series-5-devops-data/5.1-database-seeding.md

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ This is useful for:
424424
* Seeding specific row counts in different environments
425425
* Integration test setup — seed exactly the records you need via an API call
426426

427-
The command is dispatched via an API endpoint that sends a POST to `api/v1/positions/seed`. This can be called from Playwright tests, CI scripts, or Swagger UI.
427+
The command is dispatched via `POST /api/v1/positions/AddMock`. It requires a valid Bearer token (`[Authorize]`) and accepts `{ "RowCount": N }` as a JSON body. It can be called from Playwright tests, CI scripts, or Swagger UI.
428428

429429
---
430430

@@ -469,7 +469,7 @@ Playwright provides a `request` fixture — an `APIRequestContext` that makes HT
469469

470470
### Calling the Seed Endpoint
471471

472-
The `InsertMockPositionCommand` is exposed as `POST /api/v1/positions/seed`. Here is a complete implementation of `seed.spec.ts`:
472+
The `InsertMockPositionCommand` is exposed as `POST /api/v1/positions/AddMock`. It requires a Bearer token (`[Authorize]`) and accepts a JSON body. Here is a complete implementation of `seed.spec.ts`:
473473

474474
```typescript
475475
// Tests/AngularNetTutorial-Playwright/tests/seed.spec.ts
@@ -480,44 +480,58 @@ const API_BASE = process.env.API_URL ?? 'https://localhost:44378';
480480
test.describe('Database seeding via API', () => {
481481

482482
test('seed positions', async ({ request }) => {
483-
// POST /api/v1/positions/seed?rowCount=25
483+
// Acquire a Bearer token first (use your test credentials)
484+
const tokenResponse = await request.post(`${API_BASE}/api/v1/account/authenticate`, {
485+
data: { userName: 'ashtyn1', password: 'Pa$$word123' },
486+
});
487+
const { jwToken } = await tokenResponse.json();
488+
489+
// POST /api/v1/positions/AddMock with JSON body
484490
// InsertMockPositionCommand generates positions with valid FK references
485-
const response = await request.post(`${API_BASE}/api/v1/positions/seed`, {
486-
params: { rowCount: 25 },
491+
const response = await request.post(`${API_BASE}/api/v1/positions/AddMock`, {
492+
headers: { Authorization: `Bearer ${jwToken}` },
493+
data: { RowCount: 25 },
487494
});
488495

489496
expect(response.status()).toBe(200);
490497

491-
const rowsInserted: number = await response.json();
492-
console.log(`Seeded ${rowsInserted} positions`);
493-
expect(rowsInserted).toBeGreaterThan(0);
498+
const result = await response.json();
499+
console.log(`Seeded ${result.data} positions`);
500+
expect(result.succeeded).toBe(true);
494501
});
495502

496503
});
497504
```
498505

499-
**`params: { rowCount: 25 }`**Playwright serializes this as a query string: `?rowCount=25`. The command handler uses this value to control how many positions Bogus generates.
506+
**`POST /api/v1/positions/AddMock`**the actual route registered by the `[Route("AddMock")]` attribute on `PositionsController`. The endpoint is protected with `[Authorize]`, so a valid Bearer token is required.
500507

501-
**`request.post()`**uses the `request` fixture from the `test` parameters. No browser is opened. The call completes before the test body continues.
508+
**`data: { RowCount: 25 }`**Playwright serializes this as a JSON body. `InsertMockPositionCommand` binds from the request body (POST default). Property name matches the C# property: `RowCount` (Pascal case).
502509

503-
**`await response.json()`** — the endpoint returns the count of rows inserted as a plain integer. The assertion confirms at least one record was created.
510+
**`result.data`** — the endpoint returns a `Result<int>` wrapper with `{ succeeded: true, data: 25, ... }`, not a plain integer. Access the row count via `result.data`.
504511

505512
### Seeding in `beforeAll` for a Test Suite
506513

507514
When multiple tests in the same file need the seeded data, call the seed endpoint in `beforeAll` so it runs once before the suite rather than repeating it in every test:
508515

509516
```typescript
510517
// Tests/AngularNetTutorial-Playwright/tests/positions.spec.ts
511-
import { test, expect, request as playwrightRequest } from '@playwright/test';
518+
import { test, expect } from '@playwright/test';
512519

513520
const API_BASE = process.env.API_URL ?? 'https://localhost:44378';
514521

515522
test.describe('Positions list', () => {
516523

517524
test.beforeAll(async ({ request }) => {
525+
// Acquire a Bearer token
526+
const tokenResponse = await request.post(`${API_BASE}/api/v1/account/authenticate`, {
527+
data: { userName: 'ashtyn1', password: 'Pa$$word123' },
528+
});
529+
const { jwToken } = await tokenResponse.json();
530+
518531
// Ensure at least 50 positions exist before any test in this suite runs
519-
await request.post(`${API_BASE}/api/v1/positions/seed`, {
520-
params: { rowCount: 50 },
532+
await request.post(`${API_BASE}/api/v1/positions/AddMock`, {
533+
headers: { Authorization: `Bearer ${jwToken}` },
534+
data: { RowCount: 50 },
521535
});
522536
});
523537

@@ -555,8 +569,19 @@ const API_BASE = process.env.API_URL ?? 'https://localhost:44378';
555569
async function globalSetup() {
556570
const apiContext = await request.newContext({ baseURL: API_BASE });
557571

558-
const response = await apiContext.post('/api/v1/positions/seed', {
559-
params: { rowCount: 100 },
572+
// Acquire a Bearer token using test credentials
573+
const tokenResponse = await apiContext.post('/api/v1/account/authenticate', {
574+
data: { userName: 'ashtyn1', password: 'Pa$$word123' },
575+
});
576+
if (!tokenResponse.ok()) {
577+
throw new Error(`Auth failed: ${tokenResponse.status()} ${await tokenResponse.text()}`);
578+
}
579+
const { jwToken } = await tokenResponse.json();
580+
581+
// POST /api/v1/positions/AddMock with Bearer token and JSON body
582+
const response = await apiContext.post('/api/v1/positions/AddMock', {
583+
headers: { Authorization: `Bearer ${jwToken}` },
584+
data: { RowCount: 100 },
560585
});
561586

562587
if (!response.ok()) {
@@ -570,7 +595,7 @@ async function globalSetup() {
570595
export default globalSetup;
571596
```
572597

573-
`request.newContext()` in `globalSetup` creates a standalone API context outside of any test. `await apiContext.dispose()` releases it when done. Throwing an error from `globalSetup` fails the entire run immediately with a clear message rather than silently running tests against an unseeded database.
598+
`request.newContext()` in `globalSetup` creates a standalone API context outside of any test. The auth call acquires a short-lived JWT using the same test credentials used in browser-based tests. `await apiContext.dispose()` releases it when done. Throwing on either failure (auth or seed) fails the entire run immediately with a clear message rather than silently running tests against an unseeded database.
574599

575600
### When to Use Each Approach
576601

@@ -592,7 +617,7 @@ export default globalSetup;
592617

593618
**Development-only seeding with an idempotency check.** The entire seed block is inside `if (app.Environment.IsDevelopment())` — production never auto-seeds. The `needsSeed` check (`!dbContext.Departments.Any() || !dbContext.Employees.Any()`) ensures the seeder only runs on an empty database, so restarting the API doesn't duplicate records.
594619

595-
**`InsertMockPositionCommand` for runtime seeding.** The CQRS `POST /api/v1/positions/seed` endpoint lets you add more positions without restarting the API. It fetches existing departments and salary ranges from the database and passes them to `PositionInsertBogusConfig` — so every generated position has valid FK references to real rows. This pattern extends to any entity that has FK dependencies.
620+
**`InsertMockPositionCommand` for runtime seeding.** The CQRS `POST /api/v1/positions/AddMock` endpoint lets you add more positions without restarting the API. It fetches existing departments and salary ranges from the database and passes them to `PositionInsertBogusConfig` — so every generated position has valid FK references to real rows. This pattern extends to any entity that has FK dependencies.
596621

597622
---
598623

0 commit comments

Comments
 (0)