Skip to content
Merged
11 changes: 11 additions & 0 deletions coveralls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"skip_files": [
"lib/alice.ex",
"lib/alice/state_backends/redix_pool.ex"
Comment on lines +3 to +4
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files are both Supervisors. There is actually a test file for lib/alice.ex that tests the one function that isn't part of the Supervisor spec.

],
"coverage_options": {
"minimum_coverage": 77,
"treat_no_relevant_lines_as_covered": true
}
}

23 changes: 11 additions & 12 deletions lib/alice/handlers/help.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ defmodule Alice.Handlers.Help do

@doc "`help` - lists all known handlers"
def general_help(conn) do
[
"_Here are all the handlers I know about…_",
handler_list(),
"_Get info about a specific handler with_ `@alice help <handler name>`",
"_Get info about all handlers with_ `@alice help all`",
"_Feedback on Alice is appreciated. Please submit an issue at https://github.com/alice-bot/alice/issues _"
]
|> Enum.join("\n\n")
"""
_Here are all the handlers I know about…_

#{handler_list()}

_Get info about a specific handler with_ `@alice help <handler name>`

_Get info about all handlers with_ `@alice help all`

_Feedback on Alice is appreciated. Please submit an issue at https://github.com/alice-bot/alice/issues _
"""
|> reply(conn)
end

Expand Down Expand Up @@ -139,10 +142,6 @@ defmodule Alice.Handlers.Help do
{title, name, :hidden}
end

defp parse_function_doc({_function, _anno, _sig, _doc_content, _meta}, title) do
{title, nil, :hidden}
end

Comment on lines -142 to -145
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure we don't need this fall-through version of the function

defp format_route({_, _, :hidden}), do: nil

defp format_route({title, name, text}) do
Expand Down
18 changes: 17 additions & 1 deletion lib/alice/router/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,29 @@ defmodule Alice.Router.Helpers do
def delayed_reply(msg, ms, conn = %Conn{}), do: delayed_reply(conn, msg, ms)

def delayed_reply(conn = %Conn{}, message, milliseconds) do
parent = self()

Task.async(fn ->
conn = indicate_typing(conn)
forward_message(parent, :indicate_typing)

:timer.sleep(milliseconds)
reply(message, conn)

conn = reply(conn, message)
forward_message(parent, :send_message)

conn
end)
end

defp forward_message(pid, name) do
receive do
{^name, payload} -> send(pid, {name, payload})
after
0 -> nil
end
end

@doc """
Indicate typing.
"""
Expand Down
38 changes: 34 additions & 4 deletions test/alice/conn_test.exs
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
defmodule Alice.ConnTest do
use ExUnit.Case, async: true
use ExUnit.Case

alias Alice.Conn

def conn_with_text(text) do
Conn.make(%{text: text}, %{})
end

test "you can inspect a conn" do
conn = Conn.make(:message, :slack, :state)
assert inspect(conn) == "%Alice.Conn{message: :message, slack: %{...}, state: :state}"
end

test "make makes a conn" do
assert Conn.make(:m, :sl, :st) == %Conn{message: :m, slack: :sl, state: :st}
assert Conn.make(:message, :slack, :state) == %Conn{
message: :message,
slack: :slack,
state: :state
}
end

test "make makes a conn with a default state" do
assert Conn.make(:m, :sl) == %Conn{message: :m, slack: :sl, state: %{}}
assert Conn.make(:message, :slack) == %Conn{
message: :message,
slack: :slack,
state: %{}
}
end

test "make makes a conn with a tuple" do
assert Conn.make({:m, :sl, :st}) == %Conn{message: :m, slack: :sl, state: :st}
assert Conn.make({:message, :slack, :state}) == %Conn{
message: :message,
slack: :slack,
state: :state
}
end

test "command? is true when the bot is @username'd" do
Expand Down Expand Up @@ -47,6 +64,12 @@ defmodule Alice.ConnTest do
assert "SOME_TIMESTAMP" = Conn.timestamp(conn)
end

test "at_reply_user formats an at reply that slack will recognize" do
user = %{id: "user_id"}
conn = Conn.make(%{user: "user_id"}, %{users: %{"user_id" => user}})
assert Conn.at_reply_user(conn) == "<@user_id>"
end

test "add_captures adds regex captures to the conn" do
conn =
"hello test world"
Expand Down Expand Up @@ -89,6 +112,13 @@ defmodule Alice.ConnTest do
assert {:msg, :slk, :other} = {conn.message, conn.slack, conn.state.other}
end

test "delete_state_for deletes the state for a given namespace" do
state = %{{:some, :namespace} => :value}
conn = Conn.make(:msg, :slk, state)
conn = Conn.delete_state_for(conn, {:some, :namespace})
assert Conn.get_state_for(conn, :namespace) == nil
end

test "sanitize_message removes smart quotes" do
conn = conn_with_text("“”’")
assert Conn.sanitize_message(conn).message.text == ~s(""')
Expand Down
89 changes: 44 additions & 45 deletions test/alice/earmuffs_test.exs
Original file line number Diff line number Diff line change
@@ -1,87 +1,86 @@
defmodule Alice.EarmuffsTest do
use ExUnit.Case, async: true
use Alice.HandlersCase, handlers: Alice.Earmuffs

alias Alice.Conn
alias Alice.Earmuffs

@user_id "some user id"
@channel_id "some channel id"
@namespace {Earmuffs, :earmuffs}

def conn_with_state(state \\ %{}) do
Conn.make(%{user: @user_id, channel: @channel_id}, %{}, state)
end

test "block adds the user and channel to the blocklist" do
%Conn{state: state} = %{} |> conn_with_state |> Earmuffs.block()
assert %{{Earmuffs, :earmuffs} => %{@user_id => [@channel_id]}} = state
end

test "block block user and channel when user already has a channel" do
test "it responds to earmuffs command and sets the block" do
conn =
%{{Earmuffs, :earmuffs} => %{@user_id => ["another"]}}
|> conn_with_state
Conn.make(
%{text: "<@alice> earmuffs", channel: @channel_id, user: @user_id},
%{users: %{@user_id => %{id: @user_id, name: "fake_user"}}, me: %{id: :alice}}
)

conn = send_message(conn)

assert first_reply() == "<@#{@user_id}> :mute:"
assert Earmuffs.blocked?(conn)
end

test "block adds the user and channel to the blocklist" do
conn = conn_with_state(%{})
%Conn{state: state} = Earmuffs.block(conn)

%{{Earmuffs, :earmuffs} => %{@user_id => [@channel_id, "another"]}} =
state
|> assert
assert %{@namespace => %{@user_id => [@channel_id]}} = state
end

test "block block user and channel when another user has a channel" do
conn =
%{{Earmuffs, :earmuffs} => %{"user" => ["channel"]}}
|> conn_with_state
test "block still blocks a user and channel when user already has a channel" do
conn = conn_with_state(%{@namespace => %{@user_id => ["another"]}})
%Conn{state: state} = Earmuffs.block(conn)

assert %{@namespace => %{@user_id => [@channel_id, "another"]}} = state
end

test "block still blocks a user and channel when another user has a channel" do
conn = conn_with_state(%{@namespace => %{"user" => ["channel"]}})
%Conn{state: state} = Earmuffs.block(conn)

%{{Earmuffs, :earmuffs} => %{"user" => ["channel"], @user_id => [@channel_id]}} =
state
|> assert
assert %{@namespace => %{"user" => ["channel"], @user_id => [@channel_id]}} = state
end

test "blocked? is false when nothing is blocked" do
%{}
|> conn_with_state
|> Earmuffs.blocked?()
|> refute
conn = conn_with_state(%{})

refute Earmuffs.blocked?(conn)
end

test "blocked? is true when the conn is blocked for this channel and user" do
%{{Earmuffs, :earmuffs} => %{@user_id => ["another channel", @channel_id]}}
|> conn_with_state
|> Earmuffs.blocked?()
|> assert
conn = conn_with_state(%{@namespace => %{@user_id => ["another channel", @channel_id]}})

assert Earmuffs.blocked?(conn)
end

test "blocked? is false when channel is blocked but not user" do
%{{Earmuffs, :earmuffs} => %{"another user" => [@channel_id]}}
|> conn_with_state
|> Earmuffs.blocked?()
|> refute
conn = conn_with_state(%{@namespace => %{"another user" => [@channel_id]}})

refute Earmuffs.blocked?(conn)
end

test "blocked? is false when user is blocked but not channel" do
%{{Earmuffs, :earmuffs} => %{@user_id => ["another channel"]}}
|> conn_with_state
|> Earmuffs.blocked?()
|> refute
conn = conn_with_state(%{@namespace => %{@user_id => ["another channel"]}})

refute Earmuffs.blocked?(conn)
end

test "unblock adds an empty channel list for the user when state is empty" do
unblocked_conn =
%{}
|> conn_with_state
|> Earmuffs.unblock()
conn = conn_with_state(%{})
conn = Earmuffs.unblock(conn)

assert unblocked_conn.state == %{{Earmuffs, :earmuffs} => %{@user_id => []}}
assert conn.state == %{@namespace => %{@user_id => []}}
end

test "unblock removes a block for a user and channel" do
conn =
%{{Earmuffs, :earmuffs} => %{@user_id => [@channel_id, "other"]}}
|> conn_with_state
|> Earmuffs.unblock()
conn = conn_with_state(%{@namespace => %{@user_id => [@channel_id, "other"]}})
conn = Earmuffs.unblock(conn)

assert conn.state == %{{Earmuffs, :earmuffs} => %{@user_id => ["other"]}}
assert conn.state == %{@namespace => %{@user_id => ["other"]}}
end
end
95 changes: 90 additions & 5 deletions test/alice/handlers/help_test.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,94 @@
defmodule Alice.Handlers.HelpTest do
use ExUnit.Case, async: true
alias Alice.Handlers.Help
alias Alice.Handlers.TestHandler
alias Alice.Handlers.{
Help,
TestHandler
}

test "help_for_handler generates the correct output" do
assert Help.help_for_handler(TestHandler) ==
use Alice.HandlersCase, handlers: [Help, TestHandler]

test "general_help lists the handlers as well as some other info" do
send_message("<@alice> help")

assert first_reply() == """
_Here are all the handlers I know about…_

> *Help*
> *TestHandler*

_Get info about a specific handler with_ `@alice help <handler name>`

_Get info about all handlers with_ `@alice help all`

_Feedback on Alice is appreciated. Please submit an issue at https://github.com/alice-bot/alice/issues _
"""
end

test "keyword_help returns general help when the keyword isn't found" do
send_message("<@alice> help bogus handler")

assert all_replies() == [
~s(I can't find a handler matching "bogushandler"),
"""
_Here are all the handlers I know about…_

> *Help*
> *TestHandler*

_Get info about a specific handler with_ `@alice help <handler name>`

_Get info about all handlers with_ `@alice help all`

_Feedback on Alice is appreciated. Please submit an issue at https://github.com/alice-bot/alice/issues _
"""
]
end

test "keyword_help lists help for a single handler" do
send_message("<@alice> help test handler")

assert all_replies() == [
"""
_*Pro Tip:* Commands require you @ mention me, routes do not_

_Here are all the routes and commands I know for "testhandler"_

>*Alice.Handlers.TestHandler*
>
> *Routes:*
> _command1_
> `cmd1`: does some stuff
>
> *Commands:*
> _command1_
> `@alice cmd1`: does some stuff
> _command2_
> `@alice cmd2`: does some other stuff
> also this one is multiline
> _command3_
> this one doesn't start with an
> example so no @alice is added
> _no_docs_
> _no documentation provided_
"""
]
end

test "keyword_help lists help for all handlers" do
send_message("<@alice> help all")

assert all_replies() == [
"_*Pro Tip:* Commands require you @ mention me, routes do not_",
~s(_Here are all the routes and commands I know about…_),
"""
>*Alice.Handlers.Help*
>
> *Commands:*
> _general_help_
> `@alice help` - lists all known handlers
> _keyword_help_
> `@alice help all` - outputs the help text for each route in every handler
> `@alice help <handler name>` - outputs the help text for a single matching handler
""",
"""
>*Alice.Handlers.TestHandler*
>
Expand All @@ -24,5 +108,6 @@ defmodule Alice.Handlers.HelpTest do
> _no_docs_
> _no documentation provided_
"""
]
end
end
Loading