Golang客户端API测试会话跟踪

时间:2019-10-04 12:09:22

标签: api go testing client session-cookies

我正在制作一个自动化的API测试套件。我想模拟实际的最终用户呼叫,并且不想使用req.Header.Add("Set-Cookie", Session) 。除了会话(未从服务器进行身份验证)以外,其他所有程序都工作正常,即使第一次测试运行是对仅通过成功登录(基本/承载)和会话才能继续的身份验证端点进行测试。

您如何跟踪从一个响应到下一个请求的会话?其中包括完整的源代码以及相关的输出和服务器日志。

在Postman中一切正常,但是如果我导出为Go代码并运行,则会遇到相同的问题。一定是带有跟踪会话的东西。

在请求设置中,我有:

cookie := res.Header.Get("Set-Cookie")
if len(cookie) > 0 {
   Session = cookie
}

会话在响应中更新:

=== RUN   TestAuthenticate

golang-api->TestAuthenticate: Testing endpoint /authenticate...
golang-api->TestAuthenticate: > No Authorization provided in header...

golang-api->TestAuthenticate: > Basic missing and bearer present in header...

golang-api->TestAuthenticate: > Bearer missing and basic present in header...

golang-api->TestAuthenticate: > Invalid Authorization Header (Bad Basic)...

golang-api->TestAuthenticate: > Invalid Authorization Header (Bad Bearer)...

golang-api->TestAuthenticate: > Invalid Authorization Header (Bad Basic & Bearer)...

golang-api->TestAuthenticate: > Valid Authorization Header (Good Basic & Bearer)...

golang-api->TestAuthenticate: SUCCESS Ok
--- PASS: TestAuthenticate (0.03s)
=== RUN   TestBonus

golang-api->TestBonus: Testing endpoint /bonus...
golang-api->TestBonus: > No Basic Authorization provided in header...

golang-api->TestBonus: > Invalid Basic Authorization Header (Bad Basic)...

golang-api->TestBonus: > Valid Basic Authorization Header (Good Basic)...

golang-api->TestBonus: ERROR Expected Status Code 200 got 401 instead
Exiting on GET request http://10.11.2.148:8080/bonus ...

REQUEST: &{Method:GET URL:http://10.11.2.148:8080/bonus Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[User-Agent:[GoLang API Test/0.1.0] Cache-Control:[no-cache] Authorization:[Basic YXNoaXNoOmEyNkIpbl5BPyU2V2YhQXNKdw==]] Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:10.11.2.148:8080 Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:<nil>}

RESPONSE: &{Status:401 Unauthorized StatusCode:401 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[X-Xss-Protection:[1; mode=block] Referrer-Policy:[same-origin] Set-Cookie:[session=x4XJfYZsDpSqf_cWigHS3yuT5hqlEntU_WNfJW3j-661sGalr-NcvF6Ey1R340jmvjw8x6O1vie2ctHCW0C0hCjekfDESa3dxIKWt__7FSeidYg1VN-aaTljYeKHfQzuCPvjVak; Path=/; HttpOnly] Strict-Transport-Security:[max-age=31536000; includeSubDomains] X-Content-Type-Options:[nosniff] Content-Type:[application/json; charset=UTF-8] X-Frame-Options:[deny] Date:[Fri, 04 Oct 2019 11:29:01 GMT] Content-Length:[71]] Body:0xc000055100 ContentLength:71 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc0000e6c00 TLS:<nil>}

FAIL    command-line-arguments  0.038s

输出

2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Invalid authorization, none provided","status":"Bad Request"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Invalid authorization, 1 provided, requires 2","status":"Bad Request"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Invalid authorization, 1 provided, requires 2","status":"Bad Request"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Issue decoding API Key: illegal base64 data at input byte 7","status":"Bad Request"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Invalid JWT timestamp: Expired","status":"Bad Request"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Issue decoding API Key: illegal base64 data at input byte 7","status":"Bad Request"}
2019/10/04 12:01:54 NOTIC: Auth   [4537722] via Redis cached value
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Invalid authorization, none provided","status":"Forbidden"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Issue decoding API Key: illegal base64 data at input byte 7","status":"Forbidden"}
2019/10/04 12:01:54  INFO: Handlers.HandleError(): Sent-> {"message":"Error: Session not authenticated","status":"Unauthorized"}

服务器日志

package api


import (
    "time"
    "os"
    "fmt"
    "net/http"
    "testing"
)

// TODO: Create config loaded from yaml that allows external settings without recompile
// Make sure to update constants for your environment
const (
    UseHost             = "http://10.11.2.148:8080"
    ValidBasic          = "cWFfVGVzdDplaih4cVE+JyZzNS9QdThu"
    InvalidBasic        = "INVALID_BASIC"
    ValidBearer         = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.X3Jca1o8Db3AdypEu_yYCiOXmy00wgOxHS-3i2cC519UUupBKvYEx1V9x1OysrVWAmJSe7v0fs3iikq6L0Ky6zD6G75B4zmYqAbROFVCuwEKUWgVaykpJI-8u6NKkjGs5qNNxzAiHDQECv7AUUPhOuEeGpxq7y4OSMTDt4ZEYxml_5rNN-g057ViKZiBw9Q0J7FNKqWDf8XK852jcZi8VMM8dHaIv0pBP9M28lP6TaMmEqZRzIJbs2V4YxcMO5KkZhNySNeafycVAZyatiZKYyB0M7_qLSC7A1UEbK1U17n7u2FdE1JCdqKdxONvSFNzInJYwWcY9EU2qsACkRTULQ.pej62Nk9Ra_xVl5P.ZYzrJqx93_GSHgpznT2ZNq9lRWeIPd6r1bEnww0sXczsA84bYMrwLeN_Zb0nl2vvRFem32qUcWfkADVTvYl_3YgQ372darr7TW6geUEG8kQ72LcJ356iHRQ.KFzdokMLDhUlc8xkQ24uYQ"
    InvalidBearer       = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.ckkQI8TMs7kawGrI6ZA18zVk7NizpwF11aiU015lz2NM6s7gcN0OE0g2td5__TUIIjduHHKt8jCLaeyV0G_j_ai3-GMRLBEiMFizOUZqTUcPpwv0B-DQZ22B-exd-URtZL5RyxxT8FXoLnn7gMEXhURnj06Z0HbSuVIlryzQDwsfFsStw0CpxA1ZCEV3kC54xQetvBiptqJ1IDZaUh0fbAjFDURl9Q5tngyKvG8k6NZE8fpR3hbAukhuAU5cLP8P8DPeHY4wgSXN-xBJIG4vh2EAElHFDcBem_Hf6StkjPltMzUe1qbrLJOpmDNTgHqysCF7xvnJjkuYphGZa_W77g.WZvcU-KICyHg_OFu.LqQc7szbYzP8W3hsSRKWhJsKwFY_Vk38gO2y_-M_HdhgVATYzokKPladyEnOq5fuTegkd69TNwCb8ltC08n_gRLIwqdwgeJDIylWB4Lj_eTW.IJH0URIMFTnrSevMjxgt5Q"
)

type AuthorizationTest int
const (
    AuthorizationTestNone AuthorizationTest = 1 + iota      // No Authorization in header
    AuthorizationTestNoBasic                                // No Basic in Authorization
    AuthorizationTestNoBearer                               // No Bearer in Authorization
    AuthorizationTestInvalidBasic                           // Invalid Basic in Authorization
    AuthorizationTestInvalidBearer                          // Invalid Bearer in Authorization
    AuthorizationTestInvalidBasicBearer                     // Invalid Basic & Bearer in Authorization
    AuthorizationTestValid                                  // valid Basic & Bearer in Authorization
    AuthorizationTestBasicNone                              // Just Basic, none provided
    AuthorizationTestBasicInvalid                           // Just Basic, Invalid Provided
    AuthorizationTestBasicValid                             // Just Basic, Valid Provided
)

type LogType int
const (
    LogTypeEndpoint LogType = 1 + iota
    LogTypeSubEvent
    LogTypeError
    LogTypeSuccess
)

var (
    Session string
)

func Log(lt LogType, who string, what string) {
    if lt == LogTypeEndpoint {
        fmt.Printf("\n%s: Testing endpoint /%s...", who, what)
    } else if lt == LogTypeSubEvent {
        fmt.Printf("\n%s: > %s...", who, what)
    } else if lt == LogTypeError {
        fmt.Printf("\n%s: ERROR %s\n", who, what)
    } else if lt == LogTypeSuccess {
        fmt.Printf("\n%s: SUCCESS %s\n", who, what)
    } else {
        panic("Unknown Log Type")
    }
}

func addCookie(w http.ResponseWriter, name string, value string) {
    expire := time.Now().AddDate(0, 0, 1)
    cookie := http.Cookie{
        Name:    name,
        Value:   value,
        Expires: expire,
    }
    http.SetCookie(w, &cookie)
}

func doRequest(test, method string, url string, desiredStatus int, at AuthorizationTest) *http.Response {
    req, _ := http.NewRequest(method, url, nil)
    req.Header.Add("User-Agent", "GoLang API Test/0.1.0")
    req.Header.Add("Cache-Control", "no-cache")
    if len(Session) > 0 {
        //cookie, _ := req.Cookie("session")
        req.Header.Add("Set-Cookie", Session)
    }
    //req.Header.Add("Accept", "*/*")
    //req.Header.Add("Host", UseHost)
    //req.Header.Add("Accept-Encoding", "gzip, deflate")
    //req.Header.Add("Connection", "keep-alive")

    switch at {
    // Basic & Bearer
    case AuthorizationTestNone:
        break
    case AuthorizationTestNoBasic:
        req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ValidBearer))  
    case AuthorizationTestNoBearer:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s", ValidBasic))    
    case AuthorizationTestInvalidBasic:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s; Bearer %s", InvalidBasic, ValidBearer))  
    case AuthorizationTestInvalidBearer:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s; Bearer %s", ValidBasic, InvalidBearer))  
    case AuthorizationTestInvalidBasicBearer:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s; Bearer %s", InvalidBasic, InvalidBearer))    
    case AuthorizationTestValid:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s; Bearer %s", ValidBasic, ValidBearer))
    // Just Basic
    case AuthorizationTestBasicNone:
        break
    case AuthorizationTestBasicInvalid:
        req.Header.Add("Authorization", fmt.Sprintf("Basic %s", InvalidBasic))
    case AuthorizationTestBasicValid:
        req.Header.Add("Authorization", "Basic YXNoaXNoOmEyNkIpbl5BPyU2V2YhQXNKdw==")   
    }   

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    // Did we just try to authenticate?
    //if at == AuthorizationTestValid {
        // Was it successful?
        //if res.StatusCode == http.StatusOK {
            //Kewl, Save the Session for remaining tests
            //cookie := res.Header.Get("Set-Cookie")
            cookie, err := req.Cookie("session")
            if err != nil {
                //cookie, err := req.Cookie("session")
                Session = cookie.String()
                //req.Header.Add("Set-Cookie", cookie.String())
                //addCookie(res.Write,"session",cookie)
                //session=IOY2A55EKzTrqS1YLjWSq-axdDQAGLqLN5EyHdY5hd4sUTrVIPps3P1EhSP3pyFDFgIyW-EhpqzZeyVGsR_JyueVitAX6MWhpBFQiW2N8KDhedv2LviS_dhM0KGoMtTlRfYlQo-3cySIvA5oPOS_BSEuT2HmjlNCr2DzxDGX1Ig; Path=/; HttpOnly
                //Session = cookie
                fmt.Println(Session)
            }
        //} 
    //}

    if res.StatusCode != desiredStatus {
        Log(LogTypeError,test,fmt.Sprintf("Expected Status Code %d got %d instead",desiredStatus,res.StatusCode))
        fmt.Printf("Exiting on %s request %s ...\n", method, url)
        fmt.Printf("\nREQUEST: %+v\n",req)
        fmt.Printf("\nRESPONSE: %+v\n\n",res)
        os.Exit(1)
    }   

    return res
}

func doRequestAuthorization(caller string, method string, endpoint string) {

    url := fmt.Sprintf("%s/%s", UseHost, endpoint)
    if endpoint == "authenticate" {
        // User Basic & Bearer for Authorization
        Log(LogTypeSubEvent,caller,"No Authorization provided in header")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestNone)

        Log(LogTypeSubEvent,caller,"Basic missing and bearer present in header")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestNoBasic)

        Log(LogTypeSubEvent,caller,"Bearer missing and basic present in header")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestNoBearer)

        Log(LogTypeSubEvent,caller,"Invalid Authorization Header (Bad Basic)")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestInvalidBasic)

        Log(LogTypeSubEvent,caller,"Invalid Authorization Header (Bad Bearer)")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestInvalidBearer)

        Log(LogTypeSubEvent,caller,"Invalid Authorization Header (Bad Basic & Bearer)")
        _ = doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestInvalidBasicBearer)

        Log(LogTypeSubEvent,caller,"Valid Authorization Header (Good Basic & Bearer)")
        _ = doRequest(caller, method, url, http.StatusOK, AuthorizationTestValid)
    } else {
        // Use Basic only for Authorization
        Log(LogTypeSubEvent,caller,"No Basic Authorization provided in header")
        _ = doRequest(caller, method, url, http.StatusForbidden, AuthorizationTestBasicNone)

        Log(LogTypeSubEvent,caller,"Invalid Basic Authorization Header (Bad Basic)")
        _ = doRequest(caller, method, url, http.StatusForbidden, AuthorizationTestBasicInvalid)

        Log(LogTypeSubEvent,caller,"Valid Basic Authorization Header (Good Basic)")
        _ = doRequest(caller, method, url, http.StatusOK, AuthorizationTestBasicValid)
    }
}

// doRequestNoParameters tests endpoints that do not accept any parameters
func doRequestNoParameters(caller string, method string, endpoint string) *http.Response {
    Log(LogTypeSubEvent, caller, "No query parameters specified")
    url := fmt.Sprintf("%s/%s?foo=bar", UseHost, endpoint)
    return doRequest(caller, method, url, http.StatusBadRequest, AuthorizationTestInvalidBasicBearer)
}
func TestAuthenticate(t *testing.T) {
    caller := fmt.Sprintf("%s->%s", os.Getenv("REPOSITORY"), t.Name())
    Log(LogTypeEndpoint, caller, "authenticate")
    doRequestAuthorization(caller, "POST", "authenticate")
    Log(LogTypeSuccess, caller, "Ok")
}

// Test "GET", "/bonus"
func TestBonus(t *testing.T) {
    endpoint := "bonus"
    caller := fmt.Sprintf("%s->%s", os.Getenv("REPOSITORY"), t.Name())

    Log(LogTypeEndpoint, caller, endpoint)
    doRequestAuthorization(caller, "GET", endpoint)
    Log(LogTypeSubEvent,caller,"Valid Query")
    url := fmt.Sprintf("%s/%s", UseHost, endpoint)
    _ = doRequest(caller, "GET", url, http.StatusOK, AuthorizationTestBasicValid)
}

// TODO: Test "GET", "/category"
func TestCategory(t *testing.T) {
    endpoint := "category"
    caller := fmt.Sprintf("%s->%s", os.Getenv("REPOSITORY"), t.Name())

    Log(LogTypeEndpoint, caller, endpoint)
    doRequestAuthorization(caller, "GET", endpoint)
    Log(LogTypeSubEvent,caller,"Valid Query")
    url := fmt.Sprintf("%s/%s", UseHost, endpoint)
    _ = doRequest(caller, "GET", url, http.StatusOK, AuthorizationTestBasicValid)
}

完整来源 提供以显示我正在尝试的全部内容。但是,由于服务器是私有服务器,因此显然无法在您的计算机上工作。

async(flags: .barrier)

0 个答案:

没有答案