Skip to content
This repository was archived by the owner on Oct 11, 2024. It is now read-only.

Conversation

@gdbelvin
Copy link
Contributor

@gdbelvin gdbelvin commented May 1, 2018

This PR adds a long overdue unit test and unittesting framework for client operations.

Included:

  • A mock KeyTransparency server
  • An interface for mocking out the verifier
  • Removed a global variable pageSize. It's now a client variable.

@gdbelvin gdbelvin force-pushed the test/listhistory branch from e43281f to 5efe977 Compare May 1, 2018 13:26
@codecov-io
Copy link

codecov-io commented May 1, 2018

Codecov Report

Merging #968 into master will increase coverage by 2.21%.
The diff coverage is 80.76%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #968      +/-   ##
==========================================
+ Coverage   47.34%   49.55%   +2.21%     
==========================================
  Files          28       28              
  Lines        2034     2030       -4     
==========================================
+ Hits          963     1006      +43     
+ Misses        894      843      -51     
- Partials      177      181       +4
Impacted Files Coverage Δ
core/client/mutations.go 0% <0%> (ø) ⬆️
core/keyserver/keyserver.go 33.16% <100%> (ø) ⬆️
core/client/get_and_verify.go 28.94% <100%> (+28.94%) ⬆️
core/client/verify.go 57.74% <81.81%> (ø) ⬆️
core/client/client.go 28.07% <85.71%> (+12.72%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 29dfa03...0f6b485. Read the comment docs.

@gdbelvin gdbelvin requested review from pav-kv and taknira May 1, 2018 13:48
@gdbelvin gdbelvin added this to the Productionize milestone May 1, 2018
@gdbelvin
Copy link
Contributor Author

gdbelvin commented May 2, 2018

More unittests for Key Transparency. PTAL


// Verifier is used to verify specific outputs from Key Transparency.
type Verifier interface {
// Index computes the index from a VRF proof.
Copy link
Contributor

Choose a reason for hiding this comment

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

What is VRF?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

VRF means Verifiable Random Function

KT has a VRF implementation in core/crypto/vrf/

Copy link
Contributor

Choose a reason for hiding this comment

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

Ack, thank you.

Copy link
Contributor

@pav-kv pav-kv left a comment

Choose a reason for hiding this comment

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

Some nits.

type Verifier interface {
// Index computes the index from a VRF proof.
Index(vrfProof []byte, domainID, appID, userID string) ([]byte, error)
// VerifyGetEntryResponse verifies everything about a GetEntryResponse:
Copy link
Contributor

Choose a reason for hiding this comment

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

s/:/./

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


// Verifier is used to verify specific outputs from Key Transparency.
type Verifier interface {
// Index computes the index from a VRF proof.
Copy link
Contributor

Choose a reason for hiding this comment

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

Ack, thank you.

epochsWant := end - start + 1
for epochsReceived < epochsWant {
count := min(int32((end-start)+1), pageSize)
epochsWant := int(end - start + 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

I am slightly nervous about this shrinking cast. How do you feel about doing the opposite - comparing int64s?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. It produces a slightly nasty set of casts below, but it's probably safer.

Addr string
}

// NewMockKT returns a new mock Key Transparency server listening on a random port .
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove space before period

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


// NewMockKT returns a new mock Key Transparency server listening on a random port .
// Returns the started server, the listener it's using for connection and a
// close function that must be defer-called on the scope the server is meant to
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd drop the "defer-" bit because it doesn't look like a must (could simply call the function directly?), likewise the "scope" detail:
... that must be called to stop the server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep

}

// NewMockKT returns a new mock Key Transparency server listening on a random port .
// Returns the started server, the listener it's using for connection and a
Copy link
Contributor

Choose a reason for hiding this comment

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

The pure Listener object is not there, only the server wrapper.
Returns the started server, a client connected to it, and a close ...

mapVerifier *tclient.MapVerifier
logVerifier *tclient.LogVerifier
// Verify is a client helper library for verifying request and responses.
type Verify struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to name this with a noun? Some ideas:

  • Call this Verifier and the interface - IVerifier. Not sure whether Go naming conventions allows this.
  • Verification(Client), VerifierImpl, VerifiedClient ...

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 struggled with the naming conventions too.
is RealVerifier appropriate? The interface exists so we can have a FakeVerifier

}
})
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: drop empty line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Start: r.wantStart,
PageSize: r.wantSize,
})).
Return(&pb.ListEntryHistoryResponse{
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe merge this line with the previous one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That does look better.

count := min(int32((end-start)+1), pageSize)
for int64(len(allProfiles)) < epochsWant {
log.Printf("pageSize: %v", c.pageSize)
count := min(int32(epochsWant-int64(len(allProfiles))), c.pageSize)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this bit is just as problematic as the previous version due to shrinking cast. Probably making a min function for int64 is too much hassle. Instead, how about rewinding this back, and simply checking end-start+1 for an overflow before casting it down to int32 in line 175?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making min accept int64 is actually the better solution. It defers the downcast till the end when we expect the value to be small.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I agree. Wasn't aware that the min function was used only here.

@pav-kv
Copy link
Contributor

pav-kv commented May 2, 2018

This now LGTM.
@taknira do you want to do a final proofread?

One general question. I noticed you use both Vlog and glog.V for verbose logging. I wonder why you don't use just glog.

vrf vrf.PublicKey
mapVerifier *tclient.MapVerifier
logVerifier *tclient.LogVerifier
// RealVerifier is a client helper library for verifying request and responses.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you could make a note that this implements Verifier? That would explain the seemingly unclear Real bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@gdbelvin
Copy link
Contributor Author

gdbelvin commented May 4, 2018

Vlog provides visual logging on the command line during interactive operations.
It is separate from glog which provides debugging and general info.

Kat, would you mind taking a look?


// Verifier is used to verify specific outputs from Key Transparency.
type Verifier interface {
// Index computes the index from a VRF proof.
Copy link

Choose a reason for hiding this comment

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

Index computes the index of what? Add to the comment to make it really clear :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Index computes the index of an appID, userID pair from a VRF proof, obtained from the server.

// VerifyGetEntryResponse verifies everything about a GetEntryResponse.
VerifyGetEntryResponse(ctx context.Context, domainID, appID, userID string, trusted types.LogRootV1, in *pb.GetEntryResponse) (*types.MapRootV1, *types.LogRootV1, error)
// VerifyEpoch verifies that epoch is correctly signed and included in the append only log.
// VerifyEpoch also verifies that epoch.LogRoot is consistent with the last trusted SignedLogRoot.
Copy link

Choose a reason for hiding this comment

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

Did the *pb.Epoch parameter used to be called 'epoch' or something? Maybe capitalize in these comments to make it clear you're talking about the type rather than the variable name - 'VerifyEpoch verifies that the Epoch is correctly...', 'VerifyEpoch also verifies that Epoch.LogRoot is ....'

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'll just rename in to epoch :-)

gdbelvin and others added 3 commits May 8, 2018 13:38
…y into test/listhistory

* 'test/listhistory' of github.com:gdbelvin/keytransparency:
  Use DomainID rather than mapID during authorization (google#970)
  Strengthen CreateDomain (google#969)
}, nil)
}

if _, _, err = c.PaginateHistory(ctx, appID, userID, tc.start, tc.end); err != tc.wantErr {
Copy link

Choose a reason for hiding this comment

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

This method of testing PaginateHistory seems odd, given you could test based on state - i.e. just test PaginateHistory based on its inputs and outputs, rather than checking that it's calling the correct server endpoints with the right arguments.... In fact, by doing the way you currently are explicitly doesn't test some of the logic within PaginateHistory outside of what it sends/receives to/from the server.

In addition to this, (and as discussed separately from this PR,) use of gomock (and mocks in general) tends to be discouraged from a readability standpoint, preferring real objects or fakes.

If you were to test PaginateHistory based on inputs and outputs (and compare them to expected outputs) you could just fake the one rpc it calls (ListEntryHistory is it?), which would remove the need for mocking, making the test code much simpler. (Although incidentally it looks like the PaginateHistory code doesn't call that directly, but instead calls client.VerifiedListHistory, which then calls ListEntryHistory - I couldn't find a test for VerifiedListHistory, so using this method of testing could also be appropriate for that function too.)

This method of testing would be specifically testing the PaginateHistory code, and assuming the underlying functions that it calls work as expected.

gdbelvin added 3 commits May 11, 2018 14:48
…y into test/listhistory

* 'test/listhistory' of github.com:gdbelvin/keytransparency:
  Missing masterPassword input in post.go (google#971)
@gdbelvin
Copy link
Contributor Author

Yay! the mocks have been removed. Thanks @taknira for helping keep up the code excellence.
PTAL.

Copy link

@taknira taknira 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, and easier to follow now :)

}
s, stop, err := testutil.NewFakeKT(srv)
if err != nil {
t.Fatalf("NewMockKT(): %v", err)
Copy link

Choose a reason for hiding this comment

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

It's not a mock anymore :P

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:-)

}
if got, want := values, tc.wantValues; !reflect.DeepEqual(got, want) {
t.Errorf("PaginateHistory().values: \n%#v, want \n%#v, diff: \n%v",
got, want, pretty.Compare(got, want))
Copy link

Choose a reason for hiding this comment

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

(Optional) Is there any chance at all that pretty.Compare and reflect.DeepEqual would return different results? Might be worth using pretty.Compare for the initial comparison too, and checking the diff is empty of something? Completely up to you though, as the two funcs will likely be in agreement the majority of the time. Just a musing :)

@gdbelvin gdbelvin force-pushed the test/listhistory branch from d0cb53c to cdee1f7 Compare May 11, 2018 18:06
If the client always asks for the maximum amount from the server,

1. The total number of requests will be smaller.
2. The logic for determining the number of items to return is simpler.
@gdbelvin gdbelvin force-pushed the test/listhistory branch from cdee1f7 to ff25cbf Compare May 11, 2018 18:12
@gdbelvin gdbelvin merged commit ae6690b into google:master May 11, 2018
@gdbelvin gdbelvin deleted the test/listhistory branch May 11, 2018 18:46
gdbelvin added a commit to gdbelvin/keytransparency that referenced this pull request May 13, 2018
* master:
  Move authentication to grpc interceptor (google#973)
  Add UnitTest for PaginateHistory (google#968)
  Remove unused `UserProfile` message (google#972)
  Update default paths (google#910)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants