From 9e10c4eb94e7e7c015bad95322e34135bdab23a5 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:25:34 -0700 Subject: [PATCH 1/2] Refactored StartAccountHandler to better manage Goroutines liveness --- src/core/handler.go | 22 ++++++++++++++----- src/core/handler_test.go | 47 ++++++++++++++++++++++++++++++++-------- src/model/account.go | 1 + 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/core/handler.go b/src/core/handler.go index 65cb4c1..91a7760 100644 --- a/src/core/handler.go +++ b/src/core/handler.go @@ -31,7 +31,7 @@ func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) { // Iterate over accounts and start account handlers for _, account := range accounts { - go c.StartAccountHandler(account) + go c.StartAccountHandler(account.ID.Hex()) } // Update core handler status @@ -39,21 +39,33 @@ func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) { return c.status, nil } -func (c *CoreHandler) StartAccountHandler(account model.Account) { +func (c *CoreHandler) StartAccountHandler(accountId string) { for { - if !account.Settings.Active { + // Fetch account + account, _ := c.AccountRepository.FetchByAccountId(accountId) + + if account == nil { + // Terminate the goroutine (account was deleted) return } - account, _ := c.AccountRepository.FetchByDomainId(account.Domain.ID) - timeWindow, unitWindow := getTimeWindow(account.Settings.Window) + // Wait for account to be active + if !account.Settings.Active { + c.updateDomainStatus(*account, model.StatusPending, "Account was deactivated") + time.Sleep(1 * time.Minute) + continue + } + // Fetch repository data repositoryData, err := c.GitService.GetRepositoryData(account.Environment) + // Check if repository is out of sync if err == nil && isRepositoryOutSync(*account, repositoryData.CommitHash) { c.syncUp(*account, repositoryData) } + // Wait for the next cycle + timeWindow, unitWindow := getTimeWindow(account.Settings.Window) time.Sleep(time.Duration(timeWindow) * unitWindow) } } diff --git a/src/core/handler_test.go b/src/core/handler_test.go index bb1ac8e..7fc2107 100644 --- a/src/core/handler_test.go +++ b/src/core/handler_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "runtime" "testing" "time" @@ -18,14 +19,14 @@ func TestInitCoreHandlerCoroutine(t *testing.T) { coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService()) account := givenAccount() + account.Domain.ID = "123-init-core-handler" coreHandler.AccountRepository.Create(&account) // Test status, err := coreHandler.InitCoreHandlerCoroutine() // Terminate the goroutine - account.Settings.Active = false - coreHandler.AccountRepository.Update(&account) + coreHandler.AccountRepository.DeleteByDomainId(account.Domain.ID) time.Sleep(1 * time.Second) // Assert @@ -39,23 +40,47 @@ func TestStartAccountHandler(t *testing.T) { t.Run("Should not sync when account is not active", func(t *testing.T) { // Given account := givenAccount() + account.Domain.ID = "123-not-active" account.Settings.Active = false coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account) + go coreHandler.StartAccountHandler(account.ID.Hex()) time.Sleep(1 * time.Second) // Assert accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) - assert.Equal(t, model.StatusOutSync, accountFromDb.Domain.Status) - assert.Equal(t, "", accountFromDb.Domain.Message) + assert.Equal(t, model.StatusPending, accountFromDb.Domain.Status) + assert.Equal(t, "Account was deactivated", accountFromDb.Domain.Message) assert.Equal(t, "", accountFromDb.Domain.LastCommit) tearDown() }) + t.Run("Should not sync after account is deleted", func(t *testing.T) { + // Given + account := givenAccount() + account.Domain.ID = "123-deleted" + coreHandler.AccountRepository.Create(&account) + + // Test + go coreHandler.StartAccountHandler(account.ID.Hex()) + numGoroutinesBefore := runtime.NumGoroutine() + + // Terminate the goroutine + coreHandler.AccountRepository.DeleteByDomainId(account.Domain.ID) + time.Sleep(1 * time.Second) + numGoroutinesAfter := runtime.NumGoroutine() + + // Assert + _, err := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + assert.Less(t, numGoroutinesAfter, numGoroutinesBefore) + assert.NotNil(t, err) + + tearDown() + }) + t.Run("Should sync successfully when repository is out of sync", func(t *testing.T) { // Given fakeGitService := NewFakeGitService() @@ -63,11 +88,12 @@ func TestStartAccountHandler(t *testing.T) { coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService()) account := givenAccount() + account.Domain.ID = "123-out-sync" account.Domain.Version = "1" coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account) + go coreHandler.StartAccountHandler(account.ID.Hex()) // Terminate the goroutine account.Settings.Active = false @@ -94,11 +120,12 @@ func TestStartAccountHandler(t *testing.T) { coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService()) account := givenAccount() + account.Domain.ID = "123-newer-version" account.Domain.Version = "0" coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account) + go coreHandler.StartAccountHandler(account.ID.Hex()) // Terminate the goroutine account.Settings.Active = false @@ -125,10 +152,11 @@ func TestStartAccountHandler(t *testing.T) { coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService()) account := givenAccount() + account.Domain.ID = "123-api-error" coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account) + go coreHandler.StartAccountHandler(account.ID.Hex()) // Terminate the goroutine account.Settings.Active = false @@ -154,10 +182,11 @@ func TestStartAccountHandler(t *testing.T) { coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService()) account := givenAccount() + account.Domain.ID = "123-no-permission" coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account) + go coreHandler.StartAccountHandler(account.ID.Hex()) // Terminate the goroutine account.Settings.Active = false diff --git a/src/model/account.go b/src/model/account.go index d7a2978..7eaabdb 100644 --- a/src/model/account.go +++ b/src/model/account.go @@ -7,6 +7,7 @@ const ( ) const ( + StatusPending = "Pending" StatusSynced = "Synced" StatusOutSync = "OutSync" StatusError = "Error" From 78dc0b096e3acce040d8877b19b437991a861e18 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:36:58 -0700 Subject: [PATCH 2/2] chore: fix db fixture for tests --- src/core/handler_test.go | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/core/handler_test.go b/src/core/handler_test.go index 7fc2107..45164b3 100644 --- a/src/core/handler_test.go +++ b/src/core/handler_test.go @@ -20,13 +20,13 @@ func TestInitCoreHandlerCoroutine(t *testing.T) { account := givenAccount() account.Domain.ID = "123-init-core-handler" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test status, err := coreHandler.InitCoreHandlerCoroutine() // Terminate the goroutine - coreHandler.AccountRepository.DeleteByDomainId(account.Domain.ID) + coreHandler.AccountRepository.DeleteByDomainId(accountCreated.Domain.ID) time.Sleep(1 * time.Second) // Assert @@ -42,15 +42,15 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-not-active" account.Settings.Active = false - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Equal(t, model.StatusPending, accountFromDb.Domain.Status) assert.Equal(t, "Account was deactivated", accountFromDb.Domain.Message) assert.Equal(t, "", accountFromDb.Domain.LastCommit) @@ -62,19 +62,19 @@ func TestStartAccountHandler(t *testing.T) { // Given account := givenAccount() account.Domain.ID = "123-deleted" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) numGoroutinesBefore := runtime.NumGoroutine() // Terminate the goroutine - coreHandler.AccountRepository.DeleteByDomainId(account.Domain.ID) + coreHandler.AccountRepository.DeleteByDomainId(accountCreated.Domain.ID) time.Sleep(1 * time.Second) numGoroutinesAfter := runtime.NumGoroutine() // Assert - _, err := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + _, err := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Less(t, numGoroutinesAfter, numGoroutinesBefore) assert.NotNil(t, err) @@ -90,18 +90,18 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-out-sync" account.Domain.Version = "1" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) // Terminate the goroutine - account.Settings.Active = false - coreHandler.AccountRepository.Update(&account) + accountCreated.Settings.Active = false + coreHandler.AccountRepository.Update(accountCreated) time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, "Synced successfully") assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -122,18 +122,18 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-newer-version" account.Domain.Version = "0" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) // Terminate the goroutine - account.Settings.Active = false - coreHandler.AccountRepository.Update(&account) + accountCreated.Settings.Active = false + coreHandler.AccountRepository.Update(accountCreated) time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, "Synced successfully") assert.Equal(t, "111", accountFromDb.Domain.LastCommit) @@ -153,18 +153,18 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-api-error" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) // Terminate the goroutine - account.Settings.Active = false - coreHandler.AccountRepository.Update(&account) + accountCreated.Settings.Active = false + coreHandler.AccountRepository.Update(accountCreated) time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Equal(t, model.StatusError, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, "Failed to check for changes") assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -183,18 +183,18 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-no-permission" - coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.AccountRepository.Create(&account) // Test - go coreHandler.StartAccountHandler(account.ID.Hex()) + go coreHandler.StartAccountHandler(accountCreated.ID.Hex()) // Terminate the goroutine - account.Settings.Active = false - coreHandler.AccountRepository.Update(&account) + accountCreated.Settings.Active = false + coreHandler.AccountRepository.Update(accountCreated) time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(account.Domain.ID) + accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainId(accountCreated.Domain.ID) assert.Equal(t, model.StatusError, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, "authorization failed") assert.Contains(t, accountFromDb.Domain.Message, "Failed to apply changes [Repository]")