import { test } from 'node:test' import assert from 'node:assert/strict' import { API_ENDPOINTS } from '../src/shared/api/endpoints.ts' const MODULE_KEYS = [ 'files', 'dedupe', 'split', 'convert', 'publish', 'deleteBrand', 'productRisk', 'shopMatch', 'patrolDelete', 'queryAsin', 'shopDataCrawl', 'withdraw', 'priceTrack', 'similarAsin', 'appearancePatent', 'userSecret', 'notification', 'collectData', 'imageVideo', 'brand', 'permission', 'digitalHuman', ] function allEndpointValues(): string[] { const values: string[] = [] for (const moduleKey of Object.keys(API_ENDPOINTS)) { const group = (API_ENDPOINTS as Record>)[moduleKey] for (const value of Object.values(group)) { values.push(value) } } return values } test('test_endpoints_all_modules_present', () => { for (const moduleKey of MODULE_KEYS) { assert.ok( Object.prototype.hasOwnProperty.call(API_ENDPOINTS, moduleKey), `缺少模块分组: ${moduleKey}`, ) assert.ok( Object.keys((API_ENDPOINTS as Record>)[moduleKey]).length > 0, `模块分组为空: ${moduleKey}`, ) } }) test('test_endpoint_path_format', () => { for (const value of allEndpointValues()) { assert.ok( value.startsWith('/api/'), `端点应以 /api/ 开头: ${value}`, ) } }) test('test_no_newapi_prefix_in_endpoint', () => { for (const value of allEndpointValues()) { assert.ok( !value.includes('/newApi'), `端点不应包含 /newApi 前缀: ${value}`, ) } }) test('test_progress_batch_paths_match_controllers', () => { const progressPaths = allEndpointValues().filter((value) => value.endsWith('/tasks/progress/batch'), ) assert.deepEqual( [...progressPaths].sort(), [ '/api/similar-asin/tasks/progress/batch', '/api/appearance-patent/tasks/progress/batch', '/api/collect-data/tasks/progress/batch', '/api/publish/tasks/progress/batch', '/api/delete-brand/tasks/progress/batch', '/api/patrol-delete/tasks/progress/batch', '/api/price-track/tasks/progress/batch', '/api/product-risk-resolve/tasks/progress/batch', '/api/shop-match/tasks/progress/batch', '/api/query-asin/tasks/progress/batch', '/api/shop-data-crawl/tasks/progress/batch', '/api/withdraw/tasks/progress/batch', ].sort(), 'progress/batch 端点应与 Java controller 路由一一对应', ) }) test('test_download_paths_match', () => { const resultDownloads = allEndpointValues().filter((value) => value.endsWith('/download'), ) assert.ok(resultDownloads.length >= 12, `resultDownload 端点数量不足: ${resultDownloads.length}`) for (const value of resultDownloads) { assert.ok( /\/results\/\{[a-zA-Z]+\}\/download$/.test(value) || /\/tasks\/\{[a-zA-Z]+\}\/download$/.test(value), `download 端点应匹配 /results/{id}/download 或 /tasks/{id}/download 模式: ${value}`, ) } }) test('test_endpoints_frozen_snapshot', () => { assert.deepEqual(API_ENDPOINTS, { "files": { "upload": "/api/files/upload", "excelInfo": "/api/files/excel-info" }, "dedupe": { "run": "/api/dedupe/run", "runProgress": "/api/dedupe/run/{runId}/progress", "history": "/api/dedupe/history", "historyDelete": "/api/dedupe/history/{resultId}", "resultDownload": "/api/dedupe/results/{resultId}/download" }, "split": { "run": "/api/split/run", "history": "/api/split/history", "historyDelete": "/api/split/history/{resultId}", "resultDownload": "/api/split/results/{resultId}/download" }, "convert": { "run": "/api/convert/run", "history": "/api/convert/history", "historyDelete": "/api/convert/history/{resultId}", "templates": "/api/convert/templates", "templatesImport": "/api/convert/templates/import", "templateDefault": "/api/convert/templates/{templateCode}/default", "templateDelete": "/api/convert/templates/{templateCode}", "resultDownload": "/api/convert/results/{resultId}/download" }, "publish": { "parse": "/api/publish/parse", "taskActivate": "/api/publish/tasks/{taskId}/activate", "fileActivate": "/api/publish/tasks/{taskId}/files/{fileId}/activate", "items": "/api/publish/tasks/{taskId}/items", "progressBatch": "/api/publish/tasks/progress/batch", "progressLight": "/api/publish/tasks/progress/light", "result": "/api/publish/tasks/{taskId}/result", "dashboard": "/api/publish/dashboard", "history": "/api/publish/history", "taskDelete": "/api/publish/tasks/{taskId}" }, "deleteBrand": { "run": "/api/delete-brand/run", "history": "/api/delete-brand/history", "historyDelete": "/api/delete-brand/history/{resultId}", "taskDetail": "/api/delete-brand/tasks/{taskId}", "taskDetails": "/api/delete-brand/tasks/batch", "taskDownload": "/api/delete-brand/tasks/{taskId}/download", "resultDownload": "/api/delete-brand/results/{resultId}/download", "resultSubmit": "/api/delete-brand/tasks/{taskId}/result", "progressBatch": "/api/delete-brand/tasks/progress/batch", "progressLight": "/api/delete-brand/tasks/progress/light" }, "productRisk": { "candidates": "/api/product-risk-resolve/candidates", "candidateAdd": "/api/product-risk-resolve/candidates", "candidateDelete": "/api/product-risk-resolve/candidates/{id}", "countryPreference": "/api/product-risk-resolve/country-preference", "matchShops": "/api/product-risk-resolve/match-shops", "dashboard": "/api/product-risk-resolve/dashboard", "history": "/api/product-risk-resolve/history", "historyDelete": "/api/product-risk-resolve/history/{resultId}", "taskCreate": "/api/product-risk-resolve/tasks", "taskDelete": "/api/product-risk-resolve/tasks/{taskId}", "taskBatch": "/api/product-risk-resolve/tasks/batch", "progressBatch": "/api/product-risk-resolve/tasks/progress/batch", "progressLight": "/api/product-risk-resolve/tasks/progress/light", "pendingShopResult": "/api/product-risk-resolve/pending-shop-result", "resultDownload": "/api/product-risk-resolve/results/{resultId}/download" }, "shopMatch": { "candidates": "/api/shop-match/candidates", "candidateAdd": "/api/shop-match/candidates", "candidateDelete": "/api/shop-match/candidates/{id}", "countryPreference": "/api/shop-match/country-preference", "matchShops": "/api/shop-match/match-shops", "dashboard": "/api/shop-match/dashboard", "history": "/api/shop-match/history", "historyDelete": "/api/shop-match/history/{resultId}", "taskCreate": "/api/shop-match/tasks", "taskDelete": "/api/shop-match/tasks/{taskId}", "taskActivate": "/api/shop-match/tasks/{taskId}/activate", "stageFinished": "/api/shop-match/tasks/{taskId}/stage-finished", "taskBatch": "/api/shop-match/tasks/batch", "progressBatch": "/api/shop-match/tasks/progress/batch", "progressLight": "/api/shop-match/tasks/progress/light", "skipAsins": "/api/shop-match/tasks/{taskId}/skip-asins/paginated", "resultDownload": "/api/shop-match/results/{resultId}/download" }, "patrolDelete": { "candidates": "/api/patrol-delete/candidates", "candidateAdd": "/api/patrol-delete/candidates", "candidateDelete": "/api/patrol-delete/candidates/{id}", "conditions": "/api/patrol-delete/conditions", "conditionAdd": "/api/patrol-delete/conditions", "conditionDelete": "/api/patrol-delete/conditions/{id}", "matchShops": "/api/patrol-delete/match-shops", "dashboard": "/api/patrol-delete/dashboard", "history": "/api/patrol-delete/history", "historyDelete": "/api/patrol-delete/history/{resultId}", "progressBatch": "/api/patrol-delete/tasks/progress/batch", "progressLight": "/api/patrol-delete/tasks/progress/light", "taskCreate": "/api/patrol-delete/tasks", "taskDelete": "/api/patrol-delete/tasks/{taskId}", "taskResult": "/api/patrol-delete/tasks/{taskId}/result", "resultDownload": "/api/patrol-delete/results/{resultId}/download" }, "queryAsin": { "candidates": "/api/query-asin/candidates", "candidateAdd": "/api/query-asin/candidates", "candidateDelete": "/api/query-asin/candidates/{id}", "matchShops": "/api/query-asin/match-shops", "dashboard": "/api/query-asin/dashboard", "history": "/api/query-asin/history", "historyDelete": "/api/query-asin/history/{resultId}", "progressBatch": "/api/query-asin/tasks/progress/batch", "progressLight": "/api/query-asin/tasks/progress/light", "taskCreate": "/api/query-asin/tasks", "taskDelete": "/api/query-asin/tasks/{taskId}", "taskResult": "/api/query-asin/tasks/{taskId}/result", "resultDownload": "/api/query-asin/results/{resultId}/download" }, "shopDataCrawl": { "candidates": "/api/shop-data-crawl/candidates", "candidateAdd": "/api/shop-data-crawl/candidates", "candidateDelete": "/api/shop-data-crawl/candidates/{id}", "countryPreference": "/api/shop-data-crawl/country-preference", "matchShops": "/api/shop-data-crawl/match-shops", "dashboard": "/api/shop-data-crawl/dashboard", "history": "/api/shop-data-crawl/history", "historyDelete": "/api/shop-data-crawl/history/{resultId}", "progressBatch": "/api/shop-data-crawl/tasks/progress/batch", "progressLight": "/api/shop-data-crawl/tasks/progress/light", "taskCreate": "/api/shop-data-crawl/tasks", "taskDelete": "/api/shop-data-crawl/tasks/{taskId}", "resultDownload": "/api/shop-data-crawl/results/{resultId}/download" }, "withdraw": { "candidates": "/api/withdraw/candidates", "candidateAdd": "/api/withdraw/candidates", "candidateDelete": "/api/withdraw/candidates/{id}", "candidatesClear": "/api/withdraw/candidates/clear", "matchShops": "/api/withdraw/match-shops", "dashboard": "/api/withdraw/dashboard", "history": "/api/withdraw/history", "historyDelete": "/api/withdraw/history/{resultId}", "progressBatch": "/api/withdraw/tasks/progress/batch", "progressLight": "/api/withdraw/tasks/progress/light", "taskCreate": "/api/withdraw/tasks", "taskDelete": "/api/withdraw/tasks/{taskId}", "taskResult": "/api/withdraw/tasks/{taskId}/result", "resultDownload": "/api/withdraw/results/{resultId}/download" }, "priceTrack": { "candidates": "/api/price-track/candidates", "candidateAdd": "/api/price-track/candidates", "candidateDelete": "/api/price-track/candidates/{id}", "countryPreference": "/api/price-track/country-preference", "matchShops": "/api/price-track/match-shops", "taskCreate": "/api/price-track/tasks", "taskDelete": "/api/price-track/tasks/{taskId}", "tasksBatch": "/api/price-track/tasks/batch", "progressBatch": "/api/price-track/tasks/progress/batch", "progressLight": "/api/price-track/tasks/progress/light", "loopRuns": "/api/price-track/loop-runs", "loopRunDetail": "/api/price-track/loop-runs/{loopRunId}", "loopRunStop": "/api/price-track/loop-runs/{loopRunId}/stop", "loopRunDispatchNext": "/api/price-track/loop-runs/{loopRunId}/dispatch-next", "loopRunChildFinished": "/api/price-track/loop-runs/{loopRunId}/child-finished", "skipAsinCheck": "/api/price-track/tasks/{taskId}/skip-asin/check", "skipAsins": "/api/price-track/tasks/{taskId}/skip-asins/paginated", "pendingShopResult": "/api/price-track/pending-shop-result", "dispatchFailed": "/api/price-track/tasks/{taskId}/dispatch-failed", "dashboard": "/api/price-track/dashboard", "history": "/api/price-track/history", "historyDelete": "/api/price-track/history/{resultId}", "resultDownload": "/api/price-track/results/{resultId}/download" }, "similarAsin": { "parse": "/api/similar-asin/parse", "dashboard": "/api/similar-asin/dashboard", "history": "/api/similar-asin/history", "historyDelete": "/api/similar-asin/history/{resultId}", "progressBatch": "/api/similar-asin/tasks/progress/batch", "progressLight": "/api/similar-asin/tasks/progress/light", "taskActivate": "/api/similar-asin/tasks/{taskId}/activate", "taskDelete": "/api/similar-asin/tasks/{taskId}", "resultDownload": "/api/similar-asin/results/{resultId}/download" }, "appearancePatent": { "parse": "/api/appearance-patent/parse", "parsedPayload": "/api/appearance-patent/tasks/{taskId}/parsed-payload", "queuePayload": "/api/appearance-patent/tasks/{taskId}/queue-payload", "dashboard": "/api/appearance-patent/dashboard", "history": "/api/appearance-patent/history", "historyDelete": "/api/appearance-patent/history/{resultId}", "progressBatch": "/api/appearance-patent/tasks/progress/batch", "progressLight": "/api/appearance-patent/tasks/progress/light", "taskActivate": "/api/appearance-patent/tasks/{taskId}/activate", "taskDelete": "/api/appearance-patent/tasks/{taskId}", "resultDownload": "/api/appearance-patent/results/{resultId}/download" }, "userSecret": { "bundle": "/api/user-secrets", "save": "/api/user-secrets/{moduleKey}", "clear": "/api/user-secrets/{moduleKey}", "check": "/api/user-secrets/{moduleKey}/check", "migrate": "/api/user-secrets/migrate", "proxyBalance": "/api/user-secrets/proxy-balance" }, "notification": { "summary": "/api/notifications/summary", "list": "/api/notifications", "read": "/api/notifications/{id}/read", "readAll": "/api/notifications/read-all" }, "collectData": { "parse": "/api/collect-data/parse", "countryPreference": "/api/collect-data/country-preference", "dashboard": "/api/collect-data/dashboard", "history": "/api/collect-data/history", "historyDelete": "/api/collect-data/history/{resultId}", "progressBatch": "/api/collect-data/tasks/progress/batch", "progressLight": "/api/collect-data/tasks/progress/light", "taskActivate": "/api/collect-data/tasks/{taskId}/activate", "taskDelete": "/api/collect-data/tasks/{taskId}", "fail": "/api/collect-data/tasks/{taskId}/fail", "items": "/api/collect-data/tasks/{taskId}/items", "resultDownload": "/api/collect-data/results/{resultId}/download" }, "imageVideo": { "workflowRun": "/api/image-video/workflow/run", "workflowResult": "/api/image-video/workflow/result", "secrets": "/api/image-video/secrets", "taskDetail": "/api/image-video/tasks/{taskId}", "voiceClone": "/api/image-video/voice/clone", "voiceList": "/api/image-video/voice/list", "voiceDelete": "/api/image-video/voice/delete", "voiceSynthesis": "/api/image-video/voice/synthesis", "douyinCopy": "/api/image-video/douyin-copy" }, "brand": { "run": "/api/brand/run", "expandFolder": "/api/brand/expand-folder", "expandFolderRecursive": "/api/brand/expand-folder-recursive", "taskDetail": "/api/brand/tasks/{taskId}", "taskCancel": "/api/brand/tasks/{taskId}/cancel", "taskDownload": "/api/brand/tasks/{taskId}/download", "taskEvents": "/api/brand/tasks/{taskId}/events" }, "permission": { "columnPermissions": "/api/admin/permission-users/{uid}/column-permissions" }, "digitalHuman": { "versions": "/api/digital-human/versions", "upload": "/api/digital-human/versions/upload", "latest": "/api/digital-human/versions/latest", "detail": "/api/digital-human/versions/{version}", "release": "/api/digital-human/versions/{version}/release", "setLatest": "/api/digital-human/versions/{version}/set-latest", "delete": "/api/digital-human/versions/{version}", "downloadUrl": "/api/digital-human/versions/{version}/download-url" }, "tutorial": { "latest": "/api/tutorial/latest" } }, '常量表快照不应漂移') }) test('test_endpoint_values_no_trailing_slash', () => { for (const value of allEndpointValues()) { assert.ok( !value.endsWith('/'), `端点不应以斜杠结尾: ${value}`, ) } }) test('test_endpoint_keys_convention', () => { for (const [moduleKey, group] of Object.entries(API_ENDPOINTS)) { assert.ok(/^[a-z][a-zA-Z0-9]*$/.test(moduleKey), `模块键应为小驼峰: ${moduleKey}`) for (const key of Object.keys(group as Record)) { assert.ok( /^[a-z][a-zA-Z0-9]*$/.test(key), `端点键应为小驼峰: ${moduleKey}.${key}`, ) } } })