create-aiko 0.1.0
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.
- package/README.md +46 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +7 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/scaffold.d.ts +7 -0
- package/dist/scaffold.js +310 -0
- package/package.json +37 -0
- package/template/README.md +63 -0
- package/template/docs/better-sqlite3-design.md +101 -0
- package/template/docs/shared-vs-shared-auth.md +54 -0
- package/template/package.json +23 -0
- package/template/packages/admin/.env.dev +1 -0
- package/template/packages/admin/.env.prod +4 -0
- package/template/packages/admin/.env.stage +4 -0
- package/template/packages/admin/Dockerfile +37 -0
- package/template/packages/admin/README.MD +27 -0
- package/template/packages/admin/components.json +21 -0
- package/template/packages/admin/eslint.config.js +28 -0
- package/template/packages/admin/index.html +50 -0
- package/template/packages/admin/package.json +100 -0
- package/template/packages/admin/public/vite.svg +1 -0
- package/template/packages/admin/src/App.css +82 -0
- package/template/packages/admin/src/App.tsx +128 -0
- package/template/packages/admin/src/app.config.ts +3 -0
- package/template/packages/admin/src/components/admin-ui/approval-flow.tsx +205 -0
- package/template/packages/admin/src/components/admin-ui/data-table/data-table-pagination.tsx +148 -0
- package/template/packages/admin/src/components/admin-ui/data-table/data-table-sorter.tsx +50 -0
- package/template/packages/admin/src/components/admin-ui/data-table/index.tsx +296 -0
- package/template/packages/admin/src/components/admin-ui/editable-table.tsx +292 -0
- package/template/packages/admin/src/components/admin-ui/form/input-password.tsx +38 -0
- package/template/packages/admin/src/components/admin-ui/form/sign-in-form.tsx +104 -0
- package/template/packages/admin/src/components/admin-ui/layout/error-component.tsx +71 -0
- package/template/packages/admin/src/components/admin-ui/layout/header.tsx +148 -0
- package/template/packages/admin/src/components/admin-ui/layout/language-switcher.tsx +47 -0
- package/template/packages/admin/src/components/admin-ui/layout/layout.tsx +42 -0
- package/template/packages/admin/src/components/admin-ui/layout/loading-overlay.tsx +36 -0
- package/template/packages/admin/src/components/admin-ui/layout/shell-bar.tsx +232 -0
- package/template/packages/admin/src/components/admin-ui/layout/sidebar.tsx +193 -0
- package/template/packages/admin/src/components/admin-ui/layout/user-avatar.tsx +31 -0
- package/template/packages/admin/src/components/admin-ui/list-report.tsx +313 -0
- package/template/packages/admin/src/components/admin-ui/master-detail.tsx +382 -0
- package/template/packages/admin/src/components/admin-ui/notification/toaster.tsx +23 -0
- package/template/packages/admin/src/components/admin-ui/notification/undoable-notification.tsx +84 -0
- package/template/packages/admin/src/components/admin-ui/object-page.tsx +539 -0
- package/template/packages/admin/src/components/admin-ui/process-stepper.tsx +204 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-provider.tsx +160 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-select.tsx +129 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-toggle.tsx +90 -0
- package/template/packages/admin/src/components/admin-ui/timeline.tsx +137 -0
- package/template/packages/admin/src/components/admin-ui/tree-navigator.tsx +243 -0
- package/template/packages/admin/src/components/ui/accordion.tsx +64 -0
- package/template/packages/admin/src/components/ui/alert-dialog.tsx +157 -0
- package/template/packages/admin/src/components/ui/alert.tsx +66 -0
- package/template/packages/admin/src/components/ui/aspect-ratio.tsx +9 -0
- package/template/packages/admin/src/components/ui/avatar.tsx +53 -0
- package/template/packages/admin/src/components/ui/badge.tsx +47 -0
- package/template/packages/admin/src/components/ui/breadcrumb.tsx +111 -0
- package/template/packages/admin/src/components/ui/button.tsx +59 -0
- package/template/packages/admin/src/components/ui/calendar.tsx +74 -0
- package/template/packages/admin/src/components/ui/card.tsx +92 -0
- package/template/packages/admin/src/components/ui/carousel.tsx +237 -0
- package/template/packages/admin/src/components/ui/chart.tsx +351 -0
- package/template/packages/admin/src/components/ui/checkbox.tsx +32 -0
- package/template/packages/admin/src/components/ui/collapsible.tsx +33 -0
- package/template/packages/admin/src/components/ui/command.tsx +182 -0
- package/template/packages/admin/src/components/ui/context-menu.tsx +252 -0
- package/template/packages/admin/src/components/ui/dialog.tsx +141 -0
- package/template/packages/admin/src/components/ui/drawer.tsx +130 -0
- package/template/packages/admin/src/components/ui/dropdown-menu.tsx +255 -0
- package/template/packages/admin/src/components/ui/form.tsx +166 -0
- package/template/packages/admin/src/components/ui/hover-card.tsx +42 -0
- package/template/packages/admin/src/components/ui/input-otp.tsx +77 -0
- package/template/packages/admin/src/components/ui/input.tsx +21 -0
- package/template/packages/admin/src/components/ui/label.tsx +22 -0
- package/template/packages/admin/src/components/ui/menubar.tsx +274 -0
- package/template/packages/admin/src/components/ui/navigation-menu.tsx +168 -0
- package/template/packages/admin/src/components/ui/pagination.tsx +127 -0
- package/template/packages/admin/src/components/ui/popover.tsx +48 -0
- package/template/packages/admin/src/components/ui/progress.tsx +29 -0
- package/template/packages/admin/src/components/ui/radio-group.tsx +45 -0
- package/template/packages/admin/src/components/ui/resizable.tsx +54 -0
- package/template/packages/admin/src/components/ui/scroll-area.tsx +58 -0
- package/template/packages/admin/src/components/ui/select.tsx +183 -0
- package/template/packages/admin/src/components/ui/separator.tsx +26 -0
- package/template/packages/admin/src/components/ui/sheet.tsx +139 -0
- package/template/packages/admin/src/components/ui/sidebar.tsx +740 -0
- package/template/packages/admin/src/components/ui/skeleton.tsx +13 -0
- package/template/packages/admin/src/components/ui/slider.tsx +63 -0
- package/template/packages/admin/src/components/ui/sonner.tsx +23 -0
- package/template/packages/admin/src/components/ui/switch.tsx +31 -0
- package/template/packages/admin/src/components/ui/table.tsx +114 -0
- package/template/packages/admin/src/components/ui/tabs.tsx +66 -0
- package/template/packages/admin/src/components/ui/textarea.tsx +18 -0
- package/template/packages/admin/src/components/ui/toggle-group.tsx +73 -0
- package/template/packages/admin/src/components/ui/toggle.tsx +45 -0
- package/template/packages/admin/src/components/ui/tooltip.tsx +59 -0
- package/template/packages/admin/src/hooks/use-mobile.ts +21 -0
- package/template/packages/admin/src/i18n.ts +20 -0
- package/template/packages/admin/src/index.tsx +18 -0
- package/template/packages/admin/src/layouts/menu-layout.tsx +211 -0
- package/template/packages/admin/src/layouts/tile-layout.tsx +355 -0
- package/template/packages/admin/src/lib/utils.ts +6 -0
- package/template/packages/admin/src/locales/en.json +68 -0
- package/template/packages/admin/src/locales/zh.json +68 -0
- package/template/packages/admin/src/pages/dashboard.tsx +12 -0
- package/template/packages/admin/src/pages/goods-receipt/CreatePage.tsx +302 -0
- package/template/packages/admin/src/pages/goods-receipt/EditPage.tsx +221 -0
- package/template/packages/admin/src/pages/goods-receipt/ListPage.tsx +283 -0
- package/template/packages/admin/src/pages/goods-receipt/ViewPage.tsx +280 -0
- package/template/packages/admin/src/pages/goods-receipt/index.ts +4 -0
- package/template/packages/admin/src/pages/home-page.tsx +244 -0
- package/template/packages/admin/src/pages/login-page.tsx +91 -0
- package/template/packages/admin/src/pages/master-data/cost-centers/index.tsx +461 -0
- package/template/packages/admin/src/pages/master-data/currencies/index.tsx +255 -0
- package/template/packages/admin/src/pages/master-data/materials/ListPage.tsx +271 -0
- package/template/packages/admin/src/pages/master-data/materials/ViewPage.tsx +240 -0
- package/template/packages/admin/src/pages/master-data/materials/index.ts +2 -0
- package/template/packages/admin/src/pages/master-data/plants/ListPage.tsx +279 -0
- package/template/packages/admin/src/pages/master-data/plants/ViewPage.tsx +380 -0
- package/template/packages/admin/src/pages/master-data/plants/index.ts +2 -0
- package/template/packages/admin/src/pages/master-data/purchase-organizations/index.tsx +341 -0
- package/template/packages/admin/src/pages/master-data/units-of-measure/index.tsx +295 -0
- package/template/packages/admin/src/pages/master-data/vendors/ListPage.tsx +266 -0
- package/template/packages/admin/src/pages/master-data/vendors/ViewPage.tsx +274 -0
- package/template/packages/admin/src/pages/master-data/vendors/index.ts +2 -0
- package/template/packages/admin/src/pages/placeholder-page.tsx +13 -0
- package/template/packages/admin/src/pages/purchase-orders/ListPage.tsx +289 -0
- package/template/packages/admin/src/pages/purchase-orders/ViewPage.tsx +343 -0
- package/template/packages/admin/src/pages/purchase-orders/index.ts +2 -0
- package/template/packages/admin/src/pages/purchase-requisitions/CreatePage.tsx +398 -0
- package/template/packages/admin/src/pages/purchase-requisitions/EditPage.tsx +473 -0
- package/template/packages/admin/src/pages/purchase-requisitions/ListPage.tsx +307 -0
- package/template/packages/admin/src/pages/purchase-requisitions/ViewPage.tsx +304 -0
- package/template/packages/admin/src/pages/purchase-requisitions/constants.ts +51 -0
- package/template/packages/admin/src/pages/purchase-requisitions/index.ts +4 -0
- package/template/packages/admin/src/pages/reports/PurchaseOrderReport.tsx +312 -0
- package/template/packages/admin/src/pages/reports/PurchaseRequisitionReport.tsx +303 -0
- package/template/packages/admin/src/pages/reports/index.ts +2 -0
- package/template/packages/admin/src/pages/settings-page.tsx +335 -0
- package/template/packages/admin/src/providers/app-config.tsx +50 -0
- package/template/packages/admin/src/routes/auth.ts +6 -0
- package/template/packages/admin/src/routes/index.ts +85 -0
- package/template/packages/admin/src/routes/menu.ts +176 -0
- package/template/packages/admin/src/routes/modules/goods-receipt.ts +31 -0
- package/template/packages/admin/src/routes/modules/master-data.ts +41 -0
- package/template/packages/admin/src/routes/modules/purchase-orders.ts +27 -0
- package/template/packages/admin/src/routes/modules/purchase-requisitions.ts +39 -0
- package/template/packages/admin/src/routes/modules/reports.ts +33 -0
- package/template/packages/admin/src/routes/modules/settings.ts +19 -0
- package/template/packages/admin/src/routes/withSuspense.tsx +21 -0
- package/template/packages/admin/src/theme/amber.css +27 -0
- package/template/packages/admin/src/theme/blue.css +27 -0
- package/template/packages/admin/src/theme/default.css +75 -0
- package/template/packages/admin/src/theme/fiori.css +180 -0
- package/template/packages/admin/src/theme/green.css +27 -0
- package/template/packages/admin/src/theme/index.css +12 -0
- package/template/packages/admin/src/theme/rose.css +27 -0
- package/template/packages/admin/src/theme/violet.css +27 -0
- package/template/packages/admin/src/vite-env.d.ts +1 -0
- package/template/packages/admin/tsconfig.json +28 -0
- package/template/packages/admin/tsconfig.node.json +21 -0
- package/template/packages/admin/vite.config.ts +26 -0
- package/template/packages/api/.eslintrc.json +6 -0
- package/template/packages/api/.swcrc +17 -0
- package/template/packages/api/app.config.ts +160 -0
- package/template/packages/api/docs/api-document.md +497 -0
- package/template/packages/api/examples/security/README.md +664 -0
- package/template/packages/api/examples/security/complete/.env.example +26 -0
- package/template/packages/api/examples/security/complete/PROJECT_STRUCTURE.md +220 -0
- package/template/packages/api/examples/security/complete/README.md +847 -0
- package/template/packages/api/examples/security/complete/app.config.ts +69 -0
- package/template/packages/api/examples/security/complete/app.ts +63 -0
- package/template/packages/api/examples/security/complete/controller/auth.controller.ts +131 -0
- package/template/packages/api/examples/security/complete/controller/index.ts +4 -0
- package/template/packages/api/examples/security/complete/controller/permission.controller.ts +41 -0
- package/template/packages/api/examples/security/complete/controller/role.controller.ts +53 -0
- package/template/packages/api/examples/security/complete/controller/user.controller.ts +53 -0
- package/template/packages/api/examples/security/complete/dto/change-password.dto.ts +10 -0
- package/template/packages/api/examples/security/complete/dto/create-permission.dto.ts +14 -0
- package/template/packages/api/examples/security/complete/dto/create-role.dto.ts +11 -0
- package/template/packages/api/examples/security/complete/dto/create-user.dto.ts +15 -0
- package/template/packages/api/examples/security/complete/dto/index.ts +7 -0
- package/template/packages/api/examples/security/complete/dto/login.dto.ts +10 -0
- package/template/packages/api/examples/security/complete/dto/oauth-profile.dto.ts +7 -0
- package/template/packages/api/examples/security/complete/dto/register.dto.ts +17 -0
- package/template/packages/api/examples/security/complete/entity/index.ts +6 -0
- package/template/packages/api/examples/security/complete/entity/oauth-account.entity.ts +39 -0
- package/template/packages/api/examples/security/complete/entity/permission.entity.ts +31 -0
- package/template/packages/api/examples/security/complete/entity/role-permission.entity.ts +19 -0
- package/template/packages/api/examples/security/complete/entity/role.entity.ts +25 -0
- package/template/packages/api/examples/security/complete/entity/user-role.entity.ts +19 -0
- package/template/packages/api/examples/security/complete/entity/user.entity.ts +46 -0
- package/template/packages/api/examples/security/complete/init.sql +81 -0
- package/template/packages/api/examples/security/complete/middleware/auth.interceptor.ts +39 -0
- package/template/packages/api/examples/security/complete/middleware/index.ts +2 -0
- package/template/packages/api/examples/security/complete/middleware/permission.interceptor.ts +61 -0
- package/template/packages/api/examples/security/complete/package.json +54 -0
- package/template/packages/api/examples/security/complete/seed.sql +42 -0
- package/template/packages/api/examples/security/complete/service/auth.service.ts +41 -0
- package/template/packages/api/examples/security/complete/service/index.ts +5 -0
- package/template/packages/api/examples/security/complete/service/oauth.service.ts +82 -0
- package/template/packages/api/examples/security/complete/service/permission.service.ts +113 -0
- package/template/packages/api/examples/security/complete/service/role.service.ts +85 -0
- package/template/packages/api/examples/security/complete/service/user.service.ts +132 -0
- package/template/packages/api/examples/security/complete/tests/TEST_REPORT.md +318 -0
- package/template/packages/api/examples/security/complete/tests/generate-report.js +335 -0
- package/template/packages/api/examples/security/complete/tests/helpers/api-helpers.ts +116 -0
- package/template/packages/api/examples/security/complete/tests/helpers/index.ts +2 -0
- package/template/packages/api/examples/security/complete/tests/helpers/test-helpers.ts +129 -0
- package/template/packages/api/examples/security/complete/tests/integration/auth.api.test.ts +429 -0
- package/template/packages/api/examples/security/complete/tests/integration/role.api.test.ts +400 -0
- package/template/packages/api/examples/security/complete/tests/integration/user.api.test.ts +459 -0
- package/template/packages/api/examples/security/complete/tests/jest.config.js +40 -0
- package/template/packages/api/examples/security/complete/tests/run-all-tests.js +135 -0
- package/template/packages/api/examples/security/complete/tests/run-tests.js +109 -0
- package/template/packages/api/examples/security/complete/tests/setup.ts +19 -0
- package/template/packages/api/examples/security/complete/tests/unit/auth.service.test.ts +199 -0
- package/template/packages/api/examples/security/complete/tests/unit/permission.service.test.ts +377 -0
- package/template/packages/api/examples/security/complete/tests/unit/user.service.test.ts +288 -0
- package/template/packages/api/examples/security/complete/tsconfig.json +35 -0
- package/template/packages/api/examples/security/jwt/README.md +424 -0
- package/template/packages/api/examples/security/local/README.md +499 -0
- package/template/packages/api/examples/security/oauth2/README.md +637 -0
- package/template/packages/api/examples/security/permission/README.md +943 -0
- package/template/packages/api/examples/security/session/README.md +753 -0
- package/template/packages/api/package.json +56 -0
- package/template/packages/api/src/controller/auth.controller.ts +127 -0
- package/template/packages/api/src/controller/cache.controller.ts +106 -0
- package/template/packages/api/src/controller/menu.controller.ts +46 -0
- package/template/packages/api/src/controller/mq.controller.ts +35 -0
- package/template/packages/api/src/controller/role.controller.ts +51 -0
- package/template/packages/api/src/controller/upload.controller.ts +85 -0
- package/template/packages/api/src/controller/user.controller.ts +57 -0
- package/template/packages/api/src/dto/auth.dto.ts +30 -0
- package/template/packages/api/src/dto/cache.dto.ts +24 -0
- package/template/packages/api/src/dto/menu.dto.ts +37 -0
- package/template/packages/api/src/dto/mq.dto.ts +16 -0
- package/template/packages/api/src/dto/role.dto.ts +16 -0
- package/template/packages/api/src/dto/user.dto.ts +35 -0
- package/template/packages/api/src/entity/menu.entity.ts +34 -0
- package/template/packages/api/src/entity/role-menu.entity.ts +13 -0
- package/template/packages/api/src/entity/role.entity.ts +22 -0
- package/template/packages/api/src/entity/user-role.entity.ts +13 -0
- package/template/packages/api/src/entity/user.entity.ts +31 -0
- package/template/packages/api/src/mapper/menu.mapper.ts +6 -0
- package/template/packages/api/src/mapper/role-menu.mapper.ts +6 -0
- package/template/packages/api/src/mapper/role.mapper.ts +6 -0
- package/template/packages/api/src/mapper/user-role.mapper.ts +6 -0
- package/template/packages/api/src/mapper/user.mapper.ts +11 -0
- package/template/packages/api/src/scripts/init-db.ts +185 -0
- package/template/packages/api/src/server.ts +76 -0
- package/template/packages/api/src/service/auth.service.ts +106 -0
- package/template/packages/api/src/service/cache.service.ts +80 -0
- package/template/packages/api/src/service/log.request.service.ts +158 -0
- package/template/packages/api/src/service/log.service.ts +123 -0
- package/template/packages/api/src/service/menu.service.ts +94 -0
- package/template/packages/api/src/service/mq.consumer.service.ts +26 -0
- package/template/packages/api/src/service/role.service.ts +88 -0
- package/template/packages/api/src/service/user.service.ts +170 -0
- package/template/packages/api/src/types/sqljs.d.ts +18 -0
- package/template/packages/api/src/utils/auth.utils.js +0 -0
- package/template/packages/api/src/utils/jwt.util.ts +29 -0
- package/template/packages/api/tsconfig.json +17 -0
- package/template/packages/api/tsup.config.ts +9 -0
- package/template/packages/api/uploads/.gitkeep +0 -0
- package/template/packages/core/package.json +31 -0
- package/template/packages/core/src/auth/auth-client-middleware.ts +22 -0
- package/template/packages/core/src/auth/auth-service.ts +65 -0
- package/template/packages/core/src/auth/default-auth-provider.ts +37 -0
- package/template/packages/core/src/auth/index.ts +10 -0
- package/template/packages/core/src/auth/types.ts +38 -0
- package/template/packages/core/src/authorization/authorization-client-middleware.ts +38 -0
- package/template/packages/core/src/authorization/authorization-config.ts +13 -0
- package/template/packages/core/src/authorization/authorization-provider.tsx +116 -0
- package/template/packages/core/src/authorization/default-authorization-provider.ts +26 -0
- package/template/packages/core/src/authorization/index.ts +15 -0
- package/template/packages/core/src/authorization/types.ts +42 -0
- package/template/packages/core/src/index.ts +3 -0
- package/template/packages/core/src/utils/promise-result-cache.ts +18 -0
- package/template/packages/core/tsconfig.json +19 -0
- package/template/packages/mobile/README.md +55 -0
- package/template/packages/mobile/index.html +13 -0
- package/template/packages/mobile/package.json +30 -0
- package/template/packages/mobile/postcss.config.mjs +7 -0
- package/template/packages/mobile/src/App.tsx +5 -0
- package/template/packages/mobile/src/app/globals.css +1 -0
- package/template/packages/mobile/src/components/LoginForm.tsx +76 -0
- package/template/packages/mobile/src/hooks/index.ts +5 -0
- package/template/packages/mobile/src/lib/utils.ts +7 -0
- package/template/packages/mobile/src/main.tsx +13 -0
- package/template/packages/mobile/src/pages/HomePage.tsx +35 -0
- package/template/packages/mobile/src/pages/LoginPage.tsx +35 -0
- package/template/packages/mobile/src/pages/index.ts +2 -0
- package/template/packages/mobile/src/routes/ProtectedRoute.tsx +29 -0
- package/template/packages/mobile/src/routes/index.tsx +24 -0
- package/template/packages/mobile/src/routes/routes.ts +11 -0
- package/template/packages/mobile/src/types/index.ts +5 -0
- package/template/packages/mobile/src/vite-env.d.ts +1 -0
- package/template/packages/mobile/tsconfig.json +23 -0
- package/template/packages/mobile/tsconfig.node.json +11 -0
- package/template/packages/mobile/vite.config.ts +19 -0
- package/template/packages/shared/package.json +20 -0
- package/template/packages/shared/src/constants.ts +8 -0
- package/template/packages/shared/src/index.ts +5 -0
- package/template/packages/shared/tsconfig.json +13 -0
- package/template/packages/shared-auth/package.json +29 -0
- package/template/packages/shared-auth/src/AuthContext.tsx +177 -0
- package/template/packages/shared-auth/src/AuthProviderWrapper.tsx +29 -0
- package/template/packages/shared-auth/src/index.ts +10 -0
- package/template/packages/shared-auth/tsconfig.json +14 -0
- package/template/pnpm-lock.yaml +16349 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/scripts/postinstall.cjs +42 -0
- package/template/scripts/rebuild-sqlite.cjs +23 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const TEST_RESULTS_DIR = path.join(__dirname, '../test-results');
|
|
12
|
+
const COVERAGE_DIR = path.join(__dirname, '../coverage');
|
|
13
|
+
|
|
14
|
+
function ensureDirectoryExists(dir) {
|
|
15
|
+
if (!fs.existsSync(dir)) {
|
|
16
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function runCommand(command, description) {
|
|
21
|
+
console.log(`\n${description}...`);
|
|
22
|
+
try {
|
|
23
|
+
execSync(command, { stdio: 'inherit', cwd: __dirname });
|
|
24
|
+
console.log(`✓ ${description} 完成`);
|
|
25
|
+
return true;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(`✗ ${description} 失败`);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function generateTestReport() {
|
|
33
|
+
ensureDirectoryExists(TEST_RESULTS_DIR);
|
|
34
|
+
ensureDirectoryExists(COVERAGE_DIR);
|
|
35
|
+
|
|
36
|
+
console.log('========================================');
|
|
37
|
+
console.log(' Aiko Boot Security 测试报告生成器');
|
|
38
|
+
console.log('========================================\n');
|
|
39
|
+
|
|
40
|
+
const results = {
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
environment: process.env.NODE_ENV || 'development',
|
|
43
|
+
tests: {
|
|
44
|
+
unit: { passed: 0, failed: 0, total: 0 },
|
|
45
|
+
integration: { passed: 0, failed: 0, total: 0 },
|
|
46
|
+
overall: { passed: 0, failed: 0, total: 0, duration: 0 },
|
|
47
|
+
},
|
|
48
|
+
coverage: {
|
|
49
|
+
statements: 0,
|
|
50
|
+
branches: 0,
|
|
51
|
+
functions: 0,
|
|
52
|
+
lines: 0,
|
|
53
|
+
},
|
|
54
|
+
details: [],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
console.log('1. 运行单元测试...');
|
|
58
|
+
const unitSuccess = runCommand(
|
|
59
|
+
'npm run test:unit',
|
|
60
|
+
'单元测试'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
console.log('\n2. 运行集成测试...');
|
|
64
|
+
const integrationSuccess = runCommand(
|
|
65
|
+
'npm run test:integration',
|
|
66
|
+
'集成测试'
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
console.log('\n3. 生成覆盖率报告...');
|
|
70
|
+
const coverageSuccess = runCommand(
|
|
71
|
+
'npm run test:coverage',
|
|
72
|
+
'覆盖率报告'
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
console.log('\n4. 收集测试结果...');
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const coverageSummaryPath = path.join(COVERAGE_DIR, 'coverage-summary.json');
|
|
79
|
+
if (fs.existsSync(coverageSummaryPath)) {
|
|
80
|
+
const coverageData = JSON.parse(fs.readFileSync(coverageSummaryPath, 'utf8'));
|
|
81
|
+
results.coverage = coverageData.total;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.warn('无法读取覆盖率数据');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('\n5. 生成测试报告...');
|
|
88
|
+
const reportPath = path.join(TEST_RESULTS_DIR, 'test-report.json');
|
|
89
|
+
fs.writeFileSync(reportPath, JSON.stringify(results, null, 2));
|
|
90
|
+
|
|
91
|
+
console.log('\n========================================');
|
|
92
|
+
console.log(' 测试摘要');
|
|
93
|
+
console.log('========================================');
|
|
94
|
+
console.log(`单元测试: ${unitSuccess ? '✓ 通过' : '✗ 失败'}`);
|
|
95
|
+
console.log(`集成测试: ${integrationSuccess ? '✓ 通过' : '✗ 失败'}`);
|
|
96
|
+
console.log(`覆盖率报告: ${coverageSuccess ? '✓ 已生成' : '✗ 失败'}`);
|
|
97
|
+
console.log(`报告路径: ${reportPath}`);
|
|
98
|
+
console.log('========================================\n');
|
|
99
|
+
|
|
100
|
+
if (unitSuccess && integrationSuccess && coverageSuccess) {
|
|
101
|
+
console.log('✓ 所有测试通过!');
|
|
102
|
+
process.exit(0);
|
|
103
|
+
} else {
|
|
104
|
+
console.log('✗ 部分测试失败');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
generateTestReport();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
jest.setTimeout(10000);
|
|
4
|
+
|
|
5
|
+
beforeAll(() => {
|
|
6
|
+
console.log('开始测试套件...');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterAll(() => {
|
|
10
|
+
console.log('测试套件完成');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
jest.restoreAllMocks();
|
|
19
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import { AuthService } from '../../service/auth.service.js';
|
|
3
|
+
import { UserService } from '../../service/user.service.js';
|
|
4
|
+
import { OAuthService } from '../../service/oauth.service.js';
|
|
5
|
+
import { SecurityContext } from '@ai-partner-x/aiko-boot-starter-security';
|
|
6
|
+
|
|
7
|
+
describe('AuthService', () => {
|
|
8
|
+
let authService: AuthService;
|
|
9
|
+
let userService: UserService;
|
|
10
|
+
let oauthService: OAuthService;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
authService = new AuthService();
|
|
14
|
+
userService = new UserService();
|
|
15
|
+
oauthService = new OAuthService();
|
|
16
|
+
|
|
17
|
+
(authService as any).securityAuthService = {
|
|
18
|
+
login: jest.fn(),
|
|
19
|
+
register: jest.fn(),
|
|
20
|
+
refreshToken: jest.fn(),
|
|
21
|
+
logout: jest.fn(),
|
|
22
|
+
changePassword: jest.fn(),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
(authService as any).userService = userService;
|
|
26
|
+
(authService as any).oauthService = oauthService;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('login', () => {
|
|
34
|
+
it('应该成功登录用户', async () => {
|
|
35
|
+
const mockResponse = {
|
|
36
|
+
accessToken: 'mock-access-token',
|
|
37
|
+
refreshToken: 'mock-refresh-token',
|
|
38
|
+
expiresIn: 3600,
|
|
39
|
+
user: {
|
|
40
|
+
id: 1,
|
|
41
|
+
username: 'testuser',
|
|
42
|
+
email: 'test@example.com',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
((authService as any).securityAuthService.login as jest.Mock).mockResolvedValue(mockResponse);
|
|
47
|
+
|
|
48
|
+
const result = await authService.login('testuser', 'password123');
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual(mockResponse);
|
|
51
|
+
expect(((authService as any).securityAuthService.login as jest.Mock)).toHaveBeenCalledWith({
|
|
52
|
+
username: 'testuser',
|
|
53
|
+
password: 'password123',
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('应该处理登录失败', async () => {
|
|
58
|
+
const mockError = new Error('用户名或密码错误');
|
|
59
|
+
((authService as any).securityAuthService.login as jest.Mock).mockRejectedValue(mockError);
|
|
60
|
+
|
|
61
|
+
await expect(authService.login('testuser', 'wrongpassword')).rejects.toThrow('用户名或密码错误');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('register', () => {
|
|
66
|
+
it('应该成功注册新用户', async () => {
|
|
67
|
+
const mockUserData = {
|
|
68
|
+
username: 'newuser',
|
|
69
|
+
email: 'new@example.com',
|
|
70
|
+
password: 'Password123!',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const mockResponse = {
|
|
74
|
+
user: {
|
|
75
|
+
id: 2,
|
|
76
|
+
username: 'newuser',
|
|
77
|
+
email: 'new@example.com',
|
|
78
|
+
},
|
|
79
|
+
accessToken: 'new-access-token',
|
|
80
|
+
refreshToken: 'new-refresh-token',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
((authService as any).securityAuthService.register as jest.Mock).mockResolvedValue(mockResponse);
|
|
84
|
+
|
|
85
|
+
const result = await authService.register(mockUserData);
|
|
86
|
+
|
|
87
|
+
expect(result).toEqual(mockResponse);
|
|
88
|
+
expect(((authService as any).securityAuthService.register as jest.Mock)).toHaveBeenCalledWith(mockUserData);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('应该处理注册失败(用户已存在)', async () => {
|
|
92
|
+
const mockUserData = {
|
|
93
|
+
username: 'existinguser',
|
|
94
|
+
email: 'existing@example.com',
|
|
95
|
+
password: 'Password123!',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const mockError = new Error('用户名已存在');
|
|
99
|
+
((authService as any).securityAuthService.register as jest.Mock).mockRejectedValue(mockError);
|
|
100
|
+
|
|
101
|
+
await expect(authService.register(mockUserData)).rejects.toThrow('用户名已存在');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('refreshToken', () => {
|
|
106
|
+
it('应该成功刷新 token', async () => {
|
|
107
|
+
const mockResponse = {
|
|
108
|
+
accessToken: 'new-access-token',
|
|
109
|
+
refreshToken: 'new-refresh-token',
|
|
110
|
+
expiresIn: 3600,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
((authService as any).securityAuthService.refreshToken as jest.Mock).mockResolvedValue(mockResponse);
|
|
114
|
+
|
|
115
|
+
const result = await authService.refreshToken('old-refresh-token');
|
|
116
|
+
|
|
117
|
+
expect(result).toEqual(mockResponse);
|
|
118
|
+
expect(((authService as any).securityAuthService.refreshToken as jest.Mock)).toHaveBeenCalledWith('old-refresh-token');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('应该处理无效的 refresh token', async () => {
|
|
122
|
+
const mockError = new Error('无效的 refresh token');
|
|
123
|
+
((authService as any).securityAuthService.refreshToken as jest.Mock).mockRejectedValue(mockError);
|
|
124
|
+
|
|
125
|
+
await expect(authService.refreshToken('invalid-token')).rejects.toThrow('无效的 refresh token');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('logout', () => {
|
|
130
|
+
it('应该成功登出用户', async () => {
|
|
131
|
+
const mockResponse = { success: true };
|
|
132
|
+
((authService as any).securityAuthService.logout as jest.Mock).mockResolvedValue(mockResponse);
|
|
133
|
+
|
|
134
|
+
const result = await authService.logout('access-token');
|
|
135
|
+
|
|
136
|
+
expect(result).toEqual(mockResponse);
|
|
137
|
+
expect(((authService as any).securityAuthService.logout as jest.Mock)).toHaveBeenCalledWith('access-token');
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('changePassword', () => {
|
|
142
|
+
it('应该成功修改密码', async () => {
|
|
143
|
+
const mockResponse = { success: true };
|
|
144
|
+
((authService as any).securityAuthService.changePassword as jest.Mock).mockResolvedValue(mockResponse);
|
|
145
|
+
|
|
146
|
+
SecurityContext.getInstance().setCurrentUser({ id: 1, username: 'testuser' });
|
|
147
|
+
|
|
148
|
+
const result = await authService.changePassword(1, 'oldpassword', 'newpassword');
|
|
149
|
+
|
|
150
|
+
expect(result).toEqual(mockResponse);
|
|
151
|
+
expect(((authService as any).securityAuthService.changePassword as jest.Mock)).toHaveBeenCalledWith(
|
|
152
|
+
1,
|
|
153
|
+
'oldpassword',
|
|
154
|
+
'newpassword'
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('应该处理未登录用户的密码修改', async () => {
|
|
159
|
+
SecurityContext.getInstance().setCurrentUser(null);
|
|
160
|
+
|
|
161
|
+
await expect(authService.changePassword(1, 'oldpassword', 'newpassword')).rejects.toThrow('未登录');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('handleOAuthCallback', () => {
|
|
166
|
+
it('应该成功处理 OAuth 回调', async () => {
|
|
167
|
+
const mockProfile = {
|
|
168
|
+
id: 'github-123',
|
|
169
|
+
username: 'githubuser',
|
|
170
|
+
email: 'github@example.com',
|
|
171
|
+
avatar: 'https://github.com/avatar.png',
|
|
172
|
+
provider: 'github',
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const mockTokens = {
|
|
176
|
+
access_token: 'github-access-token',
|
|
177
|
+
refresh_token: 'github-refresh-token',
|
|
178
|
+
expires_at: 1234567890,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const mockResponse = {
|
|
182
|
+
user: {
|
|
183
|
+
id: 3,
|
|
184
|
+
username: 'githubuser',
|
|
185
|
+
email: 'github@example.com',
|
|
186
|
+
},
|
|
187
|
+
accessToken: 'app-access-token',
|
|
188
|
+
refreshToken: 'app-refresh-token',
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
(oauthService.handleOAuthCallback as jest.Mock).mockResolvedValue(mockResponse);
|
|
192
|
+
|
|
193
|
+
const result = await authService.handleOAuthCallback(mockProfile, mockTokens);
|
|
194
|
+
|
|
195
|
+
expect(result).toEqual(mockResponse);
|
|
196
|
+
expect(oauthService.handleOAuthCallback).toHaveBeenCalledWith(mockProfile, mockTokens);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
package/template/packages/api/examples/security/complete/tests/unit/permission.service.test.ts
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import { PermissionService } from '../../service/permission.service.js';
|
|
3
|
+
import { Permission, Role, User } from '../../entity/index.js';
|
|
4
|
+
|
|
5
|
+
describe('PermissionService', () => {
|
|
6
|
+
let permissionService: PermissionService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
permissionService = new PermissionService();
|
|
10
|
+
|
|
11
|
+
(permissionService as any).permissionMapper = {
|
|
12
|
+
selectById: jest.fn(),
|
|
13
|
+
selectList: jest.fn(),
|
|
14
|
+
insert: jest.fn(),
|
|
15
|
+
updateById: jest.fn(),
|
|
16
|
+
deleteById: jest.fn(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
(permissionService as any).roleMapper = {
|
|
20
|
+
selectById: jest.fn(),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
(permissionService as any).userMapper = {
|
|
24
|
+
selectById: jest.fn(),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
(permissionService as any).rolePermissionMapper = {
|
|
28
|
+
selectList: jest.fn(),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
(permissionService as any).userRoleMapper = {
|
|
32
|
+
selectList: jest.fn(),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('getUserPermissions', () => {
|
|
41
|
+
it('应该获取用户的所有权限', async () => {
|
|
42
|
+
const mockUserRoles = [
|
|
43
|
+
{ id: 1, userId: 1, roleId: 1, createdAt: new Date() },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const mockRolePermissions = [
|
|
47
|
+
{ id: 1, roleId: 1, permissionId: 1, createdAt: new Date() },
|
|
48
|
+
{ id: 2, roleId: 1, permissionId: 2, createdAt: new Date() },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const mockPermissions: Permission[] = [
|
|
52
|
+
{
|
|
53
|
+
id: 1,
|
|
54
|
+
name: 'user:view',
|
|
55
|
+
description: '查看用户',
|
|
56
|
+
resource: 'user',
|
|
57
|
+
action: 'view',
|
|
58
|
+
createdAt: new Date(),
|
|
59
|
+
updatedAt: new Date(),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 2,
|
|
63
|
+
name: 'user:create',
|
|
64
|
+
description: '创建用户',
|
|
65
|
+
resource: 'user',
|
|
66
|
+
action: 'create',
|
|
67
|
+
createdAt: new Date(),
|
|
68
|
+
updatedAt: new Date(),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
((permissionService as any).userMapper.selectById as jest.Mock).mockResolvedValue({ id: 1 });
|
|
73
|
+
((permissionService as any).userRoleMapper.selectList as jest.Mock).mockResolvedValue(mockUserRoles);
|
|
74
|
+
((permissionService as any).rolePermissionMapper.selectList as jest.Mock).mockResolvedValue(mockRolePermissions);
|
|
75
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock)
|
|
76
|
+
.mockResolvedValueOnce(mockPermissions[0])
|
|
77
|
+
.mockResolvedValueOnce(mockPermissions[1]);
|
|
78
|
+
|
|
79
|
+
const result = await permissionService.getUserPermissions(1);
|
|
80
|
+
|
|
81
|
+
expect(result).toHaveLength(2);
|
|
82
|
+
expect(result[0].name).toBe('user:view');
|
|
83
|
+
expect(result[1].name).toBe('user:create');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('当用户不存在时应该返回空数组', async () => {
|
|
87
|
+
((permissionService as any).userMapper.selectById as jest.Mock).mockResolvedValue(null);
|
|
88
|
+
|
|
89
|
+
const result = await permissionService.getUserPermissions(999);
|
|
90
|
+
|
|
91
|
+
expect(result).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('应该去重权限', async () => {
|
|
95
|
+
const mockUserRoles = [
|
|
96
|
+
{ id: 1, userId: 1, roleId: 1, createdAt: new Date() },
|
|
97
|
+
{ id: 2, userId: 1, roleId: 2, createdAt: new Date() },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const mockRolePermissions1 = [
|
|
101
|
+
{ id: 1, roleId: 1, permissionId: 1, createdAt: new Date() },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const mockRolePermissions2 = [
|
|
105
|
+
{ id: 2, roleId: 2, permissionId: 1, createdAt: new Date() },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const mockPermission: Permission = {
|
|
109
|
+
id: 1,
|
|
110
|
+
name: 'user:view',
|
|
111
|
+
description: '查看用户',
|
|
112
|
+
resource: 'user',
|
|
113
|
+
action: 'view',
|
|
114
|
+
createdAt: new Date(),
|
|
115
|
+
updatedAt: new Date(),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
((permissionService as any).userMapper.selectById as jest.Mock).mockResolvedValue({ id: 1 });
|
|
119
|
+
((permissionService as any).userRoleMapper.selectList as jest.Mock).mockResolvedValue(mockUserRoles);
|
|
120
|
+
((permissionService as any).rolePermissionMapper.selectList as jest.Mock)
|
|
121
|
+
.mockResolvedValueOnce(mockRolePermissions1)
|
|
122
|
+
.mockResolvedValueOnce(mockRolePermissions2);
|
|
123
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock).mockResolvedValue(mockPermission);
|
|
124
|
+
|
|
125
|
+
const result = await permissionService.getUserPermissions(1);
|
|
126
|
+
|
|
127
|
+
expect(result).toHaveLength(1);
|
|
128
|
+
expect(result[0].name).toBe('user:view');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('hasPermission', () => {
|
|
133
|
+
it('应该检查用户是否拥有指定权限', async () => {
|
|
134
|
+
const mockPermissions: Permission[] = [
|
|
135
|
+
{
|
|
136
|
+
id: 1,
|
|
137
|
+
name: 'user:view',
|
|
138
|
+
description: '查看用户',
|
|
139
|
+
resource: 'user',
|
|
140
|
+
action: 'view',
|
|
141
|
+
createdAt: new Date(),
|
|
142
|
+
updatedAt: new Date(),
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue(mockPermissions);
|
|
147
|
+
|
|
148
|
+
const result = await permissionService.hasPermission(1, 'user:view');
|
|
149
|
+
|
|
150
|
+
expect(result).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('当用户没有指定权限时应该返回 false', async () => {
|
|
154
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue([]);
|
|
155
|
+
|
|
156
|
+
const result = await permissionService.hasPermission(1, 'user:delete');
|
|
157
|
+
|
|
158
|
+
expect(result).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('hasAnyPermission', () => {
|
|
163
|
+
it('应该检查用户是否拥有任一指定权限', async () => {
|
|
164
|
+
const mockPermissions: Permission[] = [
|
|
165
|
+
{
|
|
166
|
+
id: 1,
|
|
167
|
+
name: 'user:view',
|
|
168
|
+
description: '查看用户',
|
|
169
|
+
resource: 'user',
|
|
170
|
+
action: 'view',
|
|
171
|
+
createdAt: new Date(),
|
|
172
|
+
updatedAt: new Date(),
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue(mockPermissions);
|
|
177
|
+
|
|
178
|
+
const result = await permissionService.hasAnyPermission(1, ['user:delete', 'user:view']);
|
|
179
|
+
|
|
180
|
+
expect(result).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('当用户没有任何指定权限时应该返回 false', async () => {
|
|
184
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue([]);
|
|
185
|
+
|
|
186
|
+
const result = await permissionService.hasAnyPermission(1, ['user:delete', 'user:update']);
|
|
187
|
+
|
|
188
|
+
expect(result).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('hasAllPermissions', () => {
|
|
193
|
+
it('应该检查用户是否拥有所有指定权限', async () => {
|
|
194
|
+
const mockPermissions: Permission[] = [
|
|
195
|
+
{
|
|
196
|
+
id: 1,
|
|
197
|
+
name: 'user:view',
|
|
198
|
+
description: '查看用户',
|
|
199
|
+
resource: 'user',
|
|
200
|
+
action: 'view',
|
|
201
|
+
createdAt: new Date(),
|
|
202
|
+
updatedAt: new Date(),
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 2,
|
|
206
|
+
name: 'user:create',
|
|
207
|
+
description: '创建用户',
|
|
208
|
+
resource: 'user',
|
|
209
|
+
action: 'create',
|
|
210
|
+
createdAt: new Date(),
|
|
211
|
+
updatedAt: new Date(),
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue(mockPermissions);
|
|
216
|
+
|
|
217
|
+
const result = await permissionService.hasAllPermissions(1, ['user:view', 'user:create']);
|
|
218
|
+
|
|
219
|
+
expect(result).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('当用户缺少任一权限时应该返回 false', async () => {
|
|
223
|
+
const mockPermissions: Permission[] = [
|
|
224
|
+
{
|
|
225
|
+
id: 1,
|
|
226
|
+
name: 'user:view',
|
|
227
|
+
description: '查看用户',
|
|
228
|
+
resource: 'user',
|
|
229
|
+
action: 'view',
|
|
230
|
+
createdAt: new Date(),
|
|
231
|
+
updatedAt: new Date(),
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
(permissionService.getUserPermissions as jest.Mock).mockResolvedValue(mockPermissions);
|
|
236
|
+
|
|
237
|
+
const result = await permissionService.hasAllPermissions(1, ['user:view', 'user:delete']);
|
|
238
|
+
|
|
239
|
+
expect(result).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('findById', () => {
|
|
244
|
+
it('应该根据 ID 找到权限', async () => {
|
|
245
|
+
const mockPermission: Permission = {
|
|
246
|
+
id: 1,
|
|
247
|
+
name: 'user:view',
|
|
248
|
+
description: '查看用户',
|
|
249
|
+
resource: 'user',
|
|
250
|
+
action: 'view',
|
|
251
|
+
createdAt: new Date(),
|
|
252
|
+
updatedAt: new Date(),
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock).mockResolvedValue(mockPermission);
|
|
256
|
+
|
|
257
|
+
const result = await permissionService.findById(1);
|
|
258
|
+
|
|
259
|
+
expect(result).toEqual(mockPermission);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('当权限不存在时应该返回 null', async () => {
|
|
263
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock).mockResolvedValue(null);
|
|
264
|
+
|
|
265
|
+
const result = await permissionService.findById(999);
|
|
266
|
+
|
|
267
|
+
expect(result).toBeNull();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('findAll', () => {
|
|
272
|
+
it('应该获取所有权限', async () => {
|
|
273
|
+
const mockPermissions: Permission[] = [
|
|
274
|
+
{
|
|
275
|
+
id: 1,
|
|
276
|
+
name: 'user:view',
|
|
277
|
+
description: '查看用户',
|
|
278
|
+
resource: 'user',
|
|
279
|
+
action: 'view',
|
|
280
|
+
createdAt: new Date(),
|
|
281
|
+
updatedAt: new Date(),
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: 2,
|
|
285
|
+
name: 'user:create',
|
|
286
|
+
description: '创建用户',
|
|
287
|
+
resource: 'user',
|
|
288
|
+
action: 'create',
|
|
289
|
+
createdAt: new Date(),
|
|
290
|
+
updatedAt: new Date(),
|
|
291
|
+
},
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
((permissionService as any).permissionMapper.selectList as jest.Mock).mockResolvedValue(mockPermissions);
|
|
295
|
+
|
|
296
|
+
const result = await permissionService.findAll();
|
|
297
|
+
|
|
298
|
+
expect(result).toHaveLength(2);
|
|
299
|
+
expect(result[0].name).toBe('user:view');
|
|
300
|
+
expect(result[1].name).toBe('user:create');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('create', () => {
|
|
305
|
+
it('应该成功创建新权限', async () => {
|
|
306
|
+
const permissionData = {
|
|
307
|
+
name: 'user:delete',
|
|
308
|
+
description: '删除用户',
|
|
309
|
+
resource: 'user',
|
|
310
|
+
action: 'delete',
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const mockCreatedPermission: Permission = {
|
|
314
|
+
id: 3,
|
|
315
|
+
name: 'user:delete',
|
|
316
|
+
description: '删除用户',
|
|
317
|
+
resource: 'user',
|
|
318
|
+
action: 'delete',
|
|
319
|
+
createdAt: new Date(),
|
|
320
|
+
updatedAt: new Date(),
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
((permissionService as any).permissionMapper.insert as jest.Mock).mockResolvedValue(3);
|
|
324
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock).mockResolvedValue(mockCreatedPermission);
|
|
325
|
+
|
|
326
|
+
const result = await permissionService.create(permissionData);
|
|
327
|
+
|
|
328
|
+
expect(result).toEqual(mockCreatedPermission);
|
|
329
|
+
expect(((permissionService as any).permissionMapper.insert as jest.Mock)).toHaveBeenCalled();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('update', () => {
|
|
334
|
+
it('应该成功更新权限', async () => {
|
|
335
|
+
const updateData = {
|
|
336
|
+
description: '更新后的描述',
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const mockUpdatedPermission: Permission = {
|
|
340
|
+
id: 1,
|
|
341
|
+
name: 'user:view',
|
|
342
|
+
description: '更新后的描述',
|
|
343
|
+
resource: 'user',
|
|
344
|
+
action: 'view',
|
|
345
|
+
createdAt: new Date(),
|
|
346
|
+
updatedAt: new Date(),
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
((permissionService as any).permissionMapper.updateById as jest.Mock).mockResolvedValue(1);
|
|
350
|
+
((permissionService as any).permissionMapper.selectById as jest.Mock).mockResolvedValue(mockUpdatedPermission);
|
|
351
|
+
|
|
352
|
+
const result = await permissionService.update(1, updateData);
|
|
353
|
+
|
|
354
|
+
expect(result).toEqual(mockUpdatedPermission);
|
|
355
|
+
expect(((permissionService as any).permissionMapper.updateById as jest.Mock)).toHaveBeenCalledWith(1, expect.any(Object));
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe('delete', () => {
|
|
360
|
+
it('应该成功删除权限', async () => {
|
|
361
|
+
((permissionService as any).permissionMapper.deleteById as jest.Mock).mockResolvedValue(1);
|
|
362
|
+
|
|
363
|
+
const result = await permissionService.delete(1);
|
|
364
|
+
|
|
365
|
+
expect(result).toBe(true);
|
|
366
|
+
expect(((permissionService as any).permissionMapper.deleteById as jest.Mock)).toHaveBeenCalledWith(1);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('当权限不存在时应该返回 false', async () => {
|
|
370
|
+
((permissionService as any).permissionMapper.deleteById as jest.Mock).mockResolvedValue(0);
|
|
371
|
+
|
|
372
|
+
const result = await permissionService.delete(999);
|
|
373
|
+
|
|
374
|
+
expect(result).toBe(false);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|