Merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.4 into lp:ubuntu/maverick/ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Merged at revision: 11
Proposed branch: lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.4
Merge into: lp:ubuntu/maverick/ubuntu-sso-client
Diff against target: 2354 lines (+1021/-331)
10 files modified
PKG-INFO (+1/-1)
data/ui.glade (+38/-22)
debian/changelog (+43/-0)
setup.py (+4/-3)
ubuntu_sso/gui.py (+183/-80)
ubuntu_sso/keyring.py (+89/-38)
ubuntu_sso/main.py (+22/-35)
ubuntu_sso/tests/test_gui.py (+447/-50)
ubuntu_sso/tests/test_keyring.py (+125/-29)
ubuntu_sso/tests/test_main.py (+69/-73)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.4
Reviewer Review Type Date Requested Status
Ubuntu Development Team Pending
Review via email: mp+33969@code.launchpad.net

Description of the change

High priority bug fixes after UI freeze (v0.99.4).

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'PKG-INFO'
2--- PKG-INFO 2010-08-26 01:07:41 +0000
3+++ PKG-INFO 2010-08-27 22:32:43 +0000
4@@ -1,6 +1,6 @@
5 Metadata-Version: 1.1
6 Name: ubuntu-sso-client
7-Version: 0.99.3
8+Version: 0.99.4
9 Summary: Ubuntu Single Sign-On client
10 Home-page: https://launchpad.net/ubuntu-sso-client
11 Author: Natalia Bidart
12
13=== modified file 'data/ui.glade'
14--- data/ui.glade 2010-08-26 01:07:41 +0000
15+++ data/ui.glade 2010-08-27 22:32:43 +0000
16@@ -60,6 +60,12 @@
17 <property name="visible">True</property>
18 <property name="spacing">10</property>
19 <child>
20+ <placeholder/>
21+ </child>
22+ <child>
23+ <placeholder/>
24+ </child>
25+ <child>
26 <object class="GtkHBox" id="emails_hbox">
27 <property name="visible">True</property>
28 <property name="spacing">5</property>
29@@ -77,9 +83,6 @@
30 </packing>
31 </child>
32 <child>
33- <placeholder/>
34- </child>
35- <child>
36 <object class="GtkHBox" id="passwords_hbox">
37 <property name="visible">True</property>
38 <property name="spacing">5</property>
39@@ -107,9 +110,6 @@
40 </packing>
41 </child>
42 <child>
43- <placeholder/>
44- </child>
45- <child>
46 <object class="GtkHBox" id="hbox1">
47 <property name="visible">True</property>
48 <child>
49@@ -585,27 +585,43 @@
50 <property name="visible">True</property>
51 <property name="spacing">10</property>
52 <child>
53- <object class="GtkAlignment" id="alignment1">
54+ <object class="GtkVBox" id="vbox2">
55 <property name="visible">True</property>
56- <property name="xscale">0</property>
57- <property name="yscale">0</property>
58- <child>
59- <object class="GtkVBox" id="set_new_password_details_vbox">
60- <property name="visible">True</property>
61- <property name="spacing">5</property>
62- <child>
63- <placeholder/>
64- </child>
65- <child>
66- <placeholder/>
67- </child>
68- <child>
69- <placeholder/>
70- </child>
71+ <child>
72+ <object class="GtkAlignment" id="alignment1">
73+ <property name="visible">True</property>
74+ <property name="xscale">0</property>
75+ <property name="yscale">0</property>
76+ <child>
77+ <object class="GtkVBox" id="set_new_password_details_vbox">
78+ <property name="visible">True</property>
79+ <property name="spacing">5</property>
80+ <child>
81+ <placeholder/>
82+ </child>
83+ <child>
84+ <placeholder/>
85+ </child>
86+ <child>
87+ <placeholder/>
88+ </child>
89+ </object>
90+ </child>
91+ </object>
92+ <packing>
93+ <property name="position">0</property>
94+ </packing>
95+ </child>
96+ <child>
97+ <object class="GtkLabel" id="reset_password_help_label">
98+ <property name="visible">True</property>
99+ <property name="label" translatable="yes">label</property>
100+ <property name="wrap">True</property>
101 </object>
102 </child>
103 </object>
104 <packing>
105+ <property name="padding">5</property>
106 <property name="position">0</property>
107 </packing>
108 </child>
109
110=== modified file 'debian/changelog'
111--- debian/changelog 2010-08-26 13:05:03 +0000
112+++ debian/changelog 2010-08-27 22:32:43 +0000
113@@ -1,3 +1,46 @@
114+ubuntu-sso-client (0.99.4-0ubuntu1) UNRELEASED; urgency=low
115+
116+ * New upstream release:
117+
118+ [ natalia.bidart@canonical.com ]
119+ * Validate form data for verify token page, request password token and set
120+ new password (LP: #625361).
121+ * Validate password strength on reset password page (LP: #616528).
122+ * Labels are not as wide as the parent windowm but a little bit less wide
123+ (LP: #625009).
124+
125+ [ Alejandro J. Cura <alecu@canonical.com> ]
126+ * Store the credentials after the email validation step (LP: #625003)
127+
128+ [ natalia.bidart@canonical.com
129+ * Every form can be submitted by activating the buttons and/or the entries
130+ (LP: #616421).
131+
132+ [ David Planella <david.planella@ubuntu.com> ]
133+ * Make setup.py actually use python-distutils-extra, which will allow the
134+ .deb package to build the POT file required to import translations into
135+ Launchpad (LP: #624891).
136+
137+ [ natalia.bidart@canonical.com ]
138+ * Errors from SSO servers are being shown now to users, matching
139+ error-specific to fields (LP: #616101).
140+ * Also, be robust when SSO server answer with a string where it's supposed
141+ to be a list (LP: #623447).
142+
143+ [ Alejandro J. Cura <alecu@canonical.com> ]
144+ * Use the keyring unlocking gnomekeyring APIs (LP: #623622)
145+ * Search all keyrings for the credentials (LP: #624033)
146+
147+ [ natalia.bidart@canonical.com ]
148+ * Customize "help_text" for the login only dialog (LP: #624097).
149+ * Label areas are as wide as the parent window (LP: #616551).
150+
151+ [ Alejandro J. Cura <alecu@canonical.com> ]
152+ * The list of error strings as returned by the SSO webservice can't go thru
153+ DBus (LP: #624358).
154+
155+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Fri, 27 Aug 2010 19:16:55 -0300
156+
157 ubuntu-sso-client (0.99.3-0ubuntu1) maverick; urgency=low
158
159 * New upstream release:
160
161=== modified file 'setup.py'
162--- setup.py 2010-08-26 01:07:41 +0000
163+++ setup.py 2010-08-27 22:32:43 +0000
164@@ -21,6 +21,7 @@
165
166 try:
167 import DistUtilsExtra.auto
168+ from DistUtilsExtra.command import build_extra
169 except ImportError:
170 print >> sys.stderr, 'To build this program you need '\
171 'https://launchpad.net/python-distutils-extra'
172@@ -36,7 +37,7 @@
173 CLEANFILES = ['data/com.ubuntu.sso.service']
174
175 # XXX: This needs some serious cleanup
176-class SSOBuild(build.build):
177+class SSOBuild(build_extra.build_extra):
178 """Class to build the extra files."""
179
180 description = 'build extra files needed by ubuntu-sso-client'
181@@ -61,7 +62,7 @@
182 )
183
184 # Run the parent build command
185- build.build.run(self)
186+ build_extra.build_extra.run(self)
187
188
189 class SSOClean(clean.clean):
190@@ -81,7 +82,7 @@
191
192 DistUtilsExtra.auto.setup(
193 name='ubuntu-sso-client',
194- version='0.99.3',
195+ version='0.99.4',
196 license='GPL v3',
197 author='Natalia Bidart',
198 author_email='natalia.bidart@canonical.com',
199
200=== modified file 'ubuntu_sso/gui.py'
201--- ubuntu_sso/gui.py 2010-08-26 01:07:41 +0000
202+++ ubuntu_sso/gui.py 2010-08-27 22:32:43 +0000
203@@ -168,6 +168,7 @@
204 self.warning = warning_msg
205 self.set_property('secondary-icon-stock', gtk.STOCK_DIALOG_WARNING)
206 self.set_property('secondary-icon-sensitive', True)
207+ self.set_property('secondary-icon-activatable', False)
208 self.set_property('secondary-icon-tooltip-text', warning_msg)
209
210 def clear_warning(self):
211@@ -175,6 +176,7 @@
212 self.warning = None
213 self.set_property('secondary-icon-stock', None)
214 self.set_property('secondary-icon-sensitive', False)
215+ self.set_property('secondary-icon-activatable', False)
216 self.set_property('secondary-icon-tooltip-text', None)
217
218
219@@ -253,12 +255,10 @@
220 builder.add_from_file(ui_filename)
221 builder.connect_signals(self)
222
223- # XXX: every button should have tests for 'activate' signal
224- # see LP: #616421
225-
226 self.widgets = []
227 self.warnings = []
228 self.cancels = []
229+ self.labels = []
230 for obj in builder.get_objects():
231 name = getattr(obj, 'name', None)
232 if name is None and isinstance(obj, gtk.Buildable):
233@@ -275,6 +275,10 @@
234 if 'cancel_button' in name:
235 obj.connect('clicked', self.on_close_clicked)
236 self.cancels.append(obj)
237+ if '_button' in name:
238+ obj.connect('activate', lambda w: w.clicked())
239+ if 'label' in name:
240+ self.labels.append(obj)
241
242 self.entries = ('name_entry', 'email1_entry', 'email2_entry',
243 'password1_entry', 'password2_entry',
244@@ -292,7 +296,6 @@
245 assert getattr(self, name) is not None
246
247 self.window.set_icon_name('ubuntu-logo')
248- self.captcha_reload_button.set_tooltip_text(self.CAPTCHA_RELOAD_TOOLTIP)
249
250 self.bus = dbus.SessionBus()
251 self.bus.add_signal_receiver = self._log(self.bus.add_signal_receiver)
252@@ -316,17 +319,26 @@
253 self._append_page(self._build_request_password_token_page())
254 self._append_page(self._build_set_new_password_page())
255
256+ window_size = None
257 if not login_only:
258+ window_size = (550, 600)
259 self._append_page(self._build_enter_details_page())
260 self._append_page(self._build_tc_page())
261 self._append_page(self._build_verify_email_page())
262 self.login_button.grab_focus()
263 self._set_current_page(self.enter_details_vbox)
264 else:
265+ window_size = (400, 350)
266 self.login_back_button.hide()
267 self.login_ok_button.grab_focus()
268+ self.login_vbox.help_text = help_text
269 self._set_current_page(self.login_vbox)
270
271+ self.window.set_size_request(*window_size)
272+ size_req = (int(window_size[0] * 0.9), -1)
273+ for label in self.labels:
274+ label.set_size_request(*size_req)
275+
276 self._setup_signals()
277 self._gtk_signal_log = []
278
279@@ -487,16 +499,17 @@
280
281 self.enter_details_vbox.pack_start(self.name_entry, expand=False)
282 self.enter_details_vbox.reorder_child(self.name_entry, 0)
283- self.captcha_solution_vbox.pack_start(self.captcha_solution_entry,
284- expand=False)
285- self.captcha_solution_vbox.reorder_child(self.captcha_solution_entry,
286- 0)
287-
288- self.emails_hbox.pack_start(self.email1_entry)
289- self.emails_hbox.pack_start(self.email2_entry)
290-
291- self.passwords_hbox.pack_start(self.password1_entry)
292- self.passwords_hbox.pack_start(self.password2_entry)
293+ entry = self.captcha_solution_entry
294+ self.captcha_solution_vbox.pack_start(entry, expand=False)
295+ self.captcha_solution_vbox.reorder_child(entry, 0)
296+ msg = self.CAPTCHA_RELOAD_TOOLTIP
297+ self.captcha_reload_button.set_tooltip_text(msg)
298+
299+ self.emails_hbox.pack_start(self.email1_entry, expand=False)
300+ self.emails_hbox.pack_start(self.email2_entry, expand=False)
301+
302+ self.passwords_hbox.pack_start(self.password1_entry, expand=False)
303+ self.passwords_hbox.pack_start(self.password2_entry, expand=False)
304 help_msg = '<small>%s</small>' % self.PASSWORD_HELP
305 self.password_help_label.set_markup(help_msg)
306
307@@ -515,6 +528,11 @@
308 self.tc_hbox.hide_all()
309 self.login_button.set_label(self.LOGIN_BUTTON_LABEL)
310
311+ for entry in (self.name_entry, self.email1_entry, self.email2_entry,
312+ self.password1_entry, self.password2_entry,
313+ self.captcha_solution_entry):
314+ entry.connect('activate', lambda w: self.join_ok_button.clicked())
315+
316 return self.enter_details_vbox
317
318 def _build_tc_page(self):
319@@ -530,6 +548,8 @@
320
321 def _build_verify_email_page(self):
322 """Build the verify email page."""
323+ cb = lambda w: self.verify_token_button.clicked()
324+ self.email_token_entry.connect('activate', cb)
325 self.verify_email_vbox.pack_start(self.email_token_entry, expand=False)
326 self.verify_email_vbox.reorder_child(self.email_token_entry, 0)
327
328@@ -537,7 +557,6 @@
329
330 def _build_success_page(self):
331 """Build the success page."""
332- #self.success_vbox.help_text = ''
333 self.success_label.set_markup('<span size="x-large">%s</span>' %
334 self.SUCCESS)
335 return self.success_vbox
336@@ -557,6 +576,9 @@
337 self.forgotten_password_button.set_label(msg)
338 self.login_ok_button.grab_focus()
339
340+ for entry in (self.login_email_entry, self.login_password_entry):
341+ entry.connect('activate', lambda w: self.login_ok_button.clicked())
342+
343 return self.login_vbox
344
345 def _build_request_password_token_page(self):
346@@ -566,12 +588,16 @@
347 self.request_password_token_vbox.help_text = text
348
349 entry = self.reset_email_entry
350- self.request_password_token_details_vbox.pack_start(entry)
351+ self.request_password_token_details_vbox.pack_start(entry,
352+ expand=False)
353 cb = self.on_reset_email_entry_changed
354 self.reset_email_entry.connect('changed', cb)
355 self.request_password_token_ok_button.set_label(self.NEXT)
356 self.request_password_token_ok_button.set_sensitive(False)
357
358+ cb = lambda w: self.request_password_token_ok_button.clicked()
359+ self.reset_email_entry.connect('activate', cb)
360+
361 return self.request_password_token_vbox
362
363 def _build_set_new_password_page(self):
364@@ -579,21 +605,52 @@
365 self.set_new_password_vbox.header = self.RESET_PASSWORD
366 self.set_new_password_vbox.help_text = self.SET_NEW_PASSWORD_LABEL
367
368+ cb = lambda w: self.set_new_password_ok_button.clicked()
369 for entry in (self.reset_code_entry,
370 self.reset_password1_entry,
371 self.reset_password2_entry):
372- self.set_new_password_details_vbox.pack_start(entry)
373+ self.set_new_password_details_vbox.pack_start(entry, expand=False)
374+ entry.connect('activate', cb)
375
376 cb = self.on_set_new_password_entries_changed
377 self.reset_code_entry.connect('changed', cb)
378 self.reset_password1_entry.connect('changed', cb)
379 self.reset_password2_entry.connect('changed', cb)
380+ help_msg = '<small>%s</small>' % self.PASSWORD_HELP
381+ self.reset_password_help_label.set_markup(help_msg)
382
383 self.set_new_password_ok_button.set_label(self.RESET_PASSWORD)
384 self.set_new_password_ok_button.set_sensitive(False)
385
386 return self.set_new_password_vbox
387
388+ def _validate_email(self, email1, email2=None):
389+ """Validate 'email1', return error message if not valid.
390+
391+ If 'email2' is given, must match 'email1'.
392+ """
393+ if email2 is not None and email1 != email2:
394+ return self.EMAIL_MISMATCH
395+
396+ if not email1:
397+ return self.FIELD_REQUIRED
398+
399+ if '@' not in email1:
400+ return self.EMAIL_INVALID
401+
402+ def _validate_password(self, password1, password2=None):
403+ """Validate 'password1', return error message if not valid.
404+
405+ If 'password2' is given, must match 'email1'.
406+ """
407+ if password2 is not None and password1 != password2:
408+ return self.PASSWORD_MISMATCH
409+
410+ if (len(password1) < 8 or
411+ re.search('[A-Z]', password1) is None or
412+ re.search('\d+', password1) is None):
413+ return self.PASSWORD_TOO_WEAK
414+
415 # GTK callbacks
416
417 def run(self):
418@@ -640,6 +697,9 @@
419
420 def on_join_ok_button_clicked(self, *args, **kwargs):
421 """Submit info for processing, present the processing vbox."""
422+ if not self.join_ok_button.is_sensitive():
423+ return
424+
425 self._clear_warnings()
426
427 error = False
428@@ -647,56 +707,40 @@
429 name = self.name_entry.get_text()
430 if not name:
431 self.name_entry.set_warning(self.FIELD_REQUIRED)
432- error |= True
433+ error = True
434
435 # check email
436 email1 = self.email1_entry.get_text()
437 email2 = self.email2_entry.get_text()
438- if email1 != email2:
439- self.email1_entry.set_warning(self.EMAIL_MISMATCH)
440- self.email2_entry.set_warning(self.EMAIL_MISMATCH)
441- error |= True
442-
443- if '@' not in email1:
444- self.email1_entry.set_warning(self.EMAIL_INVALID)
445- self.email2_entry.set_warning(self.EMAIL_INVALID)
446- error |= True
447-
448- if not email1:
449- self.email1_entry.set_warning(self.FIELD_REQUIRED)
450- self.email2_entry.set_warning(self.FIELD_REQUIRED)
451- error |= True
452+ msg = self._validate_email(email1, email2)
453+ if msg is not None:
454+ self.email1_entry.set_warning(msg)
455+ self.email2_entry.set_warning(msg)
456+ error = True
457
458 # check password
459 password1 = self.password1_entry.get_text()
460 password2 = self.password2_entry.get_text()
461- if password1 != password2:
462- self.password1_entry.set_warning(self.PASSWORD_MISMATCH)
463- self.password2_entry.set_warning(self.PASSWORD_MISMATCH)
464- error |= True
465-
466- if (len(password1) < 8 or
467- re.search('[A-Z]', password1) is None or
468- re.search('\d+', password1) is None):
469- self.password1_entry.set_warning(self.PASSWORD_TOO_WEAK)
470- self.password2_entry.set_warning(self.PASSWORD_TOO_WEAK)
471- error |= True
472+ msg = self._validate_password(password1, password2)
473+ if msg is not None:
474+ self.password1_entry.set_warning(msg)
475+ self.password2_entry.set_warning(msg)
476+ error = True
477
478 # check T&C
479 if not self.yes_to_tc_checkbutton.get_active():
480 self._set_warning_message(self.tc_warning_label,
481 self.TC_NOT_ACCEPTED)
482- error |= True
483+ error = True
484
485 captcha_solution = self.captcha_solution_entry.get_text()
486 if not captcha_solution:
487 self.captcha_solution_entry.set_warning(self.FIELD_REQUIRED)
488- error |= True
489+ error = True
490
491 if error:
492 return
493
494- self._clear_warnings()
495 self._set_current_page(self.processing_vbox)
496
497 logger.info('Calling register_user with email %r, password <hidden>,' \
498@@ -716,14 +760,22 @@
499
500 def on_verify_token_button_clicked(self, *args, **kwargs):
501 """The user entered the email token, let's verify!"""
502+ if not self.verify_token_button.is_sensitive():
503+ return
504+
505+ self._clear_warnings()
506+
507+ email_token = self.email_token_entry.get_text()
508+ if not email_token:
509+ self.email_token_entry.set_warning(self.FIELD_REQUIRED)
510+ return
511+
512 email = self.email1_entry.get_text()
513 password = self.password1_entry.get_text()
514- email_token = self.email_token_entry.get_text()
515-
516+ f = self.backend.validate_email
517 logger.info('Calling validate_email with email %r, password <hidden>' \
518- ', app_name %r and email_token %r.', email,
519- self.app_name, email_token)
520- f = self.backend.validate_email
521+ ', app_name %r and email_token %r.', email, self.app_name,
522+ email_token)
523 f(self.app_name, email, password, email_token,
524 reply_handler=NO_OP, error_handler=NO_OP)
525
526@@ -731,30 +783,27 @@
527
528 def on_login_connect_button_clicked(self, *args, **kwargs):
529 """User wants to connect!"""
530+ if not self.login_ok_button.is_sensitive():
531+ return
532+
533 self._clear_warnings()
534
535 error = False
536+
537 email = self.login_email_entry.get_text()
538-
539- if '@' not in email:
540- self.login_email_entry.set_warning(self.EMAIL_INVALID)
541- error |= True
542-
543- if not email:
544- self.login_email_entry.set_warning(self.FIELD_REQUIRED)
545- error |= True
546+ msg = self._validate_email(email)
547+ if msg is not None:
548+ self.login_email_entry.set_warning(msg)
549+ error = True
550
551 password = self.login_password_entry.get_text()
552-
553 if not password:
554 self.login_password_entry.set_warning(self.FIELD_REQUIRED)
555- error |= True
556+ error = True
557
558 if error:
559 return
560
561- self._clear_warnings()
562-
563 f = self.backend.login
564 f(self.app_name, email, password,
565 reply_handler=NO_OP, error_handler=NO_OP)
566@@ -771,7 +820,16 @@
567
568 def on_request_password_token_ok_button_clicked(self, *args, **kwargs):
569 """User entered the email address to reset the password."""
570+ if not self.request_password_token_ok_button.is_sensitive():
571+ return
572+
573+ self._clear_warnings()
574+
575 email = self.reset_email_entry.get_text()
576+ msg = self._validate_email(email)
577+ if msg is not None:
578+ self.reset_email_entry.set_warning(msg)
579+ return
580
581 logger.info('Calling request_password_reset_token with %r.', email)
582 f = self.backend.request_password_reset_token
583@@ -799,13 +857,30 @@
584
585 def on_set_new_password_ok_button_clicked(self, *args, **kwargs):
586 """User entered reset code and new passwords."""
587- email = self.reset_email_entry.get_text()
588+ if not self.set_new_password_ok_button.is_sensitive():
589+ return
590+
591+ self._clear_warnings()
592+
593+ error = False
594+
595 token = self.reset_code_entry.get_text()
596- # XXX: validate passwords!
597- # see LP: #616528
598+ if not token:
599+ self.reset_code_entry.set_warning(self.FIELD_REQUIRED)
600+ error = True
601+
602 password1 = self.reset_password1_entry.get_text()
603- #password2 = self.reset_password2_entry.get_text()
604-
605+ password2 = self.reset_password2_entry.get_text()
606+ msg = self._validate_password(password1, password2)
607+ if msg is not None:
608+ self.reset_password1_entry.set_warning(msg)
609+ self.reset_password2_entry.set_warning(msg)
610+ error = True
611+
612+ if error:
613+ return
614+
615+ email = self.reset_email_entry.get_text()
616 logger.info('Calling set_new_password with email %r, token %r and ' \
617 'new password: <hidden>.', email, token)
618 f = self.backend.set_new_password
619@@ -836,6 +911,17 @@
620
621 # backend callbacks
622
623+ def _build_general_error_message(self, errordict):
624+ """Concatenate __all__ and message from the errordict."""
625+ result = None
626+ msg1 = errordict.get('__all__')
627+ msg2 = errordict.get('message')
628+ if msg1 is not None and msg2 is not None:
629+ result = '\n'.join((msg1, msg2))
630+ else:
631+ result = msg1 if msg1 is not None else msg2
632+ return result
633+
634 @log_call
635 def on_captcha_generated(self, app_name, captcha_id, *args, **kwargs):
636 """Captcha image has been generated and is available to be shown."""
637@@ -858,10 +944,21 @@
638 @log_call
639 def on_user_registration_error(self, app_name, error, *args, **kwargs):
640 """Captcha image generation failed."""
641- self._set_current_page(self.enter_details_vbox,
642- warning_text=self.UNKNOWN_ERROR)
643+ msg = error.get('email')
644+ if msg is not None:
645+ self.email1_entry.set_warning(msg)
646+ self.email2_entry.set_warning(msg)
647+
648+ msg = error.get('password')
649+ if msg is not None:
650+ self.password1_entry.set_warning(msg)
651+ self.password2_entry.set_warning(msg)
652+
653+ msg = self._build_general_error_message(error)
654+ self._set_current_page(self.enter_details_vbox, warning_text=msg)
655+
656 self._gtk_signal_log.append((SIG_REGISTRATION_FAILED, self.app_name,
657- error))
658+ msg))
659
660 @log_call
661 def on_email_validated(self, app_name, email, *args, **kwargs):
662@@ -873,10 +970,15 @@
663 @log_call
664 def on_email_validation_error(self, app_name, error, *args, **kwargs):
665 """User email validation failed."""
666- self._set_current_page(self.verify_email_vbox,
667- warning_text=self.UNKNOWN_ERROR)
668+ msg = error.get('email_token')
669+ if msg is not None:
670+ self.email_token_entry.set_warning(msg)
671+
672+ msg = self._build_general_error_message(error)
673+ self._set_current_page(self.verify_email_vbox, warning_text=msg)
674+
675 self._gtk_signal_log.append((SIG_REGISTRATION_FAILED, self.app_name,
676- error))
677+ msg))
678
679 @log_call
680 def on_logged_in(self, app_name, email, *args, **kwargs):
681@@ -888,10 +990,10 @@
682 @log_call
683 def on_login_error(self, app_name, error, *args, **kwargs):
684 """User was not successfully logged in."""
685- self._set_current_page(self.login_vbox,
686- warning_text=self.UNKNOWN_ERROR)
687+ msg = self._build_general_error_message(error)
688+ self._set_current_page(self.login_vbox, warning_text=msg)
689 self._gtk_signal_log.append((SIG_LOGIN_FAILED, self.app_name,
690- error))
691+ msg))
692
693 @log_call
694 def on_password_reset_token_sent(self, app_name, email, *args, **kwargs):
695@@ -903,8 +1005,8 @@
696 @log_call
697 def on_password_reset_error(self, app_name, error, *args, **kwargs):
698 """Password reset failed."""
699- self._set_current_page(self.login_vbox,
700- warning_text=self.UNKNOWN_ERROR)
701+ msg = self._build_general_error_message(error)
702+ self._set_current_page(self.login_vbox, warning_text=msg)
703
704 @log_call
705 def on_password_changed(self, app_name, email, *args, **kwargs):
706@@ -915,5 +1017,6 @@
707 @log_call
708 def on_password_change_error(self, app_name, error, *args, **kwargs):
709 """Password reset failed."""
710+ msg = self._build_general_error_message(error)
711 self._set_current_page(self.request_password_token_vbox,
712- warning_text=self.UNKNOWN_ERROR)
713+ warning_text=msg)
714
715=== modified file 'ubuntu_sso/keyring.py'
716--- ubuntu_sso/keyring.py 2010-08-19 16:02:09 +0000
717+++ ubuntu_sso/keyring.py 2010-08-27 22:32:43 +0000
718@@ -19,77 +19,128 @@
719
720 """Handle keys in the gnome kerying."""
721
722+import socket
723+import urllib
724+import urlparse
725+
726 import gnomekeyring
727
728-from urllib import urlencode
729-from urlparse import parse_qsl
730+from ubuntu_sso.logger import setupLogging
731+logger = setupLogging("ubuntu_sso.main")
732+
733+U1_APP_NAME = "Ubuntu One"
734+U1_KEY_NAME = "UbuntuOne token for https://ubuntuone.com"
735+U1_KEY_ATTR = {
736+ "oauth-consumer-key": "ubuntuone",
737+ "ubuntuone-realm": "https://ubuntuone.com",
738+}
739+
740+
741+def get_token_name(app_name):
742+ """Build the token name."""
743+ quoted_app_name = urllib.quote(app_name)
744+ computer_name = socket.gethostname()
745+ quoted_computer_name = urllib.quote(computer_name)
746+ return "%s - %s" % (quoted_app_name, quoted_computer_name)
747
748
749 class Keyring(object):
750-
751+ """A Keyring for a given application name."""
752 KEYRING_NAME = "login"
753
754 def __init__(self, app_name):
755+ """Initialize this instance given the app_name."""
756 if not gnomekeyring.is_available():
757 raise gnomekeyring.NoKeyringDaemonError
758 self.app_name = app_name
759+ self.token_name = get_token_name(self.app_name)
760
761 def _create_keyring(self, name):
762- """Creates a keyring, if it already exists, do nothing."""
763+ """Creates a keyring, or if it already exists, it does nothing."""
764 keyring_names = gnomekeyring.list_keyring_names_sync()
765 if not name in keyring_names:
766 gnomekeyring.create_sync(name)
767
768- def _get_item_id_from_name(self, sync, name):
769- """Return the ID for a named item."""
770- for item_id in gnomekeyring.list_item_ids_sync(sync):
771- item_info = gnomekeyring.item_get_info_sync(sync, item_id)
772- display_name = item_info.get_display_name()
773- if display_name == name:
774- return item_id
775-
776- def _get_item_info_from_name(self, sync, name):
777- """Return the ID for a named item."""
778- for item_id in gnomekeyring.list_item_ids_sync(sync):
779- item_info = gnomekeyring.item_get_info_sync(sync, item_id)
780- display_name = item_info.get_display_name()
781- if display_name == name:
782- return item_info
783+ def _find_keyring_item(self):
784+ """Return the keyring item or None if not found."""
785+ try:
786+ items = gnomekeyring.find_items_sync(
787+ gnomekeyring.ITEM_GENERIC_SECRET,
788+ self._get_keyring_attr())
789+ except gnomekeyring.NoMatchError:
790+ # if no items found, return None
791+ return None
792+
793+ # we priorize the item in the "login" keyring
794+ for item in items:
795+ if item.keyring == "login":
796+ return item
797+
798+ # if not on the "login" keyring, we return the first item
799+ return items[0]
800+
801+ def _get_keyring_attr(self):
802+ """Build the keyring attributes for this credentials."""
803+ attr = {"key-type": "Ubuntu SSO credentials",
804+ "token-name": self.token_name}
805+ return attr
806
807 def set_ubuntusso_attr(self, cred):
808 """Set the credentials of the Ubuntu SSO item."""
809 # Creates the secret from the credentials
810- secret = urlencode(cred)
811+ secret = urllib.urlencode(cred)
812
813- # Create the keyring
814+ # Create the keyring if it does not exists
815 self._create_keyring(self.KEYRING_NAME)
816
817- # No need to delete the item if it already exists
818-
819- # A set of attributes for this credentials
820- attr = {"key-type": "Ubuntu SSO credentials",
821- "token-name": cred["name"].encode("utf8")}
822-
823 # Add our SSO credentials to the keyring
824 gnomekeyring.item_create_sync(self.KEYRING_NAME,
825- gnomekeyring.ITEM_GENERIC_SECRET, self.app_name, attr,
826- secret, True)
827+ gnomekeyring.ITEM_GENERIC_SECRET, self.app_name,
828+ self._get_keyring_attr(), secret, True)
829
830 def get_ubuntusso_attr(self):
831 """Return the secret of the SSO item in a dictionary."""
832 # If we have no attributes, return None
833- exist = self._get_item_info_from_name(self.KEYRING_NAME, self.app_name)
834- if exist is not None:
835- secret = self._get_item_info_from_name(self.KEYRING_NAME,
836- self.app_name).get_secret()
837- return dict(parse_qsl(secret))
838+ item = self._find_keyring_item()
839+ if item is not None:
840+ return dict(urlparse.parse_qsl(item.secret))
841+ else:
842+ # if no item found, try getting the old credentials
843+ if self.app_name == U1_APP_NAME:
844+ return try_old_credentials(self.app_name)
845+ # nothing was found
846+ return None
847
848 def delete_ubuntusso_attr(self):
849 """Delete a set of credentials from the keyring."""
850- item_id = self._get_item_id_from_name(self.KEYRING_NAME,
851- self.app_name)
852- if item_id is not None:
853- gnomekeyring.item_delete_sync(self.KEYRING_NAME, item_id)
854+ item = self._find_keyring_item()
855+ if item is not None:
856+ gnomekeyring.item_delete_sync(item.keyring, item.item_id)
857+
858+
859+class UbuntuOneOAuthKeyring(Keyring):
860+
861+ def _get_keyring_attr(self):
862+ """Build the keyring attributes for this credentials."""
863+ return U1_KEY_ATTR
864+
865+
866+def try_old_credentials(app_name):
867+ """Try to get old U1 credentials and format them as new."""
868+ logger.debug('trying to get old credentials.')
869+ old_creds = UbuntuOneOAuthKeyring(U1_KEY_NAME).get_ubuntusso_attr()
870+ if old_creds is not None:
871+ # Old creds found, build a new credentials dict with them
872+ creds = {
873+ 'consumer_key': "ubuntuone",
874+ 'consumer_secret': "hammertime",
875+ 'name': U1_KEY_NAME,
876+ 'token': old_creds["oauth_token"],
877+ 'token_secret': old_creds["oauth_token_secret"],
878+ }
879+ logger.debug('found old credentials')
880+ return creds
881+ logger.debug('try_old_credentials: No old credentials for this app.')
882
883
884 if __name__ == "__main__":
885
886=== modified file 'ubuntu_sso/main.py'
887--- ubuntu_sso/main.py 2010-08-26 01:07:41 +0000
888+++ ubuntu_sso/main.py 2010-08-27 22:32:43 +0000
889@@ -28,11 +28,9 @@
890 """
891
892 import re
893-import socket
894 import time
895 import threading
896 import traceback
897-import urllib
898 import urllib2
899 import urlparse
900
901@@ -49,7 +47,7 @@
902 from ubuntu_sso import (DBUS_IFACE_AUTH_NAME, DBUS_IFACE_USER_NAME,
903 DBUS_IFACE_CRED_NAME, DBUS_CRED_PATH, DBUS_BUS_NAME, gui)
904 from ubuntu_sso.config import get_config
905-from ubuntu_sso.keyring import Keyring
906+from ubuntu_sso.keyring import Keyring, get_token_name, U1_APP_NAME
907 from ubuntu_sso.logger import setupLogging
908 logger = setupLogging("ubuntu_sso.main")
909
910@@ -57,8 +55,6 @@
911 # Disable the invalid name warning, as we have a lot of DBus style names
912 # pylint: disable-msg=C0103
913
914-OLD_KEY_NAME = "UbuntuOne token for https://ubuntuone.com"
915-U1_APP_NAME = "Ubuntu One"
916 PING_URL = "http://edge.one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
917
918
919@@ -113,32 +109,9 @@
920 creds = Keyring(app_name).get_ubuntusso_attr()
921 logger.debug('keyring_get_credentials: Keyring returned credentials? %r',
922 creds is not None)
923- if creds is None and app_name == U1_APP_NAME:
924- logger.debug('keyring_get_credentials: trying for old service.')
925- # No new creds, try to get old credentials
926- old_creds = Keyring(OLD_KEY_NAME).get_ubuntusso_attr()
927- if old_creds is not None:
928- # Old creds found, build a new credentials dict with them
929- creds = {
930- 'consumer_key': "ubuntuone",
931- 'consumer_secret': "hammertime",
932- 'token_name': OLD_KEY_NAME,
933- 'token': old_creds["oauth_token"],
934- 'token_secret': old_creds["oauth_token_secret"],
935- }
936- else:
937- logger.debug('keyring_get_credentials: Keyring returned credentials.')
938 return creds
939
940
941-def get_token_name(app_name):
942- """Build the token name."""
943- quoted_app_name = urllib.quote(app_name)
944- computer_name = socket.gethostname()
945- quoted_computer_name = urllib.quote(computer_name)
946- return "%s - %s" % (quoted_app_name, quoted_computer_name)
947-
948-
949 class SSOLoginProcessor(object):
950 """Login and register users using the Ubuntu Single Sign On service."""
951
952@@ -163,6 +136,17 @@
953 re.search('\d+', password)) # one number
954 return res
955
956+ def _format_webservice_errors(self, errdict):
957+ """Turn each list of strings in the errdict into a LF separated str."""
958+ result = {}
959+ for k, v in errdict.iteritems():
960+ # workaround until bug #624955 is solved
961+ if isinstance(v, basestring):
962+ result[k] = v
963+ else:
964+ result[k] = "\n".join(v)
965+ return result
966+
967 def generate_captcha(self, filename):
968 """Generate a captcha using the SSO service."""
969 logger.debug('generate_captcha: requesting captcha, filename: %r',
970@@ -203,7 +187,8 @@
971 logger.info('register_user: email: %r result: %r', email, result)
972
973 if result['status'] == 'error':
974- raise RegistrationError(result['errors'])
975+ errorsdict = self._format_webservice_errors(result['errors'])
976+ raise RegistrationError(errorsdict)
977 elif result['status'] != 'ok':
978 raise RegistrationError('Received unknown status: %s' % result)
979 else:
980@@ -243,9 +228,10 @@
981 result = sso_service.accounts.validate_email(email_token=email_token)
982 logger.info('validate_email: email: %r result: %r', email, result)
983 if 'errors' in result:
984- raise EmailTokenError(result['errors'])
985+ errorsdict = self._format_webservice_errors(result['errors'])
986+ raise EmailTokenError(errorsdict)
987 elif 'email' in result:
988- return result['email']
989+ return token
990 else:
991 raise EmailTokenError('Received invalid reply: %s' % result)
992
993@@ -257,7 +243,7 @@
994 result = service(email=email)
995 except HTTPError, e:
996 logger.exception('request_password_reset_token failed with:')
997- raise ResetPasswordTokenError(e.content)
998+ raise ResetPasswordTokenError(e.content.split('\n')[0])
999
1000 if result['status'] == 'ok':
1001 return email
1002@@ -279,7 +265,7 @@
1003 new_password=new_password)
1004 except HTTPError, e:
1005 logger.exception('set_new_password failed with:')
1006- raise NewPasswordError(e.content)
1007+ raise NewPasswordError(e.content.split('\n')[0])
1008
1009 if result['status'] == 'ok':
1010 return email
1011@@ -291,7 +277,6 @@
1012 """Turn an exception into a dictionary to return thru DBus."""
1013 result = {
1014 "errtype": e.__class__.__name__,
1015- "errargs": repr(e.args),
1016 }
1017 if len(e.args) == 0:
1018 result["message"] = e.__class__.__doc__
1019@@ -414,8 +399,10 @@
1020 def f():
1021 """Inner function that will be run in a thread."""
1022 token_name = get_token_name(app_name)
1023- return self.processor().validate_email(email, password,
1024+ credentials = self.processor().validate_email(email, password,
1025 email_token, token_name)
1026+ keyring_store_credentials(app_name, credentials)
1027+ return email
1028 blocking(f, app_name, self.EmailValidated, self.EmailValidationError)
1029
1030 # request_password_reset_token signals
1031
1032=== modified file 'ubuntu_sso/tests/test_gui.py'
1033--- ubuntu_sso/tests/test_gui.py 2010-08-26 01:07:41 +0000
1034+++ ubuntu_sso/tests/test_gui.py 2010-08-27 22:32:43 +0000
1035@@ -44,7 +44,6 @@
1036 CAPTCHA_SOLUTION = 'william Byrd'
1037 EMAIL = 'test@example.com'
1038 EMAIL_TOKEN = 'B2Pgtf'
1039-ERROR = 'Something bad happened!'
1040 NAME = 'Juanito Pérez'
1041 PASSWORD = 'h3lloWorld'
1042 RESET_PASSWORD_TOKEN = '8G5Wtq'
1043@@ -269,6 +268,8 @@
1044 None)
1045 self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
1046 False)
1047+ self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
1048+ False)
1049 prop = self.entry.get_property('secondary-icon-tooltip-text')
1050 self.assertEqual(prop, None)
1051
1052@@ -281,6 +282,8 @@
1053 gtk.STOCK_DIALOG_WARNING)
1054 self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
1055 True)
1056+ self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
1057+ False)
1058 prop = self.entry.get_property('secondary-icon-tooltip-text')
1059 self.assertEqual(prop, msg)
1060
1061@@ -292,6 +295,8 @@
1062 None)
1063 self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
1064 False)
1065+ self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
1066+ False)
1067 prop = self.entry.get_property('secondary-icon-tooltip-text')
1068 self.assertEqual(prop, None)
1069
1070@@ -338,6 +343,7 @@
1071 'tc_browser', 'login', 'request_password_token',
1072 'set_new_password')
1073 self.ui = self.gui_class(**self.kwargs)
1074+ self.ERROR = {'message': self.ui.UNKNOWN_ERROR}
1075
1076 def tearDown(self):
1077 """Clean up."""
1078@@ -358,11 +364,13 @@
1079 def assert_warnings_visibility(self, visible=False):
1080 """Every warning label should be 'visible'."""
1081 msg = '"%s" should be %svisible.'
1082- warnings = filter(lambda name: 'warning' in name, self.ui.widgets)
1083- for name in warnings:
1084+ for name in self.ui.widgets:
1085 widget = getattr(self.ui, name)
1086- self.assertEqual(visible, widget.get_property('visible'),
1087- msg % (name, '' if visible else 'not '))
1088+ if 'warning' in name:
1089+ self.assertEqual(visible, widget.get_property('visible'),
1090+ msg % (name, '' if visible else 'not '))
1091+ elif 'entry' in name:
1092+ self.assertEqual(widget.warning, '')
1093
1094 def assert_correct_label_warning(self, label, message):
1095 """Check that a warning is shown displaying 'message'."""
1096@@ -533,6 +541,16 @@
1097 # text content is correct
1098 self.assertEqual(expected, actual, msg % (name, expected, actual))
1099
1100+ def test_initial_size_for_labels(self):
1101+ """Labels have the correct width."""
1102+ expected = (int(self.ui.window.get_size_request()[0] * 0.9), -1)
1103+ msg = 'Label %r must have size request %s (got %s instead).'
1104+ labels = [i for i in self.ui.widgets if 'label' in i]
1105+ for label in labels:
1106+ widget = getattr(self.ui, label)
1107+ actual = widget.get_size_request()
1108+ self.assertEqual(expected, actual, msg % (label, expected, actual))
1109+
1110 def test_password_fields_are_password(self):
1111 msg = '"%s" should be a password LabeledEntry instance.'
1112 passwords = filter(lambda name: 'password' in name,
1113@@ -543,7 +561,7 @@
1114
1115 def test_warning_fields_are_hidden(self):
1116 """Every warning label should be not visible."""
1117- self.assert_warnings_visibility(visible=False)
1118+ self.assert_warnings_visibility()
1119
1120 def test_cancel_buttons_close_window(self):
1121 """Every cancel button should close the window when clicked."""
1122@@ -558,6 +576,17 @@
1123 self.assertEqual(self._called, ((widget,), {}), msg % name)
1124 self._called = False
1125
1126+ def test_all_buttons_are_activatable(self):
1127+ """Every button should be activatable."""
1128+ msg = '"%s" should be activatable.'
1129+ buttons = filter(lambda name: '_button' in name, self.ui.widgets)
1130+ for name in buttons:
1131+ widget = getattr(self.ui, name)
1132+ widget.connect('clicked', self._set_called)
1133+ widget.activate()
1134+ self.assertEqual(self._called, ((widget,), {}), msg % name)
1135+ self._called = False
1136+
1137 def test_window_icon(self):
1138 """Main window has the proper icon."""
1139 self.assertEqual('ubuntu-logo', self.ui.window.get_icon_name())
1140@@ -585,7 +614,7 @@
1141 self.assertEqual(self._called, ((xid,), {}))
1142
1143
1144-class RegistrationEnterDetailsTestCase(UbuntuSSOClientTestCase):
1145+class EnterDetailsTestCase(UbuntuSSOClientTestCase):
1146 """Test suite for the user registration (enter details page)."""
1147
1148 def test_initial_text_for_header_label(self):
1149@@ -774,6 +803,50 @@
1150 actual = self.ui.login_button.get_label()
1151 self.assertEqual(self.ui.LOGIN_BUTTON_LABEL, actual)
1152
1153+ def test_activate_name_entry_clicks_connect(self):
1154+ """Activating any entry generates a connect attempt."""
1155+ self.ui.join_ok_button.connect('clicked', self._set_called)
1156+ self.ui.name_entry.activate()
1157+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1158+
1159+ def test_activate_email_entry_clicks_connect(self):
1160+ """Activating any entry generates a connect attempt."""
1161+ self.ui.join_ok_button.connect('clicked', self._set_called)
1162+ self.ui.email1_entry.activate()
1163+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1164+
1165+ self._called = False
1166+ self.ui.email2_entry.activate()
1167+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1168+
1169+ def test_activate_password_entry_clicks_connect(self):
1170+ """Activating any entry generates a connect attempt."""
1171+ self.ui.join_ok_button.connect('clicked', self._set_called)
1172+ self.ui.password1_entry.activate()
1173+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1174+
1175+ self._called = False
1176+ self.ui.password2_entry.activate()
1177+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1178+
1179+ def test_activate_captcha_solution_entry_clicks_connect(self):
1180+ """Activating any entry generates a connect attempt."""
1181+ self.ui.join_ok_button.connect('clicked', self._set_called)
1182+ self.ui.captcha_solution_entry.activate()
1183+ self.assertEqual(self._called, ((self.ui.join_ok_button,), {}))
1184+
1185+ def test_join_ok_button_does_nothing_if_clicked_but_disabled(self):
1186+ """The join form can only be submitted if the button is sensitive."""
1187+ self.patch(self.ui.name_entry, 'get_text', self._set_called)
1188+
1189+ self.ui.join_ok_button.set_sensitive(False)
1190+ self.ui.join_ok_button.clicked()
1191+ self.assertFalse(self._called)
1192+
1193+ self.ui.join_ok_button.set_sensitive(True)
1194+ self.ui.join_ok_button.clicked()
1195+ self.assertTrue(self._called)
1196+
1197
1198 class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase):
1199 """Test suite for the user registration (with no t&c link)."""
1200@@ -843,24 +916,46 @@
1201 test_tc_browser_opens_the_proper_uri.skip = 'The freaking test wont work.'
1202
1203
1204-class UserRegistrationErrorTestCase(UbuntuSSOClientTestCase):
1205+class RegistrationErrorTestCase(UbuntuSSOClientTestCase):
1206 """Test suite for the user registration error handling."""
1207
1208 def setUp(self):
1209 """Init."""
1210- super(UserRegistrationErrorTestCase, self).setUp()
1211+ super(RegistrationErrorTestCase, self).setUp()
1212 self.click_join_with_valid_data()
1213- self.ui.on_user_registration_error(app_name=APP_NAME, error=ERROR)
1214
1215 def test_previous_page_is_shown(self):
1216 """On UserRegistrationError the previous page is shown."""
1217+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
1218 self.assert_pages_visibility(enter_details=True)
1219
1220 def test_warning_label_is_shown(self):
1221 """On UserRegistrationError the warning label is shown."""
1222+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
1223 self.assert_correct_label_warning(self.ui.warning_label,
1224 self.ui.UNKNOWN_ERROR)
1225
1226+ def test_specific_errors_from_backend_are_shown(self):
1227+ """Specific errors from backend are used."""
1228+ error = {'errtype': 'RegistrationError',
1229+ 'message': 'We\'re so doomed.',
1230+ 'email': 'Enter a valid e-mail address.',
1231+ 'password': 'I don\'t like your password.',
1232+ '__all__': 'Wrong captcha solution.'}
1233+
1234+ self.ui.on_user_registration_error(app_name=APP_NAME, error=error)
1235+
1236+ expected = '\n'.join((error['__all__'], error['message']))
1237+ self.assert_correct_label_warning(self.ui.warning_label, expected)
1238+ self.assert_correct_entry_warning(self.ui.email1_entry,
1239+ error['email'])
1240+ self.assert_correct_entry_warning(self.ui.email2_entry,
1241+ error['email'])
1242+ self.assert_correct_entry_warning(self.ui.password1_entry,
1243+ error['password'])
1244+ self.assert_correct_entry_warning(self.ui.password2_entry,
1245+ error['password'])
1246+
1247
1248 class VerifyEmailTestCase(UbuntuSSOClientTestCase):
1249 """Test suite for the user registration (verify email page)."""
1250@@ -899,30 +994,44 @@
1251 self.click_verify_email_with_valid_data()
1252 self.assert_pages_visibility(processing=True)
1253
1254- def test_no_warning_messages_if_valid_data_on_verify_token(self):
1255+ def test_no_warning_messages_if_valid_data(self):
1256 """No warning messages are shown if the data is valid."""
1257 # this will certainly NOT generate warnings
1258 self.click_verify_email_with_valid_data()
1259- self.assert_warnings_visibility(visible=False)
1260+ self.assert_warnings_visibility()
1261
1262 def test_on_email_validated_shows_success_page(self):
1263 """On email validated the success page is shown."""
1264 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
1265 self.assert_pages_visibility(success=True)
1266
1267- def test_on_email_validated_clears_the_help_text(self):
1268- """On email validated the help text is removed."""
1269+ def test_on_email_validated_does_not_clear_the_help_text(self):
1270+ """On email validated the help text is not removed."""
1271 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
1272- self.assertEqual('', self.ui.help_label.get_text())
1273- test_on_email_validated_clears_the_help_text.skip = 'Maybe this is wrong.'
1274+ self.assertEqual(self.ui.verify_email_vbox.help_text,
1275+ self.ui.help_label.get_label())
1276
1277 def test_on_email_validation_error_verify_email_is_shown(self):
1278 """On email validation error, the verify_email page is shown."""
1279- self.ui.on_email_validation_error(app_name=APP_NAME, error=ERROR)
1280+ self.ui.on_email_validation_error(app_name=APP_NAME, error=self.ERROR)
1281 self.assert_pages_visibility(verify_email=True)
1282 self.assert_correct_label_warning(self.ui.warning_label,
1283 self.ui.UNKNOWN_ERROR)
1284
1285+ def test_specific_errors_from_backend_are_shown(self):
1286+ """Specific errors from backend are used."""
1287+ error = {'errtype': 'EmailValidationError',
1288+ 'message': 'We\'re so doomed.',
1289+ 'email_token': 'Enter a valid e-mail address.',
1290+ '__all__': 'We all are gonna die.'}
1291+
1292+ self.ui.on_email_validation_error(app_name=APP_NAME, error=error)
1293+
1294+ expected = '\n'.join((error['__all__'], error['message']))
1295+ self.assert_correct_label_warning(self.ui.warning_label, expected)
1296+ self.assert_correct_entry_warning(self.ui.email_token_entry,
1297+ error['email_token'])
1298+
1299 def test_success_label_is_correct(self):
1300 """The success message is correct."""
1301 self.assertEqual(self.ui.SUCCESS, self.ui.success_label.get_text())
1302@@ -934,15 +1043,74 @@
1303 self.ui.success_close_button.clicked()
1304 self.assertFalse(self.ui.window.get_property('visible'))
1305
1306+ def test_activate_email_token_entry_clicks_verify_token(self):
1307+ """Activating any entry generates a connect attempt."""
1308+ self.ui.verify_token_button.connect('clicked', self._set_called)
1309+ self.ui.email_token_entry.activate()
1310+ self.assertEqual(self._called, ((self.ui.verify_token_button,), {}))
1311+
1312+ def test_verify_token_button_does_nothing_if_clicked_but_disabled(self):
1313+ """The email token can only be submitted if the button is sensitive."""
1314+ self.patch(self.ui.email_token_entry, 'get_text', self._set_called)
1315+
1316+ self.ui.verify_token_button.set_sensitive(False)
1317+ self.ui.verify_token_button.clicked()
1318+ self.assertFalse(self._called)
1319+
1320+ self.ui.verify_token_button.set_sensitive(True)
1321+ self.ui.verify_token_button.clicked()
1322+ self.assertTrue(self._called)
1323+
1324+
1325+class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
1326+ """Test suite for the user registration (verify email page)."""
1327+
1328+ def setUp(self):
1329+ """Init."""
1330+ super(VerifyEmailValidationTestCase, self).setUp()
1331+ self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
1332+
1333+ def test_warning_is_shown_if_empty_email_token(self):
1334+ """A warning message is shown if email token is empty."""
1335+ self.ui.email_token_entry.set_text('')
1336+
1337+ self.ui.verify_token_button.clicked()
1338+
1339+ self.assert_correct_entry_warning(self.ui.email_token_entry,
1340+ self.ui.FIELD_REQUIRED)
1341+ self.assertNotIn('validate_email', self.ui.backend._called)
1342+
1343+ def test_no_warning_messages_if_valid_data(self):
1344+ """No warning messages are shown if the data is valid."""
1345+ # this will certainly NOT generate warnings
1346+ self.click_verify_email_with_valid_data()
1347+
1348+ self.assert_warnings_visibility()
1349+
1350+ def test_no_warning_messages_if_valid_data_after_invalid_data(self):
1351+ """No warnings if the data is valid (with prior invalid data)."""
1352+ # this will certainly generate warnings
1353+ self.ui.verify_token_button.clicked()
1354+
1355+ # this will certainly NOT generate warnings
1356+ self.click_verify_email_with_valid_data()
1357+
1358+ self.assert_warnings_visibility()
1359+
1360
1361 class RegistrationValidationTestCase(UbuntuSSOClientTestCase):
1362 """Test suite for the user registration validations."""
1363
1364+ def setUp(self):
1365+ """Init."""
1366+ super(RegistrationValidationTestCase, self).setUp()
1367+ self.ui.join_ok_button.set_sensitive(True)
1368+
1369 def test_warning_is_shown_if_name_empty(self):
1370 """A warning message is shown if name is empty."""
1371 self.ui.name_entry.set_text('')
1372
1373- self.ui.join_ok_button.clicked() # submit form
1374+ self.ui.join_ok_button.clicked()
1375
1376 self.assert_correct_entry_warning(self.ui.name_entry,
1377 self.ui.FIELD_REQUIRED)
1378@@ -953,7 +1121,7 @@
1379 self.ui.email1_entry.set_text('')
1380 self.ui.email2_entry.set_text('')
1381
1382- self.ui.join_ok_button.clicked() # submit form
1383+ self.ui.join_ok_button.clicked()
1384
1385 self.assert_correct_entry_warning(self.ui.email1_entry,
1386 self.ui.FIELD_REQUIRED)
1387@@ -966,7 +1134,7 @@
1388 self.ui.email1_entry.set_text(EMAIL)
1389 self.ui.email2_entry.set_text(EMAIL * 2)
1390
1391- self.ui.join_ok_button.clicked() # submit form
1392+ self.ui.join_ok_button.clicked()
1393
1394 self.assert_correct_entry_warning(self.ui.email1_entry,
1395 self.ui.EMAIL_MISMATCH)
1396@@ -979,7 +1147,7 @@
1397 self.ui.email1_entry.set_text('q')
1398 self.ui.email2_entry.set_text('q')
1399
1400- self.ui.join_ok_button.clicked() # submit form
1401+ self.ui.join_ok_button.clicked()
1402
1403 self.assert_correct_entry_warning(self.ui.email1_entry,
1404 self.ui.EMAIL_INVALID)
1405@@ -1000,7 +1168,7 @@
1406 self.ui.password1_entry.set_text(PASSWORD)
1407 self.ui.password2_entry.set_text(PASSWORD * 2)
1408
1409- self.ui.join_ok_button.clicked() # submit form
1410+ self.ui.join_ok_button.clicked()
1411
1412 self.assert_correct_entry_warning(self.ui.password1_entry,
1413 self.ui.PASSWORD_MISMATCH)
1414@@ -1015,7 +1183,7 @@
1415 self.ui.password1_entry.set_text(w)
1416 self.ui.password2_entry.set_text(w)
1417
1418- self.ui.join_ok_button.clicked() # submit form
1419+ self.ui.join_ok_button.clicked()
1420
1421 self.assert_correct_entry_warning(self.ui.password1_entry,
1422 self.ui.PASSWORD_TOO_WEAK)
1423@@ -1028,7 +1196,7 @@
1424 # don't agree to TC
1425 self.ui.yes_to_tc_checkbutton.set_active(False)
1426
1427- self.ui.join_ok_button.clicked() # submit form
1428+ self.ui.join_ok_button.clicked()
1429
1430 self.assert_correct_label_warning(self.ui.tc_warning_label,
1431 self.ui.TC_NOT_ACCEPTED)
1432@@ -1039,18 +1207,18 @@
1433 # captcha solution will be empty
1434 self.ui.captcha_solution_entry.set_text('')
1435
1436- self.ui.join_ok_button.clicked() # submit form
1437+ self.ui.join_ok_button.clicked()
1438
1439 self.assert_correct_entry_warning(self.ui.captcha_solution_entry,
1440 self.ui.FIELD_REQUIRED)
1441 self.assertNotIn('register_user', self.ui.backend._called)
1442
1443- def test_no_warning_messages_if_valid_data_on_enter_details(self):
1444+ def test_no_warning_messages_if_valid_data(self):
1445 """No warning messages are shown if the data is valid."""
1446 # this will certainly NOT generate warnings
1447 self.click_join_with_valid_data()
1448
1449- self.assert_warnings_visibility(visible=False)
1450+ self.assert_warnings_visibility()
1451
1452 def test_no_warning_messages_if_valid_data_after_invalid_data(self):
1453 """No warnings if the data is valid (with prior invalid data)."""
1454@@ -1060,7 +1228,7 @@
1455 # this will certainly NOT generate warnings
1456 self.click_join_with_valid_data()
1457
1458- self.assert_warnings_visibility(visible=False)
1459+ self.assert_warnings_visibility()
1460
1461
1462 class LoginTestCase(UbuntuSSOClientTestCase):
1463@@ -1136,23 +1304,58 @@
1464 def test_on_login_error_morphs_to_login_page(self):
1465 """On user login error, the previous page is shown."""
1466 self.click_connect_with_valid_data()
1467- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1468+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1469 self.assert_pages_visibility(login=True)
1470
1471 def test_on_login_error_a_warning_is_shown(self):
1472 """On user login error, a warning is shown with proper wording."""
1473 self.click_connect_with_valid_data()
1474- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1475+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1476 self.assert_correct_label_warning(self.ui.warning_label,
1477 self.ui.UNKNOWN_ERROR)
1478
1479+ def test_specific_errors_from_backend_are_shown(self):
1480+ """Specific errors from backend are used."""
1481+ error = {'errtype': 'AuthenticationError',
1482+ 'message': 'We\'re so doomed.',
1483+ '__all__': 'We all are gonna die.'}
1484+
1485+ self.ui.on_login_error(app_name=APP_NAME, error=error)
1486+
1487+ expected = '\n'.join((error['__all__'], error['message']))
1488+ self.assert_correct_label_warning(self.ui.warning_label, expected)
1489+
1490 def test_back_to_registration_hides_warning(self):
1491 """After user login error, warning is hidden when clicking 'Back'."""
1492 self.click_connect_with_valid_data()
1493- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1494+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1495 self.ui.login_back_button.clicked()
1496 self.assertFalse(self.ui.warning_label.get_property('visible'))
1497
1498+ def test_activate_email_entry_clicks_login(self):
1499+ """Activating any entry generates a connect attempt."""
1500+ self.ui.login_ok_button.connect('clicked', self._set_called)
1501+ self.ui.login_email_entry.activate()
1502+ self.assertEqual(self._called, ((self.ui.login_ok_button,), {}))
1503+
1504+ def test_activate_password_entry_clicks_login(self):
1505+ """Activating any entry generates a connect attempt."""
1506+ self.ui.login_ok_button.connect('clicked', self._set_called)
1507+ self.ui.login_password_entry.activate()
1508+ self.assertEqual(self._called, ((self.ui.login_ok_button,), {}))
1509+
1510+ def test_login_ok_button_does_nothing_if_clicked_but_disabled(self):
1511+ """The join form can only be submitted if the button is sensitive."""
1512+ self.patch(self.ui.login_email_entry, 'get_text', self._set_called)
1513+
1514+ self.ui.login_ok_button.set_sensitive(False)
1515+ self.ui.login_ok_button.clicked()
1516+ self.assertFalse(self._called)
1517+
1518+ self.ui.login_ok_button.set_sensitive(True)
1519+ self.ui.login_ok_button.clicked()
1520+ self.assertTrue(self._called)
1521+
1522
1523 class LoginValidationTestCase(UbuntuSSOClientTestCase):
1524 """Test suite for the user login validation."""
1525@@ -1166,7 +1369,7 @@
1526 """A warning message is shown if email is empty."""
1527 self.ui.login_email_entry.set_text('')
1528
1529- self.ui.login_ok_button.clicked() # submit form
1530+ self.ui.login_ok_button.clicked()
1531
1532 self.assert_correct_entry_warning(self.ui.login_email_entry,
1533 self.ui.FIELD_REQUIRED)
1534@@ -1176,7 +1379,7 @@
1535 """A warning message is shown if email is invalid."""
1536 self.ui.login_email_entry.set_text('q')
1537
1538- self.ui.login_ok_button.clicked() # submit form
1539+ self.ui.login_ok_button.clicked()
1540
1541 self.assert_correct_entry_warning(self.ui.login_email_entry,
1542 self.ui.EMAIL_INVALID)
1543@@ -1186,7 +1389,7 @@
1544 """A warning message is shown if password is empty."""
1545 self.ui.login_password_entry.set_text('')
1546
1547- self.ui.login_ok_button.clicked() # submit form
1548+ self.ui.login_ok_button.clicked()
1549
1550 self.assert_correct_entry_warning(self.ui.login_password_entry,
1551 self.ui.FIELD_REQUIRED)
1552@@ -1197,7 +1400,7 @@
1553 # this will certainly NOT generate warnings
1554 self.click_connect_with_valid_data()
1555
1556- self.assert_warnings_visibility(visible=False)
1557+ self.assert_warnings_visibility()
1558
1559 def test_no_warning_messages_if_valid_data_after_invalid_data(self):
1560 """No warnings if the data is valid (with prior invalid data)."""
1561@@ -1207,7 +1410,7 @@
1562 # this will certainly NOT generate warnings
1563 self.click_connect_with_valid_data()
1564
1565- self.assert_warnings_visibility(visible=False)
1566+ self.assert_warnings_visibility()
1567
1568
1569 class ResetPasswordTestCase(UbuntuSSOClientTestCase):
1570@@ -1301,11 +1504,86 @@
1571
1572 def test_on_password_reset_error_shows_login_page(self):
1573 """When reset token wasn't successfuly sent the login page is shown."""
1574- self.ui.on_password_reset_error(app_name=APP_NAME, error=ERROR)
1575+ self.ui.on_password_reset_error(app_name=APP_NAME, error=self.ERROR)
1576 self.assert_correct_label_warning(self.ui.warning_label,
1577 self.ui.UNKNOWN_ERROR)
1578 self.assert_pages_visibility(login=True)
1579
1580+ def test_specific_errors_from_backend_are_shown(self):
1581+ """Specific errors from backend are used."""
1582+ error = {'errtype': 'ResetPasswordTokenError',
1583+ 'message': 'We\'re so doomed.',
1584+ '__all__': 'We all are gonna die.'}
1585+
1586+ self.ui.on_password_reset_error(app_name=APP_NAME, error=error)
1587+
1588+ expected = '\n'.join((error['__all__'], error['message']))
1589+ self.assert_correct_label_warning(self.ui.warning_label, expected)
1590+
1591+ def test_activate_reset_email_entry_clicks_login(self):
1592+ """Activating any entry generates a connect attempt."""
1593+ self.ui.request_password_token_ok_button.connect('clicked',
1594+ self._set_called)
1595+ self.ui.reset_email_entry.activate()
1596+ self.assertEqual(self._called,
1597+ ((self.ui.request_password_token_ok_button,), {}))
1598+
1599+ def test_ok_button_does_nothing_if_clicked_but_disabled(self):
1600+ """The password token can be requested if the button is sensitive."""
1601+ self.patch(self.ui.reset_email_entry, 'get_text', self._set_called)
1602+
1603+ self.ui.request_password_token_ok_button.set_sensitive(False)
1604+ self.ui.request_password_token_ok_button.clicked()
1605+ self.assertFalse(self._called)
1606+
1607+ self.ui.request_password_token_ok_button.set_sensitive(True)
1608+ self.ui.request_password_token_ok_button.clicked()
1609+ self.assertTrue(self._called)
1610+
1611+
1612+class ResetPasswordValidationTestCase(UbuntuSSOClientTestCase):
1613+ """Test suite for the password reset validations."""
1614+
1615+ def test_warning_is_shown_if_empty_email(self):
1616+ """A warning message is shown if emails are empty."""
1617+ self.ui.reset_email_entry.set_text(' ')
1618+
1619+ self.ui.request_password_token_ok_button.set_sensitive(True)
1620+ self.ui.request_password_token_ok_button.clicked()
1621+
1622+ self.assert_correct_entry_warning(self.ui.reset_email_entry,
1623+ self.ui.FIELD_REQUIRED)
1624+ self.assertNotIn('request_password_reset_token',
1625+ self.ui.backend._called)
1626+
1627+ def test_warning_is_shown_if_invalid_email(self):
1628+ """A warning message is shown if email is invalid."""
1629+ self.ui.reset_email_entry.set_text('q')
1630+
1631+ self.ui.request_password_token_ok_button.clicked()
1632+
1633+ self.assert_correct_entry_warning(self.ui.reset_email_entry,
1634+ self.ui.EMAIL_INVALID)
1635+ self.assertNotIn('request_password_reset_token',
1636+ self.ui.backend._called)
1637+
1638+ def test_no_warning_messages_if_valid_data(self):
1639+ """No warning messages are shown if the data is valid."""
1640+ # this will certainly NOT generate warnings
1641+ self.click_request_password_token_with_valid_data()
1642+
1643+ self.assert_warnings_visibility()
1644+
1645+ def test_no_warning_messages_if_valid_data_after_invalid_data(self):
1646+ """No warnings if the data is valid (with prior invalid data)."""
1647+ # this will certainly generate warnings
1648+ self.ui.request_password_token_ok_button.clicked()
1649+
1650+ # this will certainly NOT generate warnings
1651+ self.click_request_password_token_with_valid_data()
1652+
1653+ self.assert_warnings_visibility()
1654+
1655
1656 class SetNewPasswordTestCase(UbuntuSSOClientTestCase):
1657 """Test suite for setting a new password functionality."""
1658@@ -1358,11 +1636,123 @@
1659
1660 def test_on_password_change_error_shows_login_page(self):
1661 """When password wasn't changed the reset password page is shown."""
1662- self.ui.on_password_change_error(app_name=APP_NAME, error=ERROR)
1663+ self.ui.on_password_change_error(app_name=APP_NAME, error=self.ERROR)
1664 self.assert_correct_label_warning(self.ui.warning_label,
1665 self.ui.UNKNOWN_ERROR)
1666 self.assert_pages_visibility(request_password_token=True)
1667
1668+ def test_specific_errors_from_backend_are_shown(self):
1669+ """Specific errors from backend are used."""
1670+ error = {'errtype': 'NewPasswordError',
1671+ 'message': 'We\'re so doomed.',
1672+ '__all__': 'We all are gonna die.'}
1673+
1674+ self.ui.on_password_change_error(app_name=APP_NAME, error=error)
1675+
1676+ expected = '\n'.join((error['__all__'], error['message']))
1677+ self.assert_correct_label_warning(self.ui.warning_label, expected)
1678+
1679+ def test_activate_reset_code_entry_clicks_set_new_password(self):
1680+ """Activating any entry generates a connect attempt."""
1681+ self.ui.set_new_password_ok_button.connect('clicked', self._set_called)
1682+ self.ui.reset_code_entry.activate()
1683+ self.assertEqual(self._called,
1684+ ((self.ui.set_new_password_ok_button,), {}))
1685+
1686+ def test_activate_password_entry_clicks_set_new_password(self):
1687+ """Activating any entry generates a connect attempt."""
1688+ self.ui.set_new_password_ok_button.connect('clicked', self._set_called)
1689+ self.ui.reset_password1_entry.activate()
1690+ self.assertEqual(self._called,
1691+ ((self.ui.set_new_password_ok_button,), {}))
1692+
1693+ self._called = False
1694+ self.ui.reset_password2_entry.activate()
1695+ self.assertEqual(self._called,
1696+ ((self.ui.set_new_password_ok_button,), {}))
1697+
1698+ def test_ok_button_does_nothing_if_clicked_but_disabled(self):
1699+ """The new passwrd can only be set if the button is sensitive."""
1700+ self.patch(self.ui.reset_code_entry, 'get_text', self._set_called)
1701+
1702+ self.ui.set_new_password_ok_button.set_sensitive(False)
1703+ self.ui.set_new_password_ok_button.clicked()
1704+ self.assertFalse(self._called)
1705+
1706+ self.ui.set_new_password_ok_button.set_sensitive(True)
1707+ self.ui.set_new_password_ok_button.clicked()
1708+ self.assertTrue(self._called)
1709+
1710+
1711+class SetNewPasswordValidationTestCase(UbuntuSSOClientTestCase):
1712+ """Test suite for validations for setting a new password."""
1713+
1714+ def test_warning_is_shown_if_reset_code_empty(self):
1715+ """A warning message is shown if reset_code is empty."""
1716+ self.ui.reset_code_entry.set_text('')
1717+
1718+ self.ui.set_new_password_ok_button.set_sensitive(True)
1719+ self.ui.set_new_password_ok_button.clicked()
1720+
1721+ self.assert_correct_entry_warning(self.ui.reset_code_entry,
1722+ self.ui.FIELD_REQUIRED)
1723+ self.assertNotIn('set_new_password', self.ui.backend._called)
1724+
1725+ def test_password_help_is_always_shown(self):
1726+ """Password help text is correctly displayed."""
1727+ visible = self.ui.reset_password_help_label.get_property('visible')
1728+ self.assertTrue(visible, 'password help text is visible.')
1729+ self.assertEqual(self.ui.reset_password_help_label.get_text(),
1730+ self.ui.PASSWORD_HELP)
1731+ self.assertNotIn('set_new_password', self.ui.backend._called)
1732+
1733+ def test_warning_is_shown_if_password_mismatch(self):
1734+ """A warning message is shown if password doesn't match."""
1735+ self.ui.reset_password1_entry.set_text(PASSWORD)
1736+ self.ui.reset_password2_entry.set_text(PASSWORD * 2)
1737+
1738+ self.ui.set_new_password_ok_button.set_sensitive(True)
1739+ self.ui.set_new_password_ok_button.clicked()
1740+
1741+ self.assert_correct_entry_warning(self.ui.reset_password1_entry,
1742+ self.ui.PASSWORD_MISMATCH)
1743+ self.assert_correct_entry_warning(self.ui.reset_password2_entry,
1744+ self.ui.PASSWORD_MISMATCH)
1745+ self.assertNotIn('set_new_password', self.ui.backend._called)
1746+
1747+ def test_warning_is_shown_if_password_too_weak(self):
1748+ """A warning message is shown if password is too weak."""
1749+ # password will match but will be too weak
1750+ for w in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
1751+ self.ui.reset_password1_entry.set_text(w)
1752+ self.ui.reset_password2_entry.set_text(w)
1753+
1754+ self.ui.set_new_password_ok_button.set_sensitive(True)
1755+ self.ui.set_new_password_ok_button.clicked()
1756+
1757+ self.assert_correct_entry_warning(self.ui.reset_password1_entry,
1758+ self.ui.PASSWORD_TOO_WEAK)
1759+ self.assert_correct_entry_warning(self.ui.reset_password2_entry,
1760+ self.ui.PASSWORD_TOO_WEAK)
1761+ self.assertNotIn('set_new_password', self.ui.backend._called)
1762+
1763+ def test_no_warning_messages_if_valid_data(self):
1764+ """No warning messages are shown if the data is valid."""
1765+ # this will certainly NOT generate warnings
1766+ self.click_set_new_password_with_valid_data()
1767+
1768+ self.assert_warnings_visibility()
1769+
1770+ def test_no_warning_messages_if_valid_data_after_invalid_data(self):
1771+ """No warnings if the data is valid (with prior invalid data)."""
1772+ # this will certainly generate warnings
1773+ self.ui.set_new_password_ok_button.clicked()
1774+
1775+ # this will certainly NOT generate warnings
1776+ self.click_set_new_password_with_valid_data()
1777+
1778+ self.assert_warnings_visibility()
1779+
1780
1781 class DbusTestCase(UbuntuSSOClientTestCase):
1782 """Test suite for the dbus calls."""
1783@@ -1408,6 +1798,10 @@
1784 """The login_ok_button has the focus."""
1785 self.assertTrue(self.ui.login_ok_button.is_focus())
1786
1787+ def test_help_text_is_used(self):
1788+ """The passed help_text is used."""
1789+ self.assertEqual(self.ui.help_label.get_text(), HELP_TEXT)
1790+
1791
1792 class SignalsTestCase(UbuntuSSOClientTestCase):
1793
1794@@ -1438,9 +1832,10 @@
1795
1796 def test_on_user_registration_error_proper_signal_is_emitted(self):
1797 """On UserRegistrationError, 'registration-failed' signal is sent."""
1798- self.ui.on_user_registration_error(app_name=APP_NAME, error=ERROR)
1799+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
1800 self.ui.on_close_clicked()
1801- self.assertEqual((self.ui.window, (APP_NAME, ERROR), {}),
1802+ expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
1803+ self.assertEqual(expected,
1804 self._called[gui.SIG_REGISTRATION_FAILED])
1805
1806 def test_on_email_validated_proper_signals_is_emitted(self):
1807@@ -1452,10 +1847,11 @@
1808
1809 def test_on_email_validation_error_proper_signals_is_emitted(self):
1810 """On EmailValidationError, 'registration-failed' signal is sent."""
1811- self.ui.on_email_validation_error(app_name=APP_NAME, error=ERROR)
1812+ self.ui.on_email_validation_error(app_name=APP_NAME, error=self.ERROR)
1813 self.ui.on_close_clicked()
1814- self.assertEqual((self.ui.window, (APP_NAME, ERROR), {}),
1815- self._called[gui.SIG_REGISTRATION_FAILED])
1816+ expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
1817+ self.assertEqual(expected,
1818+ self._called[gui.SIG_REGISTRATION_FAILED])
1819
1820 def test_on_logged_in_proper_signals_is_emitted(self):
1821 """On LoggedIn, 'login-succeeded' signal is sent."""
1822@@ -1467,15 +1863,16 @@
1823 def test_on_login_error_proper_signals_is_emitted(self):
1824 """On LoginError, 'login-failed' signal is sent."""
1825 self.click_connect_with_valid_data()
1826- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1827+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1828 self.ui.on_close_clicked()
1829- self.assertEqual((self.ui.window, (APP_NAME, ERROR), {}),
1830- self._called[gui.SIG_LOGIN_FAILED])
1831+ expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
1832+ self.assertEqual(expected,
1833+ self._called[gui.SIG_LOGIN_FAILED])
1834
1835 def test_registration_successfull_even_if_prior_registration_error(self):
1836 """Only one signal is sent with the final outcome."""
1837 self.click_join_with_valid_data()
1838- self.ui.on_user_registration_error(app_name=APP_NAME, error=ERROR)
1839+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
1840 self.click_join_with_valid_data()
1841 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
1842 self.ui.on_close_clicked()
1843@@ -1486,7 +1883,7 @@
1844 def test_login_successfull_even_if_prior_login_error(self):
1845 """Only one signal is sent with the final outcome."""
1846 self.click_connect_with_valid_data()
1847- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1848+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1849 self.click_connect_with_valid_data()
1850 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
1851 self.ui.on_close_clicked()
1852@@ -1497,7 +1894,7 @@
1853 def test_user_cancelation_even_if_prior_registration_error(self):
1854 """Only one signal is sent with the final outcome."""
1855 self.click_join_with_valid_data()
1856- self.ui.on_user_registration_error(app_name=APP_NAME, error=ERROR)
1857+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
1858 self.ui.join_cancel_button.clicked()
1859
1860 self.assertEqual(len(self._called), 1)
1861@@ -1506,7 +1903,7 @@
1862 def test_user_cancelation_even_if_prior_login_error(self):
1863 """Only one signal is sent with the final outcome."""
1864 self.click_connect_with_valid_data()
1865- self.ui.on_login_error(app_name=APP_NAME, error=ERROR)
1866+ self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
1867 self.ui.login_cancel_button.clicked()
1868
1869 self.assertEqual(len(self._called), 1)
1870
1871=== modified file 'ubuntu_sso/tests/test_keyring.py'
1872--- ubuntu_sso/tests/test_keyring.py 2010-08-19 16:02:09 +0000
1873+++ ubuntu_sso/tests/test_keyring.py 2010-08-27 22:32:43 +0000
1874@@ -17,24 +17,39 @@
1875 # with this program. If not, see <http://www.gnu.org/licenses/>.
1876 """Tests for the keyring.py module."""
1877
1878+import itertools
1879+import socket
1880+import urllib
1881+
1882 import gnomekeyring
1883 from twisted.trial.unittest import TestCase
1884
1885 from ubuntu_sso import keyring
1886
1887
1888-class MockInfoSync(object):
1889- """A mock object that represents a fake key."""
1890-
1891- def __init__(self, (key, key_name, secret)):
1892+def build_fake_gethostname(fake_hostname):
1893+ """Return a fake hostname getter."""
1894+ return lambda *a: fake_hostname
1895+
1896+
1897+class MockKeyringItem(object):
1898+ """A mock object that fakes an item found in a keyring search."""
1899+
1900+ def __init__(self, item_id, keyring, attributes, secret):
1901 """Initialize this instance."""
1902- self.key = key
1903- self.key_name = key_name
1904+ self.item_id = item_id
1905+ self.keyring = keyring
1906+ self.attributes = attributes
1907 self.secret = secret
1908
1909- def get_display_name(self):
1910- """Return info on the mocked object."""
1911- return self.key_name
1912+ def matches(self, search_attr):
1913+ """See if this item matches a given search."""
1914+ for k, v in search_attr.items():
1915+ if k not in self.attributes:
1916+ return False
1917+ if self.attributes[k] != v:
1918+ return False
1919+ return True
1920
1921
1922 class MockGnomeKeyring(object):
1923@@ -43,28 +58,33 @@
1924 def __init__(self):
1925 """Initialize this instance."""
1926 self.keyrings = set()
1927- self.store = []
1928+ self.id_counter = itertools.count()
1929+ self.store = {}
1930 self.deleted = []
1931
1932+ def _get_next_id(self):
1933+ """Return the next keyring id."""
1934+ return self.id_counter.next()
1935+
1936 def item_create_sync(self, keyring_name, item_type, key_name, attr,
1937 secret, update_if_exists):
1938- """Sets a value in the keyring."""
1939- # we'll use the attr as the dict key, so make it a tuple
1940- key = tuple(attr.items())
1941- self.store.append((key, key_name, secret))
1942+ """Add a key to a keyring."""
1943+ new_id = self._get_next_id()
1944+ i = MockKeyringItem(new_id, keyring_name, attr, secret)
1945+ self.store[new_id] = i
1946
1947 def item_delete_sync(self, keyring_name, item_id):
1948- """Makes a list of deleted items."""
1949- key, key_name, secret = self.store.pop(item_id)
1950- self.deleted.append(key_name)
1951-
1952- def list_item_ids_sync(self, keyring_name):
1953- """Return a list of ids for all the items."""
1954- return [n for n, v in enumerate(self.store)]
1955-
1956- def item_get_info_sync(self, keyring_name, item_id):
1957- """Return an info sync object for the item."""
1958- return MockInfoSync(self.store[item_id])
1959+ """Delete a key from a keyring, and keep a list of deleted keys."""
1960+ item = self.store.pop(item_id)
1961+ assert keyring_name == item.keyring
1962+ self.deleted.append(item)
1963+
1964+ def find_items_sync(self, item_type, attr):
1965+ """Find all keys that match the given attributes."""
1966+ items = [i for i in self.store.values() if i.matches(attr)]
1967+ if len(items) == 0:
1968+ raise gnomekeyring.NoMatchError()
1969+ return items
1970
1971 def list_keyring_names_sync(self):
1972 """The keyring you are looking for may be here."""
1973@@ -79,6 +99,32 @@
1974 return True
1975
1976
1977+class TestTokenNameBuilder(TestCase):
1978+ """Test the method that builds the token name."""
1979+
1980+ def test_get_simple_token_name(self):
1981+ """A simple token name is built right."""
1982+ sample_app_name = "UbuntuTwo"
1983+ sample_hostname = "Darkstar"
1984+ expected_result = "UbuntuTwo - Darkstar"
1985+
1986+ fake_gethostname = build_fake_gethostname(sample_hostname)
1987+ self.patch(socket, "gethostname", fake_gethostname)
1988+ result = keyring.get_token_name(sample_app_name)
1989+ self.assertEqual(result, expected_result)
1990+
1991+ def test_get_complex_token_name(self):
1992+ """A complex token name is built right too."""
1993+ sample_app_name = "Ubuntu Eleven"
1994+ sample_hostname = "Mate+Cocido"
1995+ expected_result = "Ubuntu%20Eleven - Mate%2BCocido"
1996+
1997+ fake_gethostname = build_fake_gethostname(sample_hostname)
1998+ self.patch(socket, "gethostname", fake_gethostname)
1999+ result = keyring.get_token_name(sample_app_name)
2000+ self.assertEqual(result, expected_result)
2001+
2002+
2003 class TestKeyring(TestCase):
2004 """Test the gnome keyring related functions."""
2005 def setUp(self):
2006@@ -89,11 +135,10 @@
2007 self.patch(gnomekeyring, "create_sync", self.mgk.create_sync)
2008 self.patch(gnomekeyring, "list_keyring_names_sync",
2009 self.mgk.list_keyring_names_sync)
2010- self.patch(gnomekeyring, "list_item_ids_sync",
2011- self.mgk.list_item_ids_sync)
2012- self.patch(gnomekeyring, "item_get_info_sync",
2013- self.mgk.item_get_info_sync)
2014+ self.patch(gnomekeyring, "find_items_sync", self.mgk.find_items_sync)
2015 self.patch(gnomekeyring, "item_delete_sync", self.mgk.item_delete_sync)
2016+ fake_gethostname = build_fake_gethostname("darkstar")
2017+ self.patch(socket, "gethostname", fake_gethostname)
2018
2019 def test_set_ubuntusso(self):
2020 """Test that the set method does not erase previous keys."""
2021@@ -113,3 +158,54 @@
2022
2023 self.assertEqual(len(self.mgk.store), 0)
2024 self.assertEqual(len(self.mgk.deleted), 1)
2025+
2026+ def test_get_credentials(self):
2027+ """Test that credentials are properly retrieved."""
2028+ sample_creds = {"name": "sample creds name"}
2029+ keyring.Keyring("appname").set_ubuntusso_attr(sample_creds)
2030+
2031+ result = keyring.Keyring("appname").get_ubuntusso_attr()
2032+ self.assertEqual(result, sample_creds)
2033+
2034+ def test_get_old_cred_found(self):
2035+ """The method returns a new set of creds if old creds are found."""
2036+ sample_oauth_token = "sample oauth token"
2037+ sample_oauth_secret = "sample oauth secret"
2038+ old_creds = {
2039+ "oauth_token": sample_oauth_token,
2040+ "oauth_token_secret": sample_oauth_secret,
2041+ }
2042+ secret = urllib.urlencode(old_creds)
2043+ self.mgk.item_create_sync(keyring.U1_APP_NAME, None,
2044+ keyring.Keyring.KEYRING_NAME,
2045+ keyring.U1_KEY_ATTR,
2046+ secret, True)
2047+
2048+ result = keyring.Keyring(keyring.U1_APP_NAME).get_ubuntusso_attr()
2049+
2050+ self.assertIn("token", result)
2051+ self.assertEqual(result["token"], sample_oauth_token)
2052+ self.assertIn("token_secret", result)
2053+ self.assertEqual(result["token_secret"], sample_oauth_secret)
2054+
2055+ def test_get_old_cred_found_but_not_asked_for(self):
2056+ """Returns None if old creds are present but the appname is not U1"""
2057+ sample_oauth_token = "sample oauth token"
2058+ sample_oauth_secret = "sample oauth secret"
2059+ old_creds = {
2060+ "oauth_token": sample_oauth_token,
2061+ "oauth_token_secret": sample_oauth_secret,
2062+ }
2063+ secret = urllib.urlencode(old_creds)
2064+ self.mgk.item_create_sync(keyring.U1_APP_NAME, None,
2065+ keyring.Keyring.KEYRING_NAME,
2066+ keyring.U1_KEY_ATTR,
2067+ secret, True)
2068+
2069+ result = keyring.Keyring("Software Center").get_ubuntusso_attr()
2070+ self.assertEqual(result, None)
2071+
2072+ def test_get_old_cred_not_found(self):
2073+ """The method returns None if no old nor new credentials found."""
2074+ result = keyring.Keyring(keyring.U1_APP_NAME).get_ubuntusso_attr()
2075+ self.assertEqual(result, None)
2076
2077=== modified file 'ubuntu_sso/tests/test_main.py'
2078--- ubuntu_sso/tests/test_main.py 2010-08-26 01:07:41 +0000
2079+++ ubuntu_sso/tests/test_main.py 2010-08-27 22:32:43 +0000
2080@@ -31,19 +31,19 @@
2081 from twisted.internet.defer import Deferred
2082 from twisted.trial.unittest import TestCase
2083
2084+import ubuntu_sso.keyring
2085 import ubuntu_sso.main
2086
2087 from contrib.testing.testcase import MementoHandler
2088 from ubuntu_sso import config, gui
2089+from ubuntu_sso.keyring import get_token_name, U1_APP_NAME
2090 from ubuntu_sso.main import (
2091 AuthenticationError, BadRealmError, blocking, EmailTokenError,
2092- except_to_errdict, get_token_name,
2093- InvalidEmailError, InvalidPasswordError,
2094+ except_to_errdict, InvalidEmailError, InvalidPasswordError,
2095 keyring_get_credentials, keyring_store_credentials, logger,
2096- LoginProcessor, NewPasswordError, OLD_KEY_NAME, PING_URL,
2097+ LoginProcessor, NewPasswordError, PING_URL,
2098 RegistrationError, ResetPasswordTokenError,
2099- SSOCredentials, SSOLogin, SSOLoginProcessor,
2100- U1_APP_NAME)
2101+ SSOCredentials, SSOLogin, SSOLoginProcessor)
2102
2103
2104 APP_NAME = 'The Coolest App Ever'
2105@@ -55,6 +55,7 @@
2106 "Can't reset password for this account"
2107 RESET_TOKEN_INVALID_CONTENT = "AuthToken matching query does not exist."
2108 EMAIL = 'test@example.com'
2109+EMAIL_ALREADY_REGISTERED = 'a@example.com'
2110 EMAIL_TOKEN = 'B2Pgtf'
2111 HELP = 'help text'
2112 PASSWORD = 'be4tiFul'
2113@@ -103,7 +104,10 @@
2114
2115 def register(self, email, password, captcha_id, captcha_solution):
2116 """Fake registration. Return a fix result."""
2117- if captcha_id is None and captcha_solution is None:
2118+ if email == EMAIL_ALREADY_REGISTERED:
2119+ return {'status': 'error',
2120+ 'errors': {'email': 'Email already registered'}}
2121+ elif captcha_id is None and captcha_solution is None:
2122 return STATUS_UNKNOWN
2123 elif captcha_id != CAPTCHA_ID or captcha_solution != CAPTCHA_SOLUTION:
2124 return STATUS_ERROR
2125@@ -147,7 +151,10 @@
2126 """Fake the email validation. Return a fix result."""
2127 if email_token is None:
2128 return STATUS_EMAIL_UNKNOWN
2129- if email_token != EMAIL_TOKEN:
2130+ elif email_token == EMAIL_ALREADY_REGISTERED:
2131+ return {'status': 'error',
2132+ 'errors': {'email': 'Email already registered'}}
2133+ elif email_token != EMAIL_TOKEN:
2134 return STATUS_EMAIL_ERROR
2135 else:
2136 return STATUS_EMAIL_OK
2137@@ -231,7 +238,19 @@
2138 failure = self.assertRaises(RegistrationError,
2139 self.processor.register_user,
2140 **self.register_kwargs)
2141- self.assertIn(STATUS_ERROR['errors'], failure)
2142+ for k, v in failure.args[0].items():
2143+ self.assertIn(k, STATUS_ERROR['errors'])
2144+ self.assertEqual(v, "\n".join(STATUS_ERROR['errors'][k]))
2145+
2146+ def test_register_user_if_status_error_with_string_message(self):
2147+ """Proper error is raised if register fails."""
2148+ self.register_kwargs['email'] = EMAIL_ALREADY_REGISTERED
2149+ failure = self.assertRaises(RegistrationError,
2150+ self.processor.register_user,
2151+ **self.register_kwargs)
2152+ for k, v in failure.args[0].items():
2153+ self.assertIn(k, {'email': 'Email already registered'})
2154+ self.assertEqual(v, 'Email already registered')
2155
2156 def test_register_user_if_status_unknown(self):
2157 """Proper error is raised if register returns an unknown status."""
2158@@ -261,7 +280,7 @@
2159 """A email is succesfuy validated in the SSO server."""
2160 self.login_kwargs['email_token'] = EMAIL_TOKEN # valid email token
2161 result = self.processor.validate_email(**self.login_kwargs)
2162- self.assertEqual(EMAIL, result, 'email validation was successful.')
2163+ self.assertEqual(TOKEN, result, 'email validation was successful.')
2164
2165 def test_validate_email_if_status_error(self):
2166 """Proper error is raised if email validation fails."""
2167@@ -269,7 +288,19 @@
2168 failure = self.assertRaises(EmailTokenError,
2169 self.processor.validate_email,
2170 **self.login_kwargs)
2171- self.assertIn(STATUS_EMAIL_ERROR['errors'], failure)
2172+ for k, v in failure.args[0].items():
2173+ self.assertIn(k, STATUS_EMAIL_ERROR['errors'])
2174+ self.assertEqual(v, "\n".join(STATUS_EMAIL_ERROR['errors'][k]))
2175+
2176+ def test_validate_email_if_status_error_with_string_message(self):
2177+ """Proper error is raised if register fails."""
2178+ self.login_kwargs['email_token'] = EMAIL_ALREADY_REGISTERED
2179+ failure = self.assertRaises(EmailTokenError,
2180+ self.processor.validate_email,
2181+ **self.login_kwargs)
2182+ for k, v in failure.args[0].items():
2183+ self.assertIn(k, {'email': 'Email already registered'})
2184+ self.assertEqual(v, 'Email already registered')
2185
2186 def test_validate_email_if_status_unknown(self):
2187 """Proper error is raised if email validation returns unknown."""
2188@@ -413,8 +444,11 @@
2189 """Assert over token and app_name."""
2190 self.assertEqual(k, APP_NAME)
2191 self.assertEqual(v, TOKEN)
2192+ self.keyring_was_set = True
2193+ self.keyring_values = k, v
2194
2195 self.patch(ubuntu_sso.main, "keyring_store_credentials", ksc)
2196+ self.keyring_was_set = False
2197
2198 def tearDown(self):
2199 """Verify the mocking bus and shut it down."""
2200@@ -427,8 +461,12 @@
2201
2202 def fake_err_blocking(self, f, a, cb, eb):
2203 """A fake blocking function that fails."""
2204- f()
2205- eb(a, except_to_errdict(BlockingSampleException()))
2206+ try:
2207+ f()
2208+ except Exception, e:
2209+ eb(a, except_to_errdict(e))
2210+ else:
2211+ eb(a, except_to_errdict(BlockingSampleException()))
2212
2213 def test_creation(self):
2214 """Test that the object creation is successful."""
2215@@ -544,6 +582,7 @@
2216 def verify(app_name, result):
2217 self.assertEqual(result, EMAIL)
2218 self.assertEqual(app_name, APP_NAME)
2219+ self.assertTrue(self.keyring_was_set, "The keyring should be set")
2220 d.callback(result)
2221
2222 client = SSOLogin(self.mockbusname,
2223@@ -556,14 +595,20 @@
2224 def test_login_error(self):
2225 """Test that the login method fails as expected."""
2226 d = Deferred()
2227- self.create_mock_processor().login(EMAIL, PASSWORD, TOKEN_NAME)
2228- self.mocker.result(TOKEN)
2229+ self.mockprocessorclass = self.mocker.mock()
2230 self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
2231+
2232+ def fake_gtn(*args):
2233+ """A fake get_token_name that fails."""
2234+ raise BlockingSampleException()
2235+
2236+ self.patch(ubuntu_sso.main, "get_token_name", fake_gtn)
2237 self.mocker.replay()
2238
2239 def verify(app_name, errdict):
2240 self.assertEqual(app_name, APP_NAME)
2241 self.assertEqual(errdict["errtype"], "BlockingSampleException")
2242+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
2243 d.callback("Ok")
2244
2245 client = SSOLogin(self.mockbusname,
2246@@ -578,13 +623,14 @@
2247 d = Deferred()
2248 self.create_mock_processor().validate_email(EMAIL, PASSWORD,
2249 EMAIL_TOKEN, TOKEN_NAME)
2250- self.mocker.result(EMAIL)
2251+ self.mocker.result(TOKEN)
2252 self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking)
2253 self.mocker.replay()
2254
2255 def verify(app_name, result):
2256 self.assertEqual(result, EMAIL)
2257 self.assertEqual(app_name, APP_NAME)
2258+ self.assertTrue(self.keyring_was_set, "The keyring should be set")
2259 d.callback(result)
2260
2261 client = SSOLogin(self.mockbusname,
2262@@ -597,17 +643,20 @@
2263 def test_validate_email_error(self):
2264 """Test that the validate_email method fails as expected."""
2265 d = Deferred()
2266- expected_result = "expected result"
2267-
2268- self.create_mock_processor().validate_email(EMAIL, PASSWORD,
2269- EMAIL_TOKEN, TOKEN_NAME)
2270- self.mocker.result(expected_result)
2271+ self.mockprocessorclass = self.mocker.mock()
2272 self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
2273+
2274+ def fake_gtn(*args):
2275+ """A fake get_token_name that fails."""
2276+ raise BlockingSampleException()
2277+
2278+ self.patch(ubuntu_sso.main, "get_token_name", fake_gtn)
2279 self.mocker.replay()
2280
2281 def verify(app_name, errdict):
2282 self.assertEqual(app_name, APP_NAME)
2283 self.assertEqual(errdict["errtype"], "BlockingSampleException")
2284+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
2285 d.callback("Ok")
2286
2287 client = SSOLogin(self.mockbusname,
2288@@ -794,7 +843,6 @@
2289 e = TestExceptToErrdictException(*sample_args)
2290 result = except_to_errdict(e)
2291 self.assertEqual(result["errtype"], e.__class__.__name__)
2292- self.assertEqual(result["errargs"], repr(sample_args))
2293
2294
2295 class KeyringCredentialsTestCase(MockerTestCase):
2296@@ -838,58 +886,6 @@
2297 token = keyring_get_credentials(APP_NAME)
2298 self.assertEqual(token, None)
2299
2300- def test_keyring_get_old_cred_found(self):
2301- """The method returns a new set of creds if old creds are found."""
2302- sample_oauth_token = "sample oauth token"
2303- sample_oauth_secret = "sample oauth secret"
2304- old_creds = {
2305- "oauth_token": sample_oauth_token,
2306- "oauth_token_secret": sample_oauth_secret,
2307- }
2308-
2309- mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring")
2310- mockKeyringClass(U1_APP_NAME)
2311- mockKeyring = self.mocker.mock()
2312- self.mocker.result(mockKeyring)
2313- mockKeyring.get_ubuntusso_attr()
2314- self.mocker.result(None)
2315-
2316- mockKeyringClass(OLD_KEY_NAME)
2317- mockKeyring2 = self.mocker.mock()
2318- self.mocker.result(mockKeyring2)
2319- mockKeyring2.get_ubuntusso_attr()
2320- self.mocker.result(old_creds)
2321-
2322- self.mocker.replay()
2323-
2324- new_creds = keyring_get_credentials(U1_APP_NAME)
2325- self.assertIn("token", new_creds)
2326- self.assertEqual(new_creds["token"], sample_oauth_token)
2327- self.assertIn("token_secret", new_creds)
2328- self.assertEqual(new_creds["token_secret"], sample_oauth_secret)
2329- self.assertIn("token_name", new_creds)
2330- self.assertEqual(new_creds["token_name"], OLD_KEY_NAME)
2331-
2332- def test_keyring_get_old_cred_not_found(self):
2333- """The method returns None if no old nor new credentials found."""
2334- mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring")
2335- mockKeyringClass(U1_APP_NAME)
2336- mockKeyring = self.mocker.mock()
2337- self.mocker.result(mockKeyring)
2338- mockKeyring.get_ubuntusso_attr()
2339- self.mocker.result(None)
2340-
2341- mockKeyringClass(OLD_KEY_NAME)
2342- mockKeyring2 = self.mocker.mock()
2343- self.mocker.result(mockKeyring2)
2344- mockKeyring2.get_ubuntusso_attr()
2345- self.mocker.result(None)
2346-
2347- self.mocker.replay()
2348-
2349- token = keyring_get_credentials(U1_APP_NAME)
2350- self.assertEqual(token, None)
2351-
2352
2353 class RegisterSampleException(Exception):
2354 """A mock exception thrown just when testing."""

Subscribers

People subscribed via source and target branches

to all changes: