Feat: graceful shutdown
This commit is contained in:
		| @@ -70,10 +70,11 @@ func getStateByCtx(ctx context.Context) (*models.UserState, error) { | ||||
| } | ||||
|  | ||||
| func saveFullInfo(fi *models.FullInfo) error { | ||||
| 	// INFO: unfortunately working no transactions; so case are possible where first object is updated but the second is not | ||||
| 	// INFO: no transactions; so case is possible where first object is updated but the second is not | ||||
| 	if err := saveState(fi.State.Username, fi.State); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.Debug("saved user state", "state", fi.State) | ||||
| 	if err := saveRoom(fi.Room); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										107
									
								
								handlers/actions_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								handlers/actions_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| package handlers | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"gralias/models" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestSaveState(t *testing.T) { | ||||
| 	// Create test state | ||||
| 	state := &models.UserState{ | ||||
| 		Username: "testuser", | ||||
| 		RoomID:   "testroom", | ||||
| 		Team:     models.UserTeamBlue, | ||||
| 		Role:     models.UserRoleMime, | ||||
| 	} | ||||
|  | ||||
| 	// Save state | ||||
| 	err := saveState(state.Username, state) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("saveState failed: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Load state | ||||
| 	loadedState, err := loadState(state.Username) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("loadState failed: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Verify loaded state matches original | ||||
| 	if loadedState.Username != state.Username { | ||||
| 		t.Errorf("Username mismatch: got %s, want %s", loadedState.Username, state.Username) | ||||
| 	} | ||||
| 	if loadedState.RoomID != state.RoomID { | ||||
| 		t.Errorf("RoomID mismatch: got %s, want %s", loadedState.RoomID, state.RoomID) | ||||
| 	} | ||||
| 	if loadedState.Team != state.Team { | ||||
| 		t.Errorf("Team mismatch: got %s, want %s", loadedState.Team, state.Team) | ||||
| 	} | ||||
| 	if loadedState.Role != state.Role { | ||||
| 		t.Errorf("Role mismatch: got %s, want %s", loadedState.Role, state.Role) | ||||
| 	} | ||||
|  | ||||
| 	// Test JSON serialization/deserialization | ||||
| 	data, err := json.Marshal(state) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("marshal failed: %v", err) | ||||
| 	} | ||||
| 	testMap := make(map[string][]byte) | ||||
| 	testMap["testkey"] = data | ||||
|  | ||||
| 	// Create a temporary file | ||||
| 	tmpFile, err := os.CreateTemp("", "test_store_*.json") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("failed to create temp file: %v", err) | ||||
| 	} | ||||
| 	tmpFileName := tmpFile.Name() | ||||
| 	// defer os.Remove(tmpFileName) | ||||
|  | ||||
| 	// Write testMap to the temp file | ||||
| 	fileData, err := json.Marshal(testMap) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("failed to marshal testMap: %v", err) | ||||
| 	} | ||||
| 	if err := os.WriteFile(tmpFileName, fileData, 0644); err != nil { | ||||
| 		t.Fatalf("failed to write to temp file: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Read the temp file | ||||
| 	readData, err := os.ReadFile(tmpFileName) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("failed to read temp file: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Unmarshal the data | ||||
| 	var testMapRead map[string][]byte | ||||
| 	if err := json.Unmarshal(readData, &testMapRead); err != nil { | ||||
| 		t.Fatalf("failed to unmarshal testMap: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Get the state bytes from the map | ||||
| 	stateBytes, ok := testMapRead["testkey"] | ||||
| 	if !ok { | ||||
| 		t.Fatalf("key 'testkey' not found in testMapRead") | ||||
| 	} | ||||
|  | ||||
| 	// Unmarshal the state bytes | ||||
| 	stateRead := &models.UserState{} | ||||
| 	if err := json.Unmarshal(stateBytes, stateRead); err != nil { | ||||
| 		t.Fatalf("failed to unmarshal state: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// Compare the state | ||||
| 	if stateRead.Username != state.Username { | ||||
| 		t.Errorf("Username mismatch from file: got %s, want %s", stateRead.Username, state.Username) | ||||
| 	} | ||||
| 	if stateRead.RoomID != state.RoomID { | ||||
| 		t.Errorf("RoomID mismatch from file: got %s, want %s", stateRead.RoomID, state.RoomID) | ||||
| 	} | ||||
| 	if stateRead.Team != state.Team { | ||||
| 		t.Errorf("Team mismatch from file: got %s, want %s", stateRead.Team, state.Team) | ||||
| 	} | ||||
| 	if stateRead.Role != state.Role { | ||||
| 		t.Errorf("Role mismatch from file: got %s, want %s", stateRead.Role, state.Role) | ||||
| 	} | ||||
| } | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	"log/slog" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -26,6 +27,7 @@ func init() { | ||||
| 	memcache = cache.MemCache | ||||
| 	cfg = config.LoadConfigOrDefault("") | ||||
| 	Notifier = broker.Notifier | ||||
| 	cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval | ||||
| } | ||||
|  | ||||
| func HandlePing(w http.ResponseWriter, r *http.Request) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder