import { describe, expect, it } from "vitest"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; const readSource = () => readFileSync(resolve(__dirname, "./PermissionAccessLogs.vue"), "utf8"); describe("PermissionAccessLogs", () => { it("prefers province, city and ISP for IP location display", () => { const source = readSource(); expect(source).toContain("formatIpLocation(row)"); expect(source).toContain("row.ip_province"); expect(source).toContain("row.ip_city"); expect(source).toContain("row.ip_isp"); expect(source).toContain('parts.join(" / ")'); }); it("renders user behavior audit instead of endpoint table or region detail", () => { const source = readSource(); expect(source).toContain("IP访问排行"); expect(source).toContain("接口访问审计日志"); expect(source).toContain("terminal-lines"); expect(source).toContain("keyword"); expect(source).not.toContain("终端式访问流水"); expect(source).not.toContain("每行保留接口访问情况,便于快速扫日志"); expect(source).not.toContain("来源地域明细"); expect(source).not.toContain('prop="endpoint_key" label="接口"'); expect(source).not.toContain("fetchIpLocations"); }); it("exposes QA and CTA in role filter presets", () => { const source = readSource(); expect(source).toContain('const ROLE_FILTER_KEYS = ["ADMIN", "PM", "CRA", "PV", "QA", "CTA"];'); }); it("uses permission template names for role filters and log lines", () => { const source = readSource(); expect(source).toContain("useRoleTemplateMeta"); expect(source).toContain("const { roleLabel, roleOptionsFor, loadRoleTemplates } = useRoleTemplateMeta();"); expect(source).toContain("const roleFilterOptions = computed(() => roleOptionsFor(ROLE_FILTER_KEYS));"); expect(source).toContain('v-for="role in roleFilterOptions"'); expect(source).toContain("role=${roleLabel(row.role)}"); expect(source).not.toContain("const ROLE_LABELS"); expect(source).not.toContain('label="医学审核" value="QA"'); }); it("uses narrow metric cards without helper subtitles", () => { const source = readSource(); expect(source).toContain("min-height: 64px"); expect(source).toContain("padding: 10px 16px"); expect(source).not.toContain("{{ card.hint }}"); expect(source).not.toContain("hint:"); expect(source).not.toContain("筛选范围内行为总量"); }); it("keeps audit grid widths consistent with SecurityCenter", () => { const source = readSource(); expect(source).toContain("grid-template-columns: minmax(320px, 1fr) minmax(0, 1.35fr);"); expect(source).toContain("@media (max-width: 1100px)"); expect(source).toContain("grid-template-columns: repeat(2, minmax(0, 1fr));"); expect(source).toContain("@media (max-width: 720px)"); expect(source).toContain("grid-template-columns: 1fr;"); }); it("removes the duplicated audit hero copy", () => { const source = readSource(); expect(source).toContain("audit-filters"); expect(source).not.toContain("audit-hero"); expect(source).not.toContain("权限监控 / 访问日志"); expect(source).not.toContain("用户行为审计"); expect(source).not.toContain("按用户行为聚合访问记录,接口访问细节保留在终端式流水中"); expect(source).not.toContain("查看详细的接口访问与安全事件记录"); }); it("streams terminal logs from top to bottom and refreshes in realtime", () => { const source = readSource(); expect(source).toContain("terminalLogRows"); expect(source).toContain("new Date(a.created_at).getTime() - new Date(b.created_at).getTime()"); expect(source).toContain("REALTIME_POLL_INTERVAL_MS"); expect(source).toContain("startRealtimePolling"); expect(source).toContain("onBeforeUnmount(stopRealtimePolling)"); expect(source).toContain('ref="terminalWindowRef"'); expect(source).toContain("scrollTerminalToBottom"); expect(source).toContain("terminal.scrollTop = terminal.scrollHeight"); }); it("surfaces source IPs in terminal logs and user ranking for network monitoring", () => { const source = readSource(); expect(source).toContain("ip=${ip}"); expect(source).toContain("user.sample_ip_address || \"未知 IP\""); expect(source).toContain("ipRankingItems"); expect(source).toContain("IP访问排行"); expect(source).toContain("账号"); expect(source).toContain("rank-location"); expect(source).not.toContain("TOP {{ userStats.length }}"); expect(source).not.toContain("rank-ip-line"); expect(source).not.toContain("来源IP"); expect(source).not.toContain("去重IP"); }); it("merges abnormal security IPs into the IP ranking and limits the list to top 10", () => { const source = readSource(); expect(source).toContain("const IP_RANKING_LIMIT = 10;"); expect(source).toContain("const SECURITY_RANKING_PAGE_SIZE = 200;"); expect(source).toContain("const ipRankingItems = computed(() =>"); expect(source).toContain("securityLogs.value.forEach((event) =>"); expect(source).toContain('const isAbnormal = event.category === "ABNORMAL_IP";'); expect(source).toContain('ABNORMAL_IP: "异常IP"'); expect(source).toContain(".slice(0, IP_RANKING_LIMIT)"); expect(source).toContain("TOP {{ ipRankingItems.length }}"); expect(source).toContain('v-for="(item, index) in ipRankingItems"'); expect(source).toContain("异常IP"); }); it("sorts IP ranking by overall access total before risk labels", () => { const source = readSource(); expect(source).toContain("existing.total += user.total_count;"); expect(source).toContain("existing.riskCount += user.denied_count;"); expect(source).toContain("if (b.total !== a.total) return b.total - a.total;"); expect(source.indexOf("if (b.total !== a.total) return b.total - a.total;")).toBeLessThan( source.indexOf("if (b.riskCount !== a.riskCount) return b.riskCount - a.riskCount;"), ); expect(source.indexOf("if (b.total !== a.total) return b.total - a.total;")).toBeLessThan( source.indexOf("if (b.isAbnormal !== a.isAbnormal) return Number(b.isAbnormal) - Number(a.isAbnormal);"), ); }); it("loads all security pages for complete abnormal IP ranking data", () => { const source = readSource(); expect(source).toContain("const first = await fetchSecurityAccessLogs({ status_min: 400, page: 1, page_size: SECURITY_RANKING_PAGE_SIZE });"); expect(source).toContain("const totalPages = Math.ceil(first.data.total / SECURITY_RANKING_PAGE_SIZE);"); expect(source).toContain("for (let nextPage = 2; nextPage <= totalPages; nextPage += 1)"); expect(source).toContain("allItems.push(...res.data.items);"); expect(source).toContain("securityLogs.value = allItems;"); expect(source).not.toContain("page_size: 80"); }); it("renders low-level security access logs for anonymous and invalid requests", () => { const source = readSource(); expect(source).toContain("showSecurityLog"); expect(source).toContain('v-if="showSecurityLog"'); expect(source).toContain("fetchSecurityAccessLogs"); expect(source).toContain("安全事件审计日志"); expect(source).toContain("securityTerminalLines"); expect(source).toContain("auth_status"); expect(source).toContain("account_label"); expect(source).toContain("client_ip"); expect(source).toContain("status_code"); expect(source).toContain("ANONYMOUS"); expect(source).toContain("INVALID_TOKEN"); expect(source).not.toContain("安全访问日志"); expect(source).not.toContain("记录匿名、无效令牌、拒绝和异常状态请求,用于排查未知 IP 攻击"); }); it("shows interface audit log total instead of current page line count", () => { const source = readSource(); expect(source).toContain("{{ formatNumber(total) }} 条"); expect(source).toContain("共 {{ formatNumber(total) }} 条"); expect(source).toContain(":total=\"total\""); expect(source).not.toContain("{{ terminalLines.length }} 条"); expect(source).not.toContain("最近 {{ terminalLines.length }} 条"); }); it("opens both audit logs in realtime downloadable dialogs", () => { const source = readSource(); expect(source).toContain("interfaceLogDialogVisible"); expect(source).toContain("securityLogDialogVisible"); expect(source).toContain("openInterfaceLogDialog"); expect(source).toContain("openSecurityLogDialog"); expect(source).toContain("if (!showSecurityLog.value) return"); expect(source).toContain("downloadInterfaceLog"); expect(source).toContain("downloadSecurityLog"); expect(source).toContain("downloadLogFile"); expect(source).toContain("