@solidxai/core 0.1.10-beta.0 → 0.1.10-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/dist/commands/run-tests.command.d.ts +2 -0
  3. package/dist/commands/run-tests.command.d.ts.map +1 -1
  4. package/dist/commands/run-tests.command.js +49 -17
  5. package/dist/commands/run-tests.command.js.map +1 -1
  6. package/dist/controllers/user.controller.d.ts.map +1 -1
  7. package/dist/controllers/user.controller.js.map +1 -1
  8. package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
  9. package/dist/helpers/bootstrap.helper.js +2 -0
  10. package/dist/helpers/bootstrap.helper.js.map +1 -1
  11. package/dist/services/crud.service.js.map +1 -1
  12. package/dist/services/field-metadata.service.js +2 -2
  13. package/dist/services/field-metadata.service.js.map +1 -1
  14. package/dist/services/queues/database-subscriber.service.js +3 -3
  15. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  16. package/dist/services/queues/rabbitmq-subscriber.service.js +4 -4
  17. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  18. package/dist/services/queues/redis-subscriber.service.d.ts.map +1 -1
  19. package/dist/services/queues/redis-subscriber.service.js +4 -1
  20. package/dist/services/queues/redis-subscriber.service.js.map +1 -1
  21. package/dist/solid-core.module.d.ts +1 -0
  22. package/dist/solid-core.module.d.ts.map +1 -1
  23. package/dist/solid-core.module.js +1 -0
  24. package/dist/solid-core.module.js.map +1 -1
  25. package/dist/testing/reporter/webhook-reporter.d.ts +54 -0
  26. package/dist/testing/reporter/webhook-reporter.d.ts.map +1 -0
  27. package/dist/testing/reporter/webhook-reporter.js +74 -0
  28. package/dist/testing/reporter/webhook-reporter.js.map +1 -0
  29. package/package.json +5 -1
  30. package/src/commands/run-tests.command.ts +45 -17
  31. package/src/controllers/user.controller.ts +16 -16
  32. package/src/helpers/bootstrap.helper.ts +3 -0
  33. package/src/services/1.js +6 -0
  34. package/src/services/crud.service.ts +2 -2
  35. package/src/services/field-metadata.service.ts +2 -2
  36. package/src/services/queues/database-subscriber.service.ts +6 -6
  37. package/src/services/queues/rabbitmq-subscriber.service.ts +5 -5
  38. package/src/services/queues/redis-subscriber.service.ts +5 -2
  39. package/src/solid-core.module.ts +1 -0
  40. package/src/testing/reporter/webhook-reporter.ts +116 -0
  41. package/dist-tests/api/authenticate.spec.js +0 -119
  42. package/dist-tests/api/authenticate.spec.js.map +0 -1
  43. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  44. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  45. package/dist-tests/api/ping.spec.js +0 -21
  46. package/dist-tests/api/ping.spec.js.map +0 -1
  47. package/dist-tests/helpers/auth.js +0 -41
  48. package/dist-tests/helpers/auth.js.map +0 -1
  49. package/dist-tests/helpers/env.js +0 -11
  50. package/dist-tests/helpers/env.js.map +0 -1
  51. package/docs/grouping-enhancements.md +0 -89
  52. package/docs/java-spring/README.md +0 -3
  53. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  54. package/docs/seed-changes.md +0 -65
  55. package/docs/test-data-workflow.md +0 -200
  56. package/docs/type-declaration-import-issue.md +0 -24
@@ -20,37 +20,37 @@ export class UserController {
20
20
  @ApiBearerAuth("jwt")
21
21
  @Post()
22
22
  @UseInterceptors(AnyFilesInterceptor())
23
- create(@Body() createDto: CreateUserDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
24
- return this.service.create(createDto, files,solidRequestContext);
23
+ create(@Body() createDto: CreateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
24
+ return this.service.create(createDto, files, solidRequestContext);
25
25
  }
26
26
 
27
27
  @ApiBearerAuth("jwt")
28
28
  @Post('/bulk')
29
29
  @UseInterceptors(AnyFilesInterceptor())
30
- insertMany(@Body() createDtos: CreateUserDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
31
- return this.service.insertMany(createDtos, filesArray,solidRequestContext);
30
+ insertMany(@Body() createDtos: CreateUserDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = [], @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
31
+ return this.service.insertMany(createDtos, filesArray, solidRequestContext);
32
32
  }
33
33
 
34
34
 
35
35
  @ApiBearerAuth("jwt")
36
36
  @Put(':id')
37
37
  @UseInterceptors(AnyFilesInterceptor())
38
- update(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File> ,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
39
- return this.service.update(id, updateDto, files,false,solidRequestContext);
38
+ update(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
39
+ return this.service.update(id, updateDto, files, false, solidRequestContext);
40
40
  }
41
41
 
42
42
 
43
43
  @ApiBearerAuth("jwt")
44
44
  @Patch(':id/update-user-and-roles')
45
- updateUser(@Param('id') id: number,@Body() updateDto: any, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
46
- return this.service.updateUser(id, updateDto, files,solidRequestContext);
45
+ updateUser(@Param('id') id: number, @Body() updateDto: any, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
46
+ return this.service.updateUser(id, updateDto, files, solidRequestContext);
47
47
  }
48
48
 
49
49
 
50
50
  @ApiBearerAuth("jwt")
51
51
  @Patch(':id')
52
52
  @UseInterceptors(AnyFilesInterceptor())
53
- partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
53
+ partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateUserDto, @UploadedFiles() files: Array<Express.Multer.File>, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
54
54
  return this.service.update(id, updateDto, files, true, solidRequestContext);
55
55
  }
56
56
 
@@ -67,8 +67,8 @@ export class UserController {
67
67
  @ApiQuery({ name: 'populateMedia', required: false, type: Array })
68
68
  @ApiQuery({ name: 'filters', required: false, type: Array })
69
69
  @Get()
70
- async findMany(@Query() query: any, @SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
71
- return this.service.find(query,solidRequestContext);
70
+ async findMany(@Query() query: any, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
71
+ return this.service.find(query, solidRequestContext);
72
72
  }
73
73
 
74
74
  @ApiBearerAuth("jwt")
@@ -81,18 +81,18 @@ export class UserController {
81
81
 
82
82
  @ApiBearerAuth("jwt")
83
83
  @Get(':id')
84
- async findOne(@Param('id') id: string, @Query() query: any,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
85
- return this.service.findOne(+id, query,solidRequestContext);
84
+ async findOne(@Param('id') id: string, @Query() query: any, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
85
+ return this.service.findOne(+id, query, solidRequestContext);
86
86
  }
87
87
 
88
88
  @Delete('/bulk')
89
- async deleteMany(@Body() ids: number[],@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
90
- return this.service.deleteMany(ids,solidRequestContext);
89
+ async deleteMany(@Body() ids: number[], @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
90
+ return this.service.deleteMany(ids, solidRequestContext);
91
91
  }
92
92
 
93
93
  @ApiBearerAuth("jwt")
94
94
  @Delete(':id')
95
- async delete(@Param('id') id: number,@SolidRequestContextDecorator() solidRequestContext:SolidRequestContextDto) {
95
+ async delete(@Param('id') id: number, @SolidRequestContextDecorator() solidRequestContext: SolidRequestContextDto) {
96
96
  return this.service.delete(id, solidRequestContext);
97
97
  }
98
98
 
@@ -1,5 +1,6 @@
1
1
  import { ValidationPipe } from '@nestjs/common';
2
2
  import { NestFactory } from '@nestjs/core';
3
+ import { WsAdapter } from '@nestjs/platform-ws';
3
4
  import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
4
5
  import { NextFunction, Request, Response } from 'express';
5
6
  import helmet from 'helmet';
@@ -178,6 +179,8 @@ export async function bootstrapSolidApp(
178
179
  const types = require('pg').types;
179
180
  types.setTypeParser(types.builtins.INT8, (val: string) => parseInt(val));
180
181
 
182
+ app.useWebSocketAdapter(new WsAdapter(app));
183
+
181
184
  await app.listen(port);
182
185
  }
183
186
 
@@ -0,0 +1,6 @@
1
+ 1. Do i need to create a storeStreams method for aws service too?
2
+ - Handle later
3
+ 2. queues handling -> if queues is enabled by default, i.e triggerExport(exportTransactionEntity.id).
4
+ - startExport should either return the data or return the transaction id
5
+ 3. How to handle scenarios wherein, nested related exist.(do i need to only get the userkey)
6
+ - show the userKey
@@ -920,7 +920,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
920
920
  );
921
921
 
922
922
  return { message: SUCCESS_MESSAGES.RECORD_RECOVERED, data: softDeletedRows };
923
- } catch (error) {
923
+ } catch (error: any) {
924
924
  if (error instanceof QueryFailedError) {
925
925
  if ((error as any).code === '23505') {
926
926
  throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
@@ -966,7 +966,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
966
966
  );
967
967
 
968
968
  return { message: SUCCESS_MESSAGES.SELECTED_RECORDS_RECOVERED, recoveredIds: ids };
969
- } catch (error) {
969
+ } catch (error: any) {
970
970
  if (error instanceof QueryFailedError) {
971
971
  if ((error as any).code === "23505") {
972
972
  throw new Error(ERROR_MESSAGES.CONFLICTING_RECORD_ON_UNARCHIVE);
@@ -745,6 +745,8 @@ export class FieldMetadataService implements OnApplicationBootstrap {
745
745
  "type",
746
746
  "ormType",
747
747
  "isSystem",
748
+ "regexPattern",
749
+ "regexPatternNotMatchingErrorMsg",
748
750
  "defaultValue",
749
751
  "min",
750
752
  "max",
@@ -772,8 +774,6 @@ export class FieldMetadataService implements OnApplicationBootstrap {
772
774
  "regexPattern",
773
775
  "regexPatternNotMatchingErrorMsg",
774
776
  "defaultValue",
775
- "min",
776
- "max",
777
777
  "required",
778
778
  "unique",
779
779
  "index",
@@ -16,9 +16,9 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
16
16
  protected readonly mqMessageQueueService: MqMessageQueueService,
17
17
  protected readonly poller: PollerService,
18
18
  ) {
19
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
20
- if (!this.serviceRole) {
21
- this.logger.debug('Queue service Role is not defined in the environment variables');
19
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
20
+ if (!process.env.QUEUES_SERVICE_ROLE) {
21
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting DatabaseSubscriber service role to "both".');
22
22
  }
23
23
  // this.logger.debug(`DatabaseSubscriber instance created with options: ${JSON.stringify(this.options())}`);
24
24
  }
@@ -60,7 +60,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
60
60
 
61
61
  await this.processMessage(message);
62
62
  }
63
- catch (error) {
63
+ catch (error: any) {
64
64
  this.logger.error(`Error processing message: ${error.message}`);
65
65
 
66
66
  // if an error occurs then if retryCount is set we start retrying.
@@ -152,7 +152,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
152
152
  private async retryMessage(message: QueueMessage<T>) {
153
153
  try {
154
154
  await this.processMessage(message);
155
- } catch (error) {
155
+ } catch (error: any) {
156
156
  if (message.currentRetry < message.retryCount) {
157
157
  await this.updateStatusInDatabase('retrying', message);
158
158
 
@@ -203,7 +203,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
203
203
  this.logger.debug(`Message status updated to ${stage} for messageId: ${mqMessage.id}`);
204
204
  }
205
205
  }
206
- catch (error) {
206
+ catch (error: any) {
207
207
  this.logger.error(error.message, error.stack);
208
208
  }
209
209
  }
@@ -31,12 +31,12 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
31
31
 
32
32
  constructor(protected readonly mqMessageService: MqMessageService, protected readonly mqMessageQueueService: MqMessageQueueService) {
33
33
  this.url = process.env.QUEUES_RABBIT_MQ_URL;
34
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
34
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
35
35
  if (!this.url) {
36
36
  this.logger.debug('RabbitMqPublisher url is not defined in the environment variables');
37
37
  }
38
- if (!this.serviceRole) {
39
- this.logger.debug('Queue service Role is not defined in the environment variables');
38
+ if (!process.env.QUEUES_SERVICE_ROLE) {
39
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting RabbitMqSubscriber service role to "both".');
40
40
  }
41
41
  // this.logger.debug(`RabbitMqSubscriber instance created with options: ${JSON.stringify(this.options())} and url: ${this.url}`);
42
42
  }
@@ -85,7 +85,7 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
85
85
  async onModuleInit(): Promise<void> {
86
86
  // Not using SettingService here as that will necessitate all implementors of RabbitMqSubscriber to also inject SettingService which is not ideal.
87
87
  // Instead we directly read the environment variables here.
88
- const defaultBroker = process.env.QUEUES_DEFAULT_BROKER || 'rabbitmq';
88
+ const defaultBroker = process.env.QUEUES_DEFAULT_BROKER || 'database';
89
89
  const solidCliRunning = process.env.SOLID_CLI_RUNNING || "false";
90
90
  const queueNameRegex = (process.env.QUEUES_QUEUE_NAME_REGEX_TO_ENABLE || '').trim();
91
91
  const roleAllowed = ['both', 'subscriber'].includes(this.serviceRole);
@@ -407,7 +407,7 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
407
407
  await this.mqMessageService.repo.update(mqMessage.id, updatedFields);
408
408
  }
409
409
  }
410
- catch (error) {
410
+ catch (error: any) {
411
411
  this.logger.error(error.message, error.stack);
412
412
  }
413
413
 
@@ -17,7 +17,10 @@ export abstract class RedisSubscriber<T> implements OnModuleInit, OnModuleDestro
17
17
  protected readonly mqMessageService: MqMessageService,
18
18
  protected readonly mqMessageQueueService: MqMessageQueueService,
19
19
  ) {
20
- this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
20
+ this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
21
+ if (!process.env.QUEUES_SERVICE_ROLE) {
22
+ this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting RedisSubscriber service role to "both".');
23
+ }
21
24
  if (!process.env.QUEUES_REDIS_URL) {
22
25
  this.logger.debug('RedisSubscriber: QUEUES_REDIS_URL is not defined in the environment variables');
23
26
  }
@@ -201,7 +204,7 @@ export abstract class RedisSubscriber<T> implements OnModuleInit, OnModuleDestro
201
204
  if (stage === 'failed') updatedFields['error'] = error;
202
205
  await this.mqMessageService.repo.update(mqMessage.id, updatedFields);
203
206
  }
204
- } catch (err) {
207
+ } catch (err: any) {
205
208
  this.logger.error(err.message, err.stack);
206
209
  }
207
210
  }
@@ -1,3 +1,4 @@
1
+ import 'multer';
1
2
  import { Global, MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
2
3
  import * as express from 'express';
3
4
  import { ConfigModule, ConfigService } from '@nestjs/config';
@@ -0,0 +1,116 @@
1
+ import { ConsoleReporter } from "./console-reporter";
2
+ import type { OpStep, ScenarioSpec } from "../contracts/testing-metadata.types";
3
+ import type { StepPhase } from "../contracts/runtime-context.types";
4
+
5
+ interface TestStepResult {
6
+ phase: string;
7
+ operation: string;
8
+ name?: string;
9
+ ok: boolean;
10
+ durationMs: number;
11
+ error?: string;
12
+ }
13
+
14
+ interface TestScenarioResult {
15
+ id: string;
16
+ name?: string;
17
+ ok: boolean;
18
+ durationMs: number;
19
+ error?: string;
20
+ steps: TestStepResult[];
21
+ }
22
+
23
+ export interface TestRunPayload {
24
+ runName: string;
25
+ startedAt: string;
26
+ completedAt: string;
27
+ durationMs: number;
28
+ exitCode: number;
29
+ total: number;
30
+ passed: number;
31
+ failed: number;
32
+ scenarios: TestScenarioResult[];
33
+ }
34
+
35
+ function formatError(err: unknown): string {
36
+ if (!err) return "";
37
+ if (err instanceof Error) return err.stack || err.message;
38
+ return String(err);
39
+ }
40
+
41
+ export class WebhookReporter extends ConsoleReporter {
42
+ private readonly startedAt = new Date();
43
+ private readonly accumulated: TestScenarioResult[] = [];
44
+ private currentSteps: TestStepResult[] = [];
45
+
46
+ constructor(
47
+ private readonly webhookUrl: string,
48
+ private readonly runName: string,
49
+ ) {
50
+ super();
51
+ }
52
+
53
+ override onStepEnd(args: {
54
+ scenarioId: string;
55
+ phase: StepPhase;
56
+ step: OpStep;
57
+ ok: boolean;
58
+ error?: unknown;
59
+ durationMs: number;
60
+ }): void {
61
+ super.onStepEnd(args);
62
+ this.currentSteps.push({
63
+ phase: String(args.phase),
64
+ operation: args.step.op,
65
+ name: args.step.name,
66
+ ok: args.ok,
67
+ durationMs: args.durationMs,
68
+ error: args.error ? formatError(args.error) : undefined,
69
+ });
70
+ }
71
+
72
+ override onScenarioEnd(
73
+ scenario: ScenarioSpec,
74
+ result: { ok: boolean; error?: unknown; durationMs: number },
75
+ ): void {
76
+ super.onScenarioEnd(scenario, result);
77
+ this.accumulated.push({
78
+ id: scenario.id,
79
+ name: scenario.name,
80
+ ok: result.ok,
81
+ durationMs: result.durationMs,
82
+ error: result.error ? formatError(result.error) : undefined,
83
+ steps: [...this.currentSteps],
84
+ });
85
+ this.currentSteps = [];
86
+ }
87
+
88
+ async flush(exitCode: number): Promise<void> {
89
+ const completedAt = new Date();
90
+ const payload: TestRunPayload = {
91
+ runName: this.runName,
92
+ startedAt: this.startedAt.toISOString(),
93
+ completedAt: completedAt.toISOString(),
94
+ durationMs: completedAt.getTime() - this.startedAt.getTime(),
95
+ exitCode,
96
+ total: this.accumulated.length,
97
+ passed: this.accumulated.filter((s) => s.ok).length,
98
+ failed: this.accumulated.filter((s) => !s.ok).length,
99
+ scenarios: this.accumulated,
100
+ };
101
+
102
+ try {
103
+ const response = await fetch(this.webhookUrl, {
104
+ method: "POST",
105
+ headers: { "Content-Type": "application/json" },
106
+ body: JSON.stringify(payload),
107
+ signal: AbortSignal.timeout(10_000),
108
+ });
109
+ if (!response.ok) {
110
+ console.warn(`[WebhookReporter] Webhook returned ${response.status}`);
111
+ }
112
+ } catch (err) {
113
+ console.warn(`[WebhookReporter] Failed to deliver test results: ${err}`);
114
+ }
115
+ }
116
+ }
@@ -1,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const env_1 = require("../helpers/env");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
7
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
8
- function base64UrlDecode(input) {
9
- const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
10
- const padded = normalized.length % 4 === 0
11
- ? normalized
12
- : normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
13
- return Buffer.from(padded, "base64").toString("utf-8");
14
- }
15
- function validateJwt(token) {
16
- const parts = token.split(".");
17
- if (parts.length !== 3) {
18
- throw new Error("JWT must have three dot-separated parts.");
19
- }
20
- const headerJson = JSON.parse(base64UrlDecode(parts[0]));
21
- const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
22
- if (!headerJson || typeof headerJson !== "object") {
23
- throw new Error("JWT header must be a JSON object.");
24
- }
25
- if (!payloadJson || typeof payloadJson !== "object") {
26
- throw new Error("JWT payload must be a JSON object.");
27
- }
28
- if (typeof payloadJson.exp !== "number") {
29
- throw new Error("JWT payload.exp must be a number.");
30
- }
31
- return payloadJson;
32
- }
33
- (0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
34
- const api = await test_1.request.newContext({
35
- baseURL,
36
- extraHTTPHeaders: {
37
- accept: "*/*",
38
- "content-type": "application/json",
39
- },
40
- });
41
- try {
42
- const res = await api.post("/api/iam/authenticate", {
43
- data: {
44
- email: TEST_USER_EMAIL,
45
- username: "",
46
- password: TEST_USER_PASSWORD,
47
- },
48
- });
49
- (0, test_1.expect)(res.status()).toBe(200);
50
- const json = await res.json();
51
- (0, test_1.expect)(json.statusCode).toBe(200);
52
- (0, test_1.expect)(Array.isArray(json.message)).toBe(true);
53
- (0, test_1.expect)(json.message.length).toBe(0);
54
- (0, test_1.expect)(json.error).toBe("");
55
- const user = json.data?.user;
56
- (0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
57
- (0, test_1.expect)(typeof user).toBe("object");
58
- const email = user?.email;
59
- (0, test_1.expect)(typeof email).toBe("string");
60
- if (email === TEST_USER_EMAIL) {
61
- (0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
62
- }
63
- else {
64
- (0, test_1.expect)(email.length).toBeGreaterThan(0);
65
- }
66
- (0, test_1.expect)(typeof user?.mobile).toBe("string");
67
- (0, test_1.expect)(typeof user?.username).toBe("string");
68
- (0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
69
- (0, test_1.expect)(typeof user?.id).toBe("number");
70
- const roles = user?.roles;
71
- (0, test_1.expect)(Array.isArray(roles)).toBe(true);
72
- if (Array.isArray(roles)) {
73
- (0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
74
- (0, test_1.expect)(roles).toContain("Admin");
75
- }
76
- const accessToken = json.data?.accessToken;
77
- const refreshToken = json.data?.refreshToken;
78
- (0, test_1.expect)(typeof accessToken).toBe("string");
79
- (0, test_1.expect)(typeof refreshToken).toBe("string");
80
- const accessPayload = validateJwt(accessToken);
81
- const refreshPayload = validateJwt(refreshToken);
82
- (0, test_1.expect)(typeof accessPayload.exp).toBe("number");
83
- (0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
84
- }
85
- finally {
86
- await api.dispose();
87
- }
88
- });
89
- (0, test_1.test)("API: authenticate fails with wrong password", async () => {
90
- const api = await test_1.request.newContext({
91
- baseURL,
92
- extraHTTPHeaders: {
93
- accept: "*/*",
94
- "content-type": "application/json",
95
- },
96
- });
97
- try {
98
- const res = await api.post("/api/iam/authenticate", {
99
- data: {
100
- email: TEST_USER_EMAIL,
101
- username: "",
102
- password: `${TEST_USER_PASSWORD}__wrong`,
103
- },
104
- });
105
- (0, test_1.expect)(res.status()).toBe(401);
106
- const json = await res.json();
107
- (0, test_1.expect)(json.statusCode).toBe(401);
108
- (0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
109
- (0, test_1.expect)(json.message).toBe("Invalid credentials");
110
- (0, test_1.expect)(json.error).toBe("Invalid credentials");
111
- (0, test_1.expect)(json.data?.statusCode).toBe(401);
112
- (0, test_1.expect)(json.data?.error).toBe("Unauthorized");
113
- (0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
114
- }
115
- finally {
116
- await api.dispose();
117
- }
118
- });
119
- //# sourceMappingURL=authenticate.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const auth_1 = require("../helpers/auth");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- async function getCityByName(name) {
7
- const api = await test_1.request.newContext({
8
- baseURL,
9
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
10
- });
11
- try {
12
- const res = await api.get(`/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`);
13
- (0, test_1.expect)(res.status()).toBe(200);
14
- const json = await res.json();
15
- const record = json?.data?.records?.[0];
16
- (0, test_1.expect)(record, `Expected city '${name}' in testData`).toBeTruthy();
17
- (0, test_1.expect)(record.id, "Expected record to have id").toBeTruthy();
18
- return record;
19
- }
20
- finally {
21
- await api.dispose();
22
- }
23
- }
24
- test_1.test.describe("CRUDService.findOne (cityMaster)", () => {
25
- (0, test_1.test)("returns a city by id", async () => {
26
- const city = await getCityByName("Mumbai");
27
- const api = await test_1.request.newContext({
28
- baseURL,
29
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
30
- });
31
- try {
32
- const res = await api.get(`/api/city-master/${city.id}`);
33
- (0, test_1.expect)(res.status()).toBe(200);
34
- const json = await res.json();
35
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
36
- (0, test_1.expect)(json?.data?.name).toBe("Mumbai");
37
- (0, test_1.expect)(json?.data?.description).toBe("Mumbai city");
38
- }
39
- finally {
40
- await api.dispose();
41
- }
42
- });
43
- (0, test_1.test)("supports fields[] selection", async () => {
44
- const city = await getCityByName("Pune");
45
- const api = await test_1.request.newContext({
46
- baseURL,
47
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
48
- });
49
- try {
50
- const res = await api.get(`/api/city-master/${city.id}?fields[]=id&fields[]=name`);
51
- (0, test_1.expect)(res.status()).toBe(200);
52
- const json = await res.json();
53
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
54
- (0, test_1.expect)(json?.data?.name).toBe("Pune");
55
- (0, test_1.expect)(json?.data?.description).toBeUndefined();
56
- (0, test_1.expect)(json?.data?.state).toBeUndefined();
57
- }
58
- finally {
59
- await api.dispose();
60
- }
61
- });
62
- (0, test_1.test)("supports populate=state", async () => {
63
- const city = await getCityByName("Bengaluru");
64
- const api = await test_1.request.newContext({
65
- baseURL,
66
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
67
- });
68
- try {
69
- const res = await api.get(`/api/city-master/${city.id}?populate=state`);
70
- (0, test_1.expect)(res.status()).toBe(200);
71
- const json = await res.json();
72
- (0, test_1.expect)(json?.data?.name).toBe("Bengaluru");
73
- (0, test_1.expect)(json?.data?.state).toBeTruthy();
74
- (0, test_1.expect)(json?.data?.state?.name).toBe("Karnataka");
75
- }
76
- finally {
77
- await api.dispose();
78
- }
79
- });
80
- (0, test_1.test)("returns 404 for missing id", async () => {
81
- const api = await test_1.request.newContext({
82
- baseURL,
83
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
84
- });
85
- try {
86
- const res = await api.get(`/api/city-master/99999999`);
87
- (0, test_1.expect)(res.status()).toBe(404);
88
- const json = await res.json();
89
- (0, test_1.expect)(String(json?.message)).toContain("cityMaster");
90
- (0, test_1.expect)(String(json?.message)).toContain("not found");
91
- }
92
- finally {
93
- await api.dispose();
94
- }
95
- });
96
- });
97
- //# sourceMappingURL=crud-service.findOne.cityMaster.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crud-service.findOne.cityMaster.spec.js","sourceRoot":"","sources":["../../tests/api/crud-service.findOne.cityMaster.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AACzD,0CAAiD;AAEjD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAC;AAEpE,KAAK,UAAU,aAAa,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE,MAAM,IAAA,qBAAc,EAAC,OAAO,CAAC;KAChD,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CACvB,uCAAuC,kBAAkB,CAAC,IAAI,CAAC,mBAAmB,CACnF,CAAC;QACF,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAE9B,MAAM,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,IAAA,aAAM,EAAC,MAAM,EAAE,kBAAkB,IAAI,eAAe,CAAC,CAAC,UAAU,EAAE,CAAC;QACnE,IAAA,aAAM,EAAC,MAAM,CAAC,EAAE,EAAE,4BAA4B,CAAC,CAAC,UAAU,EAAE,CAAC;QAE7D,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,WAAI,CAAC,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IACrD,IAAA,WAAI,EAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE3C,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;YACnC,OAAO;YACP,gBAAgB,EAAE,MAAM,IAAA,qBAAc,EAAC,OAAO,CAAC;SAChD,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACzD,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACrC,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;YACnC,OAAO;YACP,gBAAgB,EAAE,MAAM,IAAA,qBAAc,EAAC,OAAO,CAAC;SAChD,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CACvB,oBAAoB,IAAI,CAAC,EAAE,4BAA4B,CACxD,CAAC;YACF,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACrC,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtC,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;YAChD,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QAE9C,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;YACnC,OAAO;YACP,gBAAgB,EAAE,MAAM,IAAA,qBAAc,EAAC,OAAO,CAAC;SAChD,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;YACxE,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC3C,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;YACvC,IAAA,aAAM,EAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;YACnC,OAAO;YACP,gBAAgB,EAAE,MAAM,IAAA,qBAAc,EAAC,OAAO,CAAC;SAChD,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YACvD,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAA,aAAM,EAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YACtD,IAAA,aAAM,EAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACvD,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getAuthHeaders } from \"../helpers/auth\";\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\n\nasync function getCityByName(name: string) {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`\n );\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n const record = json?.data?.records?.[0];\n expect(record, `Expected city '${name}' in testData`).toBeTruthy();\n expect(record.id, \"Expected record to have id\").toBeTruthy();\n\n return record;\n } finally {\n await api.dispose();\n }\n}\n\ntest.describe(\"CRUDService.findOne (cityMaster)\", () => {\n test(\"returns a city by id\", async () => {\n const city = await getCityByName(\"Mumbai\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Mumbai\");\n expect(json?.data?.description).toBe(\"Mumbai city\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports fields[] selection\", async () => {\n const city = await getCityByName(\"Pune\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master/${city.id}?fields[]=id&fields[]=name`\n );\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Pune\");\n expect(json?.data?.description).toBeUndefined();\n expect(json?.data?.state).toBeUndefined();\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports populate=state\", async () => {\n const city = await getCityByName(\"Bengaluru\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}?populate=state`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.name).toBe(\"Bengaluru\");\n expect(json?.data?.state).toBeTruthy();\n expect(json?.data?.state?.name).toBe(\"Karnataka\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"returns 404 for missing id\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/99999999`);\n expect(res.status()).toBe(404);\n\n const json = await res.json();\n expect(String(json?.message)).toContain(\"cityMaster\");\n expect(String(json?.message)).toContain(\"not found\");\n } finally {\n await api.dispose();\n }\n });\n});\n"]}
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- (0, test_1.test)("GET /api/ping returns pong", async () => {
5
- const baseURL = process.env.BASE_URL || "http://localhost:3000";
6
- if (!baseURL) {
7
- throw new Error("baseURL is not configured. Set API_BASE_URL or use the default.");
8
- }
9
- const api = await test_1.request.newContext({ baseURL });
10
- const res = await api.get("/api/ping");
11
- (0, test_1.expect)(res.status()).toBe(200);
12
- const body = await res.json();
13
- (0, test_1.expect)(body).toEqual({
14
- statusCode: 200,
15
- message: [],
16
- error: "",
17
- data: { pong: "v1.0.2" },
18
- });
19
- await api.dispose();
20
- });
21
- //# sourceMappingURL=ping.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping.spec.js","sourceRoot":"","sources":["../../tests/api/ping.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AAEzD,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAA,aAAM,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\n\ntest(\"GET /api/ping returns pong\", async () => {\n const baseURL = process.env.BASE_URL || \"http://localhost:3000\";\n if (!baseURL) {\n throw new Error(\"baseURL is not configured. Set API_BASE_URL or use the default.\");\n }\n const api = await request.newContext({ baseURL });\n\n const res = await api.get(\"/api/ping\");\n\n expect(res.status()).toBe(200);\n\n const body = await res.json();\n expect(body).toEqual({\n statusCode: 200,\n message: [],\n error: \"\",\n data: { pong: \"v1.0.2\" },\n });\n\n await api.dispose();\n});\n"]}