Skip to content

Commit 52a29bc

Browse files
authored
Fixes repository major concurrency issues (#54)
* chore: fixes TestUpdateAccountTokensHandler * Fixes repository major concurrency issues
1 parent 9fb1e11 commit 52a29bc

File tree

6 files changed

+106
-77
lines changed

6 files changed

+106
-77
lines changed

src/controller/account.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter,
137137
accountRequest.Token = utils.Encrypt(accountRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
138138
}
139139

140-
accountUpdated, err := controller.accountRepository.Update(&accountRequest)
140+
accountUpdated, err := controller.accountRepository.UpdateByDomainEnvironment(&accountRequest)
141141
if err != nil {
142142
utils.LogError("Error updating account: %s", err.Error())
143143
utils.ResponseJSON(w, ErrorResponse{Error: "Error updating account"}, http.StatusInternalServerError)
@@ -166,7 +166,7 @@ func (controller *AccountController) UpdateAccountTokensHandler(w http.ResponseW
166166
}
167167

168168
// Encrypt token before saving
169-
accountTokensRequest.Token = utils.Encrypt(accountTokensRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
169+
newEncryptedToken := utils.Encrypt(accountTokensRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
170170

171171
// Update account tokens
172172
for _, environment := range accountTokensRequest.Environments {
@@ -177,8 +177,8 @@ func (controller *AccountController) UpdateAccountTokensHandler(w http.ResponseW
177177
return
178178
}
179179

180-
account.Token = accountTokensRequest.Token
181-
controller.accountRepository.Update(account)
180+
account.Token = newEncryptedToken
181+
controller.accountRepository.UpdateByDomainEnvironment(account)
182182
}
183183

184184
utils.ResponseJSON(w, AccountTokensResponse{

src/controller/account_test.go

Lines changed: 55 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@ package controller
33
import (
44
"bytes"
55
"encoding/json"
6-
"io"
76
"net/http"
8-
"net/http/httptest"
97
"testing"
108
"time"
119

1210
"github.com/stretchr/testify/assert"
1311
"github.com/switcherapi/switcher-gitops/src/config"
1412
"github.com/switcherapi/switcher-gitops/src/model"
15-
"github.com/switcherapi/switcher-gitops/src/repository"
1613
"github.com/switcherapi/switcher-gitops/src/utils"
1714
)
1815

@@ -26,8 +23,9 @@ func TestCreateAccountHandler(t *testing.T) {
2623

2724
t.Run("Should create an account", func(t *testing.T) {
2825
// Test
29-
accountV1.Domain.ID = "123-controller-create-account"
30-
payload, _ := json.Marshal(accountV1)
26+
account1 := accountV1
27+
account1.Domain.ID = "123-controller-create-account"
28+
payload, _ := json.Marshal(account1)
3129
req, _ := http.NewRequest(http.MethodPost, accountController.routeAccountPath, bytes.NewBuffer(payload))
3230
response := executeRequest(req, r, token)
3331

@@ -37,7 +35,7 @@ func TestCreateAccountHandler(t *testing.T) {
3735

3836
assert.Equal(t, http.StatusCreated, response.Code)
3937
assert.Nil(t, err)
40-
assert.Equal(t, accountV1.Repository, accountResponse.Repository)
38+
assert.Equal(t, account1.Repository, accountResponse.Repository)
4139
assert.Contains(t, accountResponse.Token, "...")
4240
})
4341

@@ -54,10 +52,11 @@ func TestCreateAccountHandler(t *testing.T) {
5452

5553
t.Run("Should not create an account - account already exists", func(t *testing.T) {
5654
// Create an account
57-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
55+
account1 := accountV1
56+
accountController.accountRepository.Create(&account1)
5857

5958
// Test
60-
payload, _ := json.Marshal(accountV1)
59+
payload, _ := json.Marshal(account1)
6160
req, _ := http.NewRequest(http.MethodPost, accountController.routeAccountPath, bytes.NewBuffer(payload))
6261
response := executeRequest(req, r, token)
6362

@@ -72,12 +71,13 @@ func TestFetchAccountHandler(t *testing.T) {
7271

7372
t.Run("Should fetch an account by domain ID / environment", func(t *testing.T) {
7473
// Create an account
75-
accountV1.Domain.ID = "123-controller-fetch-account"
76-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
74+
account1 := accountV1
75+
account1.Domain.ID = "123-controller-fetch-account"
76+
accountController.accountRepository.Create(&account1)
7777

7878
// Test
7979
payload := []byte("")
80-
req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, bytes.NewBuffer(payload))
80+
req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+account1.Domain.ID+"/"+account1.Environment, bytes.NewBuffer(payload))
8181
response := executeRequest(req, r, token)
8282

8383
// Assert
@@ -86,7 +86,7 @@ func TestFetchAccountHandler(t *testing.T) {
8686

8787
assert.Equal(t, http.StatusOK, response.Code)
8888
assert.Nil(t, err)
89-
assert.Equal(t, accountV1.Repository, accountResponse.Repository)
89+
assert.Equal(t, account1.Repository, accountResponse.Repository)
9090
assert.Contains(t, accountResponse.Token, "...")
9191
})
9292

@@ -102,15 +102,22 @@ func TestFetchAccountHandler(t *testing.T) {
102102
})
103103

104104
t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {
105-
// Create an account
106-
accountV1.Domain.ID = "123-controller-fetch-all-accounts"
107-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
108-
accountV1.Environment = "staging"
109-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
105+
// Create accounts
106+
domainId := "123-controller-fetch-all-accounts"
107+
108+
account1 := accountV1
109+
account1.Domain.ID = domainId
110+
account1.Environment = "default"
111+
accountController.accountRepository.Create(&account1)
112+
113+
account2 := accountV1
114+
account2.Domain.ID = domainId
115+
account2.Environment = "staging"
116+
accountController.accountRepository.Create(&account2)
110117

111118
// Test
112119
payload := []byte("")
113-
req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+accountV1.Domain.ID, bytes.NewBuffer(payload))
120+
req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+domainId, bytes.NewBuffer(payload))
114121
response := executeRequest(req, r, token)
115122

116123
// Assert
@@ -141,9 +148,10 @@ func TestUpdateAccountHandler(t *testing.T) {
141148

142149
t.Run("Should update an account", func(t *testing.T) {
143150
// Create an account
144-
accountV1.Domain.ID = "123-controller-update-account"
145-
accountV1.Environment = "default"
146-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
151+
account1 := accountV1
152+
account1.Domain.ID = "123-controller-update-account"
153+
account1.Environment = "default"
154+
accountController.accountRepository.Create(&account1)
147155

148156
// Update the account
149157
accountV2.Domain.ID = "123-controller-update-account"
@@ -162,19 +170,20 @@ func TestUpdateAccountHandler(t *testing.T) {
162170
assert.Equal(t, http.StatusOK, response.Code)
163171
assert.Nil(t, err)
164172
assert.NotEmpty(t, accountResponse.Token)
165-
assert.Equal(t, accountV1.Repository, accountResponse.Repository)
173+
assert.Equal(t, account1.Repository, accountResponse.Repository)
166174
assert.Equal(t, model.StatusSynced, accountResponse.Domain.Status)
167175
assert.Equal(t, "Updated successfully", accountResponse.Domain.Message)
168176
assert.Equal(t, "5m", accountResponse.Settings.Window)
169177
})
170178

171179
t.Run("Should update account token only", func(t *testing.T) {
172180
// Create an account
173-
accountV1.Domain.ID = "123-controller-update-account-token"
174-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
181+
account1 := accountV1
182+
account1.Domain.ID = "123-controller-update-account-token"
183+
accountController.accountRepository.Create(&account1)
175184

176185
// Test
177-
accountRequest := accountV1
186+
accountRequest := account1
178187
accountRequest.Token = "new-token"
179188

180189
payload, _ := json.Marshal(accountRequest)
@@ -187,11 +196,10 @@ func TestUpdateAccountHandler(t *testing.T) {
187196

188197
assert.Equal(t, http.StatusOK, response.Code)
189198
assert.Nil(t, err)
190-
assert.Equal(t, accountV1.Repository, accountResponse.Repository)
199+
assert.Equal(t, account1.Repository, accountResponse.Repository)
191200
assert.Contains(t, accountResponse.Token, "...")
192201

193-
accountRepository := repository.NewAccountRepositoryMongo(mongoDb)
194-
accountFromDb, _ := accountRepository.FetchByAccountId(accountResponse.ID.Hex())
202+
accountFromDb, _ := accountController.accountRepository.FetchByAccountId(accountResponse.ID.Hex())
195203

196204
decryptedToken, _ := utils.Decrypt(accountFromDb.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
197205
assert.Equal(t, "new-token", decryptedToken)
@@ -210,14 +218,15 @@ func TestUpdateAccountHandler(t *testing.T) {
210218

211219
t.Run("Should not update an account - not found", func(t *testing.T) {
212220
// Create an account
213-
accountV1.Domain.ID = "123-controller-update-account-not-found"
214-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
221+
account1 := accountV1
222+
account1.Domain.ID = "123-controller-update-account-not-found"
223+
accountController.accountRepository.Create(&account1)
215224

216225
// Replace the domain ID to force an error
217-
accountV1.Domain.ID = "111"
226+
account1.Domain.ID = "111"
218227

219228
// Test
220-
payload, _ := json.Marshal(accountV1)
229+
payload, _ := json.Marshal(account1)
221230
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath, bytes.NewBuffer(payload))
222231
response := executeRequest(req, r, token)
223232

@@ -231,24 +240,26 @@ func TestUpdateAccountTokensHandler(t *testing.T) {
231240
token := generateToken("test", time.Minute)
232241

233242
t.Run("Should update account tokens", func(t *testing.T) {
243+
domainId := "123-controller-update-account-tokens"
244+
234245
// Create accounts
235246
account1 := accountV1
236-
account1.Domain.ID = "123-controller-update-account-tokens"
247+
account1.Domain.ID = domainId
237248
account1.Environment = "default"
238-
accountController.CreateAccountHandler(givenAccountRequest(account1))
249+
accountController.accountRepository.Create(&account1)
239250

240251
account2 := accountV1
241-
account2.Domain.ID = "123-controller-update-account-tokens"
252+
account2.Domain.ID = domainId
242253
account2.Environment = "staging"
243-
accountController.CreateAccountHandler(givenAccountRequest(account2))
254+
accountController.accountRepository.Create(&account2)
244255

245256
// Test
246257
payload, _ := json.Marshal(AccountTokensRequest{
247258
Environments: []string{account1.Environment, account2.Environment},
248259
Token: "new-token",
249260
})
250261

251-
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/tokens/"+account1.Domain.ID, bytes.NewBuffer(payload))
262+
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/tokens/"+domainId, bytes.NewBuffer(payload))
252263
response := executeRequest(req, r, token)
253264

254265
// Assert
@@ -258,11 +269,10 @@ func TestUpdateAccountTokensHandler(t *testing.T) {
258269
assert.Equal(t, http.StatusOK, response.Code)
259270
assert.Nil(t, err)
260271

261-
accountRepository := repository.NewAccountRepositoryMongo(mongoDb)
262-
accountFromDb1 := accountRepository.FetchAllByDomainId(account1.Domain.ID)
272+
accountFromDb := accountController.accountRepository.FetchAllByDomainId(domainId)
273+
decryptedToken1, _ := utils.Decrypt(accountFromDb[0].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
274+
decryptedToken2, _ := utils.Decrypt(accountFromDb[1].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
263275

264-
decryptedToken1, _ := utils.Decrypt(accountFromDb1[0].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
265-
decryptedToken2, _ := utils.Decrypt(accountFromDb1[1].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
266276
assert.Equal(t, "new-token", decryptedToken1)
267277
assert.Equal(t, "new-token", decryptedToken2)
268278
})
@@ -315,11 +325,12 @@ func TestDeleteAccountHandler(t *testing.T) {
315325

316326
t.Run("Should delete an account by domain ID / environment", func(t *testing.T) {
317327
// Create an account
318-
accountV1.Domain.ID = "123-controller-delete-account"
319-
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
328+
account1 := accountV1
329+
account1.Domain.ID = "123-controller-delete-account"
330+
accountController.accountRepository.Create(&account1)
320331

321332
// Test
322-
req, _ := http.NewRequest(http.MethodDelete, accountController.routeAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, nil)
333+
req, _ := http.NewRequest(http.MethodDelete, accountController.routeAccountPath+"/"+account1.Domain.ID+"/"+account1.Environment, nil)
323334
response := executeRequest(req, r, token)
324335

325336
// Assert
@@ -381,17 +392,3 @@ func TestUnnauthorizedAccountHandler(t *testing.T) {
381392
assert.Equal(t, "{\"error\":\"Invalid token\"}", response.Body.String())
382393
})
383394
}
384-
385-
// Helpers
386-
387-
func givenAccountRequest(data model.Account) (*httptest.ResponseRecorder, *http.Request) {
388-
w := httptest.NewRecorder()
389-
r := httptest.NewRequest(http.MethodPost, accountController.routeAccountPath, nil)
390-
391-
// Encode the account request as JSON
392-
body, _ := json.Marshal(data)
393-
r.Body = io.NopCloser(bytes.NewReader(body))
394-
r.Header.Set("Content-Type", "application/json")
395-
396-
return w, r
397-
}

src/controller/controller_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func setup() {
3434
os.Setenv("GO_ENV", "test")
3535
config.InitEnv()
3636
mongoDb = db.InitDb()
37+
mongoDb.Drop(context.Background())
3738

3839
// Init modules
3940
accountRepository := repository.NewAccountRepositoryMongo(mongoDb)

src/core/handler.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,5 +271,9 @@ func (c *CoreHandler) updateDomainStatus(account model.Account, status string, m
271271
account.Domain.Status = status
272272
account.Domain.Message = message
273273
account.Domain.LastDate = time.Now().Format(time.ANSIC)
274-
c.accountRepository.Update(&account)
274+
275+
c.accountRepository.UpdateByDomainEnvironment(&model.Account{
276+
Environment: account.Environment,
277+
Domain: account.Domain,
278+
})
275279
}

src/repository/account.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type AccountRepository interface {
1919
FetchByDomainIdEnvironment(domainId string, environment string) (*model.Account, error)
2020
FetchAllByDomainId(domainId string) []model.Account
2121
FetchAllAccounts() []model.Account
22-
Update(account *model.Account) (*model.Account, error)
22+
UpdateByDomainEnvironment(account *model.Account) (*model.Account, error)
2323
DeleteByAccountId(accountId string) error
2424
DeleteByDomainIdEnvironment(domainId string, environment string) error
2525
}
@@ -49,7 +49,7 @@ func (repo *AccountRepositoryMongo) Create(account *model.Account) (*model.Accou
4949
return account, nil
5050
}
5151

52-
func (repo *AccountRepositoryMongo) Update(account *model.Account) (*model.Account, error) {
52+
func (repo *AccountRepositoryMongo) UpdateByDomainEnvironment(account *model.Account) (*model.Account, error) {
5353
collection, ctx, cancel := getDbContext(repo)
5454
defer cancel()
5555

@@ -169,24 +169,24 @@ func registerAccountRepositoryValidators(db *mongo.Database) {
169169
func getUpdateFields(account *model.Account) bson.M {
170170
setMap := bson.M{}
171171

172-
setMap["repository"] = account.Repository
173-
setMap["branch"] = account.Branch
174-
setMap["path"] = account.Path
175-
setMap["environment"] = account.Environment
176-
setMap["domain.name"] = account.Domain.Name
177-
setMap["settings.active"] = account.Settings.Active
178-
setMap["settings.window"] = account.Settings.Window
179-
setMap["settings.forcePrune"] = account.Settings.ForcePrune
180-
172+
setIfNotEmpty(setMap, "environment", account.Environment)
173+
setIfNotEmpty(setMap, "repository", account.Repository)
174+
setIfNotEmpty(setMap, "branch", account.Branch)
175+
setIfNotEmpty(setMap, "path", account.Path)
181176
setIfNotEmpty(setMap, "token", account.Token)
177+
178+
setIfNotEmpty(setMap, "domain.name", account.Domain.Name)
182179
setIfNotEmpty(setMap, "domain.version", account.Domain.Version)
183180
setIfNotEmpty(setMap, "domain.lastCommit", account.Domain.LastCommit)
184181
setIfNotEmpty(setMap, "domain.lastDate", account.Domain.LastDate)
185182
setIfNotEmpty(setMap, "domain.status", account.Domain.Status)
186183
setIfNotEmpty(setMap, "domain.message", account.Domain.Message)
187184

188-
update := bson.M{"$set": setMap}
185+
setIfNotEmpty(setMap, "settings.window", account.Settings.Window)
186+
setIfNotEmpty(setMap, "settings.forcePrune", account.Settings.ForcePrune)
187+
setIfNotEmpty(setMap, "settings.active", account.Settings.Active)
189188

189+
update := bson.M{"$set": setMap}
190190
return update
191191
}
192192

0 commit comments

Comments
 (0)