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

client: Improve testing and increase coverage #636

Merged
merged 16 commits into from
Nov 28, 2024
Merged

client: Improve testing and increase coverage #636

merged 16 commits into from
Nov 28, 2024

Conversation

cthulhu-rider
Copy link
Contributor

@cthulhu-rider cthulhu-rider commented Nov 18, 2024

  • inline conn code from api-go
  • refactor client to use grpc.Conn directly
  • prepare the ground for testing operations over gRPC
  • add a typical test of one RPC
  • do the rest similarly

One of the added tests fails: it is planned to be corrected in the
future.

Signed-off-by: Leonard Lyubich <[email protected]>
Previously, `Client.Dial` lost causing error of network endpoint parsing.
Although the method returned "invalid endpoint options" error, the
original reason could not be obtained. This made debugging difficult.

Now causes are kept and returned. For this, function
`WithNetworkURIAddress` is inlined and the `ParseURI` error is not
ignored.

Fixes one of the corresponding tests.

Signed-off-by: Leonard Lyubich <[email protected]>
@cthulhu-rider cthulhu-rider force-pushed the client branch 12 times, most recently from fb1e0e2 to 774c74c Compare November 22, 2024 16:00
Copy link

codecov bot commented Nov 22, 2024

Codecov Report

Attention: Patch coverage is 68.72428% with 152 lines in your changes missing coverage. Please review.

Project coverage is 58.80%. Comparing base (2da6182) to head (195a8f2).
Report is 17 commits behind head on master.

Files with missing lines Patch % Lines
client/container.go 57.14% 28 Missing and 14 partials ⚠️
client/object_get.go 67.56% 18 Missing and 6 partials ⚠️
client/object_put.go 60.00% 16 Missing and 2 partials ⚠️
client/object_replicate.go 57.14% 8 Missing and 4 partials ⚠️
client/reputation.go 57.14% 8 Missing and 4 partials ⚠️
client/netmap.go 75.00% 6 Missing and 4 partials ⚠️
client/client.go 78.94% 5 Missing and 3 partials ⚠️
client/session.go 57.14% 4 Missing and 2 partials ⚠️
client/common.go 81.48% 5 Missing ⚠️
client/object_delete.go 66.66% 2 Missing and 2 partials ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #636      +/-   ##
==========================================
+ Coverage   57.47%   58.80%   +1.32%     
==========================================
  Files         164      165       +1     
  Lines       22503    22751     +248     
==========================================
+ Hits        12934    13378     +444     
+ Misses       9175     8935     -240     
- Partials      394      438      +44     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@cthulhu-rider
Copy link
Contributor Author

oops, streamTimeout regressed, will return

Copy link
Member

@roman-khimov roman-khimov left a comment

Choose a reason for hiding this comment

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

A number of good fixes.

Regarding embedded gRPC --- it makes testing harder at this level, previously gRPC was layered out and could be tested on its own. I fear we'd get a complete node in tests soon. At the same time we have to handle gRPC somewhere anyway and going through the whole cycle can be beneficial. Take httptest for example, it's an HTTP server and many APIs are tested against it (like NeoGo's JSON-RPC). gRPC is not much different here.

client/client.go Outdated Show resolved Hide resolved
internal/util/net.go Outdated Show resolved Hide resolved
client/accounting.go Show resolved Hide resolved
client/api.go Outdated Show resolved Hide resolved
client/netmap_test.go Show resolved Hide resolved
client/netmap_test.go Outdated Show resolved Hide resolved
defer func() {
c.sendStatistic(stat.MethodBalanceGet, err)()
}()
if c.prm.statisticCallback != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Likely you can just defer c.sendStatistic(...)() and handle statisticCallback and startTime internally.

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 if is inevitable, then it is better to make a conditional defer imo

Copy link
Member

Choose a reason for hiding this comment

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

but why? you turned out the func that is commonly used and made more lines, no? i think it was planned as defer c.sendStatistic(...)() but smth went wrong, why make things harder?

Copy link
Member

Choose a reason for hiding this comment

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

Not doing a defer is obviously more efficient, but for these (network) operations the difference is so negligible that I'd rather opt for simpler code.

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've done this not just for negligible optimization but for more clear code. Previously, i saw unconditional deferred stats, making me think that some stats are always tracked while sendStatistics encapsulates the logic that there can be no stats at all! Also, seeing defer func() { c.method()() }() blows my mind, i definitely cannot agree this is a simpler code

finally, if no one has any categorical objections, i'd leave the fixed version

Copy link
Member

Choose a reason for hiding this comment

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

any categorical objections

mine is many repetitive lines for every method. also, you still use defer so why not, at least i would try to inline time calculation: time.Since(time.Now())

seeing defer func() { c.method()() }() blows my mind

agree but once you get it you just see in every method, it is our general approach

Copy link
Contributor Author

Choose a reason for hiding this comment

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

at least i would try to inline time calculation: time.Since(time.Now())

hm, how would it be?

Copy link
Member

@carpawell carpawell Nov 27, 2024

Choose a reason for hiding this comment

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

i meant start time can be an arg of the deferred func, provided wrong example

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

@cthulhu-rider
Copy link
Contributor Author

cthulhu-rider commented Nov 25, 2024

it makes testing harder at this level

yeah, tests will definitely become harder todo, but the closer test code is to the real execution, the better it works in error and regression control. I want to be able to track problems at all transmission levels

i also believe that after all tests will be ready, future support wont be so hard todo

I fear we'd get a complete node in tests soon.

to some extent it will be so, and I think it is inevitable. For example, u wanna test that the sent request is correctly signed. To do this, u still have to intercept and check it. Then u wanna check that the client will return a certain result to the user for a certain server response. So, the abstraction interface should work with both requests and responses, as it does in the proposed version. That means that regardless of whether gRPC layer is abstracted or not, test implementations will be almost the same. That's why i included it into the test framework

u can also see some gRPC code we used has already become deprecated. To make sure our future upgrade passed purely and compatible, we should not abstract this level

The function is placed inside the lib to facilitate its support and as a
preliminary preparation for the obsolescence of
`github.com/nspcc-dev/neofs-api-go/v2` module.

Refs #381.

Signed-off-by: Leonard Lyubich <[email protected]>
The function return zeros on multiaddr input.

Signed-off-by: Leonard Lyubich <[email protected]>
Previously, `Client.Dial` method sometimes tried to connect to obviously
invalid addresses (e.g. with missing port). although a preliminary
endpoint check is performed, some errors were ignored, and they still
popped up during the subsequent dial. Thus, in general the client's
behavior was correct and the expected error was still caught. Thus, in
general, the client's behavior was correct and the error was still
caught, however, unnecessary obviously failed actions were made, which
could be cut off at the pre-check stage.

Now the `util.ParseURI` function has been improved and catches most
cases.

Test endpoints are corrected for `pool.Pool`.

Signed-off-by: Leonard Lyubich <[email protected]>
There is no any point to have them different.

Signed-off-by: Leonard Lyubich <[email protected]>
Nil response without an error can lead to the undesired behavior. Since
other methods are not expected to be called, unimplemented error fits
the best.

Signed-off-by: Leonard Lyubich <[email protected]>
There is already an interface for this. Also, changing global variables
is always weird.

Signed-off-by: Leonard Lyubich <[email protected]>
For two operations it has already been properly done, and for the rest
for some reason through global variables.

This also returns skipped tests to action: in approach with interfaces
any method can be overridden.

Signed-off-by: Leonard Lyubich <[email protected]>
One step closer to github.com/nspcc-dev/neofs-api-go/v2 module
deprecation. Previously, for the `Client`, network connecting, sending a
request and receiving a response was almost completely hidden in the
lib. Now the code is built into the client itself.

The test framework used to be too abstract and far from the production
version. Although the gRPC lib is vendored, it covers most of the
`Client` mechanics. In fact, proper testing requires emulating the
behavior of the client-server communication as realistically as possible.
This reworks the tests by abstracting only the network transport
via in-memory buffer implementation supplied by gRPC lib itself.
From now unit tests should provide protocol-declared RPC handlers
exactly like NeoFS storage nodes does. This automatically includes gRPC
layer to testing, making the client implementation more resilient to
corresponding module updates.

Signed-off-by: Leonard Lyubich <[email protected]>
Signed-off-by: Leonard Lyubich <[email protected]>
Mainly checks the correctness and integrity of messages sent over the
network

To be continued: other methods will be covered by similar tests in the
future.

Signed-off-by: Leonard Lyubich <[email protected]>
Previous name 'missing' could be misinterpreted: missing what?

Signed-off-by: Leonard Lyubich <[email protected]>
There is no point in keeping this test separate from the others, so
merged.

Signed-off-by: Leonard Lyubich <[email protected]>
It speaks the truth, but it's not that easy to fix.

Signed-off-by: Leonard Lyubich <[email protected]>
Previously, an incorrect duration was passed to the statistics handler
of all operations. Deferred function sent the time elapsed from the
start point to the almost instant calculation of the closure arguments
(nanoseconds).

Now client correctly measures the execution time. Also, as a slight
improvement, these actions are deferred conditionally now.

Signed-off-by: Leonard Lyubich <[email protected]>
@roman-khimov roman-khimov merged commit 591dd25 into master Nov 28, 2024
10 checks passed
@roman-khimov roman-khimov deleted the client branch November 28, 2024 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants