| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 |
- package job
- import (
- "os"
- "path/filepath"
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
- "github.com/mhsanaei/3x-ui/v3/internal/tuic"
- "github.com/mhsanaei/3x-ui/v3/internal/xray"
- )
- func TestTuicShutdownJournalSurvivesDatabaseFailureAndReplaysOnce(t *testing.T) {
- dir := t.TempDir()
- t.Setenv("XUI_DB_FOLDER", dir)
- dbtest.InitDB(t, filepath.Join(dir, "x-ui.db"))
- db := database.GetDB()
- row := xray.ClientTraffic{Email: "journal@x", Enable: true}
- if err := db.Create(&row).Error; err != nil {
- t.Fatal(err)
- }
- batch := tuicTrafficBatch{ID: "shutdown-batch", Deltas: []tuic.ClientTrafficDelta{{Email: row.Email, TrafficID: row.Id, Up: 123, Down: 456}}}
- path, err := storeTuicBatch(batch)
- if err != nil {
- t.Fatal(err)
- }
- if err := db.Exec(`CREATE TRIGGER fail_tuic_write BEFORE UPDATE ON client_traffics BEGIN SELECT RAISE(FAIL, 'disk fault'); END`).Error; err != nil {
- t.Fatal(err)
- }
- job := NewTuicJob()
- if err := job.FlushStoppedTraffic(); err == nil {
- t.Fatal("failed traffic update must return an error")
- }
- if _, err := os.Stat(path); err != nil {
- t.Fatalf("durable recovery file missing: %v", err)
- }
- var receipts int64
- if err := db.Table("tuic_traffic_receipts").Count(&receipts).Error; err == nil && receipts != 0 {
- t.Fatal("failed transaction committed a receipt")
- }
- if err := db.Exec("DROP TRIGGER fail_tuic_write").Error; err != nil {
- t.Fatal(err)
- }
- // A new job has no knowledge of the previous process's in-memory deltas.
- recovered := NewTuicJob()
- if err := recovered.replayTuicJournal(); err != nil {
- t.Fatal(err)
- }
- // Recreate a stale file, as if file removal was lost after DB commit.
- if _, err := storeTuicBatch(batch); err != nil {
- t.Fatal(err)
- }
- if err := recovered.replayTuicJournal(); err != nil {
- t.Fatal(err)
- }
- if err := db.Where("id = ?", row.Id).First(&row).Error; err != nil {
- t.Fatal(err)
- }
- if row.Up != 123 || row.Down != 456 {
- t.Fatalf("replay billed %d/%d", row.Up, row.Down)
- }
- entries, err := os.ReadDir(tuicJournalDir())
- if err != nil || len(entries) != 0 {
- t.Fatalf("journal not drained: %v %v", entries, err)
- }
- }
|