{"version":3,"file":"aseansc-admin-sea-http-testing.mjs","sources":["../../../../libs/sea-http/testing/asc-envelope.ts","../../../../libs/sea-http/testing/aseansc-admin-sea-http-testing.ts"],"sourcesContent":["import { http, HttpResponse, delay } from 'msw';\n\n/**\n * Helper khớp với envelope protocol của @aseansc-admin/sea-http.\n *\n * sea-http gửi mọi request dạng POST với cùng một URL endpoint, phân biệt thao tác\n * bằng `body.authenType` (API thường) hoặc `body.command` (login). Vì vậy KHÔNG\n * match được bằng URL path — phải đọc body rồi route theo operation key.\n *\n * Xem README section \"Testing with MSW\" để biết ví dụ đầy đủ.\n */\n\n// ─── Response envelope helpers ────────────────────────────────────────────────\n\n/**\n * Bọc data vào response envelope mà sea-http mong đợi (body.status === 'OK').\n * sea-http tự unwrap và trả về `body.data` cho caller.\n *\n * Dùng trực tiếp khi cần tuỳ chỉnh envelope ngoài phạm vi của ascOperation():\n * @example\n * return HttpResponse.json(ascOk({ meetings: [] }));\n */\nexport function ascOk<T>(data: T) {\n  return {\n    header: { resType: 'RESPONSE', status: 'OK' },\n    body: { status: 'OK', data },\n  };\n}\n\n/**\n * Bọc data vào error envelope — sea-http sẽ ném `AscApiError` khi status !== 'OK'.\n *\n * @example\n * return HttpResponse.json(ascFail('UNAUTHORIZED'));\n * return HttpResponse.json(ascFail('VALIDATION_ERROR', { field: 'email', msg: 'invalid' }));\n */\nexport function ascFail(status: string, data: unknown = null) {\n  return {\n    header: { resType: 'RESPONSE', status },\n    body: { status, data },\n  };\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AscMockResolver = (input: {\n  /** `body.body.data` từ request — payload thực tế consumer gửi vào */\n  data: unknown;\n  /** Toàn bộ request body (envelope đầy đủ) */\n  body: unknown;\n  /** Request gốc của MSW */\n  request: Request;\n}) => unknown | Promise<unknown>;\n\nexport interface AscMockOperationOptions {\n  /**\n   * Lỗi nghiệp vụ trong envelope: sea-http ném `AscApiError` khi `body.status !== 'OK'`.\n   * Resolver VẪN được gọi — giá trị trả về sẽ vào `body.data` của envelope lỗi.\n   *\n   * @example\n   * ascOperation('getUser', () => ({ reason: 'Not found' }), { errorStatus: 'USER_NOT_FOUND' })\n   * // → AscApiError { status: 'USER_NOT_FOUND', data: { reason: 'Not found' } }\n   */\n  errorStatus?: string;\n\n  /**\n   * HTTP-level error status code (401 | 403 | 404 | 500 ...).\n   * Resolver KHÔNG được gọi. Phản hồi bypass envelope hoàn toàn.\n   *\n   * Hành vi của `ascErrorInterceptor` khi nhận HTTP error:\n   * - **401** → xoá session, redirect về `/login`\n   * - **403** → gọi `ASC_HTTP_ERROR_HANDLER`\n   * - **500** → retry tối đa 3 lần (delay 1s / 2s / 3s) rồi gọi `ASC_HTTP_ERROR_HANDLER`\n   * - **0**   → dùng `networkError: true` thay vì httpStatus\n   *\n   * @example\n   * ascOperation('getUser', () => null, { httpStatus: 401 })\n   * ascOperation('getReport', () => null, { httpStatus: 500, delayMs: 200 })\n   */\n  httpStatus?: number;\n\n  /**\n   * Giả lập mất kết nối mạng — Angular nhận `HttpErrorResponse` với `status === 0`.\n   * `ascErrorInterceptor` gọi `ASC_HTTP_ERROR_HANDLER` với thông báo 'Network Error'.\n   * Resolver KHÔNG được gọi.\n   *\n   * @example\n   * ascOperation('getData', () => null, { networkError: true })\n   */\n  networkError?: boolean;\n\n  /**\n   * Giả lập độ trễ mạng (ms) trước khi trả response.\n   * Hữu ích để kiểm tra loading indicator và skeleton UI.\n   *\n   * @example\n   * ascOperation('heavyReport', () => rows, { delayMs: 2000 })\n   */\n  delayMs?: number;\n}\n\nexport interface AscMock {\n  operationKey: string;\n  resolver: AscMockResolver;\n  options: AscMockOperationOptions;\n}\n\n// ─── ascOperation ─────────────────────────────────────────────────────────────\n\n/**\n * Khai báo mock cho một operation (authenType hoặc command).\n * Gom nhiều operations lại trong `createAscHandler()`.\n *\n * @param operationKey  `body.authenType` (API thường) hoặc `body.command` (login)\n * @param resolver      Hàm trả về data — bọc tự động trong OK envelope\n *                      (không gọi khi httpStatus hoặc networkError được đặt)\n * @param options       errorStatus | httpStatus | networkError | delayMs\n */\nexport function ascOperation(\n  operationKey: string,\n  resolver: AscMockResolver,\n  options: AscMockOperationOptions = {},\n): AscMock {\n  return { operationKey, resolver, options };\n}\n\n// ─── createAscHandler ─────────────────────────────────────────────────────────\n\n/**\n * Tạo một MSW POST handler cho endpoint của sea-http.\n * Handler tự route theo `authenType`/`command` trong body đến đúng mock.\n *\n * @param endpoint  URL pattern khớp với baseUrl trong provideAscHttp.\n *                  Dùng wildcard để khớp mọi origin: `'*\\/api'`\n *                  Hoặc dùng URL chính xác: `'https://api.example.com/api'`\n * @param mocks     Danh sách operations tạo bằng ascOperation()\n *\n * Thứ tự ưu tiên khi xử lý một mock:\n * 1. `networkError: true`  → trả lỗi mạng ngay (resolver không gọi)\n * 2. `httpStatus`          → trả HTTP error response (resolver không gọi)\n * 3. `errorStatus`         → gọi resolver, bọc kết quả trong error envelope\n * 4. (mặc định)            → gọi resolver, bọc kết quả trong OK envelope\n */\nexport function createAscHandler(endpoint: string, mocks: AscMock[]) {\n  const byKey = new Map(mocks.map((m) => [m.operationKey, m]));\n\n  return http.post(endpoint, async ({ request }) => {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const body = await request.clone().json() as any;\n    // authenType cho API thường; command cho login\n    const key: string | undefined = body?.body?.authenType ?? body?.body?.command;\n    const mock = key ? byKey.get(key) : undefined;\n\n    if (!mock) {\n      return _unhandledOperation(key);\n    }\n\n    if (mock.options.delayMs) {\n      await delay(mock.options.delayMs);\n    }\n\n    // 1. Network error — resolver không được gọi\n    if (mock.options.networkError) {\n      return HttpResponse.error();\n    }\n\n    // 2. HTTP-level error — resolver không được gọi\n    if (mock.options.httpStatus) {\n      return HttpResponse.json(null, { status: mock.options.httpStatus });\n    }\n\n    // 3 & 4. Business error hoặc OK — resolver luôn được gọi\n    const data = await mock.resolver({ data: body?.body?.data, body, request });\n\n    if (mock.options.errorStatus) {\n      return HttpResponse.json(ascFail(mock.options.errorStatus, data));\n    }\n    return HttpResponse.json(ascOk(data));\n  });\n}\n\n// ─── internal ─────────────────────────────────────────────────────────────────\n\n/**\n * Trả envelope lỗi rõ ràng khi không có mock cho operation.\n * sea-http sẽ ném AscApiError { status: 'NOT_MOCKED' } — dễ phát hiện khi dev.\n */\nfunction _unhandledOperation(key: string | undefined) {\n  console.warn(`[asc-mock] Chưa có mock cho operation: \"${key ?? '(unknown)'}\"`);\n  return HttpResponse.json(\n    ascFail('NOT_MOCKED', { operation: key }),\n    { status: 200 },\n  );\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}