| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package parking
- import (
- "errors"
- "fmt"
- "testing"
- "time"
- "github.com/glebarez/sqlite"
- "github.com/stretchr/testify/require"
- "go.uber.org/zap"
- "gorm.io/gorm"
- "wails-app/internal/dao"
- "wails-app/internal/global"
- incidentService "wails-app/internal/modules/incident/service"
- )
- // failingGateController 注入失败的闸机控制器,模拟开闸失败。
- type failingGateController struct{}
- func (c *failingGateController) OpenGate(deviceCode string, _ byte) error {
- return errors.New("relay timeout")
- }
- func (c *failingGateController) CloseGate(deviceCode string, _ byte) error {
- return errors.New("relay timeout")
- }
- func (c *failingGateController) IsGateConnected(string) bool { return true }
- func setupIncidentHookTest(t *testing.T) {
- t.Helper()
- dsn := fmt.Sprintf("file:incident-hook-%d?mode=memory&cache=shared", time.Now().UnixNano())
- db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
- require.NoError(t, err)
- sqlDB, err := db.DB()
- require.NoError(t, err)
- t.Cleanup(func() {
- _ = sqlDB.Close()
- if global.GVA_DB == db {
- global.GVA_DB = nil
- }
- })
- global.GVA_DB = db
- global.GVA_LOG = zap.NewNop() // 避免 nil 日志
- t.Cleanup(func() { global.GVA_LOG = nil })
- require.NoError(t, db.AutoMigrate(&dao.IncidentRecord{}, &dao.DeviceCommandLog{}, &dao.UHFReader{}))
- }
- // TestOpenGateFailureCreatesIncidentAndCommandLog 开闸失败时:
- // 指令流水(failed) + 异常事件(gate_failed) 同时产生,且流水回填异常关联。
- func TestOpenGateFailureCreatesIncidentAndCommandLog(t *testing.T) {
- setupIncidentHookTest(t)
- previous := currentGateController()
- SetGateController(&failingGateController{})
- t.Cleanup(func() { SetGateController(previous) })
- require.NoError(t, global.GVA_DB.Create(&dao.UHFReader{DeviceCode: "GATE-01", DeviceName: "东门道闸"}).Error)
- svc := &PassageService{}
- commandLogID, err := svc.OpenGateWithContext(GateCommandContext{
- Source: incidentService.SourcePassage, SessionID: 7,
- }, "GATE-01", 0)
- require.Error(t, err)
- require.Contains(t, err.Error(), "relay timeout")
- // 调用方(passage.go 同款逻辑):记录异常并回填指令流水关联
- incidentID := incidentSvc.RecordIncident(incidentService.RecordIncidentRequest{
- Category: incidentService.CategoryGateFailed,
- Source: incidentService.SourcePassage,
- VehicleRecordID: 7,
- DeviceCode: "GATE-01",
- Description: "入场记录已创建,但开闸失败",
- Detail: err.Error(),
- })
- require.NotZero(t, incidentID)
- incidentSvc.LinkIncidentToCommand(commandLogID, incidentID)
- // 指令流水已写入且为 failed
- require.NotZero(t, commandLogID)
- var cmd dao.DeviceCommandLog
- require.NoError(t, global.GVA_DB.First(&cmd, commandLogID).Error)
- require.Equal(t, "open", cmd.Action)
- require.Equal(t, incidentService.CommandFailed, cmd.Result)
- require.Equal(t, "东门道闸", cmd.DeviceName)
- require.Equal(t, uint(7), cmd.SessionID)
- // 异常事件已生成(gate_failed)
- var incident dao.IncidentRecord
- require.NoError(t, global.GVA_DB.Where("category = ?", incidentService.CategoryGateFailed).First(&incident).Error)
- require.Equal(t, incidentService.StatusPending, incident.Status)
- require.Equal(t, incidentService.SourcePassage, incident.Source)
- require.Equal(t, incidentService.LevelCritical, incident.Level)
- require.Equal(t, "GATE-01", incident.DeviceCode)
- // 指令流水回填了异常关联
- require.NoError(t, global.GVA_DB.First(&cmd, commandLogID).Error)
- require.Equal(t, incident.ID, cmd.IncidentID)
- }
- // TestOpenGateSuccessRecordsCommandLog 开闸成功只写指令流水,不产生异常。
- func TestOpenGateSuccessRecordsCommandLog(t *testing.T) {
- setupIncidentHookTest(t)
- previous := currentGateController()
- SetGateController(&gateControllerStub{})
- t.Cleanup(func() { SetGateController(previous) })
- svc := &PassageService{}
- commandLogID, err := svc.OpenGateWithContext(GateCommandContext{
- Source: incidentService.SourceManual, OperatorID: 5,
- }, "GATE-01", 0)
- require.NoError(t, err)
- require.NotZero(t, commandLogID)
- var cmd dao.DeviceCommandLog
- require.NoError(t, global.GVA_DB.First(&cmd, commandLogID).Error)
- require.Equal(t, incidentService.CommandSuccess, cmd.Result)
- require.Equal(t, incidentService.SourceManual, cmd.Source)
- require.Equal(t, uint(5), cmd.OperatorID)
- var count int64
- require.NoError(t, global.GVA_DB.Model(&dao.IncidentRecord{}).Count(&count).Error)
- require.Equal(t, int64(0), count)
- }
- // TestManualRaiseRecordedAsResolved 人工抬杆(带原因)应生成已解决的 manual_raise 记录。
- func TestManualRaiseRecordedAsResolved(t *testing.T) {
- setupIncidentHookTest(t)
- // 模拟 API 层人工开闸成功后的埋点调用
- incidentID := incidentService.NewIncidentService().RecordIncident(incidentService.RecordIncidentRequest{
- Category: incidentService.CategoryManualRaise,
- Source: incidentService.SourceManual,
- VehicleRecordID: 3,
- PlateNumber: "SU-001",
- DeviceCode: "GATE-01",
- OperatorID: 5,
- Description: "人工开闸",
- HandleType: incidentService.HandleManualGate,
- HandleRemark: "车主称设备未识别",
- })
- require.NotZero(t, incidentID)
- var incident dao.IncidentRecord
- require.NoError(t, global.GVA_DB.First(&incident, incidentID).Error)
- require.Equal(t, incidentService.StatusResolved, incident.Status)
- require.Equal(t, incidentService.HandleManualGate, incident.HandleType)
- require.Equal(t, "车主称设备未识别", incident.HandleRemark)
- require.NotNil(t, incident.HandledAt)
- require.Equal(t, uint(5), incident.HandlerID)
- }
|