215 lines
6.4 KiB
C++
215 lines
6.4 KiB
C++
/**
|
||
* @file basic_test.cpp
|
||
* @brief 基础测试文件 - 使用标准库 assert,无外部依赖
|
||
*/
|
||
|
||
#include "app.hpp"
|
||
#include <cassert>
|
||
#include <iostream>
|
||
#include <vector>
|
||
#include <string>
|
||
|
||
/// 测试枚举转换函数
|
||
static void testEnumToString() {
|
||
std::cout << "[TEST] 枚举转换函数..." << std::endl;
|
||
|
||
assert(checkinMethodToString(CheckinMethod::FACE) == "人脸识别");
|
||
assert(checkinMethodToString(CheckinMethod::GPS) == "GPS定位");
|
||
assert(checkinMethodToString(CheckinMethod::CARD) == "工牌刷卡");
|
||
|
||
assert(attendanceStatusToString(AttendanceStatus::NORMAL) == "正常");
|
||
assert(attendanceStatusToString(AttendanceStatus::LATE) == "迟到");
|
||
assert(attendanceStatusToString(AttendanceStatus::EARLY) == "早退");
|
||
assert(attendanceStatusToString(AttendanceStatus::ABSENT) == "缺勤");
|
||
assert(attendanceStatusToString(AttendanceStatus::OVERTIME) == "加班");
|
||
|
||
assert(alertTypeToString(AlertType::UNCHECKED) == "未打卡");
|
||
assert(alertTypeToString(AlertType::EARLY) == "早退预警");
|
||
assert(alertTypeToString(AlertType::LATE) == "迟到预警");
|
||
assert(alertTypeToString(AlertType::MISSING) == "连续缺勤");
|
||
|
||
std::cout << " PASS" << std::endl;
|
||
}
|
||
|
||
/// 测试记录ID生成
|
||
static void testGenerateRecordId() {
|
||
std::cout << "[TEST] recordId生成..." << std::endl;
|
||
std::string id1 = generateRecordId();
|
||
std::string id2 = generateRecordId();
|
||
|
||
assert(!id1.empty());
|
||
assert(!id2.empty());
|
||
assert(id1 != id2); // 两次生成的ID应该不同
|
||
assert(id1.substr(0, 2) == "CK");
|
||
|
||
std::cout << " PASS: id1=" << id1 << ", id2=" << id2 << std::endl;
|
||
}
|
||
|
||
/// 测试打卡处理
|
||
static void testProcessCheckin() {
|
||
std::cout << "[TEST] 打卡处理..." << std::endl;
|
||
|
||
GeoLocation loc = {39.9042, 116.4074};
|
||
std::vector<uint8_t> face = {0x01, 0x02, 0x03};
|
||
int64_t ts = 1700000000000LL; // 固定时间戳
|
||
|
||
// 人脸打卡
|
||
CheckinRecord rec1 = processCheckin("EMP001", CheckinMethod::FACE, ts, loc, face);
|
||
assert(rec1.employeeId == "EMP001");
|
||
assert(rec1.status == CheckinStatus::SUCCESS);
|
||
assert(!rec1.recordId.empty());
|
||
|
||
// GPS打卡
|
||
CheckinRecord rec2 = processCheckin("EMP002", CheckinMethod::GPS, ts, loc, {});
|
||
assert(rec2.employeeId == "EMP002");
|
||
assert(rec2.status == CheckinStatus::SUCCESS);
|
||
|
||
// 工牌刷卡
|
||
CheckinRecord rec3 = processCheckin("EMP003", CheckinMethod::CARD, ts, loc, {});
|
||
assert(rec3.employeeId == "EMP003");
|
||
assert(rec3.status == CheckinStatus::SUCCESS);
|
||
|
||
std::cout << " PASS: 三种打卡方式均成功" << std::endl;
|
||
}
|
||
|
||
/// 测试考勤计算
|
||
static void testCalculateAttendance() {
|
||
std::cout << "[TEST] 考勤计算..." << std::endl;
|
||
|
||
ShiftRule shift;
|
||
shift.shiftName = "早班";
|
||
shift.startHour = 9;
|
||
shift.startMinute = 0;
|
||
shift.endHour = 18;
|
||
shift.endMinute = 0;
|
||
shift.breakDuration = 60;
|
||
shift.overtimeThreshold = 30;
|
||
|
||
// 场景1: 准时打卡 (9:00)
|
||
{
|
||
struct tm tm_in = {};
|
||
tm_in.tm_year = 2025 - 1900;
|
||
tm_in.tm_mon = 0;
|
||
tm_in.tm_mday = 15;
|
||
tm_in.tm_hour = 9;
|
||
tm_in.tm_min = 0;
|
||
time_t sec = mktime(&tm_in);
|
||
CheckinRecord rec;
|
||
rec.employeeId = "EMP001";
|
||
rec.timestamp = static_cast<int64_t>(sec) * 1000;
|
||
|
||
AttendanceResult res = calculateAttendance(rec, shift);
|
||
assert(res.lateMinutes == 0);
|
||
assert(res.status == AttendanceStatus::NORMAL || res.status == AttendanceStatus::NORMAL);
|
||
}
|
||
|
||
// 场景2: 迟到 (9:45)
|
||
{
|
||
struct tm tm_in = {};
|
||
tm_in.tm_year = 2025 - 1900;
|
||
tm_in.tm_mon = 0;
|
||
tm_in.tm_mday = 15;
|
||
tm_in.tm_hour = 9;
|
||
tm_in.tm_min = 45;
|
||
time_t sec = mktime(&tm_in);
|
||
CheckinRecord rec;
|
||
rec.employeeId = "EMP002";
|
||
rec.timestamp = static_cast<int64_t>(sec) * 1000;
|
||
|
||
AttendanceResult res = calculateAttendance(rec, shift);
|
||
assert(res.lateMinutes > 0);
|
||
assert(res.status == AttendanceStatus::LATE);
|
||
}
|
||
|
||
std::cout << " PASS: 正常与迟到判定正确" << std::endl;
|
||
}
|
||
|
||
/// 测试异常检测
|
||
static void testDetectAnomaly() {
|
||
std::cout << "[TEST] 异常检测..." << std::endl;
|
||
|
||
// 正常情况 - 不应生成预警
|
||
{
|
||
AttendanceResult normal;
|
||
normal.employeeId = "EMP001";
|
||
normal.date = "2025-01-15";
|
||
normal.lateMinutes = 0;
|
||
normal.absent = false;
|
||
|
||
AlertNotification alert = detectAnomaly(normal);
|
||
assert(alert.alertType == AlertType::UNCHECKED);
|
||
assert(alert.reason == "正常");
|
||
}
|
||
|
||
// 迟到超过30分钟 - 应生成迟到预警
|
||
{
|
||
AttendanceResult late;
|
||
late.employeeId = "EMP002";
|
||
late.date = "2025-01-15";
|
||
late.lateMinutes = 45;
|
||
late.absent = false;
|
||
|
||
AlertNotification alert = detectAnomaly(late);
|
||
assert(alert.alertType == AlertType::LATE);
|
||
assert(!alert.notifiedChannels.empty());
|
||
}
|
||
|
||
std::cout << " PASS" << std::endl;
|
||
}
|
||
|
||
/// 测试报表生成
|
||
static void testGenerateReport() {
|
||
std::cout << "[TEST] 报表生成..." << std::endl;
|
||
|
||
ReportRequest req;
|
||
req.dimension = ReportDimension::MONTH;
|
||
req.startDate = "2025-01-01";
|
||
req.endDate = "2025-01-31";
|
||
req.format = ExportFormat::EXCEL;
|
||
|
||
ReportResponse resp = generateReport(req);
|
||
assert(resp.success);
|
||
assert(!resp.reportUrl.empty());
|
||
assert(resp.fileSize > 0);
|
||
|
||
std::cout << " PASS: URL=" << resp.reportUrl << std::endl;
|
||
}
|
||
|
||
/// 测试加密模块
|
||
static void testEncryption() {
|
||
std::cout << "[TEST] 人脸特征加密..." << std::endl;
|
||
|
||
std::vector<uint8_t> original = {0x10, 0x20, 0x30, 0x40, 0x50};
|
||
std::vector<uint8_t> encrypted = encryptFaceFeature(original);
|
||
|
||
assert(encrypted.size() == original.size());
|
||
|
||
// 加密后应该与原文不同
|
||
bool different = false;
|
||
for (size_t i = 0; i < original.size(); i++) {
|
||
if (encrypted[i] != original[i]) {
|
||
different = true;
|
||
break;
|
||
}
|
||
}
|
||
assert(different);
|
||
|
||
std::cout << " PASS: 加密后数据已变更" << std::endl;
|
||
}
|
||
|
||
/// 主测试入口
|
||
int main() {
|
||
std::cout << "========== 考勤系统单元测试 ==========" << std::endl;
|
||
|
||
testEnumToString();
|
||
testGenerateRecordId();
|
||
testProcessCheckin();
|
||
testCalculateAttendance();
|
||
testDetectAnomaly();
|
||
testGenerateReport();
|
||
testEncryption();
|
||
|
||
std::cout << "\n========== 所有测试通过! ==========" << std::endl;
|
||
return 0;
|
||
}
|