SBTUITestTunnel icon indicating copy to clipboard operation
SBTUITestTunnel copied to clipboard

Monitored requests have no httpBody

Open fermoya opened this issue 1 year ago • 5 comments

Hi, I have this UITest that was previously working in iOS 16.4 simulator. We've moved now to use iOS 17.2 simulator. We're using latest version 10.0.0. I'm monitoring a request and debugging I can see we send a body. However, when I peek the requests:

app.monitoredRequestsPeekAll()

I see my requests have no httpBody somehow whereas if I run the same test on iOS 16.4 I can see the body. This is a regular request using URLSession.dataTask(with:completionHandler).

Any ideas what's wrong?

fermoya avatar Feb 15 '24 10:02 fermoya

Hi there! I just tried running the library's test suite on an iOS 17.2 simulator, and tests are passing including a few that also verify the httpBody. Moreover we have several tests in the codebase @Subito that monitor requests and checks the httpBody. Did you try to replicate the issue in a separate project that can be shared? It would make debugging the issue easier.

tcamin avatar Feb 15 '24 21:02 tcamin

Hey @tcamin, I have the same problem. The issue doesn't seem to reproduce on a small body.

Try to change httpBody in this test to String(repeatElement("a", count: 20000)) and you will see this error in the console:

Dropping oversized protocol property key SBTUITunneledNSURLProtocolHTTPBodyKey in <NSMutableURLRequest: 0x60000000c1e0> { URL: https://httpbin.org/post }

magauran avatar Feb 20 '24 16:02 magauran

That's interesting! I'll try to take a look as soon as I can.

tcamin avatar Feb 20 '24 16:02 tcamin

I've have this open branch to test out hotfix/large_body. I still need to investigate some issues when running in CI but locally it seems to be working. If you try it out let me know!

tcamin avatar Feb 23 '24 08:02 tcamin

hey @tcamin thanks for the quick fix. I just tried and whereas now the request has an httpBody it seems that the ObjC casting is off:

Could not cast value of type '__NSCFString' (0x1f1d04258) to 'NSData' (0x1f1d09130).

httpBody is supposed to have NSData type. I've seen this before while working in ObjC

fermoya avatar Feb 23 '24 11:02 fermoya

I'm checking the diff:

+ (void)setProperty:(id)property forKey:(nonnull NSString *)key inRequest:(nonnull NSMutableURLRequest *)request
{
    if ([property isKindOfClass:[NSData class]] && ((NSData *)property).length > 16834) { ... }
    else { ... }
}

+ (id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;
{
    id property = [NSURLProtocol propertyForKey:key inRequest:request];
    return [storage objectForKey:property] ?: property;
}

it seems that storage is always storing NSData but propertyForKey returns anything. And there's also a collision of types. If I'm not wrong, [storage objectForKey:property] should return NSData whereas ?: property returns NSString

fermoya avatar Feb 23 '24 14:02 fermoya

Yeah so the original body is something like this:

{
    "foo": <very_long_text>
    "bar": <something_of_interest_for_the_test>
}

and what I get in the monitored [request HTTPBody] is a UUID: D68DD094-EAB4-4CEA-B401-AB1FA21BB34A

fermoya avatar Feb 23 '24 16:02 fermoya

The logic should be right, I've slightly modified the implementation which should address the issue you are experiencing. Let me know if it works now.

tcamin avatar Feb 23 '24 21:02 tcamin

Thanks @tcamin . I've updated my dependency to point at 985d63ec9a6080aeacbc2ca9d76b86cf2cea189e but unfortunately I still see the same issue.

What is it that you tweaked?

fermoya avatar Feb 26 '24 10:02 fermoya

Same issue as in getting UUID or empty body?

tcamin avatar Feb 26 '24 10:02 tcamin

Same issue as in getting UUID or empty body?

Getting UUID

fermoya avatar Feb 26 '24 11:02 fermoya

Could you try again on e5c0f7f4bd343627e55aecc39b76c39b32c8b444?

tcamin avatar Feb 27 '24 15:02 tcamin

hey same issue on e5c0f7f4bd343627e55aecc39b76c39b32c8b444, I'm getting a UUID:

(lldb) po _originalRequest.HTTPBody
E8A5F5B2-65EF-432D-B7C4-F26E44E8DFA3

fermoya avatar Feb 28 '24 10:02 fermoya

@tcamin it seems that the properties saved in SBTRequestPropertyStorage from the app process are not visible in the UI Tests process.

magauran avatar Mar 05 '24 01:03 magauran

@tcamin we have the same issue, any updates about the fix

AhmedAshraf605 avatar Mar 19 '24 08:03 AhmedAshraf605

Hi! Sorry for the late reply. It would help if you could provide a way to replicate this locally, ideally by adding a failing test to the libary.

tcamin avatar Mar 19 '24 08:03 tcamin

@tcamin We appreciate your support and adding this test will help reproduce the issue locally. Please add the following test to DownloadUploadTests:

func testMonitorPostRequestWithHTTPLargeBodyInAppProcess() {

        let largeBody = String(repeating: "a", count: 20000)
        let matchingRequest = SBTRequestMatch(url: "httpbin.org", method: "POST")
        app.monitorRequests(matching: matchingRequest)

        XCTAssertTrue(app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].waitForExistence(timeout: 5))
        app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].tap()

        XCTAssertTrue(app.waitForMonitoredRequests(matching: matchingRequest, timeout: 10))
        let requests = app.monitoredRequestsFlushAll()
        XCTAssertEqual(requests.count, 1)

        print(requests.map(\.debugDescription))

        for request in requests {
            guard let httpBody = request.request?.httpBody else {
                XCTFail("Missing http body")
                continue
            }

            XCTAssertEqual(String(data: httpBody, encoding: .utf8), largeBody)

            XCTAssert((request.responseString()!).contains("httpbin.org"))
            XCTAssert(request.timestamp > 0.0)
            XCTAssert(request.requestTime > 0.0)
        }

        XCTAssert(app.stubRequestsRemoveAll())
        XCTAssert(app.monitorRequestRemoveAll())
    }

and add in SBTTableViewController

@objc func executePostDataTaskRequestWithLargeHTTPBody() {
        let largeBody = String(repeating: "a", count: 20000)
        dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: largeBody)
    }

The issue is that the request being fired is from the app process and not from the UITest process. As a result, we are unable to obtain the original request body.

AhmedAshraf605 avatar Mar 19 '24 22:03 AhmedAshraf605

@fermoya @magauran Try this.

AhmedOS avatar Mar 26 '24 10:03 AhmedOS

this is working @AhmedAshraf605 , thanks for the contribution 🙌

@tcamin will you cut a new version soon?

fermoya avatar Mar 27 '24 10:03 fermoya

Hi! If everything works on our tests suites for a few days I will release a new version in the coming week.

tcamin avatar Mar 27 '24 13:03 tcamin