Repository navigation
Invalid Type with ChannelTest Sockets聽#5509
Description
Environment
- Elixir version (elixir -v): 1.15.2
- Phoenix version (mix deps): 1.7.6
- Operating system: macOS 13.4.1
Actual behavior
Hello 馃憢馃徏
This relates to the intersection of production code and test code, so it may not be an actual issue.
The type Phoenix.Socket.t() specifies the types of most fields, however Phoenix.ChannelTest initializes sockets with several fields that are either missing (such as channel_pid or have a different type (like transport). As a result, the following code (from the ChannelTest module documentation) is caught by Dialyzer as having no return:
{:ok, _, socket} =
socket(UserSocket, "user:id", %{some_assigns: 1})
|> subscribe_and_join(RoomChannel, "room:lobby", %{"id" => 3})
# or...
{:ok, socket} = connect(UserSocket, %{"some" => "params"}, %{})
{:ok, _, socket} = subscribe_and_join(socket, "room:lobby", %{"id" => 3})At the application level, Dialyzer generally doesn't give a descriptive error beyond "has no local return", however if you copy the library code in place of the calls, it warns about the call to Phoenix.Channel.Server.join/4:
The function call will not succeed.
Phoenix.Channel.Server.join(
_socket :: %Phoenix.Socket{
:assigns => _,
:channel => nil,
:channel_pid => nil,
:endpoint => atom(),
:handler => _,
:id => _,
:join_ref => nil,
:joined => false,
:private => %{},
:pubsub_server => _,
:ref => nil,
:serializer => Phoenix.ChannelTest.NoopSerializer,
:topic => nil,
:transport => {Phoenix.ChannelTest, pid()},
:transport_pid => pid()
},
MyAppWeb.RoomChannel,
_message :: %Phoenix.Socket.Message{
:event => <<_::64>>,
:join_ref => nil,
:payload => %{},
:ref => integer(),
:topic => <<_::56>>
},
[
{:starter,
(_, _, _ -> {:error, _} | {:ok, :undefined | pid()} | {:ok, :undefined | pid(), _})},
...
]
)
breaks the contract
(Phoenix.Socket.t(), module(), Phoenix.Socket.Message.t(), :elixir.keyword()) ::
{:ok, term(), pid()} | {:error, term()}
Notably, I discovered this mismatch only because I chose to call ChannelTest functions in the setup block contained within my application's ChannelCase module. Normally, Dialyzer would not have the chance to catch this code in test files.
Expected behavior
It seems unlikely that the Phoenix.Socket.t() type should be expanded to include the socket created in ChannelTest (except possibly for fields like transport), but on the other hand I wonder if it is even feasible for the fake Socket to be "complete" in this context. Since these functions are generally called in test files anyway, maybe it's not worth exploring further.
鉂わ笍
Activity
chrismccord commentedon Jul 7, 2023
I'd happily review a PR that fixes this, but I'm not a dialyzer user myself so it's not something I'll be able to prioritize. Thanks!
osazemeu commentedon Oct 7, 2026
I opened #6870 to fix this. ChannelTest now keeps its supervisor in
socket.privateinstead of in atransporttuple, andtopicandchannel_pidare typed as nilable inPhoenix.Socket.t. With both changes, a minimal repro goes from 4 Dialyzer errors to 0.Fix Phoenix.ChannelTest sockets violating Phoenix.Socket.t (#6870)
Fix Phoenix.ChannelTest sockets violating Phoenix.Socket.t (#6870)