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

Troubles stubbing a URL with complex query parameters, i.e. has "{" and "}" in URL #16

Closed
Rexrover2 opened this issue May 6, 2024 · 6 comments
Assignees

Comments

@Rexrover2
Copy link
Contributor

I am using SystemTestingTools Version 2.0.32. I am using the AppendHttpCallStub to set up my stub for the endpoint that I'm writing tests for. I am aware that when I feed in a URI into the said method, I need to escape the curly brackets by doubling them up. Not escaping them will cause an exception to be thrown in the method SetEndpoint as String.Format will throw a FormatException error. However, escaping before calling AppendHttpCallStub does not suffice. Later when the stubbed endpoint is called. The GetEndpoint method is invoked, and since the the previous String.Format call in SetEndpoint removes the escaped characters, the String.Format in this method is thrown.

Any advise on how to get around this is deeply appreciated!

var baseUrl= @"http://localhost:23068/";
var endpointUrl= @"{{ ""key"": ""value"" }}";
var stubUrl = $"{baseUrl}{endpointUrl}";
client.AppendHttpCallStub(HttpMethod.Get, new Uri(stubUrl), response);
@AlanCS
Copy link
Owner

AlanCS commented May 6, 2024

hello !
Glad you found this package useful !

QueryString parameters don't generally have curly braces, they are generally look like ?Age=35&Name=Tony
those key value pairs with braces look more like header parameters to me!

If heades is what you need, there is a parameter for that in AppendHttpCallStub for such scenarios (https://github.com/AlanCS/SystemTestingTools/blob/master/Tool/SystemTestingTools/Extensions/HttpClientExtensions.cs#L54)

If you absolutely must send a query string param with curly braces, maybe formatting it with https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.urlencode?view=net-8.0#system-web-httputility-urlencode(system-string) would solve your problem

if the solutions above don't work, could you please raise a PR with a failing test containing your problem ?
from there I can better simulate and fix :)

This package is also tested using component testing, and this is the perfect situation to create a test for such "wiring" edge case :) you can add a new method in an existing or new controller in https://github.com/AlanCS/SystemTestingTools/tree/master/Examples/MovieProject/MovieProject.Web/Controllers, showcasing the complex parameters you are using

and then add a failing test https://github.com/AlanCS/SystemTestingTools/tree/master/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting that will highlight your problem

I hope this helps, let me know how it goes one way or another !

@AlanCS AlanCS self-assigned this May 6, 2024
@Rexrover2
Copy link
Contributor Author

Hey Alan,

Thank you very much for your reply.

Yeah it's unfortunate that the endpoint that we need to integrate with expects complex query parameters like the example described, but alas it has to be done.

I will try to raise a PR within the week to add a failing test.

@AlanCS
Copy link
Owner

AlanCS commented May 27, 2024

Hello !

the latest build has fixed the problem

I had to make some adjustments to your test, as the problem you described was about sending a querystring parameter with brackets
that was easy to fix, but your test also forwards the same json downstream via query string, and for the component test to identify that downstream call and return a stub, the json needed to be EXACTLY THE SAME, meaning spacing and casing, which is quite difficult as you were serializing the class in tests with NewtownSoft, and in the library with System.Text.Json
I simplified the example to just send the username downstream, as this was not a core part of the problem you initially described

let me know if it makes sense, or if you have other problems

PS: I am having a bit of difficulty publishing the new nuget package, as the pipeline mechanism in github changed a lot since I last worked on it, it no longer works. I am focusing on fixing it now, should be available to you in the next few days at worst

@Rexrover2
Copy link
Contributor Author

Rexrover2 commented May 27, 2024

Hey Alan,

Thanks for addressing this issue!

However, I think the test case did suit my use case, where we require passing the query string parameter with brackets to the proxy. As a result, I tried to pass the URL + query string to AppendHttpCallStub. We are connecting to a legacy in-house API that we cannot make changes to, thus our situation 😆.

I agree that it makes the identification of the downstream call quite difficult. How would you recommend stubbing the proxy if it expects complex query parameters? (Guessing that as long as I get the syntax exactly the same as the downstream's call, it should be fine, so maybe I should use System.Text.Json to serialise the class?)

PS: Thanks again for fixing this and your responsiveness, a few days' wait will be totally fine!

@AlanCS
Copy link
Owner

AlanCS commented May 28, 2024

Sorry for the delay, the build pipeline was very out of date, took me a while to fix it up !
the new version of the package is available: https://www.nuget.org/packages/SystemTestingTools/2.0.33
this will allow you to use QueryStrings with brackets

about you particular problem, I think I visualize it now
I have worked on a similar problem once, there are a few potential solutions, so here are some thoughts that might be useful to you:

  • if your app is merely a proxy, and you don't need to do anything with the class being sent via query string; then you don't need to receive it as a class, deserialize and reserialize it again. You can receive it as a string, and just pass the exact same string to downstream. This will allow you to have simpler code, and later on do an asserting comparing the inbound / outbound requests, making sure they have EXACTLY the same query string, because it has been unchanged.
  • if you need this proxy like functionality for a lot of endpoints, you might want to do something more generic: instead of receiving each individual parameter and forwarding it, you read the whole querystring, even if it has many parameters, as one string:
        private readonly IHttpContextAccessor accessor;

        public CarController(IHttpContextAccessor accessor)
        {
            this.accessor = accessor;
        }

        [HttpGet]
        public async Task<List<Car>> DoSomething() // many query string parameters here, but you don't need to catch as inputs to this method
        {
            string querystring = this.accessor.HttpContext.Request.QueryString;

            return await _carService.Foward(querystring);
        }
  • if you do need to make some changes to the class before forwarding it to another app, then it's a matter of what level of assertion you will be comfortable with, maybe you don't have to assert the whole url exactly,

you could check for something in the beginning and the middle"

client.GetSessionOutgoingRequests()[0].GetEndpoint().Should().StartWith("GET http://www.mydoing.com/myapi?myfield={{").And.Contains("Bret");

if you need to check the whole URL, you could also try to remove all whitespace and lower case the whole string, to make sure that different serialization methods would still return the same result

Let me know if it makes sense

@Rexrover2
Copy link
Contributor Author

Rexrover2 commented May 29, 2024

Hey Alan,

Again, thanks for the fix. I could use the new package version to stub the legacy proxy endpoint call that was causing us much trouble.

My team is using this for a couple of component/ acceptance testing projects to ensure that the endpoints are invoked with client.PostAsync(url, requestBody) or client.SendAsync(url) provide the correct responses when their proxies are stubbed (with AppendHttpCallStub).

My solution was to carefully craft the URL to be fed into AppendHttpCallStub, in that the queryString appended was processed with:

  1. var content = JsonSerializer.Serialize(object, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false } - removes spaces, newlines and makes object fields start in lower-case.
  2. WebUtility.Encode(content)

Then when invoking the proxy within the service, I would serialise the object with the same options listed above, with no encoding done. That allowed me to create an exact matching stub for the call required.

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

No branches or pull requests

2 participants