import { describe, it, expect, vi } from "vitest"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { mount } from "@vue/test-utils"; import PermissionMonitoring from "@/components/PermissionMonitoring.vue"; vi.mock("@/api/projectPermissions", () => ({ fetchPermissionMetrics: vi.fn().mockResolvedValue({ data: { check_metrics: { total_checks: 1000, allowed_checks: 950, denied_checks: 50, total_time: 5.234, min_time: 0.001, max_time: 0.05, avg_time: 0.005, allow_rate: 95.0, deny_rate: 5.0, error_rate: 0.1, errors: 1, }, cache_metrics: { total_accesses: 1000, cache_hits: 850, cache_misses: 150, cache_invalidations: 5, hit_rate: 85.0, miss_rate: 15.0, window_seconds: 3600, scope: "sliding_window", by_cache: { project_permissions: { total_accesses: 800, cache_hits: 700, cache_misses: 100, cache_invalidations: 5, hit_rate: 87.5, miss_rate: 12.5, }, member_roles: { total_accesses: 200, cache_hits: 150, cache_misses: 50, cache_invalidations: 5, hit_rate: 75, miss_rate: 25, }, }, }, uptime_seconds: 3600, }, }), fetchPermissionHealth: vi.fn().mockResolvedValue({ data: { status: "healthy", health_score: 95, issues: [], sample_sufficient: true, score_breakdown: { performance: { weight: 25, deduction: 0 }, deny_rate: { weight: 20, deduction: 0 }, cache: { weight: 25, deduction: 5, sample_sufficient: true, scope: "sliding_window", window_seconds: 3600, }, alerts: { weight: 10, deduction: 0 }, runtime: { weight: 20, deduction: 0 }, }, last_hour: { total_checks: 30, denied_checks: 2, deny_rate: 6.67, avg_elapsed_ms: 3.5, error_alerts: 0, warning_alerts: 0, }, cache_stats: { cache_items: { total_count: 60 }, cache_metrics: {} }, components: { database: { status: "healthy", latency_ms: 2.1 }, permission_log_writer: { status: "healthy", running: true, queue_size: 0, queue_capacity: 10000, dropped_entries: 0 }, security_log_writer: { status: "healthy", running: true, queue_size: 0, queue_capacity: 20000, dropped_entries: 0 }, retention: { status: "healthy", running: true, access_log_retention_days: 90, metric_retention_days: 400 }, }, }, }), fetchPermissionAlerts: vi.fn().mockResolvedValue({ data: { total: 0, alerts: [] } }), fetchTopDenied: vi.fn().mockResolvedValue({ data: { items: [] } }), fetchStatsSummary: vi.fn().mockResolvedValue({ data: { total_logs: 5000, today: { total_checks: 200, allowed_checks: 190, denied_checks: 10, avg_elapsed_ms: 3.5, max_elapsed_ms: 45, allow_rate: 95, deny_rate: 5 }, last_hour: { total_checks: 30, allowed_checks: 28, denied_checks: 2, requests_per_minute: 0.5 }, }, }), fetchPermissionTrends: vi.fn().mockResolvedValue({ data: { period: "24h", data_points: [] } }), fetchAccessLogs: vi.fn().mockResolvedValue({ data: { total: 0, page: 1, page_size: 50, items: [] } }), })); describe("PermissionMonitoring.vue", () => { it("renders monitoring dashboard with tabs", () => { const wrapper = mount(PermissionMonitoring, { global: { stubs: { ElTabs: false, ElTabPane: false, PermissionTrendCharts: true, PermissionAccessLogs: true, PermissionIpLocations: true, SecurityCenter: true, }, }, }); expect(wrapper.find(".permission-monitoring").exists()).toBe(true); }); it("exposes refresh method", () => { const wrapper = mount(PermissionMonitoring, { global: { stubs: { ElTabs: true, ElTabPane: true, PermissionTrendCharts: true, PermissionAccessLogs: true, PermissionIpLocations: true, SecurityCenter: true, }, }, }); expect(typeof wrapper.vm.refresh).toBe("function"); }); it("keeps security events in security center instead of access logs", () => { const source = readFileSync(resolve(__dirname, "./PermissionMonitoring.vue"), "utf8"); expect(source).toContain("isAdmin?: boolean"); expect(source).toContain(''); expect(source).not.toContain(':show-security-log="props.isAdmin"'); }); it("uses a full-height source-analysis tab without forcing scroll on other tabs", () => { const source = readFileSync(resolve(__dirname, "./PermissionMonitoring.vue"), "utf8"); expect(source).toContain(":class=\"{ 'is-source-tab': activeTab === 'ip-locations' }\""); expect(source).toContain('class="ip-locations-pane"'); expect(source).toContain(".permission-monitoring.is-source-tab .soybean-monitoring-tabs :deep(.el-tabs__content)"); expect(source).toContain("overflow: hidden"); expect(source).toContain(".soybean-monitoring-tabs :deep(.el-tabs__content)"); expect(source).toContain("flex: 1 1 0;"); expect(source).toContain("height: auto;"); expect(source).toContain("overflow-y: auto;"); expect(source).toContain('height: auto;\n min-height: 100%;'); expect(source).toContain('.permission-monitoring.is-source-tab .soybean-monitoring-tabs :deep(.el-tab-pane[aria-hidden="false"])'); expect(source).toContain("flex: 0 0 52px;"); expect(source).toContain("overscroll-behavior: contain;"); }); it("resizes the source-analysis chart when its tab becomes active", () => { const source = readFileSync(resolve(__dirname, "./PermissionMonitoring.vue"), "utf8"); expect(source).toContain("onUnmounted"); expect(source).toContain("type IpLocationsExpose"); expect(source).toContain("resize: () => void | Promise"); expect(source).toContain('if (tab === "ip-locations")'); expect(source).toContain("await nextTick()"); expect(source).toContain("await ipLocationsRef.value?.resize()"); }); it("uses system monitoring tab names and operation-oriented overview copy", () => { const source = readFileSync(resolve(__dirname, "./PermissionMonitoring.vue"), "utf8"); expect(source).toContain('label="运行概览"'); expect(source).toContain('label="安全中心"'); expect(source).toContain('label="运行趋势"'); expect(source).toContain('label="访问日志"'); expect(source).toContain('label="来源分析"'); expect(source).toContain('label="运行趋势" name="trends" lazy'); expect(source).toContain('@drilldown="openTrendDrilldown"'); expect(source).toContain('accessLogsRef.value?.applyTrendFilter(payload)'); expect(source).toContain('label="访问日志" name="logs" lazy'); expect(source).toContain('label="来源分析" name="ip-locations" lazy'); expect(source).toContain('综合状态'); expect(source).toContain('class="health-signal-item"'); expect(source).toContain('item.deduction ? `-${item.deduction}` : "正常"'); expect(source).toContain('class="overview-pane"'); expect(source).toContain('class="overview-hero-card"'); expect(source).toContain('class="operations-card"'); expect(source).toContain('class="operations-grid"'); expect(source).toContain('class="alerts-section"'); expect(source).toContain("grid-template-columns: minmax(210px, 0.85fr)"); expect(source).toContain('class="status-section runtime-section"'); expect(source).toContain('class="empty-alert-state"'); expect(source).toContain('const hasCacheSamples = computed'); expect(source).toContain("异常拒绝排行"); expect(source).toContain("告警数据加载失败,不能据此判断系统正常"); expect(source).toContain("const formatUpdatedTime"); expect(source).toContain("更新 {{ formatUpdatedTime(lastUpdatedAt) }}"); expect(source).toContain("runtimeComponentItems"); expect(source).not.toContain('label="实时概览"'); expect(source).not.toContain('label="趋势分析"'); expect(source).not.toContain('label="访问审计"'); expect(source).not.toContain('label="IP属地"'); expect(source).not.toContain('label: "今日总检查"'); expect(source).not.toContain('label: "每分钟请求"'); expect(source).not.toContain('label: "今日允许率"'); expect(source).not.toContain('label: "历史总记录"'); expect(source).not.toContain('label: "历史事件总数"'); expect(source).not.toContain('class="summary-card"'); expect(source).not.toContain('class="overview-toolbar"'); expect(source).not.toContain("被拒绝最多的权限"); expect(source).not.toContain("暂无告警,系统运行正常"); }); });