The new module mocks out calls to the requests library, which
is used by many of our bots that use third party services.
Mocking of requests.py is mostly orthogonal to our other
testing concerns.
All of the functionality of BotTestCase was either directly
moved to StubBotTestCase or replaced by similar functions,
and all bots have been ported to StubBotTestCase.
Chess Bot is a bot that allows you to play chess against either another
user or the computer. Use `start with other user` or
`start as <color> with computer` to start a game.
In order to play against a computer, `chess.conf` must be set with the
key `stockfish_location` set to the location of the Stockfish program on
this computer.
Use `bot_handler.storage` to preserve game state across messages.
(@showell also did minor work here to have the test use verify_dialog()
and have the bot respond to empty messages)
This is mostly a pure refactoring, but it also ensures
that `initialize` is called in a consistent way by most
of our test helpers. (This didn't cause problems before,
since some bots don't require initialization.)
From @showell:
We had a PR here with lots going on, and the commits weren't
very well organized, and then there were some tricky merge
conflicts from another PR. So I just squashed them all into
one commit.
What this does:
* allow you to configure your followup stream
* provide help in followup stream
* add more testing to followup stream
* add get_response() helper for tests
Fixes#173Fixes#174
We can avoid `Any` annotations for `self`, since it is more
noise than signal and since the type of self is already
implicit from how Python classes work.
For three of the tests we use the simpler verify_reply()
API. For the 403 test, we don't need to rely on setUp
any more to simulate everything for us, and we do more
surgical patching.
Move `get_response` inside of `mock_http_conversation`, as it is not
used anywhere else. Also create `assert_called_with_fields`.
`assert_called_with_fields` calls the `assert_called_with` method of a
mock object by using an HTTP request and a list of fields to look for.
This sets us up to validate more aspects of the conversation,
and it also introduces the more rigorously checked
`unique_response` helper.
(This also fixes a minor copy/paste error from a prior commit
that was harmless.)
Note that we now only have one conversation, since the case
of sending to streams has the same mechanics as sending PMs.
We'll eventually want a separate test to drive out differences
in the actual mechanics of the reply.
This method had two pretty easy-to-separate concerns:
* find the fixture data using our directory conventions
* use the fixture data to simulate a real HTTP request
Part of the goal here is to make the extracted functions a
bit easier to use in other TestCase-based classes without
needing to subclass from BotTestCaseBase, which is kind of
complex with its setUp/tearDown.
This program replaces zulip_bot_output.py, which had
gotten a little out of date.
It should be able to simulate a terminal conversation for
all of our bots, including those that use "advanced" features:
third party config files: tested with giphy
message updates: tested with incrementor
storage: tested with virtual_fs and others
Before this change, we were looking for config files in
default locations in source control, which is not a good
place to look for them. Now `run.py` and friends have a
command line argument where users can specify the config
files.
Note that the change to server.py is only a partial fix
to make it so that bots that don't use third party config
files won't crash. That program needs an overhaul, anyway.
This patch is particularly useful in the scenario that your
API key is wrong or out of date, but it's targeted more
generally at any error that `client.get_profile()` reports.
The API has aggressive retry logic for connecting to a
server, which may make sense for situation where you have
connection blips or server restarts.
When you're first connecting to the API, however, connection
failures are almost certainly a sign of misconfiguration, so
now we fail fast.
The bot lib takes advantage of this API change by catching the
ZulipError exception and exiting gracefully.
Before this commit, you would get a traceback if you supplied
a non-existent filename for your config file. Now we exit
gracefully with a useful error message.
We now require users to specify where their config file is
located, and we no longer default to ~/.zuliprc.
As part of this, we needed to make the "usage" more accurate
in the command line, which I mostly achieved by cutting out
unnecessary stuff.
This makes the StateHandler functional. To reduce the
number of server roundtrips when fetching/updating the
state, the entire state is fetched ocne at bot
initialization and cached. All changes are stored in the
cache and only saved externally after handle_message()
has been executed.
Fixes#141.
With this change, StateHandler.put() does only accept JSON-able
objects by default. The incrementor test tried to store the return
value of send_reply(), a non-JSON-able MockObject, in the state.
Therefore, this commits also sets functional default test return
values for send_message() and send_reply().
Finally, it fixes the tictactoe bot which relied on directly
modifying the state_ attribute.
unittest includes by default all module-level classes that inherit
from TestCase and implement at least one method starting with 'test'.
Since it doesn't provide a convenient way for excluding TestSuites,
we need to manually filter out the unwanted testing of our test base
class itself.
No bot is dependant on this module. No
future bot should be dependant on it,
since it is not a bot itself and is thus
blurring the structure of the bots dir.
This simplifies testing stateful bots by integrating the StateHandler
into the test library. As a side-effect, the mock bot handler gets
reused during a test, making the tests more realistic. The
StateHandler now keeps its state during a call to check_expected_responses,
forcing some stateful tests to be more verbose and explicit.
The Zulip server, starting in 1.7, no longer sends
`is_mentioned` in the message payload, and it was buggy in
earlier versions, so now we check `flags`.
This commit does the following:
* Minor improvements to the writing wherever possible.
* Replace links to screenshots with links that would work when
rendering said screenshots on the main repo. This would mean
the screenshots won't be rendered outside the main repo.
* Adds a section that links to our Bots Guide's How to run a bot
tutorial by using a Markdown a macro.
These functions were rendered redundant by changes in our
approach on how the main repo renders docs and logos contained
in the zulip_bots package (see #6103). So, these should now be
removed.
This allows a user to exclusively enter a bot's name or a bot's
directory as the first and only positional argument. Therefore,
no complicated checks for multiple bot specifications are required
anymore.
Additionally, this cleans help messages and makes arguments more
accessible.
This allows a user to exclusively enter a bot's name or a bot's
directory as the first and only positional argument. Therefore,
no complicated checks for multiple bot specifications are required
anymore.
If a user provides both the name and the path to the bot
as input then we either will have to chose one of them
or alert the user to check the input. Selecting the latter
by sending an error message to the user.
Very few bots like followup bot directly call 'send_message'
function from the bot code. Since ExternalBotHandler class is
mocked, we'll have to mock 'send_message' function as well.
Added dummy field value of 'sender_email' for the message to be
as followup bot requires that field while processing
the message.
Since send_message is directly called from a specific bot's code,
so it can be sent to a different stream or under a different topic
than that of the incoming message. So, print the entire message
along with stream name.
A bot calling 'send_reply' function will reply to the incoming
message in the same stream under the same topic. So, only printing
bot's response message content for that.
This eliminates the need to setup dev environment and to
create a bot, setup zuliprc file, subscribe the bot to the
stream in order to try out a bot.
Manual command get-bot-output gives the bots response content
directly.
When testing bots with state and using type="all", it is expected that
the passed-in state will be applied for each source individually.
This commit moves away from alternating between sources for each test,
to running all the tests on each source with a copy of state_handler.
This is done so that Embedded bot system can also make use of
this function directly, as only id is needed in this function.
Tweaked by tabbott to have a cleaner interface and simpler
documentation.
get_bot_logo_path now first formats the path string with the name
of the bot before checking if the path exists. Not doing so is a
bug and causes the function to always return None.
This bot depends on PyDictionary, which isn't very well-implemented
or well-maintained. PyDictionary's dependency on goslate and
goslate's dependency on concurrent.futures has been known to cause
problems in Python 3 virtualenvs. This bot has also been the
source of disruptive BeautifulSoup warnings. Since this bot is only
meant to be an example bot, and for all the above reasons,
it makes sense to remove this bot. The cons of debugging the above
issues outweight the pros of having the bot at all.
This is part of our efforts to have the documentation for a
particular bot live in this repo but still be able to render it
in the main repo (in a way similar to how we render the webhooks
docs).
This function allows us to specify the path to a bot's doc.md file
in zerver.lib.integrations.BotIntegration.
doc.md better describes the style of documentation that will live
inside these files, since we want these to be similar to our
webhooks' doc.md files in terms of how these are rendered and
composed of Markdown macros.
This is part of our efforts to have the documentation for a
particular bot live in this repo but still be able to render it
in the main repo (in a way similar to how we render the webhooks
docs).
This function allows the calling code to get the absolute path
to the bots/ directory. This will allow us to specify an
arbitrary path (jinja2.env.loader.searchpath)
to look for templates for bots' documentation.
This is part of our efforts to have the documentation for a
particular bot live in this repo but still be able to render it
in the main repo (in a way similar to how we render the webhooks
docs).
For the logo, if a particular bot has a logo, get_bot_logo_path
expects to find it as /zulip_bots/bots/{bot_name}/logo.png or
/zulip_bots/bots/{bot_name}/logo.svg.
The help message string was modified in yoda.py file to correctly
instruct the user. But the help message string was not modified
accordingly in the test file.
In case of an errored input (not consistent with the format
of input that the bot seeks), Converter bot was displaying the
errored part first, along with the error message.
This can lead to many failure, if removing_at_mention function
is not working properly then the input '@bot-name 2 m cm' leads
to the bot getting stuck in infinite loop as converter bot will
want to output '@bot-name not a valid number' and hence calling
itself again.
Add error handling support for empty messages and invalid
input to yoda bot, which was previously making the bot crash.
Add comments to describe tests.
Since we want our bots to be both python 2 and python 3 compatible,
we use six to make up for both of them and run the bot smoothly.
'http.client' was basically used for error-handling by the author
of the bot, urllib errors can be handled by the urllib itself. So,
using this for simplicity.
urllib.request.urlopen raises URLError on protocol errors.
The zulip-run-bot script now supports passing in a --path-to-bot
option. This allows users to specify the path to the source file
for their own custom bots, the first step towards being able to
support out-of-tree bots.
To run an existing bot in the zulip_bots package, just passing in
the name of the bot should suffice.
There is no library as 'http.bot_handler'.
'http.client' is what the author of this bots initially wrote.
Searching through the git history shows that someone mistakenly
replaced 'client' with 'bot_handler' here.
get_config_info function in 'ExternalBotHandler' class was
using 2 undefined variables, which wasn't allowing bots to
access information from config file.
Fixed it, bots with api key working now.
Previously, the annotation for the (now removed)send-reply
function was also wrong, as "send-message" function of
EmbeddedBotHandler class does not return any value, contrary
to "Dict[str, Any]" as specified by the funtion.
This is done as a follow up for zulip/zulip#5842.