@jeremydmiller Hi! Do you know if anyone has taken a look at adapting Alba for ASP.NET Core 3.0? There seems to be at least one rather major obstacle, which is that TestServer will now forcibly replace the Response Body stream with an implementation that doesn't allow for seeking -- this happens regardless of whether one uses an earlier phase of the pipeline to stick in e.g. a MemoryStream.

Jeremy D. Miller

@jeremydmiller

Not that I know of yet if you’re volunteering;) It’s hopefully less churn than it was going from 1 to 2 this time.

Lauri Kotilainen

@rytmis

Haha. Ahahaha.

See, the thing is, now that they're moving to a monorepo model, the NuGet packages that had the word "Internal" in them are in fact going to become internal.

Most of that seems to have only minor effects. But the TestServer changes might actually hurt. I'm trying to bother David Fowler about this over at the aspnetcore slack.

Lauri Kotilainen

@rytmis

On that note, what are your thoughts about versioning this time around? Should I assume that netcoreapp3.0 means ASP.NET Core 3.0? It's either that or yet another separate package, I guess.

Jeremy D. Miller

@jeremydmiller

I’d like to try to multi-target this time. Even knowing that means some conditional compilation.

Lauri Kotilainen

@rytmis

I thought as much.

Jeremy D. Miller

@jeremydmiller

How close is 3.0 to hitting?

Lauri Kotilainen

@rytmis

It's RC with a Go-live license with a commitment to avoid API breakage from now on.

Jeremy D. Miller

@jeremydmiller

Gotcha. I’d been ignoring it for about this reason

Lauri Kotilainen

@rytmis

The new TestServer usage pattern is to use a sort of builder to construct it and then it provides you with a HttpClient that will dispatch the request, so you get the ResponseMessage with its assorted things. Most of the APIs Alba uses are still available -- I've got some initial work with conditional compilation locally. It's the response body that's giving me headache right now.

I should probably look more closely into the TestServer APIs at some point to see if there are other ways of going about this.

Well, I shouldn't say new because for all I know, it could have been that to begin with.

Jeremy D. Miller

@jeremydmiller

You can always cheat and wrap middleware around the normal stack that substitutes out the response body w/ a MemoryStream

Lauri Kotilainen

@rytmis

Nope. I tried that.

Jeremy D. Miller

@jeremydmiller

Why wouldn’t that work?

Lauri Kotilainen

@rytmis

Just a sec, I'll look it up.

Here, I'm in TestServer.SendAsync, and the httpContext has the MemoryStream as a Body that I've set up:

Jeremy D. Miller

@jeremydmiller

So their theory of how everything is going to work now is to use HttpClient for everything?

Lauri Kotilainen

@rytmis

This is further down the stack when TestServer begins to return the response:

The ResponseBodyReaderStream is what we see when looking at the response.

Jeremy D. Miller

@jeremydmiller

Okay. Might not be a killer, but the ResponseBody stuff may need to be smarter. Maybe we buffer that on the way back out

_

Lauri Kotilainen

@rytmis

Yeah. The problem I'm currently looking at is, because of this TestServer behavior, I haven't yet found any way to read the response body from within the HttpContext.

There has got to be something I'm missing here, though. There's no way this is intentional: this would mean that production code that assumes it can replace the Body stream won't work under TestServer either. 🤔

Jeremy D. Miller

@jeremydmiller

Gotcha. Could we have Alba run the request, then replace the response body with a stream that copies (lazily) the content from the stream we get from the HttpClient invocation?

Lauri Kotilainen

@rytmis

Don't know yet. I should probably play around with a plain TestServer to get a handle on how it's meant to be used.

Lauri Kotilainen

@rytmis

Welp, it turns out that the correct thing to do is to not replace the response stream (which is what Alba already did). By letting TestServer keep its own response stream, the resulting ResponseBodyReaderStream actually contains the output.

I still don't quite see how this would work out with a middleware that actually needs to modify the output, but maybe I'm missing something.

Lauri Kotilainen

@rytmis

And with that realization, I'm down to 36 failing tests on Core 3.0.

Jeremy D. Miller

@jeremydmiller

What else is it failing on besides the response body?

Lauri Kotilainen

@rytmis

As of two minutes ago, nothing.

Jeremy D. Miller

@jeremydmiller

That sounds a whole lot better;-)

Lauri Kotilainen

@rytmis

Most of the failures were due to me not having done the 3.0 work for the WebApp project. Once I had that down, it was a couple of minor things. The TestServer changes might still cause some issues, but at least now it all compiles and all tests pass, so I can run it with some real world tests.

Jeremy D. Miller

@jeremydmiller

Good deal

Lauri Kotilainen

@rytmis

I've got an app that has a small number of Alba-based tests that I upgraded to 3.0, I should be able to give that a try soon-ish. In the meantime, I've opened a WIP PR on Github.

Jeremy D. Miller

@jeremydmiller

Cool, I’ll try to take a look today

Lauri Kotilainen

@rytmis

Take your time. I'm thinking the response body stuff is something I should try and get a response for from MS though. It would spare some #ifs here and there and overall give me more confidence that things won't mysteriously break for no apparent reason.

Lauri Kotilainen

@rytmis

Yeah, so uh. I tried the TestServer example code from the aspnetcore docs and added a middleware that sticks in a MemoryStream. It no worky. The pipeline writes to the MemoryStream just fine, but the code I screenshotted yesterday returns a different stream from the respnse, and the end result is an empty response even with the MS-provided example code.

Jeremy D. Miller

@jeremydmiller

Does it really matter? Can’t we just have REsponseBody read from the response stream that TestServer sticks in the HttpContext?

Lauri Kotilainen

@rytmis

Right now, we don't care -- but it's definitely a mistake in TestServer.

I wrote a simple test case. Blank MVC app, single unit test that exercises the default scaffolded index via TestServer. Works fine.

I then add a RequestDelegate before Endpoint Routing that does nothing other than replace the Body stream -- and the test fails, because TestServer doesn't return the content. And mind you, this would work in Kestrel. So I'm writing this up as a bug report against AspNetCore, and if they fix it, we can get rid of some #ifs in Alba.

Eh. Egg on my face, the scenario I described doesn't actually work in Kestrel either. It used to, I think, but in the new Pipeline world, I guess it can't. So now, if one wants to intercept the body, you can, but you are then responsible for restoring the original stream to the response and writing the captured content there.