Skip to content
41 changes: 40 additions & 1 deletion src/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@
)
from bmconfigparser import BMConfigParser
from debug import logger
from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure
from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready
from inventory import Inventory
from network.threads import StoppableThread
from six.moves import queue
from version import softwareVersion

try: # TODO: write tests for XML vulnerabilities
Expand Down Expand Up @@ -230,6 +231,7 @@ class StoppableRPCServer(RPCServerBase):

def serve_forever(self, poll_interval=None):
"""Start the RPCServer"""
sql_ready.wait()
while state.shutdown == 0:
self.handle_request()

Expand Down Expand Up @@ -302,12 +304,27 @@ def __new__(mcs, name, bases, namespace):
return result


class testmode(object): # pylint: disable=too-few-public-methods
"""Decorator to check testmode & route to command decorator"""

def __init__(self, *aliases):
self.aliases = aliases

def __call__(self, func):
"""Testmode call method"""

if not state.testmode:
return None
return command(self.aliases[0]).__call__(func)


class command(object): # pylint: disable=too-few-public-methods
"""Decorator for API command method"""
def __init__(self, *aliases):
self.aliases = aliases

def __call__(self, func):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why space?

if BMConfigParser().safeGet(
'bitmessagesettings', 'apivariant') == 'legacy':
def wrapper(*args):
Expand Down Expand Up @@ -1422,11 +1439,33 @@ def HandleAdd(self, a, b):
"""Test two numeric params"""
return a + b

@testmode('clearUISignalQueue')
def HandleclearUISignalQueue(self):
"""clear UISignalQueue"""
queues.UISignalQueue.queue.clear()
return "success"

@command('statusBar')
def HandleStatusBar(self, message):
"""Update GUI statusbar message"""
queues.UISignalQueue.put(('updateStatusBar', message))

@testmode('getStatusBar')
def HandleGetStatusBar(self):
"""Get GUI statusbar message"""
try:
_, data = queues.UISignalQueue.get(block=False)
except queue.Empty:
return None
return data

@testmode('undeleteMessage')
def HandleUndeleteMessage(self, msgid):
"""Undelete message"""
msgid = self._decode(msgid, "hex")
helper_inbox.undeleteMessage(msgid)
return "Undeleted message"

@command('deleteAndVacuum')
def HandleDeleteAndVacuum(self):
"""Cleanup trashes and vacuum messages database"""
Expand Down
5 changes: 5 additions & 0 deletions src/bitmessagemain.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import shared
import shutdown
import state

from testmode_init import populate_api_test_data
from bmconfigparser import BMConfigParser
from debug import logger # this should go before any threads
from helper_startup import (
Expand Down Expand Up @@ -295,6 +297,9 @@ def start(self):
else:
config.remove_option('bitmessagesettings', 'dontconnect')

if state.testmode:
Comment thread
PeterSurda marked this conversation as resolved.
populate_api_test_data()

if daemon:
while state.shutdown == 0:
time.sleep(1)
Expand Down
5 changes: 5 additions & 0 deletions src/helper_inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def trash(msgid):
queues.UISignalQueue.put(('removeInboxRowByMsgid', msgid))


def undeleteMessage(msgid):
"""Undelte the message"""
sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', msgid)


def isMessageAlreadyInInbox(sigHash):
"""Check for previous instances of this message"""
queryReturn = sqlQuery(
Expand Down
40 changes: 40 additions & 0 deletions src/testmode_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import time
Comment thread
kdcis marked this conversation as resolved.
import uuid

import helper_inbox
import helper_sql

# from .tests.samples import sample_inbox_msg_ids, sample_deterministic_addr4
sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK'
sample_inbox_msg_ids = ['27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b',
'2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24']


def populate_api_test_data():
'''Adding test records in inbox table'''
helper_sql.sql_ready.wait()

test1 = (
Comment thread
kdcis marked this conversation as resolved.
sample_inbox_msg_ids[0], sample_deterministic_addr4,
sample_deterministic_addr4, 'Test1 subject', int(time.time()),
'Test1 body', 'inbox', 2, 0, uuid.uuid4().bytes
)
test2 = (
sample_inbox_msg_ids[1], sample_deterministic_addr4,
sample_deterministic_addr4, 'Test2 subject', int(time.time()),
'Test2 body', 'inbox', 2, 0, uuid.uuid4().bytes
)
test3 = (
sample_inbox_msg_ids[2], sample_deterministic_addr4,
sample_deterministic_addr4, 'Test3 subject', int(time.time()),
'Test3 body', 'inbox', 2, 0, uuid.uuid4().bytes
)
test4 = (
sample_inbox_msg_ids[3], sample_deterministic_addr4,
sample_deterministic_addr4, 'Test4 subject', int(time.time()),
'Test4 body', 'inbox', 2, 0, uuid.uuid4().bytes
)
helper_inbox.insert(test1)
helper_inbox.insert(test2)
helper_inbox.insert(test3)
helper_inbox.insert(test4)
7 changes: 7 additions & 0 deletions src/tests/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@
sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK'
sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381
sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004

sample_statusbar_msg = "new status bar message"
sample_inbox_msg_ids = ['27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b',
'2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24']
# second address in sample_test_subscription_address is for the announcement broadcast
sample_test_subscription_address = ['BM-2cWQLCBGorT9pUGkYSuGGVr9LzE4mRnQaq', 'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw']
Comment thread
kdcis marked this conversation as resolved.
Comment thread
kdcis marked this conversation as resolved.
sample_subscription_name = 'test sub'
103 changes: 92 additions & 11 deletions src/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import json
import time

from binascii import hexlify
from six.moves import xmlrpc_client # nosec

import psutil

from .samples import (
sample_seed, sample_deterministic_addr3, sample_deterministic_addr4)
sample_seed, sample_deterministic_addr3, sample_deterministic_addr4, sample_statusbar_msg,
sample_inbox_msg_ids, sample_test_subscription_address, sample_subscription_name)

from .test_process import TestProcessProto

Expand Down Expand Up @@ -46,17 +48,9 @@ def test_shutdown(self):


# TODO: uncovered API commands
# getAllInboxMessages
# getAllInboxMessageIds
# getInboxMessageById
# getInboxMessagesByReceiver
# trashMessage
# trashInboxMessage
# addSubscription
# disseminatePreEncryptedMsg
# disseminatePubkey
# getMessageDataByDestinationHash
# statusBar


class TestAPI(TestAPIProto):
Expand Down Expand Up @@ -91,6 +85,71 @@ def test_invalid_method(self):
'API Error 0020: Invalid method: test'
)

def test_statusbar_method(self):
"""Test statusbar method"""
self.api.clearUISignalQueue()
self.assertEqual(
Comment thread
kdcis marked this conversation as resolved.
self.api.statusBar(sample_statusbar_msg),
'null'
)
Comment thread
kdcis marked this conversation as resolved.
self.assertEqual(
self.api.getStatusBar(),
sample_statusbar_msg
)

def test_message_inbox(self):
"""Test message inbox methods"""
self.assertEqual(
len(json.loads(
Comment thread
kdcis marked this conversation as resolved.
self.api.getAllInboxMessages())["inboxMessages"]),
4,
json.loads(self.api.getAllInboxMessages())["inboxMessages"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe add a comment explaining this is the custom error message to aid in debugging.

)
self.assertEqual(
len(json.loads(
Comment thread
kdcis marked this conversation as resolved.
self.api.getAllInboxMessageIds())["inboxMessageIds"]),
4
)
self.assertEqual(
len(json.loads(
self.api.getInboxMessageById(hexlify(sample_inbox_msg_ids[2])))["inboxMessage"]),
1
)
self.assertEqual(
len(json.loads(
Comment thread
kdcis marked this conversation as resolved.
self.api.getInboxMessagesByReceiver(sample_deterministic_addr4))["inboxMessages"]),
4
)

def test_message_trash(self):
"""Test message inbox methods"""

messages_before_delete = len(json.loads(self.api.getAllInboxMessageIds())["inboxMessageIds"])
self.assertEqual(
self.api.trashMessage(hexlify(sample_inbox_msg_ids[0])),
'Trashed message (assuming message existed).'
)
self.assertEqual(
self.api.trashInboxMessage(hexlify(sample_inbox_msg_ids[1])),
'Trashed inbox message (assuming message existed).'
)
self.assertEqual(
len(json.loads(self.api.getAllInboxMessageIds())["inboxMessageIds"]),
messages_before_delete - 2
)
self.assertEqual(
self.api.undeleteMessage(hexlify(sample_inbox_msg_ids[0])),
'Undeleted message'
)
self.assertEqual(
self.api.undeleteMessage(hexlify(sample_inbox_msg_ids[1])),
'Undeleted message'
)
self.assertEqual(
len(json.loads(self.api.getAllInboxMessageIds())["inboxMessageIds"]),
messages_before_delete
)

Comment thread
PeterSurda marked this conversation as resolved.
def test_clientstatus_consistency(self):
"""If networkStatus is notConnected networkConnections should be 0"""
status = json.loads(self.api.clientStatus())
Expand Down Expand Up @@ -193,9 +252,28 @@ def test_addressbook(self):

def test_subscriptions(self):
"""Testing the API commands related to subscriptions"""

Comment thread
kdcis marked this conversation as resolved.
self.assertEqual(
self.api.addSubscription(sample_test_subscription_address[0], sample_subscription_name.encode('base64')),
'Added subscription.'
)

added_subscription = {'label': None, 'enabled': False}
# check_address
for sub in json.loads(self.api.listSubscriptions())['subscriptions']:
# special address, added when sqlThread starts
if sub['address'] == sample_test_subscription_address[0]:
added_subscription = sub
break

self.assertEqual(
base64.decodestring(added_subscription['label']) if added_subscription['label'] else None,
sample_subscription_name)
self.assertTrue(added_subscription['enabled'])

for s in json.loads(self.api.listSubscriptions())['subscriptions']:
# special address, added when sqlThread starts
if s['address'] == 'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw':
if s['address'] == sample_test_subscription_address[1]:
self.assertEqual(
base64.decodestring(s['label']),
'Bitmessage new releases/announcements')
Expand All @@ -206,7 +284,10 @@ def test_subscriptions(self):
'Could not find Bitmessage new releases/announcements'
' in subscriptions')
self.assertEqual(
self.api.deleteSubscription('BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw'),
self.api.deleteSubscription(sample_test_subscription_address[0]),
'Deleted subscription if it existed.')
self.assertEqual(
self.api.deleteSubscription(sample_test_subscription_address[1]),
'Deleted subscription if it existed.')
self.assertEqual(
json.loads(self.api.listSubscriptions())['subscriptions'], [])
Expand Down