From d8c6cb7c0a44f2e499f9bb880693cb15a9ab3352 Mon Sep 17 00:00:00 2001 From: Viraat Chandra Date: Tue, 26 Dec 2017 23:47:47 +0530 Subject: [PATCH] interactive bots: Create Baremetrics bot. --- .../zulip_bots/bots/baremetrics/__init__.py | 0 .../bots/baremetrics/assets/list-commands.png | Bin 0 -> 34546 bytes .../bots/baremetrics/baremetrics.conf | 2 + .../bots/baremetrics/baremetrics.py | 172 ++++++++++++++++++ zulip_bots/zulip_bots/bots/baremetrics/doc.md | 41 +++++ .../baremetrics/fixtures/account_info.json | 59 ++++++ .../baremetrics/fixtures/list_customers.json | 43 +++++ .../bots/baremetrics/fixtures/list_plans.json | 42 +++++ .../baremetrics/fixtures/list_sources.json | 48 +++++ .../fixtures/list_subscriptions.json | 42 +++++ .../bots/baremetrics/test_baremetrics.py | 53 ++++++ 11 files changed, 502 insertions(+) create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/__init__.py create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/assets/list-commands.png create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/baremetrics.conf create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/doc.md create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/fixtures/account_info.json create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_customers.json create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_plans.json create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_sources.json create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_subscriptions.json create mode 100644 zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py diff --git a/zulip_bots/zulip_bots/bots/baremetrics/__init__.py b/zulip_bots/zulip_bots/bots/baremetrics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zulip_bots/zulip_bots/bots/baremetrics/assets/list-commands.png b/zulip_bots/zulip_bots/bots/baremetrics/assets/list-commands.png new file mode 100644 index 0000000000000000000000000000000000000000..15d27e6092a050fad4e10c8ae1482400d3b57a66 GIT binary patch literal 34546 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 z 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 diff --git a/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py b/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py new file mode 100644 index 0000000..3627f23 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py @@ -0,0 +1,172 @@ +# See readme.md for instructions on running this code. + +from typing import Any +import requests + +class BaremetricsHandler(object): + def initialize(self, bot_handler: Any) -> None: + self.config_info = bot_handler.get_config_info('baremetrics') + self.api_key = self.config_info['api_key'] + + self.auth_header = { + 'Authorization': 'Bearer ' + self.api_key + } + + self.commands = ['help', 'list-commands', 'account-info', 'list-sources', 'list-plans ', + 'list-customers ', + 'list-subscriptions '] + + self.descriptions = ['Display bot info', 'Display the list of available commands', 'Display the account info', + 'List the sources', 'List the plans for the source', 'List the customers in the source', + 'List the subscriptions in the source'] + + def usage(self) -> str: + return ''' + This bot gives updates about customer behavior, financial performance, and analytics + for an organization using the Baremetrics Api.\n + Enter `list-commands` to show the list of available commands. + Version 1.0 + ''' + + def handle_message(self, message: Any, bot_handler: Any) -> None: + message['content'] = message['content'].strip() + + if message['content'].lower() == 'help': + bot_handler.send_reply(message, self.usage()) + return + + if message['content'].lower() == 'list-commands': + response = '**Available Commands:** \n' + for command, description in zip(self.commands, self.descriptions): + response += ' - {} : {}\n'.format(command, description) + + bot_handler.send_reply(message, response) + return + + if message['content'] == '': + bot_handler.send_reply(message, 'No Command Specified') + return + + response = self.generate_response(message['content']) + bot_handler.send_reply(message, response) + + def generate_response(self, command: str) -> str: + try: + if command.lower() == 'account-info': + return self.get_account_info() + + if command.lower() == 'list-sources': + return self.get_sources() + + part_commands = command.split() + + try: + if part_commands[0].lower() == 'list-plans': + return self.get_plans(part_commands[1]) + + if part_commands[0].lower() == 'list-customers': + return self.get_customers(part_commands[1]) + + if part_commands[0].lower() == 'list-subscriptions': + return self.get_subscriptions(part_commands[1]) + + except IndexError: + return 'Missing Params.' + except KeyError: + return 'Invalid Response From API.' + + return 'Invalid Command.' + + def get_account_info(self) -> str: + url = "https://api.baremetrics.com/v1/account" + account_response = requests.get(url, headers=self.auth_header) + + account_data = account_response.json() + account_data = account_data['account'] + + response = '**Your account information:** \n' + response += 'Id: {id}\n'.format(id=account_data['id']) + response += 'Company: {company}\n'.format(company=account_data['company']) + response += 'Default Currency: {currency_name}'.format(currency_name=account_data['default_currency']['name']) + + return response + + def get_sources(self) -> str: + url = 'https://api.baremetrics.com/v1/sources' + sources_response = requests.get(url, headers=self.auth_header) + + sources_data = sources_response.json() + sources_data = sources_data['sources'] + + response = '**Listing sources:** \n' + for index, source in enumerate(sources_data): + response += '{}.ID: {}\nProvider: {}\nProvider ID: {}\n\n'.format(index + 1, source['id'], + source['provider'], + source['provider_id']) + + return response + + def get_plans(self, source_id: str) -> str: + url = 'https://api.baremetrics.com/v1/{}/plans'.format(source_id) + plans_response = requests.get(url, headers=self.auth_header) + + plans_data = plans_response.json() + plans_data = plans_data['plans'] + + template = '{}.Name: {}\nActive: {}\nInterval: {}\nInterval Count: {}\nAmounts: \n' + response = '**Listing plans:** \n' + for index, plan in enumerate(plans_data): + response += template.format(index + 1, plan['name'], plan['active'], plan['interval'], + plan['interval_count']) + + for amount in plan['amounts']: + response += ' - {} {}\n'.format(amount['amount'], amount['currency']) + + response += '\n' + + return response + + def get_customers(self, source_id: str) -> str: + url = 'https://api.baremetrics.com/v1/{}/customers'.format(source_id) + customers_response = requests.get(url, headers=self.auth_header) + + customers_data = customers_response.json() + customers_data = customers_data['customers'] + + template = '{}.Name: {}\nDisplay Name: {}\nOID: {}\nActive: {}\nEmail: {}\nNotes: {}\nCurrent Plans: \n' + response = '**Listing customers:** \n' + for index, customer in enumerate(customers_data): + response += template.format(index + 1, customer['display_name'], customer['name'], customer['oid'], + customer['is_active'], customer['email'], customer['notes']) + + for plan in customer['current_plans']: + response += ' - {}\n'.format(plan['name']) + + response += '\n' + + return response + + def get_subscriptions(self, source_id: str) -> str: + url = 'https://api.baremetrics.com/v1/{}/subscriptions'.format(source_id) + subscriptions_response = requests.get(url, headers=self.auth_header) + + subscriptions_data = subscriptions_response.json() + subscriptions_data = subscriptions_data['subscriptions'] + + template = '{}.Customer Name: {}\nCustomer Display Name: {}\nCustomer OID: {}\nCustomer Email: {}\n' \ + 'Active: {}\nPlan Name: {}\nPlan Amounts: \n' + response = '**Listing subscriptions:** \n' + for index, subscription in enumerate(subscriptions_data): + response += template.format(index + 1, subscription['customer']['name'], + subscription['customer']['display_name'], + subscription['customer']['oid'], subscription['customer']['email'], + subscription['active'], subscription['plan']['name']) + + for amount in subscription['plan']['amounts']: + response += ' - {} {}\n'.format(amount['amount'], amount['symbol']) + + response += '\n' + + return response + +handler_class = BaremetricsHandler diff --git a/zulip_bots/zulip_bots/bots/baremetrics/doc.md b/zulip_bots/zulip_bots/bots/baremetrics/doc.md new file mode 100644 index 0000000..720ca3b --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/doc.md @@ -0,0 +1,41 @@ +# Baremetrics bot + +The Baremetrics bot is a Zulip bot that gives updates about customer behavior, financial performance, and +analytics for an organization using the [Baremetrics](https://baremetrics.com/) API. + +To use the Baremetrics bot, you can simply call it with `@` followed +by a command, like so: + +``` +@Baremetrics help +``` + +## Setup + +Before usage, you will need to configure the bot by putting the value of the `` in the config file. +To do this, follow the given steps: + +1. Login at [Baremetrics Console](https://app.baremetrics.com/settings/api). +2. Note the `Live API Key`. +3. Open up `zulip_bots/bots/baremetrics/baremetrics.conf` in an editor and + change the value of the `` attribute to the noted `Live API Key`. + +## Developer Notes + +Be sure to add the command and its description to their respective lists (named `commands` and `descriptions`) +so that it can be displayed with the other available commands using `@ list-commands`. Also modify +the `test_list_commands_command` in `test_baremetrics.py`. + +## Links + + - [Baremetrics](https://baremetrics.com/) + - [Baremetrics Developer API](https://developers.baremetrics.com/reference) + - [Baremetrics Dashboard](https://app.baremetrics.com/setup) + +## Usage + +`@Baremetrics list-commands` - This command gives a list of all available commands along with short +short descriptions. + +Example: +![](assets/list-commands.png) diff --git a/zulip_bots/zulip_bots/bots/baremetrics/fixtures/account_info.json b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/account_info.json new file mode 100644 index 0000000..466f9aa --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/account_info.json @@ -0,0 +1,59 @@ +{ + "request": { + "api_url": "https://api.baremetrics.com/v1/account", + "headers": { + "Authorization": "Bearer TEST" + } + }, + "response": { + "account": { + "id": 376418, + "default_currency": { + "id": "usd", + "alternate_symbols": [ + "US$" + ], + "decimal_mark": ".", + "disambiguate_symbol": "US$", + "html_entity": "$", + "iso_code": "USD", + "iso_numeric": "840", + "name": "United States Dollar", + "priority": 1, + "smallest_denomination": 1, + "subunit": "Cent", + "subunit_to_unit": 100, + "symbol": "$", + "symbol_first": true, + "thousands_separator": "," + }, + "company": "NA", + "created_at": 1514301115, + "stack": "seg" + } + }, + "response-headers": { + "X-TokenExpires": "0", + "Server": "cloudflare-nginx", + "X-RateLimit-Remaining": "3593", + "X-Powered-By": "Phusion Passenger 5.0.30", + "Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/", + "X-Runtime": "0.031730", + "Access-Control-Allow-Credentials": "false", + "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE", + "Content-Encoding": "gzip", + "Connection": "keep-alive", + "ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"", + "Date": "Wed, 27 Dec 2017 11:36:08 GMT", + "X-RateLimit-Limit": "3600", + "X-Version": "721", + "X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c", + "Status": "200 OK", + "Content-Type": "application/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type", + "Cache-Control": "max-age=0, private, must-revalidate", + "X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63", + "CF-RAY": "3d3bfaf6feee2eff-DEL" + } +} diff --git a/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_customers.json b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_customers.json new file mode 100644 index 0000000..ef9989d --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_customers.json @@ -0,0 +1,43 @@ +{ + "request": { + "api_url": "https://api.baremetrics.com/v1/TEST/customers", + "headers": { + "Authorization": "Bearer TEST" + } + }, + "response": { + "customers": [], + "meta": { + "pagination": { + "has_more": false, + "page": 0, + "per_page": 30, + "total_count": 0 + } + } + }, + "response-headers": { + "X-TokenExpires": "0", + "Server": "cloudflare-nginx", + "X-RateLimit-Remaining": "3593", + "X-Powered-By": "Phusion Passenger 5.0.30", + "Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/", + "X-Runtime": "0.031730", + "Access-Control-Allow-Credentials": "false", + "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE", + "Content-Encoding": "gzip", + "Connection": "keep-alive", + "ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"", + "Date": "Wed, 27 Dec 2017 11:36:08 GMT", + "X-RateLimit-Limit": "3600", + "X-Version": "721", + "X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c", + "Status": "200 OK", + "Content-Type": "application/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type", + "Cache-Control": "max-age=0, private, must-revalidate", + "X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63", + "CF-RAY": "3d3bfaf6feee2eff-DEL" + } +} diff --git a/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_plans.json b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_plans.json new file mode 100644 index 0000000..68bc536 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_plans.json @@ -0,0 +1,42 @@ +{ + "request": { + "api_url": "https://api.baremetrics.com/v1/TEST/plans", + "headers": { + "Authorization": "Bearer TEST" + } + }, + "response": { + "plans": [], + "meta": { + "pagination": { + "has_more": false, + "page": 0, + "per_page": 30 + } + } + }, + "response-headers": { + "X-TokenExpires": "0", + "Server": "cloudflare-nginx", + "X-RateLimit-Remaining": "3593", + "X-Powered-By": "Phusion Passenger 5.0.30", + "Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/", + "X-Runtime": "0.031730", + "Access-Control-Allow-Credentials": "false", + "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE", + "Content-Encoding": "gzip", + "Connection": "keep-alive", + "ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"", + "Date": "Wed, 27 Dec 2017 11:36:08 GMT", + "X-RateLimit-Limit": "3600", + "X-Version": "721", + "X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c", + "Status": "200 OK", + "Content-Type": "application/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type", + "Cache-Control": "max-age=0, private, must-revalidate", + "X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63", + "CF-RAY": "3d3bfaf6feee2eff-DEL" + } +} diff --git a/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_sources.json b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_sources.json new file mode 100644 index 0000000..77b33fb --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_sources.json @@ -0,0 +1,48 @@ +{ + "request": { + "api_url": "https://api.baremetrics.com/v1/sources", + "headers": { + "Authorization": "Bearer TEST" + } + }, + "response": { + "sources": [ + { + "id": "5f7QC5NC0Ywgcu", + "provider": "baremetrics", + "provider_id": null + } + ], + "meta": { + "pagination": { + "has_more": false, + "page": 0, + "per_page": 30 + } + } + }, + "response-headers": { + "X-TokenExpires": "0", + "Server": "cloudflare-nginx", + "X-RateLimit-Remaining": "3593", + "X-Powered-By": "Phusion Passenger 5.0.30", + "Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/", + "X-Runtime": "0.031730", + "Access-Control-Allow-Credentials": "false", + "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE", + "Content-Encoding": "gzip", + "Connection": "keep-alive", + "ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"", + "Date": "Wed, 27 Dec 2017 11:36:08 GMT", + "X-RateLimit-Limit": "3600", + "X-Version": "721", + "X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c", + "Status": "200 OK", + "Content-Type": "application/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type", + "Cache-Control": "max-age=0, private, must-revalidate", + "X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63", + "CF-RAY": "3d3bfaf6feee2eff-DEL" + } +} diff --git a/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_subscriptions.json b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_subscriptions.json new file mode 100644 index 0000000..0cd1e67 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/fixtures/list_subscriptions.json @@ -0,0 +1,42 @@ +{ + "request": { + "api_url": "https://api.baremetrics.com/v1/TEST/subscriptions", + "headers": { + "Authorization": "Bearer TEST" + } + }, + "response": { + "subscriptions": [], + "meta": { + "pagination": { + "has_more": false, + "page": 0, + "per_page": 30 + } + } + }, + "response-headers": { + "X-TokenExpires": "0", + "Server": "cloudflare-nginx", + "X-RateLimit-Remaining": "3593", + "X-Powered-By": "Phusion Passenger 5.0.30", + "Set-Cookie": "__cfduid=dd26464e3e6779b6a05b27d2681aea0851514374567; expires=Thu, 27-Dec-18 11:36:07 GMT; path=/; domain=.baremetrics.com; HttpOnly; Secure, LSW_WEB=\"LSW_WEB1\"; path=/", + "X-Runtime": "0.031730", + "Access-Control-Allow-Credentials": "false", + "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE", + "Content-Encoding": "gzip", + "Connection": "keep-alive", + "ETag": "W/\"110de39b319ed195bf1414ac4f3c9a01\"", + "Date": "Wed, 27 Dec 2017 11:36:08 GMT", + "X-RateLimit-Limit": "3600", + "X-Version": "721", + "X-Commit": "2dcec7190a2bd01c8d3d8527d386486c4bb5474c", + "Status": "200 OK", + "Content-Type": "application/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Access-Control-Allow-Headers": "Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type", + "Cache-Control": "max-age=0, private, must-revalidate", + "X-Request-Id": "0b292131-81bb-4df2-bdb9-089a50b17e63", + "CF-RAY": "3d3bfaf6feee2eff-DEL" + } +} diff --git a/zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py b/zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py new file mode 100644 index 0000000..b71fa55 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py @@ -0,0 +1,53 @@ +from zulip_bots.test_lib import BotTestCase + +class TestBaremetricsBot(BotTestCase): + bot_name = "baremetrics" + + def test_bot_responds_to_empty_message(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + self.verify_reply('', 'No Command Specified') + + def test_help_query(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + self.verify_reply('help', ''' + This bot gives updates about customer behavior, financial performance, and analytics + for an organization using the Baremetrics Api.\n + Enter `list-commands` to show the list of available commands. + Version 1.0 + ''') + + def test_list_commands_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + self.verify_reply('list-commands', '**Available Commands:** \n - help : Display bot info\n - list-commands ' + ': Display the list of available commands\n - account-info : Display ' + 'the account info\n - list-sources : List the sources\n - list-plans ' + ' : List the plans for the source\n - list-customers ' + ' : List the customers in the source\n - list-subscriptions ' + ' : List the subscriptions in the source\n') + + def test_account_info_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + with self.mock_http_conversation('account_info'): + self.verify_reply('account-info', '**Your account information:** \nId: 376418\nCompany: NA\nDefault ' + 'Currency: United States Dollar') + + def test_list_sources_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + with self.mock_http_conversation('list_sources'): + self.verify_reply('list-sources', '**Listing sources:** \n1.ID: 5f7QC5NC0Ywgcu\nProvider: ' + 'baremetrics\nProvider ID: None\n\n') + + def test_list_plans_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + with self.mock_http_conversation('list_plans'): + self.verify_reply('list-plans TEST', '**Listing plans:** \n') + + def test_list_customers_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + with self.mock_http_conversation('list_customers'): + self.verify_reply('list-customers TEST', '**Listing customers:** \n') + + def test_list_subscriptions_command(self) -> None: + with self.mock_config_info({'api_key': 'TEST'}): + with self.mock_http_conversation('list_subscriptions'): + self.verify_reply('list-subscriptions TEST', '**Listing subscriptions:** \n')