Skip to content

Commit bcff3c5

Browse files
jlstackChristopher Waldon
authored and
Christopher Waldon
committed
Added additional tests and missing functionality.
Added pin tests Added log functions/tests Added other random tests Removed tailing separator on directory paths in multipart License: MIT Signed-off-by: Luke Stack <[email protected]>
1 parent e76712d commit bcff3c5

File tree

3 files changed

+177
-1
lines changed

3 files changed

+177
-1
lines changed

ipfsApi/client.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class Client(object):
7474
config -- controls configuration variables
7575
config_show -- returns a dict containing the server's configuration
7676
config_replace -- replaces the existing config with a user-defined config
77+
log_level -- changes the logging output of a running daemon
78+
log_ls -- lists the logging subsystems of a running daemon
79+
log_tail -- reads log outputs as they are written
7780
version -- returns the software version of the currently connected node
7881
files_cp -- copies files into MFS
7982
files_ls -- lists directory contents in MFS
@@ -166,6 +169,9 @@ def __init__(self, host=None, port=None,
166169
self._config = ArgCommand('/config')
167170
self._config_show = Command('/config/show')
168171
self._config_replace = ArgCommand('/config/replace')
172+
self._log_level = ArgCommand('/log/level')
173+
self._log_ls = Command('/log/ls')
174+
self._log_tail = Command('/log/tail')
169175
self._version = Command('/version')
170176

171177
# MFS COMMANDS
@@ -837,6 +843,41 @@ def config_replace(self, *args, **kwargs):
837843
"""
838844
return self._config_replace.request(self._client, *args, **kwargs)
839845

846+
def log_level(self, subsystem, level, **kwargs):
847+
"""Changes the logging output of a running daemon.
848+
849+
Keyword arguments:
850+
subsystem -- the subsystem logging identifier
851+
(Use 'all' for all subsystems)
852+
level -- one of: debug, info, warning, error, fatal, panic
853+
kwargs -- additional named arguments
854+
"""
855+
return self._log_level.request(self._client, subsystem,
856+
level, **kwargs)
857+
858+
def log_ls(self, *args, **kwargs):
859+
"""Lists the logging subsystems of a running daemon.
860+
861+
Keyword arguments:
862+
args -- additional unnamed arguments
863+
kwargs -- additional named arguments
864+
"""
865+
return self._log_ls.request(self._client, *args, **kwargs)
866+
867+
def log_tail(self, *args, **kwargs):
868+
"""Reads log outputs as they are written.
869+
870+
This function returns a reponse object that can be iterated over
871+
by the user. The user should make sure to close the response object
872+
when they are done reading from it.
873+
874+
Keyword arguments:
875+
args -- additional unnamed arguments
876+
kwargs -- additional named arguments
877+
"""
878+
return self._log_tail.request(self._client, stream=True,
879+
*args, **kwargs)
880+
840881
def version(self, **kwargs):
841882
"""Returns the software version of the currently connected node.
842883

ipfsApi/multipart.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ def headers(self):
408408
def _prepare(self):
409409
"""Pre-formats the multipart HTTP request to transmit the directory."""
410410
names = []
411+
if self.directory.endswith(os.sep):
412+
self.directory = self.directory[:-1]
411413
# identify the unecessary portion of the relative path
412414
truncate = os.path.dirname(self.directory)
413415
# traverse the filesystem downward from the target directory's uri

test/functional/tests.py

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# _*_ coding: utf-8 -*-
22
import os
33
import shutil
4+
import json
45
import unittest
56

67
import ipfsApi
@@ -84,6 +85,19 @@ def tearDown(self):
8485
# TESTS #
8586
#########
8687

88+
def test_version(self):
89+
expected = ['Repo', 'Commit', 'Version']
90+
resp_version = self.api.version()
91+
for key in expected:
92+
self.assertTrue(key in resp_version)
93+
94+
def test_id(self):
95+
expected = ['PublicKey', 'ProtocolVersion',
96+
'ID', 'AgentVersion', 'Addresses']
97+
resp_id = self.api.id()
98+
for key in expected:
99+
self.assertTrue(key in resp_id)
100+
87101
def test_add_single_from_str(self):
88102
res = self.api.add(self.fake_file)
89103
self.assertEqual(res, self.fake_file_only_res)
@@ -104,7 +118,6 @@ def test_add_multiple_from_dirname(self):
104118
sorted(self.fake_dir_res,
105119
key=lambda x: x['Name']))
106120

107-
108121
def test_add_recursive(self):
109122
res = self.api.add(self.fake_dir, recursive=True)
110123
self.assertEqual(sorted(res,
@@ -170,6 +183,117 @@ def test_cat_single_file_str(self):
170183
self.assertEqual("dsadsad\n", res)
171184

172185

186+
class IpfsApiLogTest(unittest.TestCase):
187+
188+
def setUp(self):
189+
self.api = ipfsApi.Client()
190+
191+
def test_log_ls_level(self):
192+
"""
193+
Unfortunately there is no way of knowing the logging levels prior
194+
to this test. This makes it impossible to guarantee that the logging
195+
levels are the same as before the test was run.
196+
"""
197+
# Retrieves the list of logging subsystems for a running daemon.
198+
resp_ls = self.api.log_ls()
199+
# The response should be a dictionary with only one key ('Strings').
200+
self.assertTrue('Strings' in resp_ls)
201+
202+
# Sets the logging level to 'error' for the first subsystem found.
203+
sub = resp_ls['Strings'][0]
204+
resp_level = self.api.log_level(sub, 'error')
205+
self.assertEqual(resp_level['Message'],
206+
"Changed log level of \'%s\' to 'error'\n" % sub)
207+
208+
def test_log_tail(self):
209+
# Gets the response object.
210+
tail = self.api.log_tail()
211+
212+
# Takes the first log received.
213+
line = tail.readline()
214+
log = json.loads(line.decode())
215+
216+
# Closes the response object.
217+
tail.close()
218+
219+
# The log should have been parsed into a dictionary object with
220+
# various keys depending on the event that occured.
221+
self.assertTrue(type(log) is dict)
222+
223+
224+
class IpfsApiPinTest(unittest.TestCase):
225+
226+
fake_dir_hash = 'QmYqqgRahxbZvudnzDu2ZzUS1vFSNEuCrxghM8hgT8uBFY'
227+
228+
def setUp(self):
229+
self.api = ipfsApi.Client()
230+
# Add resources to be pinned.
231+
self.resource = self.api.add_str(u'Mary had a little lamb')
232+
resp_add = self.api.add('fake_dir', recursive=True)
233+
self.fake_dir_hashes = [el['Hash'] for el in resp_add if 'Hash' in el]
234+
235+
def test_pin_ls_add_rm_single(self):
236+
# Get pinned objects at start.
237+
pins_begin = self.api.pin_ls()['Keys']
238+
239+
# Unpin the resource if already pinned.
240+
if self.resource in pins_begin.keys():
241+
self.api.pin_rm(self.resource)
242+
243+
# No matter what, the resource should not be pinned at this point.
244+
self.assertFalse(self.resource in self.api.pin_ls()['Keys'])
245+
246+
for option in [True, False]:
247+
# Pin the resource.
248+
resp_add = self.api.pin_add(self.resource,
249+
opts={"recursive":str(option)})
250+
pins_afer_add = self.api.pin_ls()['Keys']
251+
self.assertEqual(resp_add['Pins'], [self.resource])
252+
self.assertTrue(self.resource in pins_afer_add)
253+
self.assertEqual(pins_afer_add[self.resource]['Type'] == 'recursive',
254+
option)
255+
256+
# Unpin the resource.
257+
resp_rm = self.api.pin_rm(self.resource)
258+
pins_afer_rm = self.api.pin_ls()['Keys']
259+
self.assertEqual(resp_rm['Pins'], [self.resource])
260+
self.assertFalse(self.resource in pins_afer_rm)
261+
262+
# Get pinned objects at end.
263+
pins_end = self.api.pin_ls()['Keys']
264+
265+
# Compare pinned items from start to finish of test.
266+
self.assertFalse(self.resource in pins_end.keys())
267+
268+
def test_pin_ls_add_rm_directory(self):
269+
# Get pinned objects at start.
270+
pins_begin = self.api.pin_ls()['Keys']
271+
272+
# Remove fake_dir if it had previously been pinned.
273+
if self.fake_dir_hash in pins_begin.keys() and \
274+
pins_begin[self.fake_dir_hash]['Type'] == 'recursive':
275+
self.api.pin_rm(self.fake_dir_hash)
276+
277+
# Make sure I removed it
278+
pins_after_rm = self.api.pin_ls()['Keys']
279+
self.assertFalse(self.fake_dir_hash in pins_after_rm.keys() and \
280+
pins_after_rm[self.fake_dir_hash]['Type'] == 'recursive')
281+
282+
# Add 'fake_dir' recursively.
283+
self.api.pin_add(self.fake_dir_hash)
284+
285+
# Make sure all appear on the list of pinned objects.
286+
pins_after_add = self.api.pin_ls()['Keys'].keys()
287+
for el in self.fake_dir_hashes:
288+
self.assertTrue(el in pins_after_add)
289+
290+
# Clean up.
291+
self.api.pin_rm(self.fake_dir_hash)
292+
pins_end = self.api.pin_ls()['Keys'].keys()
293+
self.assertFalse(self.fake_dir_hash in pins_end and \
294+
pins_after_rm[self.fake_dir_hash]['Type'] == 'recursive')
295+
296+
173297
class IpfsApiMFSTest(unittest.TestCase):
174298

175299
test_files = {
@@ -269,6 +393,8 @@ def setUp(self):
269393
self.api = ipfsApi.Client()
270394
self._olddir = os.getcwd()
271395
os.chdir(HERE)
396+
# Add a resource to get the stats for.
397+
self.resource = self.api.add_str(u'Mary had a little lamb')
272398

273399
def tearDown(self):
274400
os.chdir(self._olddir)
@@ -279,6 +405,13 @@ def test_object_new(self):
279405
for key in expected_keys:
280406
self.assertTrue(key in res)
281407

408+
def test_object_stat(self):
409+
expected = ['Hash', 'CumulativeSize', 'DataSize',
410+
'NumLinks', 'LinksSize', 'BlockSize']
411+
resp_stat = self.api.object_stat(self.resource)
412+
for key in expected:
413+
self.assertTrue(key in resp_stat)
414+
282415
def test_object_put_get(self):
283416
# Set paths to test json files
284417
path_no_links = os.path.join(os.path.dirname(__file__),

0 commit comments

Comments
 (0)