-
-
Notifications
You must be signed in to change notification settings - Fork 127
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
fix: add "address()" on mock Socket #549
fix: add "address()" on mock Socket #549
Conversation
Sharing our conversation from Discord:
That's tempting but I would recommend against it. Instead, we know the mocked and passthrough contexts because we have methods passthrough() {
const realSocket = createConnection()
// Fro actual connections, infer the address function.
this.address = realSocket.address
}
respondWith(response) {
this.address = () => addressFromResponse(response)
} |
} | ||
} | ||
|
||
return super.address() |
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 will have incorrect (empty?) value for bypass scenarios because MockHttpSocket
has no connection information.
I proposed to infer this from the original socket but now I wonder, socket.address()
is generally available throughout the socket's life-cycle, yes? Like, you can grab the address at any point in time after constructing the socket. Hmm.
}); | ||
|
||
|
||
it.only('returns real socket address', 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.
Oops, accidental it.only
committed! Sorry, don't have an eslint for this.
@kettanaito I fixed this as suggested. I still think we need to separate the socket-related logic from the HTTP-specific one if we want to support more protocols, but that can wait for another PR. I have a couple of questions before it's ready for merge from my side:
|
@@ -124,6 +124,7 @@ export class MockHttpSocket extends MockSocket { | |||
*/ | |||
public passthrough(): void { | |||
const socket = this.createConnection() | |||
this.address = () => socket.address() |
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.
Technically, this would be safer:
this.address = () => socket.address() | |
this.address = socket.address.bind(socket) |
In case this
in the address()
points to the socket instance, which I think it does. Let's preserve the context.
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 both ways preserve the context, but I agree that your solution is more explicit.
@@ -205,6 +206,13 @@ export class MockHttpSocket extends MockSocket { | |||
return | |||
} | |||
|
|||
// Return fake address information for the socket. | |||
this.address = () => ({ | |||
address: '0.0.0.0', |
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.
What do you think about setting 127.0.0.1
as the fake address? Seeing localhost can hint people that this address is, indeed, mocked.
// Return fake address information for the socket. | ||
this.address = () => ({ | ||
address: '0.0.0.0', | ||
family: 'IPv4', |
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 wonder if there's any info in connectionOptions
that we can use to set the family
? 🤔
Yeah, there's .family
and .host
. Do you think we should respect those?
this.address = () => {
return {
address: this.connectionOptions?.host || '127.0.0.1',
family: this.connectionOptions?.family || 'IPv6',
port: this.connectionOptions?.port || 0
}
}
We can also add automated tests for the cases when explicit connection options are set. Granted, this is likely better tested in the unit tests on MockHttpSocket
. You can leave the existing http.*
tests as-is, those are useful too!
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 the address info resolved in the socket. I didn't verfiy this, but the connectionOptions
includes only the port
.
Also, please note the host
!== address
.
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.
You're right. Let's infer the port
only for now then?
const request = http.get('http://example.com') | ||
request.once('socket', socket => { | ||
socket.once('connect', () => { | ||
connectPromise.resolve(socket.address()) |
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 would be great to also have a test for socket.address()
before the connect
is emitted. I believe it should still be set?
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 the lookup happens after the socket event is emitted.
request.once('socket', socket => {
console.log(socket.address()); // prints empty object {}
});
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, this is correct. ClientRequest emits socket
as soon as the socket is assigned, then the socket goes through its life-cycle, emitting lookup
and connect
.
@@ -298,7 +306,7 @@ export class MockHttpSocket extends MockSocket { | |||
} | |||
|
|||
private mockConnect(): void { | |||
this.emit('lookup', null, '::1', 6, this.connectionOptions.host) | |||
this.emit('lookup', null, '127.0.0.1', 4, this.connectionOptions.host) |
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 setting this.address
should be done in mockConnect()
for mocked responses. This will couple the .address()
function withe actual address we emit in the lookup even so those two stay consistent (right now, they are not).
const address = {
host: '...',
family: '...',
port: '...'
}
this.address = () => address
this.emit('lookup', null, address.host, address.family, this.connectionOptions.host)
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.
Wonderful! I'm ashamed that I didn't think about it myself.😅
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.
You think of a ton of things that never crossed my mind 🙌 The power of collaboration.
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 looks great! I've pushed a minor polishing to the tests, this should be ready for merge after CI passes.
66c6046
into
feat/yet-another-socket-interceptor
No description provided.