add test coverage

for the following auth related packages:
* authn
* http
* server

fix minor bugs, do some cleaning up, etc in `authn/authenticate.go` and `http/handler.go`
This commit is contained in:
reesporte 2022-01-21 13:57:47 -06:00
parent c7208cf5e3
commit 836df379ac
5 changed files with 525 additions and 82 deletions

View file

@ -117,6 +117,7 @@ type Groups struct {
// Authenticate takes in a bearer token `bearer` and returns UserInfo from that token
func (a *Auth) Authenticate(bearer string) (*UserInfo, error) {
// parse the bearer token into a jwt.Token
// this also validates the token, and checks that it's not expired
token, err := jwt.Parse(bearer, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
@ -124,39 +125,21 @@ func (a *Auth) Authenticate(bearer string) (*UserInfo, error) {
return a.secretKey, nil
})
if token == nil || token.Claims == nil || err != nil || !token.Valid {
return nil, errors.Wrap(err, fmt.Sprintf("%#v parsing jwt claims from access tokens", token))
return nil, fmt.Errorf("parsing bearer token: %v", err)
}
userInfo := UserInfo{}
// check that token does not expire now
switch claimType := token.Claims.(type) {
case jwt.MapClaims:
if exp, ok := claimType["exp"]; ok {
var e int64
switch expType := exp.(type) {
case float64:
e = int64(expType)
case json.Number:
e, _ = expType.Int64()
}
if e <= time.Now().Unix() {
return nil, fmt.Errorf("token expired")
}
}
userInfo.UserID = claimType["oid"].(string)
userInfo.UserName = claimType["name"].(string)
userInfo.Token = bearer
claims := token.Claims.(jwt.MapClaims)
userInfo.UserID = claims["oid"].(string)
userInfo.UserName = claims["name"].(string)
userInfo.Token = bearer
g := claimType["molecula-idp-groups"].(string)
groups, err := FromGob64(g)
if err != nil {
return nil, errors.Wrap(err, "decoding groups")
}
userInfo.Groups = groups
default:
return nil, fmt.Errorf("could not parse jwt claims of type %T, expected jwt.MapClaims", claimType)
g := claims["molecula-idp-groups"].(string)
groups, err := FromGob64(g)
if err != nil {
return nil, errors.Wrap(err, "decoding groups")
}
userInfo.Groups = groups
return &userInfo, nil
}
@ -194,8 +177,16 @@ func (a *Auth) Redirect(w http.ResponseWriter, r *http.Request) {
return
}
// with vitamin A!
enrichedTkn, err := a.addGroupMembership(token.AccessToken)
// enrich token with groups!
g, err := a.getGroups(token.AccessToken)
if err != nil {
a.logger.Warnf("getting groups from IdP: %+v", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// with vitamin G! (for groups)
enrichedTkn, err := a.addGroupMembership(token.AccessToken, g)
if err != nil {
a.logger.Warnf("enriching token with group membership: %+v", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
@ -217,40 +208,28 @@ func (a *Auth) getToken(r *http.Request, code string) (*oauth2.Token, error) {
// addGroupMembership is only called in `a.Redirect`. It adds groups to a jwt's
// claims, and signs it using `a.secretKey`.
func (a *Auth) addGroupMembership(token string) (string, error) {
g, err := a.getGroups(token)
if err != nil {
return "", err
}
func (a *Auth) addGroupMembership(token string, g []Group) (string, error) {
// parse token into jwt
unenriched, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
if unenriched == nil || unenriched.Claims == nil || err != nil {
return "", errors.Wrap(err, fmt.Sprintf("%v parsing jwt claims from access tokens", token))
return "", fmt.Errorf("parsing bearer token: %v", err)
}
enriched := jwt.New(jwt.SigningMethodHS256)
enriched.Claims = unenriched.Claims
var tokenStr string
// parse groups into string format
switch claims := enriched.Claims.(type) {
case jwt.MapClaims:
groupString, err := ToGob64(g)
if err != nil {
return "", errors.Wrap(err, "failed to serialize groups")
}
claims := enriched.Claims.(jwt.MapClaims)
groupString, err := ToGob64(g)
if err != nil {
return "", errors.Wrap(err, "failed to serialize groups")
}
// stick it into jwt claims
claims["molecula-idp-groups"] = groupString
// stick it into jwt claims
claims["molecula-idp-groups"] = groupString
// get stringified and signed jwt
tokenStr, err = enriched.SignedString(a.secretKey)
if err != nil {
return "", errors.Wrap(err, "signing jwt")
}
default:
return "", fmt.Errorf("could not parse jwt claims of type %T, expected jwt.MapClaims", claims)
// get stringified and signed jwt
tokenStr, err := enriched.SignedString(a.secretKey)
if err != nil {
return "", errors.Wrap(err, "signing jwt")
}
return tokenStr, nil
@ -303,7 +282,7 @@ func decodeHex(hexstr string) ([]byte, error) {
return nil, errors.Wrap(err, "decoding hex string to byte slice")
}
if len(data) != 32 {
return nil, errors.Wrap(err, "invalid key length")
return nil, fmt.Errorf("invalid key length")
}
return data, nil
}

View file

@ -1,16 +1,24 @@
package authn
import (
"bytes"
"encoding/hex"
"fmt"
"net/http"
"net/http/httptest"
"os"
"reflect"
"strings"
"testing"
"time"
"github.com/golang-jwt/jwt"
"github.com/molecula/featurebase/v3/logger"
"github.com/pkg/errors"
)
func TestAuth(t *testing.T) {
func NewTestAuth(t *testing.T) *Auth {
t.Helper()
var (
ClientID = "e9088663-eb08-41d7-8f65-efb5f54bbb71"
ClientSecret = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
@ -20,7 +28,6 @@ func TestAuth(t *testing.T) {
LogoutURL = "https://login.microsoftonline.com/common/oauth2/v2.0/logout"
Scopes = []string{"https://graph.microsoft.com/.default", "offline_access"}
Key = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
ShortKey = "DEADBEEFD"
)
a, err := NewAuth(
@ -36,9 +43,13 @@ func TestAuth(t *testing.T) {
Key,
)
if err != nil {
t.Errorf("building auth object%s", err)
t.Fatalf("building auth object%s", err)
}
return a
}
func TestAuth(t *testing.T) {
a := NewTestAuth(t)
t.Run("SetCookie", func(t *testing.T) {
w := httptest.NewRecorder()
err := a.setCookie(w, "a cookie value", time.Now().Add(time.Hour))
@ -53,23 +64,267 @@ func TestAuth(t *testing.T) {
if got, want := w.Result().Cookies()[0].Path, "/"; got != want {
t.Fatalf("path=%s, want %s", got, want)
}
})
t.Run("KeyLength", func(t *testing.T) {
_, err := NewAuth(
logger.NewStandardLogger(os.Stdout),
"http://localhost:10101/",
Scopes,
AuthorizeURL,
TokenURL,
GroupEndpointURL,
LogoutURL,
ClientID,
ClientSecret,
ShortKey,
[]string{"https://graph.microsoft.com/.default", "offline_access"},
"https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize",
"https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token",
"https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true",
"https://login.microsoftonline.com/common/oauth2/v2.0/logout",
"e9088663-eb08-41d7-8f65-efb5f54bbb71",
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
"DEADBEEFD",
)
if err == nil || !strings.Contains(err.Error(), "decoding secret key") {
t.Fatalf("expected error decoding secret key got: %v", err)
}
})
t.Run("GetSecretKey", func(t *testing.T) {
want, _ := hex.DecodeString("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
if got := a.SecretKey(); !bytes.Equal(got, want) {
t.Fatalf("expected %v, got %v", got, want)
}
})
cases := []struct {
name string
uid string
uname string
exp interface{}
groups []Group
err error
}{
{
name: "GoodToken",
uid: "42",
uname: "A. Token",
groups: []Group{
{
GroupID: "ac97c9e2-346b-42a2-b6da-18bcb61a32fe",
GroupName: "adminGroup",
},
},
},
{
name: "ExpiredToken",
uid: "42",
uname: "A. Token",
groups: []Group{
{
GroupID: "ac97c9e2-346b-42a2-b6da-18bcb61a32fe",
GroupName: "adminGroup",
},
},
exp: "-17764800",
err: errors.Wrap(fmt.Errorf("Token is expired"), "parsing bearer token"),
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
tkn := jwt.New(jwt.SigningMethodHS256)
claims := tkn.Claims.(jwt.MapClaims)
groupString, err := ToGob64(test.groups)
if err != nil {
t.Fatalf("unexpected error when gobbing groups %v", err)
}
claims["molecula-idp-groups"] = groupString
claims["oid"] = test.uid
claims["name"] = test.uname
if test.exp != nil {
claims["exp"] = test.exp
}
token, err := tkn.SignedString(a.SecretKey())
if err != nil {
t.Fatalf("unexpected error when signing token %v", err)
}
uinfo, err := a.Authenticate(token)
// okay this part kind of sucks bc we need to check errors and i
// dont want to write a whole new test for things that should have
// errors just to avoid this mess. errors.Is doesn't work either
if (test.err == nil && err != nil) || (test.err != nil && err == nil) {
t.Fatalf("expected %v, but got %v", test.err, err)
} else if test.err != nil && err != nil {
if test.err.Error() != err.Error() {
t.Fatalf("expected %v, but got %v", test.err, err)
} else {
return
}
}
if !reflect.DeepEqual(uinfo.Groups, test.groups) {
t.Fatalf("expected %v, got %v", test.groups, uinfo.Groups)
}
if !reflect.DeepEqual(uinfo.UserID, test.uid) {
t.Fatalf("expected %v, got %v", test.uid, uinfo.UserID)
}
if !reflect.DeepEqual(uinfo.UserName, test.uname) {
t.Fatalf("expected %v, got %v", test.uname, uinfo.UserName)
}
})
}
}
func TestGobs(t *testing.T) {
t.Run("goodGob!", func(t *testing.T) {
g := []Group{
{
GroupID: "groupA",
GroupName: "groupA-Name",
},
{
GroupID: "groupB",
GroupName: "groupB-Name",
},
{
GroupID: "groupC",
GroupName: "groupC-Name",
},
}
gobbed, err := ToGob64(g)
if err != nil {
t.Fatalf("could not gob %+v", g)
}
ungobbed, err := FromGob64(gobbed)
if err != nil {
t.Fatalf("could not ungob %+v", gobbed)
}
if !reflect.DeepEqual(ungobbed, g) {
t.Fatalf("expected %v, got %v", g, ungobbed)
}
})
}
func TestDecodeHex(t *testing.T) {
t.Run("cantDecode", func(t *testing.T) {
_, err := decodeHex("gggg")
if err == nil {
t.Fatalf("expected err cannot decode slice, got nil")
}
})
t.Run("tooSmall", func(t *testing.T) {
_, err := decodeHex("DEADBEEF")
if err == nil {
t.Fatalf("expected err wrong length, got nil")
}
})
t.Run("tooBig", func(t *testing.T) {
_, err := decodeHex("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
if err == nil {
t.Fatalf("expected err wrong length, got nil")
}
})
t.Run("justRight", func(t *testing.T) {
_, err := decodeHex("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
if err != nil {
t.Fatalf("expected nil, got %v", err)
}
})
}
func TestAddGroupMembership(t *testing.T) {
cases := []struct {
name string
groups []Group
err error
}{
{
name: "emptyGroups",
groups: []Group{},
err: nil,
},
{
name: "happyPath",
groups: []Group{
{
GroupID: "ac97c9e2-346b-42a2-b6da-18bcb61a32fe",
GroupName: "adminGroup",
},
},
err: nil,
},
}
a := NewTestAuth(t)
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
tkn := jwt.New(jwt.SigningMethodHS256)
token, err := tkn.SignedString(a.SecretKey())
if err != nil {
t.Fatalf("unexpected error when signing token %v", err)
}
tokenWithGroups, err := a.addGroupMembership(token, test.groups)
// okay this part kind of sucks bc we need to check errors and i
// dont want to write a whole new test for things that should have
// errors just to avoid this mess. errors.Is doesn't work either
if (test.err == nil && err != nil) || (test.err != nil && err == nil) {
t.Fatalf("expected %v but got %v", test.err, err)
} else if test.err != nil && err != nil {
if test.err.Error() != err.Error() {
t.Fatalf("expected %v, but got %v", test.err, err)
} else {
return
}
}
parsed, _, err := new(jwt.Parser).ParseUnverified(tokenWithGroups, jwt.MapClaims{})
if err != nil {
t.Fatalf("unexpected error parsing token %v", err)
}
claims := parsed.Claims.(jwt.MapClaims)
groups, err := FromGob64(claims["molecula-idp-groups"].(string))
if err != nil {
t.Fatalf("unexpected error parsing groupString %v", err)
}
if !reflect.DeepEqual(groups, test.groups) {
t.Fatalf("expected %v, got %v", test.groups, groups)
}
})
}
}
func TestHandlers(t *testing.T) {
a := NewTestAuth(t)
t.Run("login", func(t *testing.T) {
req := httptest.NewRequest("GET", "/login", nil)
w := httptest.NewRecorder()
a.Login(w, req)
resp := w.Result()
if resp.StatusCode != http.StatusTemporaryRedirect {
t.Fatalf("expected redirect, got %v", resp.StatusCode)
}
redirect := a.oAuthConfig.AuthCodeURL(a.oAuthConfig.Endpoint.AuthURL)
if got, err := resp.Location(); err != nil || got.String() != redirect {
t.Fatalf("expected %v, got %v", redirect, got.Path)
}
})
t.Run("logout", func(t *testing.T) {
req := httptest.NewRequest("GET", "/logout", nil)
w := httptest.NewRecorder()
a.Logout(w, req)
resp := w.Result()
if resp.StatusCode != http.StatusTemporaryRedirect {
t.Fatalf("expected redirect, got %v", resp.StatusCode)
}
redirect := fmt.Sprintf("%s?post_logout_redirect_uri=%s/", a.logoutEndpoint, a.fbURL)
if got, err := resp.Location(); err != nil || got.String() != redirect {
t.Fatalf("expected %v, got %v", redirect, got.Path)
}
for _, c := range resp.Cookies() {
if c.Name == "molecula-chip" {
if c.Value != "" {
t.Fatalf("cookie not set to empty value!")
}
want := time.Unix(0, 0).Unix()
got := c.Expires.Unix()
if want != got {
t.Fatalf("expected %v, got %v", want, got)
}
break
}
}
})
}

View file

@ -544,9 +544,13 @@ func (h *Handler) chkInternal(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if h.auth != nil {
secret, ok := r.Header["X-Feature-Key"]
decodedString, err := hex.DecodeString(secret[0])
secretString := ""
if ok {
secretString = secret[0]
}
decodedString, err := hex.DecodeString(secretString)
if err != nil || !ok || !bytes.Equal(decodedString, h.auth.SecretKey()) {
http.Error(w, errors.Wrap(err, "internal secret key validation failed").Error(), http.StatusUnauthorized)
http.Error(w, "internal secret key validation failed", http.StatusUnauthorized)
return
}
}

View file

@ -6,6 +6,7 @@ import (
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
gohttp "net/http"
"net/http/httptest"
"net/url"
@ -235,7 +236,7 @@ func TestAuthentication(t *testing.T) {
claims["name"] = "todd"
validToken, err := tkn.SignedString([]byte(secretKey))
if err != nil {
panic(err)
t.Fatal(err)
}
validToken = "Bearer " + validToken
@ -247,15 +248,10 @@ func TestAuthentication(t *testing.T) {
}
// make an expired token
expiredTkn := jwt.New(jwt.SigningMethodHS256)
expiredClaims := expiredTkn.Claims.(jwt.MapClaims)
expiredClaims["molecula-idp-groups"] = groupString
expiredClaims["oid"] = "42"
expiredClaims["name"] = "todd"
expiredClaims["exp"] = "1"
expiredToken, err := expiredTkn.SignedString([]byte(secretKey))
claims["exp"] = "1"
expiredToken, err := tkn.SignedString([]byte(secretKey))
if err != nil {
panic(err)
t.Fatal(err)
}
expiredToken = "Bearer " + expiredToken
@ -502,9 +498,7 @@ admin: "ac97c9e2-346b-42a2-b6da-18bcb61a32fe"`
},
},
{
// this tests that there are no permissions read in even though
// auth is turned on, so we get a 500
name: "MW-CreateIndexGood",
name: "MW-CreateIndexInsufficientPerms",
path: "/index/abcd",
kind: "bearer",
method: gohttp.MethodPost,
@ -622,3 +616,172 @@ admin: "ac97c9e2-346b-42a2-b6da-18bcb61a32fe"`
}
}
func TestChkAuthN(t *testing.T) {
a := NewTestAuth(t)
h := Handler{
logger: logger.NewStandardLogger(os.Stdout),
queryLogger: logger.NewStandardLogger(os.Stdout),
auth: a,
}
// make a valid token
tkn := jwt.New(jwt.SigningMethodHS256)
claims := tkn.Claims.(jwt.MapClaims)
groupString, _ := authn.ToGob64([]authn.Group{{GroupID: "thing", GroupName: "whatever"}})
claims["molecula-idp-groups"] = groupString
claims["oid"] = "42"
claims["name"] = "A. Token"
validToken, err := tkn.SignedString(a.SecretKey())
if err != nil {
t.Fatal(err)
}
validToken = "Bearer " + validToken
// make an invalid token
invalidKey, err := hex.DecodeString("DEADBEEDDEADBEEDDEADBEEDDEADBEEDDEADBEEDDEADBEEDDEADBEEDDEADBEED")
if err != nil {
t.Fatal(err)
}
invalidToken, err := tkn.SignedString(invalidKey)
if err != nil {
t.Fatal(err)
}
invalidToken = "Bearer " + invalidToken
// make an expired token
claims["exp"] = "1"
expiredToken, err := tkn.SignedString(a.SecretKey())
if err != nil {
t.Fatal(err)
}
expiredToken = "Bearer " + expiredToken
testingHandler := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("good"))
}
cases := []struct {
name string
endpoint string
token string
handler http.HandlerFunc
statusCode int
}{
{
name: "Valid",
token: validToken,
handler: h.chkAuthN(testingHandler),
statusCode: http.StatusOK,
},
{
name: "Invalid",
token: invalidToken,
handler: h.chkAuthN(testingHandler),
statusCode: http.StatusUnauthorized,
},
{
name: "Expired",
token: expiredToken,
handler: h.chkAuthN(testingHandler),
statusCode: http.StatusUnauthorized,
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/whatever", nil)
r.Header.Add("Authorization", test.token)
test.handler(w, r)
resp := w.Result()
if resp.StatusCode != test.statusCode {
t.Fatalf("expected %v, got %v", test.statusCode, resp.StatusCode)
}
})
}
}
func TestChkInternal(t *testing.T) {
a := NewTestAuth(t)
authKey := "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
h := Handler{
logger: logger.NewStandardLogger(os.Stdout),
queryLogger: logger.NewStandardLogger(os.Stdout),
auth: a,
}
testingHandler := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("good"))
}
cases := []struct {
name string
statusCode int
handler http.HandlerFunc
key string
}{
{
name: "happyPath",
statusCode: http.StatusOK,
handler: h.chkInternal(testingHandler),
key: authKey,
},
{
name: "unhappyPath-empty",
statusCode: http.StatusUnauthorized,
handler: h.chkInternal(testingHandler),
key: "",
},
{
name: "unhappyPath-wrong",
statusCode: http.StatusUnauthorized,
handler: h.chkInternal(testingHandler),
key: "BEABBEEFBEABBEEFBEABBEEFBEABBEEFBEABBEEFBEABBEEFBEABBEEFBEABBEEF",
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/whatever", nil)
if test.key != "" {
r.Header.Add("X-Feature-Key", test.key)
}
test.handler(w, r)
resp := w.Result()
if resp.StatusCode != test.statusCode {
t.Fatalf("expected %v, got %v", test.statusCode, resp.StatusCode)
}
})
}
}
func NewTestAuth(t *testing.T) *authn.Auth {
t.Helper()
var (
ClientID = "e9088663-eb08-41d7-8f65-efb5f54bbb71"
ClientSecret = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
AuthorizeURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/authorize"
TokenURL = "https://login.microsoftonline.com/4a137d66-d161-4ae4-b1e6-07e9920874b8/oauth2/v2.0/token"
GroupEndpointURL = "https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true"
LogoutURL = "https://login.microsoftonline.com/common/oauth2/v2.0/logout"
Scopes = []string{"https://graph.microsoft.com/.default", "offline_access"}
Key = "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
)
a, err := authn.NewAuth(
logger.NewStandardLogger(os.Stdout),
"http://localhost:10101/",
Scopes,
AuthorizeURL,
TokenURL,
GroupEndpointURL,
LogoutURL,
ClientID,
ClientSecret,
Key,
)
if err != nil {
t.Fatalf("building auth object%s", err)
}
return a
}

View file

@ -2,6 +2,7 @@
package server_test
import (
"bytes"
"context"
"encoding/hex"
"fmt"
@ -1427,6 +1428,47 @@ func TestCRUDIndexes(t *testing.T) {
})
}
func TestLogQuery(t *testing.T) {
method := "test!"
uinfo := authn.UserInfo{
UserID: "ID",
UserName: "name",
}
ctx := context.WithValue(context.Background(), "userinfo", &uinfo)
cases := []struct {
name string
req interface{}
expected string
}{
{
name: "nonQueryReq",
req: "nope",
expected: fmt.Sprintf("GRPC: %v, %v, %v, %v, %v\n", "", []string{}, "test!", uinfo.UserID, uinfo.UserName),
},
{
name: "QuerySQLReq",
req: &pb.QuerySQLRequest{Sql: "show fields from table"},
expected: fmt.Sprintf("GRPC: %v, %v, %v, %v, %v, %v\n", "", []string{}, "test!", uinfo.UserID, uinfo.UserName, "show fields from table"),
},
{
name: "QueryPQLReq",
req: &pb.QueryPQLRequest{Pql: "Count(All())"},
expected: fmt.Sprintf("GRPC: %v, %v, %v, %v, %v, %v\n", "", []string{}, "test!", uinfo.UserID, uinfo.UserName, "Count(All())"),
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
buf := new(bytes.Buffer)
l := logger.NewStandardLogger(buf)
server.LogQuery(ctx, method, test.req, l)
if !strings.HasSuffix(buf.String(), test.expected) {
t.Errorf("expected '%v', got '%v'", test.expected, buf.String())
}
})
}
}
func setUpTestQuerySQLUnary(ctx context.Context, t *testing.T) (gh *server.GRPCHandler, tearDownFunc func()) {
t.Helper()