package tests import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "os" "testing" "time" "gitea.kleinsense.nl/DariusKlein/kleinTodo/common" "gitea.kleinsense.nl/DariusKlein/kleinTodo/server/handler" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestIntegrationSync(t *testing.T) { // 1. Setup Environment tempDir, err := os.MkdirTemp("", "todo-test-*") require.NoError(t, err) defer os.RemoveAll(tempDir) // Override storage path for BoltStore by setting HOME or XDG_CONFIG_HOME // BoltStore uses os.UserConfigDir() which looks at XDG_CONFIG_HOME on Linux os.Setenv("XDG_CONFIG_HOME", tempDir) os.Setenv("JWT_SECRET", "test-secret-123") // 2. Initialize Server mux := http.NewServeMux() mux.HandleFunc("POST /register", handler.RegisterHandler) mux.HandleFunc("POST /login", handler.LoginHandler) mux.HandleFunc("POST /sync", handler.SyncHandler) ts := httptest.NewServer(mux) defer ts.Close() client := ts.Client() username := "testuser" password := "testpass" // 3. Register User regPayload, _ := json.Marshal(common.Credentials{Username: username, Password: password}) resp, err := client.Post(ts.URL+"/register", "application/json", bytes.NewBuffer(regPayload)) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) // 4. Login and Get Token resp, err = client.Post(ts.URL+"/login", "application/json", bytes.NewBuffer(regPayload)) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) token := resp.Header.Get(common.AuthHeader) require.NotEmpty(t, token) // 5. Create Local Todos store, err := common.GetTodoDataStore() require.NoError(t, err) defer store.Close() todo1 := common.Todo{ Id: uuid.New().String(), Name: "Task 1", Description: "Desc 1", Status: common.NotStarted, Owner: username, LastModified: time.Now().UTC(), } todo2 := common.Todo{ Id: uuid.New().String(), Name: "Task 2", Description: "Desc 2", Status: common.WIP, Owner: username, LastModified: time.Now().UTC(), } err = todo1.Store(store, username) require.NoError(t, err) err = todo2.Store(store, username) require.NoError(t, err) // 6. Sync to Server localTodos := store.GetTodoList(username, true) syncReq := common.TodoList{Todos: localTodos} syncPayload, _ := json.Marshal(syncReq) req, _ := http.NewRequest("POST", ts.URL+"/sync", bytes.NewBuffer(syncPayload)) req.Header.Set("Content-Type", "application/json") req.Header.Set(common.AuthHeader, "Bearer "+token) resp, err = client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) var syncResp common.SyncResponse err = json.NewDecoder(resp.Body).Decode(&syncResp) require.NoError(t, err) resp.Body.Close() // Verify both todos are in sync response assert.Len(t, syncResp.SyncedTodos, 2) assert.Empty(t, syncResp.MisMatchingTodos) // 7. Verify Server State (By syncing again with empty local) emptySyncReq := common.TodoList{Todos: []common.Todo{}} emptySyncPayload, _ := json.Marshal(emptySyncReq) req, _ = http.NewRequest("POST", ts.URL+"/sync", bytes.NewBuffer(emptySyncPayload)) req.Header.Set("Content-Type", "application/json") req.Header.Set(common.AuthHeader, "Bearer "+token) resp, err = client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) err = json.NewDecoder(resp.Body).Decode(&syncResp) require.NoError(t, err) resp.Body.Close() assert.Len(t, syncResp.SyncedTodos, 2, "Server should have kept the synced todos") // 8. Test Pulling from Server (New Client) // Create another temporary directory for a different client client2TempDir, err := os.MkdirTemp("", "todo-test-client2-*") require.NoError(t, err) defer os.RemoveAll(client2TempDir) // Since common.GetTodoDataStore() uses sync.Once, we can't easily get a new database in the same process // BUT for this test, we can just use GetCustomDataStore("todo2.db") if it was public, // however it seems we can just check if the server returns the data when requested. req, _ = http.NewRequest("POST", ts.URL+"/sync", bytes.NewBuffer(emptySyncPayload)) req.Header.Set("Content-Type", "application/json") req.Header.Set(common.AuthHeader, "Bearer "+token) resp, err = client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) err = json.NewDecoder(resp.Body).Decode(&syncResp) require.NoError(t, err) resp.Body.Close() assert.Len(t, syncResp.SyncedTodos, 2, "Server should return the 2 todos for a sync request") }