|
|
@@ -0,0 +1,183 @@
|
|
|
+import { IntegrationTestDatabase } from './integration-test-db';
|
|
|
+import { EntityTarget, ObjectLiteral } from 'typeorm';
|
|
|
+
|
|
|
+/**
|
|
|
+ * 集成测试断言工具
|
|
|
+ * 通用版本,不依赖特定实体
|
|
|
+ */
|
|
|
+export class IntegrationTestAssertions {
|
|
|
+ /**
|
|
|
+ * 断言响应状态码
|
|
|
+ */
|
|
|
+ static expectStatus(response: { status: number }, expectedStatus: number): void {
|
|
|
+ if (response.status !== expectedStatus) {
|
|
|
+ throw new Error(`Expected status ${expectedStatus}, but got ${response.status}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言响应包含特定字段
|
|
|
+ */
|
|
|
+ static expectResponseToHave(response: { data: any }, expectedFields: Record<string, any>): void {
|
|
|
+ for (const [key, value] of Object.entries(expectedFields)) {
|
|
|
+ if (response.data[key] !== value) {
|
|
|
+ throw new Error(`Expected field ${key} to be ${value}, but got ${response.data[key]}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言响应包含特定结构
|
|
|
+ */
|
|
|
+ static expectResponseStructure(response: { data: any }, structure: Record<string, any>): void {
|
|
|
+ for (const key of Object.keys(structure)) {
|
|
|
+ if (!(key in response.data)) {
|
|
|
+ throw new Error(`Expected response to have key: ${key}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言实体存在于数据库中
|
|
|
+ */
|
|
|
+ static async expectEntityToExist<T extends ObjectLiteral>(
|
|
|
+ entity: EntityTarget<T>,
|
|
|
+ condition: Partial<T>
|
|
|
+ ): Promise<void> {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ if (!dataSource) {
|
|
|
+ throw new Error('Database not initialized');
|
|
|
+ }
|
|
|
+
|
|
|
+ const repository = dataSource.getRepository(entity);
|
|
|
+ const entityInstance = await repository.findOne({ where: condition });
|
|
|
+
|
|
|
+ if (!entityInstance) {
|
|
|
+ throw new Error(`Expected entity to exist in database with condition: ${JSON.stringify(condition)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言实体不存在于数据库中
|
|
|
+ */
|
|
|
+ static async expectEntityNotToExist<T extends ObjectLiteral>(
|
|
|
+ entity: EntityTarget<T>,
|
|
|
+ condition: Partial<T>
|
|
|
+ ): Promise<void> {
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
+ if (!dataSource) {
|
|
|
+ throw new Error('Database not initialized');
|
|
|
+ }
|
|
|
+
|
|
|
+ const repository = dataSource.getRepository(entity);
|
|
|
+ const entityInstance = await repository.findOne({ where: condition });
|
|
|
+
|
|
|
+ if (entityInstance) {
|
|
|
+ throw new Error(`Expected entity not to exist in database with condition: ${JSON.stringify(condition)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言响应包含错误信息
|
|
|
+ */
|
|
|
+ static expectErrorResponse(response: { data: any }, expectedError: string): void {
|
|
|
+ if (!response.data || !response.data.error || !response.data.error.includes(expectedError)) {
|
|
|
+ throw new Error(`Expected error response to contain "${expectedError}", but got: ${JSON.stringify(response.data)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言响应包含成功信息
|
|
|
+ */
|
|
|
+ static expectSuccessResponse(response: { data: any }): void {
|
|
|
+ if (!response.data || response.data.error) {
|
|
|
+ throw new Error(`Expected success response, but got: ${JSON.stringify(response.data)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言列表响应包含特定数量的项目
|
|
|
+ */
|
|
|
+ static expectListResponseCount(response: { data: any }, expectedCount: number): void {
|
|
|
+ if (!Array.isArray(response.data)) {
|
|
|
+ throw new Error(`Expected array response, but got: ${typeof response.data}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (response.data.length !== expectedCount) {
|
|
|
+ throw new Error(`Expected ${expectedCount} items, but got ${response.data.length}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言响应包含分页信息
|
|
|
+ */
|
|
|
+ static expectPaginationResponse(response: { data: any }): void {
|
|
|
+ if (!response.data || typeof response.data !== 'object') {
|
|
|
+ throw new Error(`Expected object response, but got: ${typeof response.data}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ const requiredFields = ['data', 'total', 'page', 'pageSize'];
|
|
|
+ for (const field of requiredFields) {
|
|
|
+ if (!(field in response.data)) {
|
|
|
+ throw new Error(`Expected pagination response to have field: ${field}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!Array.isArray(response.data.data)) {
|
|
|
+ throw new Error(`Expected pagination data to be an array, but got: ${typeof response.data.data}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * API 响应断言工具
|
|
|
+ */
|
|
|
+export class ApiResponseAssertions {
|
|
|
+ /**
|
|
|
+ * 断言成功的 API 响应
|
|
|
+ */
|
|
|
+ static expectSuccess(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 200);
|
|
|
+ IntegrationTestAssertions.expectSuccessResponse(response);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言创建的 API 响应
|
|
|
+ */
|
|
|
+ static expectCreated(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 201);
|
|
|
+ IntegrationTestAssertions.expectSuccessResponse(response);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言未授权的 API 响应
|
|
|
+ */
|
|
|
+ static expectUnauthorized(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 401);
|
|
|
+ IntegrationTestAssertions.expectErrorResponse(response, 'Unauthorized');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言禁止访问的 API 响应
|
|
|
+ */
|
|
|
+ static expectForbidden(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 403);
|
|
|
+ IntegrationTestAssertions.expectErrorResponse(response, 'Forbidden');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言未找到的 API 响应
|
|
|
+ */
|
|
|
+ static expectNotFound(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 404);
|
|
|
+ IntegrationTestAssertions.expectErrorResponse(response, 'Not found');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断言验证错误的 API 响应
|
|
|
+ */
|
|
|
+ static expectValidationError(response: { status: number; data: any }): void {
|
|
|
+ IntegrationTestAssertions.expectStatus(response, 400);
|
|
|
+ IntegrationTestAssertions.expectErrorResponse(response, 'Validation');
|
|
|
+ }
|
|
|
+}
|