Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(tests): Unit test for edgraph/validateToken () #8470

Merged
merged 12 commits into from
Dec 15, 2022

Conversation

jbhamra1
Copy link
Contributor

@jbhamra1 jbhamra1 commented Dec 1, 2022

Problem

Missing unit tests for validateToken (), getAcessJwt (), getRefreshJwt () of edgraph package

Solution

TestValidateToken (), TestGetAccessJwt () and TestRefreshJwt () generates a signed JSON Web token using the static data defined in the test file, using the helper function generateJWT (). The generated token includes the token expiry time which is passed as an argument to the helper function.
TestValidateToken () calls validateToken and extracts data from the signed token which is then compared to validate with the original data.

TestGetAccessJwt() and TestRefreshJwt () are direct wrappers over jwt.NewWithClaims () but with their predefined expiry duration.

                      getAcessJwt ()
                      getRefreshJwt ()
@coveralls
Copy link

coveralls commented Dec 1, 2022

Coverage Status

Coverage increased (+27.2%) to 64.428% when pulling 0052337 on edgraph_validatetoken_getaccessjwt_getrefreshjwt into 5ac56a1 on main.

edgraph/access_ee_test.go Outdated Show resolved Hide resolved
edgraph/access_ee_test.go Outdated Show resolved Hide resolved
@all-seeing-code
Copy link
Contributor

We can improve the description with details on the tests.

edgraph/access_ee_test.go Outdated Show resolved Hide resolved
edgraph/access_ee_test.go Outdated Show resolved Hide resolved
edgraph/access_ee_test.go Show resolved Hide resolved
edgraph/access_ee_test.go Outdated Show resolved Hide resolved
edgraph/access_ee_test.go Show resolved Hide resolved
Changes incorporated for @anurags92 offline comments.
'go fmt' auto formatting changes.
edgraph/access_ee_test.go Outdated Show resolved Hide resolved
)

var (
userDataList = []userData{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this inside the test where it is used

e1 = time.Now().Add(time.Minute * 30).Unix()
}

claims := jwt.MapClaims{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could put this whole thing in one line, should fit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree with the statement (that it could be collapsed), I am not convinced that readability is improved by doing so. If the length of the entire function was excessive, I would be more inclined to lean toward's Aman's suggestion here. As it stands, I personally would leave it as-is, but I would not object to it being collapsed to a single line.

edgraph/access_ee_test.go Outdated Show resolved Hide resolved
}

if t1[p-1] != t2[p-1] {
match = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could just return here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

}

func compareTokenParts(tok1 string, tok2 string, part ...int) bool {
var (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this variable at all

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

edgraph/access_ee_test.go Outdated Show resolved Hide resolved
)

for _, userdata := range userDataList {
if tokenString, err = generateJWT(userdata.namespace, userdata.userId, userdata.groupIds, 0); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use := here

}

func TestGetAccessJwt(t *testing.T) {
var (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not declare variables again, just use := when needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. Here and elsewhere.
Variable declaration should be deferred as late as possible to the point where it is going to be used.
I think the only one I would pre-declare would be the expiry (sic) variable, since it requires a context switch to initialize this.

* One of the factor [signature] would differ is based on the expiry time.
* This will differ for tokstr and jwtstr and hence ignoring it.
*/
match := compareTokenParts(tokstr, jwtstr, 1, 2, 0) // compare 1st and 2nd part, ignore the 3rd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather do some sort of validateToken call than actually checking what is inside the token

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I am ok with using a canonical set of tokens compared with the retrieved token.
The validation process for the other parts of the token would likely not look much different from "Hey, I expected it to match THIS string!", so the string comparison, I think is valid.

Having said that, our handling of making sure to ignore the timestamp seems a bit clunky. I agree that we should ignore the timestamp token (or at least have a different comparison mechanism), but having it controlled via a varargs type structure where the value either indicates an index (offset by 1) or a 0 seems odd.

A couple ideas here:

  1. Pass a slice of booleans for whether or not to compare instead of index offsets with 0 having special value.
  2. A bit overboard, but if we want to apply different logic based on token types, we could pass in a slice of types where the validation procedure is decided based on the type.

Copy link
Contributor

@billprovince billprovince left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments inline. Nothing that really prevents checkin in my opinion, but some suggestions for improvements.

edgraph/access_ee_test.go Outdated Show resolved Hide resolved
e1 = time.Now().Add(time.Minute * 30).Unix()
}

claims := jwt.MapClaims{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree with the statement (that it could be collapsed), I am not convinced that readability is improved by doing so. If the length of the entire function was excessive, I would be more inclined to lean toward's Aman's suggestion here. As it stands, I personally would leave it as-is, but I would not object to it being collapsed to a single line.

var token *jwt.Token

e1 := exp
if e1 == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe that this logic belongs here: I believe that it belongs in the caller.
There is one place where you invoke generateJWT with a 0 for the exp time, and that is in TestValidateToken.
Instead of having to create additional logic here, you could simply define exp in the call to this function.

}

func compareTokenParts(tok1 string, tok2 string, part ...int) bool {
var (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

}

if t1[p-1] != t2[p-1] {
match = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

}
)

func generateJWT(namespace uint64, userId string, groupIds []string, exp int64) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer the name "expiry" over "exp". I see "exp", and I think "exponent", not "expiry".

The pedantic golang style prefers very terse names -- similar to c-style. However, descriptive names are much simpler to read.

Comment applies here and later in the file.

}

func TestGetAccessJwt(t *testing.T) {
var (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. Here and elsewhere.
Variable declaration should be deferred as late as possible to the point where it is going to be used.
I think the only one I would pre-declare would be the expiry (sic) variable, since it requires a context switch to initialize this.

* One of the factor [signature] would differ is based on the expiry time.
* This will differ for tokstr and jwtstr and hence ignoring it.
*/
match := compareTokenParts(tokstr, jwtstr, 1, 2, 0) // compare 1st and 2nd part, ignore the 3rd.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I am ok with using a canonical set of tokens compared with the retrieved token.
The validation process for the other parts of the token would likely not look much different from "Hey, I expected it to match THIS string!", so the string comparison, I think is valid.

Having said that, our handling of making sure to ignore the timestamp seems a bit clunky. I agree that we should ignore the timestamp token (or at least have a different comparison mechanism), but having it controlled via a varargs type structure where the value either indicates an index (offset by 1) or a 0 seems odd.

A couple ideas here:

  1. Pass a slice of booleans for whether or not to compare instead of index offsets with 0 having special value.
  2. A bit overboard, but if we want to apply different logic based on token types, we could pass in a slice of types where the validation procedure is decided based on the type.

* This will differ for tokstr and jwtstr and hence ignoring it.
*/
match := compareTokenParts(tokstr, jwtstr, 1, 2, 0) // compare 1st and 2nd part, ignore the 3rd.
if match == false {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer if !match over if match == false
Or, even better:
if !compareTokenParts(...) (since you don't really need the match variable).

Not a big deal, but I think this would be slightly preferable.


var (
userDataList = []userData{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved this slice to TestValidateToken(), TestGetAccessJwt(), TestGetRefreshJwt().
Hope this was the expectation.

Copy link
Member

@mangalaman93 mangalaman93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we should compare tokens first and the second part to check whether a token is valid. A token is valid if it has the right data and signature and that is why we should call a validataToken sort of a function in order to verify the token.

@all-seeing-code all-seeing-code changed the title Unit test for edgraph/validateToken () chore(tests): Unit test for edgraph/validateToken () Dec 13, 2022
generated by getAccessJwt() and getRefreshJwt().
}

for _, userdata := range userDataList {
tokstr := generateJWT(userdata.namespace, userdata.userId, nil, expiry)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwtstr, _ := getRefreshJwt(userdata.userId, userdata.namespace)
ud2, _ := validateToken(jwtstr)

This is all you need and then you can check whether the values you passed to getRefreshJwt vs what was returned by validateToken.

}

for _, userdata := range userDataList {
tokstr := generateJWT(userdata.namespace, userdata.userId, g, expiry)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Contributor

@billprovince billprovince left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an issue with our usage of validateToken. To put it bluntly, I don't see how the tests would pass with the change you specified unless you have additional changes not reflected in github.


for _, userdata := range userDataList {
jwtstr, _ := getAccessJwt(userdata.userId, grpLst, userdata.namespace)
ud, _ := validateToken (jwtstr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar issue with validateToken.
See definition here:
https://github.com/dgraph-io/dgraph/blob/bc71dc3297880e8c282be638506eddab9270b010/edgraph/access.go#L93

It doesn't do anything aside from return nil, nil.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to look at the file access_ee.go instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And therefore, we need to add the following to the test file

//go:build !oss
// +build !oss

for _, userdata := range userDataList {
tokenString := generateJWT(userdata.namespace, userdata.userId, userdata.groupIds, expiry)

ud, _ := validateToken(tokenString)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that you have a local change, as otherwise, at line 51 below, we should expect an error since ud would be nil.
See: https://github.com/dgraph-io/dgraph/blob/bc71dc3297880e8c282be638506eddab9270b010/edgraph/access.go#L93

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edgraph/access.go and edgraph/access_ee.go declares "func validateToken(jwtStr string) ([]string, error)" and "func validateToken(jwtStr string) (*userData, error)" respectively.

The validateToken(tokenString) call from edgraph/access_ee_test.go resolves to "func validateToken(jwtStr string) (*userData, error)" declared in edgraph/access_ee.go but not to "func validateToken(jwtStr string) ([]string, error)" declared in edgraph/access.go, at least, in my linux workspace. The test output shown below. The text Italic bold is the result of an extra statement I have added to the test code, 'fmt.Println (ud)', for verification.

jassi Wed Dec 14 11:58:08 edgraph_validatetoken_getaccessjwt_getrefreshjwt edgraph > go test -v -count=1 ./...
=== RUN TestValidateToken
&{1234567890 user1 [701 702]}
&{2345678901 user2 [703 701]}
&{3456789012 user3 [702 703]}

--- PASS: TestValidateToken (0.00s)
=== RUN TestGetAccessJwt
--- PASS: TestGetAccessJwt (0.00s)
=== RUN TestGetRefreshJwt
--- PASS: TestGetRefreshJwt (0.00s)
=== RUN TestParseNQuads
--- PASS: TestParseNQuads (0.00s)
=== RUN TestValNquads
. . .

When I write a sample program with an Add() function I see a different behaviour altogether, go interpreter throws a message- 'Add redeclared', whereas this is not seen for edgraph/ above.
This is the sample program code-

jassi Wed Dec 14 12:11:06 edgraph_validatetoken_getaccessjwt_getrefreshjwt math > ls -l
total 12
-rw-r--r-- 1 jassi users 65 Dec 14 07:56 m.go
-rw-r--r-- 1 jassi users 148 Dec 14 08:04 m_1.go
-rw-r--r-- 1 jassi users 515 Dec 14 10:45 m_1_test.go
jassi Wed Dec 14 12:11:08 edgraph_validatetoken_getaccessjwt_getrefreshjwt math >
jassi Wed Dec 14 12:11:09 edgraph_validatetoken_getaccessjwt_getrefreshjwt math > cat m.go
package math

func Add (x int, y int) ([]string) {
return nil
}
jassi Wed Dec 14 12:11:15 edgraph_validatetoken_getaccessjwt_getrefreshjwt math > cat m_1.go
package math

type userData struct {
name string
uid int64
}

func Add (x int, y int) (*userData) {
return &userData{name: "Hello", uid: 2233}
}
jassi Wed Dec 14 12:11:25 edgraph_validatetoken_getaccessjwt_getrefreshjwt math >
jassi Wed Dec 14 12:11:33 edgraph_validatetoken_getaccessjwt_getrefreshjwt math > cat m_1_test.go
package math

import (
"testing"
)

func Test_Add (t testing.T) {
Add (3, 5)
/

got := Add (3, 5)
want := 8

if got != want {
	t.Errorf ("got %v, wanted %v", got, want)
}

*/
}
jassi Wed Dec 14 12:12:03 edgraph_validatetoken_getaccessjwt_getrefreshjwt math > go test -v -count=1 ./...

github.com/dgraph-io/dgraph/gotest/math [github.com/dgraph-io/dgraph/gotest/math.test]

./m_1.go:8:6: Add redeclared in this block
./m.go:3:6: other declaration of Add
FAIL github.com/dgraph-io/dgraph/gotest/math [build failed]
FAIL
jassi Wed Dec 14 12:12:25 edgraph_validatetoken_getaccessjwt_getrefreshjwt math >

I don't know what is causing this discrepancy.

Copy link
Contributor

@meghalims meghalims left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@jbhamra1 jbhamra1 merged commit 14b9121 into main Dec 15, 2022
@jbhamra1 jbhamra1 deleted the edgraph_validatetoken_getaccessjwt_getrefreshjwt branch December 15, 2022 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

7 participants