Как написать тестовый случай для проверки токена OAuth?

# #unit-testing #go #testing #integration-testing

Вопрос:

У нас есть серверная служба GoLang(включена поддержка OAuth), которая принимает http-запросы со Authorization значением заголовка "Bearer" OAuthTokenString .

Как написать модульный или интеграционный тестовый случай для серверной службы, чтобы убедиться, что серверная служба включена OAuth(проверяет токен)? я не уверен, что мы не можем создать макет службы( httptest.NewServer ) с включенным OAuth….

Ответ №1:

Это очень интересный вопрос. Я вижу, что ваша команда заботится о минимизации возможных ошибок путем тестирования кода. Это аспект, о котором многие разработчики часто забывают.

Не видя вашего кода, немного сложно предложить 100% правильный ответ для вашего случая.

Я буду считать, что мой пример послужит руководством для написания вашего собственного теста или, в лучшем случае, для оптимизации примера, который я предлагаю

Я использовал gin gonic в качестве веб-платформы HTTP для своего проекта и написал метод аутентификации, который называется промежуточным программным обеспечением для каждой защищенной конечной точки. Затем для тестирования я создал http-сервер только с помощью gin.Default () метода

 // Authenticate auth an endpoint
func Authenticate() gin.HandlerFunc {
  return func(c *gin.Context) {
    var someErr errors.BukyError
    someErr.SetUnauthorized()

    // Fetch token from the headers
    requiredToken := c.GetHeader(constants.AuthorizationHeader)
    if len(requiredToken) == 0 {
        c.AbortWithStatusJSON(someErr.HttpErrorCode, someErr.JSON())
        return
    }

    splittedToken := strings.SplitN(requiredToken, " ", 2)
    if len(splittedToken) != 2 || strings.ToLower(splittedToken[0]) != "bearer" {
        primErr := fmt.Errorf("wrong bearer token format on Authorization Header")
        someErr.PrimitiveErr = amp;primErr
        c.AbortWithStatusJSON(someErr.HttpErrorCode, someErr.JSON())
        return
    }

    // Get email from encoded token
    jwtToken, claims, err := helpers.DecodeJWT(splittedToken[1], false)
    if err != nil {
        someErr.PrimitiveErr = amp;err
        c.AbortWithStatusJSON(someErr.HttpErrorCode, someErr.JSON())
        return
    }

    if _, err := helpers.VerifyObjectIDs(claims.Subject); !err.IsNilError() {
        c.AbortWithStatusJSON(someErr.HttpErrorCode, someErr.JSON())
        return
    }

    // Set the User variable so that we can easily retrieve from other middlewares
    // c.Set("User", result)
    c.Set(constants.ReqBukyJWTKey, jwtToken)
    c.Set(constants.ReqBukyClaimsKey, claims)

    // Call the next middlware
    c.Next()
  }
}
 

А потом я просто проверил, как показано ниже

 func TestAuthenticate(t *testing.T) {
  userID := primitive.NewObjectID().Hex()
  email := "email@email.com"
  firstName := "My Name"
  lastName := "My Lastname"
  scopes := []string{"im_scope"}

  statusOK := "statusOK"
  someProtectedPath := constants.UsersPath   "/"   userID

  engine := gin.Default()
  engine.GET(someProtectedPath, Authenticate(), func(c *gin.Context) {
      c.String(http.StatusOK, statusOK)
  })

  t.Run("NoTokenHeader", func(t *testing.T) {
    t.Run("UnsetHeader", func(t *testing.T) {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", someProtectedPath, nil)
        engine.ServeHTTP(w, req)
        assert.Equal(t, http.StatusUnauthorized, w.Code)
    })

    t.Run("EmptyHeader", func(t *testing.T) {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", someProtectedPath, nil)
        req.Header.Set(constants.AuthorizationHeader, "")
        engine.ServeHTTP(w, req)
        assert.Equal(t, http.StatusUnauthorized, w.Code)
    })
  })

  t.Run("TokenWithBadFormat", func(t *testing.T) {
    t.Run("1", func(t *testing.T) {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", someProtectedPath, nil)
        badFormatedToken := "hola.hola"
        req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("Bearer %s", badFormatedToken))
        engine.ServeHTTP(w, req)
        assert.Equal(t, http.StatusUnauthorized, w.Code)
    })

    t.Run("2", func(t *testing.T) {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", someProtectedPath, nil)
        badFormatedToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."
        req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("Bearer %s", badFormatedToken))
        engine.ServeHTTP(w, req)
        assert.Equal(t, http.StatusUnauthorized, w.Code)
    })

    t.Run("3", func(t *testing.T) {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", someProtectedPath, nil)
        badFormatedToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.hola.hola.hola"
        req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("Bearere %s", badFormatedToken))
        engine.ServeHTTP(w, req)
        assert.Equal(t, http.StatusUnauthorized, w.Code)
    })
  })

  t.Run("ExpiredToken", func(t *testing.T) {
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", someProtectedPath, nil)
    expirationTime := time.Second
    expiredToken, _, err := helpers.GenerateAccessJWT(userID, email, firstName, lastName, scopes, expirationTime)
    time.Sleep(expirationTime * 2)
    req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("Bearer %s", expiredToken))
    engine.ServeHTTP(w, req)
    assert.Equal(t, http.StatusUnauthorized, w.Code)
    assert.Nil(t, err)
  })

  t.Run("ValidToken", func(t *testing.T) {
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", someProtectedPath, nil)
    validToken, _, err := helpers.GenerateAccessJWT(userID, email, firstName, lastName, scopes)
    req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("Bearer %s", validToken))
    engine.ServeHTTP(w, req)
    assert.Nil(t, err)
    assert.Equal(t, http.StatusOK, w.Code)

  })
}
 

Комментарии:

1. Я пытаюсь с httptest.NewServer