286 lines
8.3 KiB
C++
286 lines
8.3 KiB
C++
#include "app.hpp"
|
||
#include <cassert>
|
||
#include <iostream>
|
||
#include <cstring>
|
||
|
||
// ============================================================
|
||
// 单元测试(使用标准库 assert,无外部依赖)
|
||
// ============================================================
|
||
|
||
static void testEmployeeManagement() {
|
||
AttendanceService svc;
|
||
|
||
// 添加员工
|
||
Employee e;
|
||
e.employeeId = "T001";
|
||
e.name = "测试员工";
|
||
e.department = "测试部";
|
||
e.position = "测试工程师";
|
||
e.shiftId = "S001";
|
||
e.phone = "13900001111";
|
||
e.role = RoleType::Employee;
|
||
svc.addEmployee(e);
|
||
|
||
// 查找
|
||
const Employee* found = svc.findEmployee("T001");
|
||
assert(found != nullptr);
|
||
assert(found->name == "测试员工");
|
||
assert(found->department == "测试部");
|
||
|
||
// 脱敏手机号
|
||
std::string masked = found->maskedPhone();
|
||
assert(masked == "*******1111");
|
||
std::cout << "[PASS] testEmployeeManagement: 员工管理正常" << std::endl;
|
||
}
|
||
|
||
static void testDuplicateEmployee() {
|
||
AttendanceService svc;
|
||
Employee e;
|
||
e.employeeId = "T002";
|
||
e.name = "重复测试";
|
||
svc.addEmployee(e);
|
||
|
||
bool thrown = false;
|
||
try {
|
||
svc.addEmployee(e);
|
||
} catch (const std::runtime_error&) {
|
||
thrown = true;
|
||
}
|
||
assert(thrown);
|
||
std::cout << "[PASS] testDuplicateEmployee: 重复添加异常正常" << std::endl;
|
||
}
|
||
|
||
static void testCheckInAndRecords() {
|
||
AttendanceService svc;
|
||
|
||
Employee e;
|
||
e.employeeId = "T003";
|
||
e.name = "打卡测试";
|
||
e.department = "测试部";
|
||
e.shiftId = "S001";
|
||
svc.addEmployee(e);
|
||
|
||
// 上班打卡
|
||
std::time_t now = std::time(nullptr);
|
||
auto rec1 = svc.processCheckIn("T003", CheckType::ClockIn, "DEVICE_TEST", now);
|
||
assert(rec1.employeeId == "T003");
|
||
assert(rec1.type == CheckType::ClockIn);
|
||
assert(rec1.verified == true);
|
||
assert(!rec1.recordId.empty());
|
||
|
||
// 下班打卡
|
||
auto rec2 = svc.processCheckIn("T003", CheckType::ClockOut, "DEVICE_TEST", now + 28800); // 8h后
|
||
assert(rec2.type == CheckType::ClockOut);
|
||
|
||
// 查询记录
|
||
auto records = svc.getRecords("T003");
|
||
assert(records.size() == 2);
|
||
assert(records[0].type == CheckType::ClockIn);
|
||
assert(records[1].type == CheckType::ClockOut);
|
||
|
||
// 未知员工打卡应抛异常
|
||
bool thrown = false;
|
||
try {
|
||
svc.processCheckIn("UNKNOWN", CheckType::ClockIn, "DEVICE", now);
|
||
} catch (const std::runtime_error&) {
|
||
thrown = true;
|
||
}
|
||
assert(thrown);
|
||
|
||
std::cout << "[PASS] testCheckInAndRecords: 打卡流程正常" << std::endl;
|
||
}
|
||
|
||
static void testDailyCalculation() {
|
||
AttendanceService svc;
|
||
|
||
Employee e;
|
||
e.employeeId = "T004";
|
||
e.name = "核算测试";
|
||
e.department = "测试部";
|
||
e.shiftId = "S001";
|
||
svc.addEmployee(e);
|
||
|
||
// 模拟当日打卡:8:30 上班,18:00 下班
|
||
std::time_t now = std::time(nullptr);
|
||
std::tm* tm = std::localtime(&now);
|
||
std::tm tm_in = *tm;
|
||
tm_in.tm_hour = 8;
|
||
tm_in.tm_min = 30;
|
||
tm_in.tm_sec = 0;
|
||
std::time_t tIn = std::mktime(&tm_in);
|
||
|
||
std::tm tm_out = *tm;
|
||
tm_out.tm_hour = 18;
|
||
tm_out.tm_min = 0;
|
||
tm_out.tm_sec = 0;
|
||
std::time_t tOut = std::mktime(&tm_out);
|
||
|
||
svc.processCheckIn("T004", CheckType::ClockIn, "DEVICE", tIn);
|
||
svc.processCheckIn("T004", CheckType::ClockOut, "DEVICE", tOut);
|
||
|
||
// 获取核算结果
|
||
std::ostringstream oss;
|
||
oss << std::put_time(tm, "%Y-%m-%d");
|
||
std::string today = oss.str();
|
||
|
||
auto da = svc.calculateDaily("T004", today);
|
||
assert(!da.dateStr.empty());
|
||
assert(da.employeeId == "T004");
|
||
assert(da.expectedMinutes > 0);
|
||
assert(da.actualMinutes > 0);
|
||
|
||
// 实际工时应约为 8.5h - 1h(午休) = 7.5h = 450min,有弹性
|
||
// 但至少大于0
|
||
assert(da.actualMinutes > 100);
|
||
std::cout << "[PASS] testDailyCalculation: 核算正常, 出勤 " << da.actualMinutes << " 分钟" << std::endl;
|
||
}
|
||
|
||
static void testAbnormalDetection() {
|
||
AttendanceService svc;
|
||
|
||
Employee e;
|
||
e.employeeId = "T005";
|
||
e.name = "异常测试";
|
||
e.department = "测试部";
|
||
e.shiftId = "S001";
|
||
svc.addEmployee(e);
|
||
|
||
// 没有打卡记录 -> 旷工
|
||
std::time_t now = std::time(nullptr);
|
||
std::tm* tm = std::localtime(&now);
|
||
std::ostringstream oss;
|
||
oss << std::put_time(tm, "%Y-%m-%d");
|
||
std::string today = oss.str();
|
||
|
||
auto da = svc.calculateDaily("T005", today);
|
||
assert(da.abnormal == AbnormalFlag::Absenteeism);
|
||
std::cout << "[PASS] testAbnormalDetection: 无打卡=旷工判定正常" << std::endl;
|
||
}
|
||
|
||
static void testReportGeneration() {
|
||
AttendanceService svc;
|
||
|
||
// 添加员工
|
||
Employee e1;
|
||
e1.employeeId = "T006";
|
||
e1.name = "报表A";
|
||
e1.department = "报表部";
|
||
svc.addEmployee(e1);
|
||
|
||
Employee e2;
|
||
e2.employeeId = "T007";
|
||
e2.name = "报表B";
|
||
e2.department = "报表部";
|
||
svc.addEmployee(e2);
|
||
|
||
// 添加打卡
|
||
std::time_t now = std::time(nullptr);
|
||
std::tm* tm = std::localtime(&now);
|
||
std::tm tm_in = *tm;
|
||
tm_in.tm_hour = 9;
|
||
tm_in.tm_min = 0;
|
||
std::time_t tIn = std::mktime(&tm_in);
|
||
|
||
std::tm tm_out = *tm;
|
||
tm_out.tm_hour = 18;
|
||
tm_out.tm_min = 0;
|
||
std::time_t tOut = std::mktime(&tm_out);
|
||
|
||
svc.processCheckIn("T006", CheckType::ClockIn, "DEVICE", tIn);
|
||
svc.processCheckIn("T006", CheckType::ClockOut, "DEVICE", tOut);
|
||
|
||
svc.processCheckIn("T007", CheckType::ClockIn, "DEVICE", tIn);
|
||
svc.processCheckIn("T007", CheckType::ClockOut, "DEVICE", tOut);
|
||
|
||
// 生成报表
|
||
std::ostringstream oss;
|
||
oss << std::put_time(tm, "%Y-%m-%d");
|
||
std::string today = oss.str();
|
||
|
||
auto report = svc.generateReport("报表部", today, today);
|
||
assert(!report.empty());
|
||
assert(report.size() == 2);
|
||
std::cout << "[PASS] testReportGeneration: 报表生成正常, 共 " << report.size() << " 条记录" << std::endl;
|
||
}
|
||
|
||
static void testAlertCreation() {
|
||
AttendanceService svc;
|
||
|
||
// 创建预警
|
||
auto alert = svc.createAlert("T999", AbnormalFlag::Late);
|
||
assert(!alert.alertId.empty());
|
||
assert(alert.employeeId == "T999");
|
||
assert(alert.type == AbnormalFlag::Late);
|
||
assert(alert.status == AlertStatus::Pending);
|
||
|
||
// 查询
|
||
auto alerts = svc.getAlerts("T999");
|
||
assert(alerts.size() == 1);
|
||
assert(alerts[0].type == AbnormalFlag::Late);
|
||
|
||
std::cout << "[PASS] testAlertCreation: 预警创建与查询正常" << std::endl;
|
||
}
|
||
|
||
static void testReportTask() {
|
||
AttendanceService svc;
|
||
|
||
auto task = svc.createReportTask("ADMIN", "部门X", "2025-01-01", "2025-01-31", ReportFormat::PDF);
|
||
assert(task.taskId.length() > 0);
|
||
assert(task.status == "Pending");
|
||
assert(task.format == ReportFormat::PDF);
|
||
|
||
svc.completeTask(task.taskId, "/download/test.pdf");
|
||
assert(task.status == "Done");
|
||
assert(task.downloadUrl == "/download/test.pdf");
|
||
|
||
std::cout << "[PASS] testReportTask: 报表任务流程正常" << std::endl;
|
||
}
|
||
|
||
static void testMaskSensitive() {
|
||
assert(maskSensitive("12345678901", 3, 4) == "123****8901");
|
||
assert(maskSensitive("abc", 1, 1) == "abc"); // 太短时不脱敏
|
||
assert(maskSensitive("", 0, 0).empty());
|
||
std::cout << "[PASS] testMaskSensitive: 脱敏函数正常" << std::endl;
|
||
}
|
||
|
||
static void testShiftRule() {
|
||
ShiftRule rule;
|
||
rule.shiftId = "S999";
|
||
rule.startHour = 8;
|
||
rule.startMin = 30;
|
||
rule.endHour = 17;
|
||
rule.endMin = 30;
|
||
|
||
assert(rule.workStartMinutes() == 8 * 60 + 30); // 510
|
||
assert(rule.workEndMinutes() == 17 * 60 + 30); // 1050
|
||
|
||
std::cout << "[PASS] testShiftRule: 排班规则计算正常" << std::endl;
|
||
}
|
||
|
||
// ============================================================
|
||
// 主测试入口
|
||
// ============================================================
|
||
|
||
int main() {
|
||
std::cout << "============================================" << std::endl;
|
||
std::cout << " 智能考勤管理系统 - 单元测试" << std::endl;
|
||
std::cout << "============================================" << std::endl;
|
||
|
||
testEmployeeManagement();
|
||
testDuplicateEmployee();
|
||
testCheckInAndRecords();
|
||
testDailyCalculation();
|
||
testAbnormalDetection();
|
||
testReportGeneration();
|
||
testAlertCreation();
|
||
testReportTask();
|
||
testMaskSensitive();
|
||
testShiftRule();
|
||
|
||
std::cout << "============================================" << std::endl;
|
||
std::cout << "所有测试通过!" << std::endl;
|
||
std::cout << "============================================" << std::endl;
|
||
|
||
return 0;
|
||
}
|