package service import ( "fmt" "testing" "time" "github.com/glebarez/sqlite" "github.com/stretchr/testify/require" "gorm.io/gorm" "wails-app/internal/dao" "wails-app/internal/global" "wails-app/internal/modules/parking-session/model/request" ) func setupParkingSessionTest(t *testing.T) *ParkingSessionService { t.Helper() dsn := fmt.Sprintf("file:parking-session-%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 require.NoError(t, db.AutoMigrate(&dao.VehicleRecord{})) return NewParkingSessionService() } func TestParkingSessionOpenResolveAndClose(t *testing.T) { svc := setupParkingSessionTest(t) var session *dao.VehicleRecord require.NoError(t, global.GVA_DB.Transaction(func(tx *gorm.DB) error { var err error session, err = svc.OpenSessionTx(tx, request.OpenSessionRequest{ PlateNumber: "SESSION-001", RFIDTag: "RFID-001", ParkingLotID: 1, EntryChannelID: 11, EntryChannelCode: "ENTRY-A", EntryChannelName: "东入口通道", EntryDeviceCode: "GATE-ENTRY-A", EntryDeviceName: "东入口读卡器", }) return err })) resolved, err := svc.ResolveActiveSession(request.ResolveActiveSessionRequest{ PlateNumber: "SESSION-001", RFIDTag: "RFID-001", }) require.NoError(t, err) require.Equal(t, session.ID, resolved.ID) require.NoError(t, global.GVA_DB.Transaction(func(tx *gorm.DB) error { if _, err := svc.MarkPaidTx(tx, session.ID, "counter", "cash", 12); err != nil { return err } _, err := svc.CloseSessionTx(tx, request.CloseSessionRequest{ SessionID: session.ID, StayTime: 60, Fee: 12, PaymentEntry: "counter", PaymentMethod: "cash", ExitChannelID: 22, ExitChannelCode: "EXIT-B", ExitChannelName: "西出口通道", ExitDeviceCode: "GATE-EXIT-B", ExitDeviceName: "西出口读卡器", }) return err })) var persisted dao.VehicleRecord require.NoError(t, global.GVA_DB.First(&persisted, session.ID).Error) require.EqualValues(t, 11, persisted.EntryChannelID) require.Equal(t, "ENTRY-A", persisted.EntryChannelCode) require.Equal(t, "东入口通道", persisted.EntryChannelName) require.Equal(t, "GATE-ENTRY-A", persisted.EntryDeviceCode) require.Equal(t, "东入口读卡器", persisted.EntryDeviceName) require.EqualValues(t, 22, persisted.ExitChannelID) require.Equal(t, "EXIT-B", persisted.ExitChannelCode) require.Equal(t, "西出口通道", persisted.ExitChannelName) require.Equal(t, "GATE-EXIT-B", persisted.ExitDeviceCode) require.Equal(t, "西出口读卡器", persisted.ExitDeviceName) _, err = svc.ResolveActiveSession(request.ResolveActiveSessionRequest{SessionID: session.ID}) require.ErrorIs(t, err, ErrSessionAlreadyClosed) } func TestParkingSessionRejectsDuplicateAndConflictingIdentity(t *testing.T) { svc := setupParkingSessionTest(t) first, err := svc.OpenSessionTx(global.GVA_DB, request.OpenSessionRequest{PlateNumber: "SESSION-A", RFIDTag: "RFID-A"}) require.NoError(t, err) _, err = svc.OpenSessionTx(global.GVA_DB, request.OpenSessionRequest{PlateNumber: "SESSION-A"}) require.ErrorIs(t, err, ErrSessionAlreadyOpen) second, err := svc.OpenSessionTx(global.GVA_DB, request.OpenSessionRequest{PlateNumber: "SESSION-B", RFIDTag: "RFID-B"}) require.NoError(t, err) require.NotEqual(t, first.ID, second.ID) _, err = svc.ResolveActiveSession(request.ResolveActiveSessionRequest{PlateNumber: "SESSION-A", RFIDTag: "RFID-B"}) require.ErrorIs(t, err, ErrSessionIdentityConflict) }