-
Notifications
You must be signed in to change notification settings - Fork 789
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
Use Clock
from CE
instead of one from java.time
in CSRF
Middleware
#7166
Conversation
@deprecated( | ||
"This method doesn't change anything since the `java.time.Clock` has been purged from CSRFBuilder", | ||
"v1.0.0-M40", | ||
) | ||
def withClock(clock: Clock): CSRFBuilder[F, G] = this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worrying about this breaking behaviour, but I didn't come up with anything better than that. At least, users will be get notified by the compiler. 🤷🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a fine heads up.
Flatmapping is challenging in Scala 3 😓
|
_ <- control.advanceAndTick(1.millis) | ||
res <- | ||
control.results.flatMap(_.get match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like TestControl
behaves differently for JS or maybe I'm doing something wrong 🤔 cc @armanbilge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, TestControl
should behave the same. Does the code make some side-effecting call somewhere? e.g. something that relies on delay(...)
, blocking(...)
or async(...)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, yes it does. It needs HMAC cryptography. This is async on JS (i.e. it takes real time to execute) so it cannot be used on the test runtime with mock time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can just disable this test on JS, like this.
http4s/tests/shared/src/test/scala/org/http4s/UriSuite.scala
Lines 998 to 999 in 88f6688
if (Platform.isJvm || Platform.isNative) | |
test("Uri parameter convenience methods should set a parameter with a float values") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is async on JS (i.e. it takes real time to execute) so it cannot be used on the test runtime with mock time.
This is also worth noting somewhere on CE docs. It's a good-to-know thing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks. However, it's more about deadlock, rather than just getting different behaviour for JS. I mean, we didn't get any of the tests hanged forever. They were executed but without mocking the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They were executed but without mocking the time.
No, they did mock the time :) it just deadlock
I mean, we didn't get any of the tests hanged forever.
It's because in TestControl
deadlock does not mean the tests hang forever. Deadlock is detected as no result even though there are no fibers to run.
I realize this is all very confusing :) if you use executeEmbed
as I recommended you would get a more informative error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's baffling why we don't get the same errors when ticking then (if an IO
is deadlocked) 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because when using the advanced API, it's not an error to have no result. The advanced API simply lets check if a result is present or not, which is why it returns an Option
. If you use .get
on the Option
, then you may get a surprising error.
http4s/server/shared/src/test/scala/org/http4s/server/middleware/CSRFSuite.scala
Line 166 in 8bc0fee
control.results.flatMap(_.get match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, gotcha
TestControl.execute(program).flatMap { control => | ||
for { | ||
_ <- control.advanceAndTick(1.millis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, this is a bit of an anti-pattern. Can we write this test case using executeEmbed(...)
and IO.sleep(...)
instead of advanceAndTick
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, really? Wasn't aware that this usage of TestControl
is considered an anti-pattern 😓 Is this piece of knowledge presented on some CE resources?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, see here. Unless this is an advanced situation, executeEmbed
is the preferred way.
https://typelevel.org/cats-effect/docs/core/test-runtime#full-execution
The first sort of test we will attempt to write comes in the form of a complete execution. This is generally the most common scenario, in which most of the power of
TestControl
is unnecessary
...
For more advanced cases,executeEmbed
may not be enough to properly measure the functionality under test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestControl#execute
+ advanceAndTick
seems less tricky than TestControl#executeEmbed
+ IO.sleep
. Or maybe I'm just biased about any usage of IO.sleep
anywhere. But I really wonder in which ways this is precisely anti-pattern...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I really wonder in which ways this is precisely anti-pattern...
You can ask Fabio about it more on the Discord. For example see comment here.
https://discord.com/channels/632277896739946517/632310980449402880/947533678546939935
Maybe "anti-pattern" was too strong a word. I meant it in the sense: using advanced, complex APIs when simple APIs will work is an anti-pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure I understand the objection to sleep
, it seems something along the lines of:
"having time pass in tests (albeit simulated) is weird, I want to inject mocked time"
but I think the opposite is true, having simulated time pass in tests is better than mocking time as a general pattern, because if the implementation of the code uses sleep
, the mocked time has to be consistent with that.
It also has the conceptual advantage that it's behaving like the real program would, and the convenience advantage that using TestControl then reduces to calling a single function: executeEmbed
.
In fact, you can often just write a normal program with sleep
, concurrency , Ref
s to collect results etc, try it out in the repl, and then convert it to a deterministic case by adding executeEmbed
.
Finally, on the trickiness aspect, manual advance
is definitely trickier, there are some cases where the abstraction is a bit leaky
@SystemFw @armanbilge thank you, folks, for bearing with me and explaining the difference between |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dare I say ... shall we run this through 0.23 😛 nahh, although I think it's do-able.
Thanks for cleaning this one up :)
server/shared/src/main/scala/org/http4s/server/middleware/CSRF.scala
Outdated
Show resolved
Hide resolved
server/shared/src/test/scala/org/http4s/server/middleware/CSRFSuite.scala
Outdated
Show resolved
Hide resolved
server/shared/src/test/scala/org/http4s/server/middleware/CSRFSuite.scala
Outdated
Show resolved
Hide resolved
server/shared/src/test/scala/org/http4s/server/middleware/CSRFSuite.scala
Outdated
Show resolved
Hide resolved
Co-authored-by: Arman Bilge <armanbilge@gmail.com>
This could be controversial, so I'd be glad to hear your opinions.