172 lines
8.5 KiB
TypeScript
172 lines
8.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
const readSource = () => readFileSync(resolve(__dirname, "./SecurityCenter.vue"), "utf8");
|
|
|
|
describe("SecurityCenter", () => {
|
|
it("renders a compact security analysis summary in the same audit-card style as access logs", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).toContain("audit-filters");
|
|
expect(source).toContain("audit-metrics");
|
|
expect(source).toContain("metric-card");
|
|
expect(source).toContain("security-analysis-card");
|
|
expect(source).toContain("risk-preview-list");
|
|
expect(source).toContain("risk-summary-panel");
|
|
expect(source).toContain("risk-summary-title");
|
|
expect(source).toContain("topRiskPreview");
|
|
expect(source).toContain('class="security-overview"');
|
|
expect(source).toContain('aria-label="安全指标"');
|
|
expect(source).toContain('class="security-toolbar"');
|
|
expect(source).toContain("timeRangePreset");
|
|
expect(source).toContain("timeRangeOptions");
|
|
expect(source).toContain("buildTimeRangeFromPreset");
|
|
expect(source).toContain("onTimeRangeChange");
|
|
expect(source).not.toContain("audit-grid");
|
|
expect(source).not.toContain("ranking-card");
|
|
expect(source).not.toContain("security-summary");
|
|
expect(source).not.toContain("security-card");
|
|
});
|
|
|
|
it("places filters inside the event details section instead of the page header", () => {
|
|
const source = readSource();
|
|
const tableIndex = source.indexOf('<section ref="securityEventsSectionRef" class="security-table-wrap">');
|
|
const filtersIndex = source.indexOf('<section class="audit-filters detail-filters">');
|
|
|
|
expect(tableIndex).toBeGreaterThan(-1);
|
|
expect(filtersIndex).toBeGreaterThan(tableIndex);
|
|
expect(source).not.toContain('<section class="audit-filters">\n <el-select');
|
|
});
|
|
|
|
it("keeps risk levels on the page and opens abnormal sources in a compact ranking dialog", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).toContain("异常分析");
|
|
expect(source).toContain("查看异常分析");
|
|
expect(source).toContain("analysisDialogVisible");
|
|
expect(source).toContain("openAnalysisDialog");
|
|
expect(source).toContain("jumpToSecurityEvents");
|
|
expect(source).toContain("securityEventsSectionRef");
|
|
expect(source).toContain("scrollIntoView");
|
|
expect(source).toContain('<el-dialog v-model="analysisDialogVisible"');
|
|
expect(source).toContain('width="min(1180px, 96vw)"');
|
|
expect(source).toContain("analysis-ranking-dialog");
|
|
expect(source).toContain("analysis-ranking-head");
|
|
expect(source).toContain("endpoint-summary-chips");
|
|
expect(source).toContain("analysis-rank-grid");
|
|
expect(source).toContain("analysis-rank-card");
|
|
expect(source).toContain("analysis-rank-card-stats");
|
|
expect(source).toContain("查看事件明细");
|
|
expect(source).not.toContain('<section class="analysis-dialog-panel');
|
|
expect(source).toContain("风险等级");
|
|
expect(source).toContain("ipRiskStats");
|
|
expect(source).toContain("endpointTypeStats");
|
|
expect(source).toContain("riskLevelStats");
|
|
expect(source.match(/v-for="item in riskLevelStats"/g)).toHaveLength(1);
|
|
expect(source).not.toContain("compact-risk-section");
|
|
expect(source).toContain("resolveEndpointType");
|
|
expect(source).toContain("formatIpLocation");
|
|
expect(source).toContain("fallbackIpLocation");
|
|
expect(source).toContain("row.ip_province");
|
|
expect(source).toContain("row.ip_city");
|
|
expect(source).toContain("row.ip_isp");
|
|
expect(source).toContain("item.location");
|
|
expect(source).toContain("categoryText");
|
|
expect(source).toContain("analysis-rank-card-ip");
|
|
expect(source).toContain("analysis-rank-card-meta");
|
|
expect(source).toContain("lastSeenAt");
|
|
expect(source).toContain("最后访问");
|
|
expect(source).toContain("securitySummary.value.top_ips");
|
|
expect(source).toContain("securitySummary.value.endpoint_type_counts");
|
|
expect(source).toContain("securitySummary.value.severity_counts");
|
|
expect(source).not.toContain("公网位置待解析");
|
|
expect(source).not.toContain("异常IP(位置)排行");
|
|
expect(source).not.toContain("risk-level-card");
|
|
});
|
|
|
|
it("keeps detailed security events searchable and inspectable", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).toContain("securityQueryParams");
|
|
expect(source).toContain("keyword: keyword.value.trim() || undefined");
|
|
expect(source).toContain("paginatedEvents");
|
|
expect(source).toContain("openDetail");
|
|
expect(source).toContain("security-table-wrap");
|
|
expect(source).toContain('label="IP"');
|
|
expect(source).toContain('label="位置"');
|
|
expect(source).not.toContain('label="来源 IP"');
|
|
expect(source).not.toContain('label="IP来源"');
|
|
expect(source).toContain("搜索 IP、位置、账号、路径或 UA");
|
|
expect(source).toContain("loadSecurityEvents");
|
|
expect(source).toContain("安全事件加载失败,页面中可能是上次成功获取的数据");
|
|
expect(source).toContain("lastUpdatedAt");
|
|
expect(source).toContain("安全事件详情");
|
|
expect(source).toContain("请求概览");
|
|
expect(source).toContain("原始 HTTP 请求");
|
|
expect(source).toContain("User-Agent");
|
|
expect(source).toContain("构建信息");
|
|
expect(source).toContain("更多审计信息");
|
|
expect(source).toContain("日志标识");
|
|
expect(source).toContain("传输信息");
|
|
expect(source).toContain("客户端标识");
|
|
expect(source).toContain("securityRequestOverviewFields(selectedEvent)");
|
|
expect(source).toContain("securityAuditMetadataFields(selectedEvent)");
|
|
expect(source).toContain("securityRequestSnapshotFields(selectedEvent)");
|
|
expect(source).toContain("buildSecurityRawRequestBlock(selectedEvent)");
|
|
expect(source).toContain("securityClientSourceLabel(selectedEvent)");
|
|
expect(source).toContain("历史日志未采集原始请求快照");
|
|
expect(source).toContain("历史日志未采集原始 HTTP 请求");
|
|
expect(source).not.toContain("securityDetailFieldItems(selectedEvent)");
|
|
expect(source).not.toContain("<h4>接口</h4>");
|
|
expect(source).not.toContain("<h4>访问字段</h4>");
|
|
expect(source).not.toContain("<h4>请求快照</h4>");
|
|
expect(source).not.toContain("原始请求快照(脱敏)");
|
|
expect(source).not.toContain("请求头(脱敏)");
|
|
expect(source).not.toContain("未记录请求头");
|
|
});
|
|
|
|
it("opens full security logs as a dialog inside security center", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).toContain("安全日志");
|
|
expect(source).toContain("securityLogDialogVisible");
|
|
expect(source).toContain("securityLogDialogLoading");
|
|
expect(source).toContain("securityTerminalWindowRef");
|
|
expect(source).toContain("securityTerminalLines");
|
|
expect(source).toContain("buildSecurityTerminalLine");
|
|
expect(source).toContain("openSecurityLogDialog");
|
|
expect(source).toContain("scrollSecurityTerminalToBottom");
|
|
expect(source).toContain("downloadSecurityLog");
|
|
expect(source).toContain("saveFileWithFeedback");
|
|
expect(source).toContain("securityRequestTarget(event)");
|
|
expect(source).toContain("<el-dialog v-model=\"securityLogDialogVisible\"");
|
|
expect(source).toContain("securityTerminalLines.join");
|
|
expect(source).toContain("security-access-logs.log");
|
|
expect(source).toContain("terminal-window dialog-terminal-window");
|
|
});
|
|
|
|
it("labels abnormal IP events and paginates details like account management", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).not.toContain('value: "ABNORMAL_IP"');
|
|
expect(source).toContain('label: "风险来源 IP"');
|
|
expect(source).toContain('v-model:current-page="page"');
|
|
expect(source).toContain('v-model:page-size="pageSize"');
|
|
expect(source).toContain(':page-sizes="[10, 20, 50]"');
|
|
expect(source).toContain('layout="prev, pager, next, sizes, total"');
|
|
});
|
|
|
|
it("uses server-side pagination and aggregate facets instead of loading every event", () => {
|
|
const source = readSource();
|
|
|
|
expect(source).toContain("statsEvents");
|
|
expect(source).toContain("securitySummary");
|
|
expect(source).toContain("loadSecurityStats");
|
|
expect(source).toContain("response.data.summary");
|
|
expect(source).toContain("@current-change=\"loadSecurityEvents\"");
|
|
expect(source).not.toContain("for (let nextPage = 2; nextPage <= totalPages; nextPage += 1) {\n const res = await fetchSecurityAccessLogs");
|
|
expect(source).toContain("fetchAllSecurityEvents");
|
|
});
|
|
});
|