Skip to content

Commit 710bab9

Browse files
committed
Merge branch '0.2.6.1'
2 parents 5a4b58f + 3582acd commit 710bab9

File tree

11 files changed

+210
-11
lines changed

11 files changed

+210
-11
lines changed

features/upload_resume.feature

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Feature: resume upload feature
2+
3+
Background: setup the command configuration
4+
Given the home directory is "/tmp/test"
5+
And the Longaccess directory exists in HOME
6+
And the mock API "longaccessmock"
7+
And the environment variable "LA_API_URL" is "{api_url}path/to/api"
8+
And the username "test"
9+
And the password "test"
10+
And I have 2 capsules
11+
And I store my credentials in "{homedir}/.netrc"
12+
13+
@dev
14+
Scenario: I pause an archive
15+
Given the command line arguments "archive create -t foobar lacli"
16+
And an S3 bucket named "foobucket"
17+
When I run console script "lacli"
18+
Then I wait 10 seconds to see "archive prepared"
19+
Given the command line arguments "archive upload"
20+
When I run console script "lacli"
21+
Then I see "ETA:"
22+
When I send control-c
23+
And I wait until I don't see "ETA:" anymore
24+
Then I see "paused"
25+
26+
Scenario: I resume an archive
27+
Given I have 1 pending uploads
28+
And an S3 bucket named "foobucket"
29+
Given the command line arguments "-d 4 archive upload"
30+
When I run console script "lacli"
31+
Then I see "ETA:"
32+
When the upload status is "completed"
33+
Then I wait 5 seconds to see "done."

lacli/__init__.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
from __future__ import unicode_literals
2-
import platform
32

43
try:
54
from lacli.version import __version__
65
except ImportError:
7-
__version__ = "0.2.6"
6+
__version__ = "0.2.6.1"
87

98

109
def get_client_info(terse=False):
10+
from lacore import get_client_info as lacore_info
1111
el = ["Longaccess client"]
1212
el.append(__version__)
13-
el.append(platform.platform(True, terse))
14-
el.append("py"+platform.python_version())
15-
el.append(platform.machine())
13+
el.append(lacore_info(terse))
1614
return " ".join(el)
1715
# vim: et:sw=4:ts=4

lacli/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ def settings(options):
7979
'pass': options.get('--password'),
8080
'url': os.getenv('LA_API_URL'),
8181
'verify': verify,
82-
'factory': RequestsFactory
82+
'factory': RequestsFactory,
83+
'user_agent': get_client_info(terse=True)
8384
},
8485
'command': {
8586
'debug': debug,

lacli/server/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ def do_run(self, port=9090):
7878
self.logincmd.username = self.prefs['gui']['username']
7979
self.logincmd.password = self.prefs['gui']['password']
8080
self.logincmd.email = self.prefs['gui']['email']
81+
ua = os.getenv("LA_API_AGENT_INFO")
82+
if ua is not None:
83+
self.registry.session.prefs['user_agent'] += " (" + ua + ")"
8184
reactor.run()
8285
self.batch = False
8386

lacli/t/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from contextlib import contextmanager
66
from tempfile import mkdtemp
77
from shutil import rmtree
8+
from logging import Handler as LogHandler
89

910

1011
def setup():
@@ -71,3 +72,36 @@ def _temp_home():
7172
d = mkdtemp()
7273
yield d
7374
rmtree(d)
75+
76+
77+
class MockLoggingHandler(LogHandler):
78+
"""Mock logging handler to check for expected logs.
79+
80+
Messages are available from an instance's ``messages`` dict,
81+
in order, indexed by a lowercase log level string (e.g.,
82+
'debug', 'info', etc.).
83+
84+
Adapted from: http://stackoverflow.com/questions/899067/
85+
"""
86+
87+
def __init__(self, *args, **kwargs):
88+
self.messages = {'debug': [], 'info': [], 'warning': [], 'error': [],
89+
'critical': []}
90+
super(MockLoggingHandler, self).__init__(*args, **kwargs)
91+
92+
def emit(self, record):
93+
"Store a message from ``record`` in the instance's ``messages`` dict."
94+
self.acquire()
95+
try:
96+
self.messages[record.levelname.lower()].append(
97+
(record.getMessage(), record.exc_info))
98+
finally:
99+
self.release()
100+
101+
def reset(self):
102+
self.acquire()
103+
try:
104+
for message_list in self.messages.values():
105+
del message_list[:]
106+
finally:
107+
self.release()

lacli/t/test_main.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,12 @@ def test_make_home(self, stdin):
5959
finally:
6060
if (tmpdir and os.path.isdir(tmphome)):
6161
shutil.rmtree(tmpdir)
62+
63+
@patch('lacli.main.get_client_info')
64+
def test_user_agent(self, info):
65+
settings = self._makeit()
66+
info.return_value = self.getUniqueString()
67+
prefs, _ = settings({'--home': self.home})
68+
self.assertIn('api', prefs)
69+
self.assertIn('user_agent', prefs['api'])
70+
self.assertEqual(info.return_value, prefs['api']['user_agent'])

lacli/t/test_registry.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
import os
22

33
from testtools import TestCase
4-
from . import makeprefs
5-
from mock import Mock
4+
from . import makeprefs, MockLoggingHandler
5+
from mock import Mock, patch
66

77

88
class RegistryTest(TestCase):
9+
@classmethod
10+
def setUpClass(cls):
11+
super(RegistryTest, cls).setUpClass()
12+
cls._lacli_log_handler = MockLoggingHandler(level='DEBUG')
13+
cls.lacli_log_messages = cls._lacli_log_handler.messages
14+
915
def setUp(self):
1016
super(RegistryTest, self).setUp()
1117
self.prefs = makeprefs(lambda x: None)
1218
self.home = os.path.join('t', 'data', 'home')
19+
self._lacli_log_handler.reset()
20+
from lacli.log import getLogger
21+
getLogger().addHandler(self._lacli_log_handler)
1322

1423
def tearDown(self):
1524
super(RegistryTest, self).tearDown()
@@ -20,3 +29,43 @@ def _makeit(self, *args, **kwargs):
2029

2130
def test_registry(self):
2231
assert self._makeit(Mock(), self.prefs, Mock())
32+
33+
def test_new_session(self):
34+
s = self.getUniqueString()
35+
f = Mock(return_value=s)
36+
prefs = makeprefs(f)
37+
r = self._makeit(Mock(), prefs, Mock())
38+
self.assertEqual(s, r.session)
39+
self.assertEqual(prefs['api'], f.call_args[0][0])
40+
41+
@patch('lacli.registry.API_URL', new='http://wtf')
42+
def test_new_session_default_url(self):
43+
f = Mock()
44+
prefs = makeprefs(f)
45+
del prefs['api']['url']
46+
self._makeit(Mock(), prefs, Mock())
47+
self.assertEqual('http://wtf', f.call_args[0][0]['url'])
48+
49+
def test_new_session_prefs(self):
50+
s = self.getUniqueString()
51+
f = Mock(return_value=s)
52+
myprefs = {self.getUniqueString(): self.getUniqueString()}
53+
r = self._makeit(Mock(), makeprefs(f), Mock())
54+
self.assertEqual(s, r.new_session(myprefs))
55+
self.assertEqual(myprefs, f.call_args[0][0])
56+
57+
@patch('lacli.registry.netrc')
58+
def test_saved_session_error(self, netrc):
59+
f = Mock()
60+
prefs = makeprefs(f)
61+
prefs['api']['user'] = False
62+
exc = Exception(self.getUniqueString())
63+
netrc.side_effect = exc
64+
self._makeit(Mock(home='foo'), prefs, Mock())
65+
self.assertEqual(None, f.call_args[0][0]['pass'])
66+
self.assertEqual(os.path.expanduser('~/.netrc'),
67+
netrc.call_args[0][0])
68+
for debug_msg, debug_exc in self.lacli_log_messages['debug']:
69+
self.assertIn("Couldn't read from netrc", debug_msg)
70+
self.assertTrue(debug_exc is not None)
71+
self.assertEqual(exc, debug_exc[1])

lacli/t/test_server.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22
import os
3+
import crochet
34

45
from testtools import TestCase
56
from twisted.internet import defer
@@ -8,6 +9,11 @@
89

910

1011
class ServerTest(TestCase):
12+
13+
@classmethod
14+
def setup_class(cls):
15+
crochet.setup()
16+
1117
def setUp(self):
1218
super(ServerTest, self).setUp()
1319
self.prefs = makeprefs()
@@ -24,6 +30,7 @@ def ttypes(self):
2430
from lacli.server.interface.ClientInterface import ttypes
2531
return ttypes
2632

33+
@crochet.wait_for(2.0)
2734
@defer.inlineCallbacks
2835
def test_get_latest_version(self):
2936
registry = Mock()
@@ -45,6 +52,7 @@ def test_get_latest_version(self):
4552
self.assertEqual(
4653
v, self.ttypes().VersionInfo('1.2.3'))
4754

55+
@crochet.wait_for(2.0)
4856
@defer.inlineCallbacks
4957
def test_get_latest_version_from_api(self):
5058
registry = Mock()

lacli/t/test_upload_state.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from testtools import TestCase
2+
from mock import Mock
3+
4+
5+
class UploadStateTest(TestCase):
6+
def setUp(self):
7+
from lacli.upload import UploadState
8+
UploadState.states = None
9+
super(UploadStateTest, self).setUp()
10+
11+
def tearDown(self):
12+
super(UploadStateTest, self).tearDown()
13+
14+
def _mockcache(self, uploads={}, archives={}):
15+
return Mock(_get_uploads=Mock(return_value=uploads),
16+
_for_adf=Mock(return_value=archives))
17+
18+
def test_no_uploads(self):
19+
from lacli.upload import UploadState
20+
UploadState.init(self._mockcache())
21+
archive = self.getUniqueString()
22+
self.assertEqual(archive, UploadState.get(archive).archive)
23+
self.assertTrue(archive in UploadState.states)
24+
25+
def test_no_change_upload_size(self):
26+
from lacli.upload import UploadState
27+
UploadState.init(self._mockcache())
28+
archive = self.getUniqueString()
29+
size = 123
30+
self.assertEqual(size, UploadState.get(archive, size=size).size)
31+
e = self.assertRaises(AssertionError,
32+
UploadState.get, archive, size=size+1)
33+
self.assertEqual("Can't change size for upload", str(e))
34+
35+
def test_no_change_sandbox_status(self):
36+
from lacli.upload import UploadState
37+
UploadState.init(self._mockcache())
38+
archive = self.getUniqueString()
39+
self.assertFalse(UploadState.get(archive).sandbox)
40+
e = self.assertRaises(AssertionError,
41+
UploadState.get, archive, sandbox=True)
42+
self.assertEqual("Can't change sandbox status for upload", str(e))
43+
44+
def test_no_change_capsule(self):
45+
from lacli.upload import UploadState
46+
UploadState.init(self._mockcache())
47+
archive = self.getUniqueString()
48+
self.assertEqual(
49+
1, UploadState.get(archive, capsule={'id': 1}).capsule['id'])
50+
e = self.assertRaises(
51+
AssertionError, UploadState.get, archive, capsule={'id': 2})
52+
self.assertEqual("Can't change capsule for upload", str(e))
53+
54+
def test_change_capsule(self):
55+
from lacli.upload import UploadState
56+
UploadState.init(self._mockcache())
57+
archive = self.getUniqueString()
58+
self.assertIs(None, UploadState.get(archive).capsule)
59+
self.assertEqual(
60+
2, UploadState.get(archive, capsule={'id': 2}).capsule['id'])

lacli/upload.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ def get(cls, fname, size=None, capsule=None, sandbox=False):
6060
assert state.size == size, msg.format('size')
6161
cls.states[fname].size = size
6262
if capsule is not None:
63-
assert state.capsule.id == capsule.id, msg.format('capsule')
63+
# might be helpful if you want to change the capsule
64+
if state.capsule is None:
65+
state.capsule = capsule
66+
cid = state.capsule.get('id', None)
67+
assert cid == capsule['id'], msg.format('capsule')
6468
cls.states[fname].capsule = capsule
6569
if sandbox is True:
6670
assert state.sandbox == sandbox, msg.format('sandbox status')

0 commit comments

Comments
 (0)