From d8c6cb7c0a44f2e499f9bb880693cb15a9ab3352 Mon Sep 17 00:00:00 2001 From: Viraat Chandra <viraat.chandra@hotmail.com> 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 00000000..e69de29b 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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 literal 0 HcmV?d00001 diff --git a/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.conf b/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.conf new file mode 100644 index 00000000..500d36e6 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/baremetrics/baremetrics.conf @@ -0,0 +1,2 @@ +[baremetrics] +api_key = <api_key> 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 00000000..3627f234 --- /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 <source_id>', + 'list-customers <source_id>', + 'list-subscriptions <source_id>'] + + 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 00000000..720ca3bc --- /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 `@<botname>` followed +by a command, like so: + +``` +@Baremetrics help +``` + +## Setup + +Before usage, you will need to configure the bot by putting the value of the `<api_key>` 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 `<api_key>` 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 `@<botname> 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: + 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 00000000..466f9aa2 --- /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 00000000..ef9989d6 --- /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 00000000..68bc5368 --- /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 00000000..77b33fb2 --- /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 00000000..0cd1e675 --- /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 00000000..b71fa55a --- /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 ' + '<source_id> : List the plans for the source\n - list-customers ' + '<source_id> : List the customers in the source\n - list-subscriptions ' + '<source_id> : 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')