Skip to content

Commit

Permalink
fix race conditions in TestTelegramConfirmedRequest
Browse files Browse the repository at this point in the history
There were two different race conditions between logic in TestTelegramConfirmedRequest
and TelegramAPIMock.GetUpdatesFunc and TelegramAPIMock.SendFunc:
* GetUpdatesFunc may start before token was fetched,
then it produces empty telegramUpdate response, which causes assertions in SendFunc to fail.
* When token becomes used and removed from wait queue after successful login completion,
then GetUpdatesFunc may be still called and new telegram update is created for same token.
This breaks telegram update processing logic,
and SendFunc gets called with the error parameter, which also breaks assertions.
  • Loading branch information
cyb3r4nt committed Aug 30, 2024
1 parent ec38494 commit 523974c
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 24 deletions.
39 changes: 27 additions & 12 deletions provider/telegram_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,37 @@ func TestTelegramUnconfirmedRequest(t *testing.T) {

func TestTelegramConfirmedRequest(t *testing.T) {
var servedToken string
var mu sync.Mutex

// is set when token becomes used,
// no sync is required because only a single goroutine in TelegramHandler.Run() reads and writes it
var tokenAlreadyUsed bool

var wgToken sync.WaitGroup
wgToken.Add(1)
defer func() {
if t.Failed() && servedToken == "" {
wgToken.Done() // for the case when test fails before token is generated
}
}()

m := &TelegramAPIMock{
GetUpdatesFunc: func(ctx context.Context) (*telegramUpdate, error) {
var upd telegramUpdate
wgToken.Wait()

mu.Lock()
defer mu.Unlock()
if servedToken != "" {
resp := fmt.Sprintf(getUpdatesResp, servedToken)
if tokenAlreadyUsed || t.Failed() {
return nil, fmt.Errorf("token %s has been already used", servedToken)
}

err := json.Unmarshal([]byte(resp), &upd)
if err != nil {
t.Fatal(err)
}
var upd telegramUpdate
resp := fmt.Sprintf(getUpdatesResp, servedToken)
err := json.Unmarshal([]byte(resp), &upd)
if err != nil {
t.Fatal(err)
}

// token is served only once
tokenAlreadyUsed = true

return &upd, nil
},
AvatarFunc: func(ctx context.Context, userID int) (string, error) {
Expand Down Expand Up @@ -147,10 +162,10 @@ func TestTelegramConfirmedRequest(t *testing.T) {
err := json.Unmarshal(w.Body.Bytes(), &resp)
assert.NoError(t, err)
assert.Equal(t, "my_auth_bot", resp.Bot)
assert.NotEmpty(t, resp.Token)

mu.Lock()
servedToken = resp.Token
mu.Unlock()
wgToken.Done()

// Check the token confirmation
assert.Eventually(t, func() bool {
Expand Down
38 changes: 26 additions & 12 deletions v2/provider/telegram_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,36 @@ func TestTelegramUnconfirmedRequest(t *testing.T) {

func TestTelegramConfirmedRequest(t *testing.T) {
var servedToken string
var mu sync.Mutex
// is set when token becomes used,
// no sync is required because only a single goroutine in TelegramHandler.Run() reads and writes it
var tokenAlreadyUsed bool

var wgToken sync.WaitGroup
wgToken.Add(1)
defer func() {
if t.Failed() && servedToken == "" {
wgToken.Done() // for the case when test fails before token is generated
}
}()

m := &TelegramAPIMock{
GetUpdatesFunc: func(ctx context.Context) (*telegramUpdate, error) {
var upd telegramUpdate
wgToken.Wait()

mu.Lock()
defer mu.Unlock()
if servedToken != "" {
resp := fmt.Sprintf(getUpdatesResp, servedToken)
if tokenAlreadyUsed || t.Failed() {
return nil, fmt.Errorf("token %s has been already used", servedToken)
}

err := json.Unmarshal([]byte(resp), &upd)
if err != nil {
t.Fatal(err)
}
var upd telegramUpdate
resp := fmt.Sprintf(getUpdatesResp, servedToken)
err := json.Unmarshal([]byte(resp), &upd)
if err != nil {
t.Fatal(err)
}

// token is served only once
tokenAlreadyUsed = true

return &upd, nil
},
AvatarFunc: func(ctx context.Context, userID int) (string, error) {
Expand Down Expand Up @@ -147,10 +161,10 @@ func TestTelegramConfirmedRequest(t *testing.T) {
err := json.Unmarshal(w.Body.Bytes(), &resp)
assert.NoError(t, err)
assert.Equal(t, "my_auth_bot", resp.Bot)
assert.NotEmpty(t, resp.Token)

mu.Lock()
servedToken = resp.Token
mu.Unlock()
wgToken.Done()

// Check the token confirmation
assert.Eventually(t, func() bool {
Expand Down

0 comments on commit 523974c

Please sign in to comment.