1
# Copyright 2008-2015 Canonical
3
# This program is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU Affero General Public License as
5
# published by the Free Software Foundation, either version 3 of the
6
# License, or (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU Affero General Public License for more details.
13
# You should have received a copy of the GNU Affero General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
# For further info, check http://launchpad.net/filesync-server
18
"""Test sharing operations."""
23
from StringIO import StringIO
25
from twisted.internet import reactor, defer
27
from backends.filesync.data import errors, dbmanager, model
28
from ubuntuone.storage.server.testing.testcase import (
29
TestWithDatabase, FactoryHelper)
30
from ubuntuone.storageprotocol import request
31
from ubuntuone.storageprotocol import errors as protocol_errors
32
from ubuntuone.storageprotocol.content_hash import content_hash_factory, crc32
34
EMPTY_HASH = content_hash_factory().content_hash()
38
class TestCreateShare(TestWithDatabase):
39
"""Test crate_share command."""
41
def test_delete_share(self):
42
"""Test deletion of an offered by me share."""
44
@defer.inlineCallbacks
45
def _do_delete(client):
46
"""Callback to do delete."""
47
# authenticate and create a root to share
48
yield client.dummy_authenticate("open sesame")
49
root = yield client.get_root()
50
res = yield client.create_share(root, self.usr1.username,
52
share_id = uuid.UUID(res.share_id)
53
yield client.delete_share(share_id)
54
self.assertRaises(errors.DoesNotExist,
55
self.usr1.get_share, share_id)
58
def do_delete(client):
59
"""Test body callback."""
60
d = _do_delete(client)
61
d.addErrback(client.test_fail)
63
return self.callback_test(do_delete)
65
def test_delete_volume(self):
66
"""Test deletion of an offered to me and accepted share."""
69
share = self.usr1.root.share(self.usr0.id, u"sharename",
71
self.usr0.get_share(share.id).accept()
75
# authenticate and create the share
76
d = client.dummy_authenticate("open sesame")
77
d.addCallback(create_share)
79
# delete the share and check
80
d.addCallback(lambda share: client.delete_volume(share.id))
81
d.addCallbacks(client.test_done, client.test_fail)
83
return self.callback_test(auth)
85
def test_create_view(self):
86
"""Create a share using View."""
90
reg = self.usr1.get_shared_to()[0]
91
self.assertEqual(reg.name, "name")
92
self.assertEqual(reg.access, "View")
94
# authenticate and create a root to share
95
d = client.dummy_authenticate("open sesame")
96
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
100
lambda r: client.create_share(
101
r, self.usr1.username, u"name", "View"),
104
d.addCallback(lambda _: verifyDB())
106
d.addCallbacks(client.test_done, client.test_fail)
108
return self.callback_test(auth)
110
def test_create_basic_modify(self):
111
"""Create a share using Modify."""
114
# authenticate and create a root to share
115
d = client.dummy_authenticate("open sesame")
116
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
120
lambda r: client.create_share(
121
r, self.usr1.username, u"name", "Modify"),
124
d.addCallbacks(client.test_done, client.test_fail)
126
return self.callback_test(auth)
128
def test_create_usr_twice(self):
129
"""Create a share that tries to share twice same node to same user."""
132
# authenticate and create a root to share
133
d = client.dummy_authenticate("open sesame")
134
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
138
lambda r: client.create_share(
139
r, self.usr1.username, u"name1", "Modify"),
142
# create a share of the same node to the same user
144
lambda r: client.create_share(
145
r, self.usr1.username, u"name2", "Modify"),
148
d.addCallbacks(client.test_fail, lambda x: client.test_done("ok"))
150
return self.callback_test(auth)
152
def test_create_same_name_different_user(self):
153
"""Create two shares repeating the name but changing the user."""
156
# authenticate and create a root to share
157
d = client.dummy_authenticate("open sesame")
158
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
159
d.addCallback(self.save_req, 'root_id')
161
# create the share to one user
163
lambda r: client.create_share(self._state.root_id,
165
u"same name", "View"),
168
# create the share to other user
170
lambda r: client.create_share(self._state.root_id,
172
u"same name", "View"),
175
d.addCallbacks(client.test_done, client.test_fail)
177
return self.callback_test(auth)
179
def test_create_different_name_same_user(self):
180
"""Create two shares repeating the name but changing the user."""
183
# authenticate and create a root to share
184
d = client.dummy_authenticate("open sesame")
185
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
186
d.addCallback(self.save_req, 'root_id')
188
# create other directory to share
190
lambda r: client.make_dir(request.ROOT, r, "hola"),
193
# create the share using the just created directory
195
lambda r: client.create_share(r.new_id,
200
# create the share using root
202
lambda r: client.create_share(self._state.root_id,
204
u"root dir", "View"),
207
d.addCallbacks(client.test_done, client.test_fail)
209
return self.callback_test(auth)
211
def test_create_same_name_same_user(self):
212
"""Create two shares repeating the name and the user."""
213
@defer.inlineCallbacks
216
# authenticate and create a root to share
217
yield client.dummy_authenticate("open sesame")
218
root_id = yield client.get_root()
220
# create other directory to share
221
makedir_req = yield client.make_dir(request.ROOT, root_id, u"hola")
223
# create the share using the just created directory
224
yield client.create_share(makedir_req.new_id, self.usr1.username,
225
u"same_name", "View")
227
# create the share using root
228
d = client.create_share(root_id, self.usr1.username,
229
u"same_name", "View")
231
# the last call must fail with AlreadyExist
232
yield self.assertFailure(d, protocol_errors.AlreadyExistsError)
234
return self.callback_test(test, add_default_callbacks=True)
236
def test_create_share_file(self):
237
"""Create a share on a file."""
240
# authenticate and create a root to share
241
d = client.dummy_authenticate("open sesame")
242
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
244
# create the file to share
246
lambda r: client.make_file(request.ROOT, r, "hola"),
251
lambda r: client.create_share(r.new_id, self.usr1.username,
255
d.addCallbacks(client.test_fail, lambda x: client.test_done("ok"))
257
return self.callback_test(auth)
259
def test_notify_creation_to_myself(self):
260
"""Create a share to myself and listen to the notification."""
263
def notif(share_notif):
265
share_notif.subtree, str(self._state.root_id))
266
self.assertEqual(share_notif.share_name, "name")
267
self.assertEqual(share_notif.from_username,
269
self.assertEqual(share_notif.from_visible_name,
270
self.usr0.visible_name)
271
self.assertEqual(share_notif.access_level, "View")
274
# authenticate and create a root to share
275
d = client.dummy_authenticate("open sesame")
276
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
277
d.addCallback(self.save_req, 'root_id')
279
# hook ourselves and create the share
280
d.addCallbacks(lambda r: client.set_share_change_callback(notif),
283
lambda _: client.create_share(
284
self._state.root_id, "usr0", u"name", "View"),
287
return self.callback_test(auth)
289
def test_notify_creation_to_myself_accepted_yes(self):
290
"""Create a share to myself and accept it."""
293
def notif_share(share_notif):
295
shares = self.usr0.get_shared_to(accepted=False)
297
self.assertEquals(share.name, u"acc Y")
298
self.assertEqual(str(share.root_id), self._state.root_id)
299
self.assertEqual(share.access, "View")
300
self.assertEqual(share.accepted, False)
303
client.accept_share(share_notif.share_id, "Yes")
305
def notif_answer(share_id, answer):
306
# check the notification
307
self.assertEqual(answer, "Yes")
310
share = self.usr0.get_share(share_id)
311
self.assertEqual(str(share.root_id), self._state.root_id)
312
self.assertEqual(share.accepted, True)
316
# authenticate and create a root to share
317
d = client.dummy_authenticate("open sesame")
318
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
319
d.addCallback(self.save_req, 'root_id')
321
# hook ourselves to both notifications
323
lambda r: client.set_share_change_callback(notif_share),
327
lambda r: client.set_share_answer_callback(notif_answer),
332
lambda _: client.create_share(
333
self._state.root_id, self.usr0.username, u"acc Y", "View"),
336
return self.callback_test(auth)
338
def test_notify_creation_to_myself_accepted_no(self):
339
"""Create a share to myself and reject it."""
342
def notif_share(share_notif):
345
reg = self.usr0.get_shared_to()[0]
346
self.assertEqual(str(reg.root_id), self._state.root_id)
347
self.assertEqual(reg.access, "View")
348
self.assertEqual(reg.accepted, False)
349
self.assertEqual(reg.status, "Live")
352
client.accept_share(share_notif.share_id, "No")
354
def notif_answer(share_id, answer):
355
# check the notification
356
self.assertEqual(answer, "No")
359
self.assertRaises(errors.DoesNotExist,
360
self.usr0.get_share, share_id)
365
# authenticate and create a root to share
366
d = client.dummy_authenticate("open sesame")
367
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
368
d.addCallback(self.save_req, 'root_id')
370
# hook ourselves to both notifications
372
lambda r: client.set_share_change_callback(notif_share),
376
lambda r: client.set_share_answer_callback(notif_answer),
381
lambda _: client.create_share(
382
self._state.root_id, self.usr0.username, u"acc N", "View"),
385
return self.callback_test(auth)
387
def test_notify_answer_two_users(self):
388
"""Create check answer notifications between two clients."""
391
self._state.client1 = client
392
client.set_share_answer_callback(client1_notif_answer)
393
d = client.dummy_authenticate("open sesame") # for user #0
394
d.addCallback(new_client)
395
d.addCallback(make_share)
398
self._state.client2 = client
399
client.set_share_change_callback(client2_notif_change)
400
d = client.dummy_authenticate("friend") # for user #1
401
d.addCallback(done_auth)
404
factory = FactoryHelper(login1)
405
factory2 = FactoryHelper(login2)
406
d1 = defer.Deferred()
407
d2 = defer.Deferred()
408
timeout = reactor.callLater(3, d1.errback, Exception("timeout"))
411
reactor.connectTCP('localhost', self.port, factory2)
414
def done_auth(result):
419
client1 = self._state.client1
420
d = client1.get_root()
421
d.addCallback(self.save_req, 'root_id')
423
lambda _: client1.create_share(
424
self._state.root_id, self.usr1.username, u"acc Y", "View"),
427
def client2_notif_change(share_notif):
428
#received change notification for the new share
430
shares = self.usr1.get_shared_to(accepted=False)
432
self.assertEquals(share.name, u"acc Y")
433
self.assertEqual(str(share.root_id), self._state.client1.root_id)
434
self.assertEqual(share.access, "View")
435
self.assertEqual(share.accepted, False)
437
self._state.client2.accept_share(share_notif.share_id, "Yes")
439
def client1_notif_answer(share_id, answer):
440
# check the notification
441
self.assertEqual(answer, "Yes")
443
share = self.usr0.get_share(share_id)
444
self.assertEqual(share.accepted, True)
447
factory.timeout.cancel()
448
factory2.timeout.cancel()
450
d1.callback((share_id, answer))
451
self._state.client1.transport.loseConnection()
452
self._state.client2.transport.loseConnection()
454
reactor.connectTCP('localhost', self.port, factory)
457
def test_notify_creation_bad(self):
458
"""Create a share and see that the own user is not notified."""
462
raise ValueError("This notification shouldn't exist!")
464
# authenticate and create a root to share
465
d = client.dummy_authenticate("open sesame")
466
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
467
d.addCallback(self.save_req, 'root_id')
469
# hook ourselves and create the share
470
d.addCallbacks(lambda r: client.set_share_change_callback(notif),
473
lambda _: client.create_share(
474
self._state.root_id, self.usr1.username, u"name", "View"),
477
d.addCallbacks(client.test_done, client.test_fail)
478
return self.callback_test(auth)
480
def test_create_got_shareid(self):
481
"""Create a share using and check if the share_id is returned. """
484
# authenticate and create a root to share
485
d = client.dummy_authenticate("open sesame")
486
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
489
def check_share_id(share_id):
490
self.assertFalse(share_id is None, 'Oops!, share_id is None!')
494
d = client.create_share(r, self.usr1.username, u"name", "View")
495
d.addCallbacks(check_share_id, client.test_fail)
497
d.addCallbacks(lambda r: create_share(r), client.test_fail)
498
d.addCallbacks(client.test_done, client.test_fail)
500
return self.callback_test(auth)
502
def test_accept_dead_share(self):
503
"""Try to accept a dead share."""
504
share = self.usr0.root.share(self.usr1.id, u"a dead share")
508
# authenticate and create a root to share
509
d = client.dummy_authenticate("open sesame")
510
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
511
# accept the Dead share
512
d.addCallbacks(lambda _: client.accept_share(share.id, "Yes"),
514
self.assertFails(d, "DOES_NOT_EXIST")
515
d.addCallbacks(client.test_done)
517
return self.callback_test(auth)
520
class TestSharesWithData(TestWithDatabase):
521
"""Tests that require simple sharing data setup."""
523
@defer.inlineCallbacks
525
"""Create users, files and shares."""
526
yield super(TestSharesWithData, self).setUp()
528
root1 = self.usr1.root
529
root2 = self.usr2.root
530
filero = root2.make_file(u"file")
531
rwdir = root2.make_subdirectory(u"rwdir")
532
filerw = rwdir.make_file(u"file")
533
subdir = root2.make_subdirectory(u"subdir")
534
subfile = subdir.make_file(u"subfile")
535
subsubdir = subdir.make_subdirectory(u"subsubdir")
536
subsubfile = subsubdir.make_file(u"subsubfile")
538
share = root2.share(0, u"foo", readonly=True)
539
share_other = root2.share(self.usr1.id, u"foo", readonly=True)
540
subshare = subdir.share(0, u"foo2", readonly=True)
541
share_owner = root1.share(0, u"foo3", readonly=True)
542
share_modify = rwdir.share(0, u"foo4")
543
for s in self.usr0.get_shared_to(accepted=False):
545
for s in self.usr1.get_shared_to(accepted=False):
548
self.share = str(share.id)
549
self.share_modify = str(share_modify.id)
550
self.subshare = str(subshare.id)
551
self.share_other = str(share_other.id)
552
self.share_owner = str(share_owner.id)
554
self.filero = str(filero.id)
555
self.subdir = subdir.id
556
self.subfile = subfile.id
557
self.subsubdir = subsubdir.id
558
self.subsubfile = subsubfile.id
559
self.rwdir = rwdir.id
560
self.filerw = str(filerw.id)
562
def test_unlink_mount_point(self):
563
"""Test unlinking a dir."""
567
d = client.dummy_authenticate("open sesame")
569
lambda r: client.unlink(self.subshare, str(self.subdir)))
570
self.assertFails(d, "NO_PERMISSION")
571
d.addCallback(client.test_done)
573
return self.callback_test(auth)
575
def test_mkfile_on_share(self):
576
"""Create a file on a share."""
580
d = client.dummy_authenticate("open sesame")
582
lambda r: client.make_file(
583
self.share_modify, self.rwdir, "test_file"))
584
d.addCallbacks(client.test_done, client.test_fail)
586
return self.callback_test(auth)
588
def test_mkfile_on_share_ro(self):
589
"""Create a file on a share thats read only."""
593
d = client.dummy_authenticate("open sesame")
595
lambda r: client.make_file(self.share, self.root, "test_file"))
596
d.addCallbacks(client.test_fail, lambda x: client.test_done())
598
return self.callback_test(auth)
600
def test_mkdir_on_share(self):
601
"""Create a dir on a share."""
605
d = client.dummy_authenticate("open sesame")
607
lambda r: client.make_dir(
608
self.share_modify, self.rwdir, "test_dir"))
609
d.addCallbacks(client.test_done, client.test_fail)
611
return self.callback_test(auth)
613
def test_mkdir_on_share_ro(self):
614
"""Create a dir on a share thats read only."""
618
d = client.dummy_authenticate("open sesame")
620
lambda r: client.make_dir(self.share, self.root, "test_dir"))
621
d.addCallbacks(client.test_fail, lambda x: client.test_done())
623
return self.callback_test(auth)
625
def test_unlink_on_share(self):
626
"""unlink a file on a share."""
630
d = client.dummy_authenticate("open sesame")
632
lambda r: client.unlink(self.share_modify, self.filerw))
633
d.addCallbacks(client.test_done, client.test_fail)
635
return self.callback_test(auth)
637
def test_unlink_on_share_ro(self):
638
"""unlink a file on a share thats read only."""
642
d = client.dummy_authenticate("open sesame")
644
lambda r: client.unlink(self.share, self.filero))
645
d.addCallbacks(client.test_fail, lambda x: client.test_done())
647
return self.callback_test(auth)
649
def test_move_on_share(self):
650
"""move a file on a share."""
654
d = client.dummy_authenticate("open sesame")
656
lambda r: client.move(
657
self.share_modify, self.filerw, self.rwdir, "newname"))
658
d.addCallbacks(client.test_done, client.test_fail)
660
return self.callback_test(auth)
662
def test_move_on_share_ro(self):
663
"""move a file on a share thats read only."""
667
d = client.dummy_authenticate("open sesame")
669
lambda r: client.move(
670
self.share, self.filero, self.root, "newname"))
671
d.addCallbacks(client.test_fail, lambda x: client.test_done())
673
return self.callback_test(auth)
675
def test_move_on_share_from_mine(self):
676
"""make sure we cant move across roots"""
680
d = client.dummy_authenticate("open sesame")
681
d.addCallback(lambda r: client.get_root())
683
lambda r: client.make_file("", r, "file"))
685
lambda r: client.move(
686
self.share_modify, r.new_id, self.root, "newname"))
687
self.assertFails(d, "DOES_NOT_EXIST")
688
d.addCallback(client.test_done)
690
return self.callback_test(auth)
692
def test_get_content_on_share(self):
693
"""read a file on a share."""
695
deflated_data = zlib.compress(data)
696
hash_object = content_hash_factory()
697
hash_object.update(data)
698
hash_value = hash_object.content_hash()
699
crc32_value = crc32(data)
701
deflated_size = len(deflated_data)
705
d = client.dummy_authenticate("open sesame")
706
# need to put data to be able to retrieve it!
708
lambda r: client.put_content(
709
self.share_modify, self.filerw, NO_CONTENT_HASH,
710
hash_value, crc32_value, size, deflated_size,
711
StringIO(deflated_data)))
713
lambda r: client.get_content(
714
self.share_modify, self.filerw, EMPTY_HASH))
715
d.addCallbacks(client.test_done, client.test_fail)
717
return self.callback_test(auth)
719
def test_put_content_on_share(self):
720
"""write a file on a share."""
722
deflated_data = zlib.compress(data)
723
hash_object = content_hash_factory()
724
hash_object.update(data)
725
hash_value = hash_object.content_hash()
726
crc32_value = crc32(data)
728
deflated_size = len(deflated_data)
732
d = client.dummy_authenticate("open sesame")
734
lambda r: client.put_content(
735
self.share_modify, self.filerw, NO_CONTENT_HASH,
736
hash_value, crc32_value, size, deflated_size,
737
StringIO(deflated_data)))
738
d.addCallbacks(client.test_done, client.test_fail)
740
return self.callback_test(auth)
742
def test_put_content_on_share_ro(self):
743
"""Write a file on a share thats read only."""
745
hash_object = content_hash_factory()
746
hash_object.update(data)
747
hash_value = hash_object.content_hash()
748
crc32_value = crc32(data)
753
d = client.dummy_authenticate("open sesame")
755
lambda r: client.put_content(
756
self.share, self.filero, NO_CONTENT_HASH, hash_value,
757
crc32_value, size, StringIO(data)))
758
d.addCallbacks(client.test_fail, lambda x: client.test_done())
760
return self.callback_test(auth)
763
class TestSharesWithDataLegacy(TestWithDatabase):
764
"""Tests that require simple sharing data setup, but legacy."""
766
@defer.inlineCallbacks
768
"""Create users, files and shares."""
769
yield super(TestSharesWithDataLegacy, self).setUp()
771
root1 = self.usr1.root
772
root2 = self.usr2.root
773
file = root2.make_file(u"file")
774
filero = root2.make_file(u"file")
775
rwdir = root2.make_subdirectory(u"rwdir")
776
filerw = rwdir.make_file(u"file")
777
subdir = root2.make_subdirectory(u"subdir")
778
subfile = subdir.make_file(u"subfile")
779
subsubdir = subdir.make_subdirectory(u"subsubdir")
780
subsubfile = subsubdir.make_file(u"subsubfile")
781
store = dbmanager.get_shard_store(self.usr2.shard_id)
782
#set all files with an empty hash
783
store.find(model.StorageObject, model.StorageObject.kind == 'File'
784
).set(_content_hash=EMPTY_HASH)
787
share = root2.share(0, u"foo", readonly=True)
788
share_other = root2.share(self.usr1.id, u"foo", readonly=True)
789
subshare = subdir.share(0, u"foo2", readonly=True)
790
share_owner = root1.share(0, u"foo3", readonly=True)
791
share_modify = rwdir.share(0, u"foo4")
792
for s in self.usr0.get_shared_to(accepted=False):
794
for s in self.usr1.get_shared_to(accepted=False):
797
self.share = str(share.id)
798
self.share_modify = str(share_modify.id)
799
self.subshare = str(subshare.id)
800
self.share_other = str(share_other.id)
801
self.share_owner = str(share_owner.id)
803
self.file = str(file.id)
804
self.filero = str(filero.id)
805
self.subdir = subdir.id
806
self.subfile = subfile.id
807
self.subsubdir = subsubdir.id
808
self.subsubfile = subsubfile.id
809
self.rwdir = rwdir.id
810
self.filerw = str(filerw.id)
812
def test_get_content_on_share(self):
813
"""read a file on a share."""
817
d = client.dummy_authenticate("open sesame")
818
# need to put data to be able to retrieve it!
820
lambda r: client.get_content(
821
self.share, self.file, EMPTY_HASH))
822
d.addCallbacks(client.test_done, client.test_fail)
824
return self.callback_test(auth)
826
def test_put_content_on_share(self):
827
"""write a file on a share."""
829
deflated_data = zlib.compress(data)
830
hash_object = content_hash_factory()
831
hash_object.update(data)
832
hash_value = hash_object.content_hash()
833
crc32_value = crc32(data)
835
deflated_size = len(deflated_data)
839
d = client.dummy_authenticate("open sesame")
841
lambda r: client.put_content(
842
self.share_modify, self.filerw, EMPTY_HASH, hash_value,
843
crc32_value, size, deflated_size, StringIO(deflated_data)))
844
d.addCallbacks(client.test_done, client.test_fail)
846
return self.callback_test(auth)
849
class TestListShares(TestWithDatabase):
850
"""Test list_shares command."""
852
def test_one_share_view(self):
853
"""List a created share from_me with View."""
856
self.assertEqual(len(resp.shares), 1)
857
share = resp.shares[0]
858
self.assertEqual(share.direction, "from_me")
859
self.assertEqual(share.other_username, u"usr1")
860
self.assertEqual(share.name, "n1")
861
self.assertEqual(share.access_level, "View")
862
self.assertEqual(share.subtree, uuid.UUID(self._state.root_id))
865
# authenticate and create a root to share
866
d = client.dummy_authenticate("open sesame")
867
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
868
d.addCallback(self.save_req, 'root_id')
872
lambda r: client.create_share(
873
r, self.usr1.username, u"n1", "View"),
876
# list the shares and check
877
d.addCallbacks(lambda _: client.list_shares(), client.test_fail)
878
d.addCallbacks(check, client.test_fail)
879
d.addCallbacks(client.test_done, client.test_fail)
881
return self.callback_test(auth)
883
def test_one_share_modify(self):
884
"""List a created share from_me with Modify."""
887
self.assertEqual(len(resp.shares), 1)
888
share = resp.shares[0]
889
self.assertEqual(share.direction, "from_me")
890
self.assertEqual(share.other_username, self.usr1.username)
891
self.assertEqual(share.name, "n1")
892
self.assertEqual(share.access_level, "Modify")
893
self.assertEqual(share.subtree, uuid.UUID(self._state.root_id))
896
# authenticate and create a root to share
897
d = client.dummy_authenticate("open sesame")
898
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
899
d.addCallback(self.save_req, 'root_id')
903
lambda r: client.create_share(
904
r, self.usr1.username, u"n1", "Modify"),
907
# list the shares and check
908
d.addCallbacks(lambda _: client.list_shares(), client.test_fail)
909
d.addCallbacks(check, client.test_fail)
910
d.addCallbacks(client.test_done, client.test_fail)
912
return self.callback_test(auth)
914
def _create_share(self, _, accepted=False):
915
"""Creates a share, optionally accepted."""
916
root = self.usr1.root.load()
917
share = root.share(0, u"sharename", readonly=True)
919
self.usr0.get_share(share.id).accept()
920
# save the node id to be able to compare it later
922
self.save_req(root.id, 'root_id')
924
def test_shares_to_me_noaccept(self):
925
"""List shares where user is recipient, no accepted."""
928
self.assertEqual(len(resp.shares), 1)
929
share = resp.shares[0]
930
self.assertEqual(share.direction, "to_me")
931
self.assertEqual(share.other_username, self.usr1.username)
932
self.assertEqual(share.name, "sharename")
933
self.assertEqual(share.access_level, "View")
934
self.assertEqual(share.accepted, False)
935
self.assertEqual(share.subtree, self._state.root_id)
938
# authenticate and create the share
939
d = client.dummy_authenticate("open sesame")
940
d.addCallback(self._create_share)
942
# list the shares and check
943
d.addCallback(lambda _: client.list_shares())
945
d.addCallbacks(client.test_done, client.test_fail)
947
return self.callback_test(auth)
949
def test_shares_to_me_accepted(self):
950
"""List shares where user is recipient, accepted."""
953
self.assertEqual(len(resp.shares), 0)
956
# authenticate and create the share
957
d = client.dummy_authenticate("open sesame")
958
d.addCallback(self._create_share, accepted=True)
960
# list the shares and check
961
d.addCallback(lambda _: client.list_shares())
963
d.addCallbacks(client.test_done, client.test_fail)
965
return self.callback_test(auth)
967
def test_several_shares(self):
968
"""List several mixed shares."""
971
self.usr1.root.share(self.usr0.id, u"share 3", readonly=True)
974
self.assertEqual(len(resp.shares), 3)
976
share = [s for s in resp.shares if s.name == "share 1"][0]
977
self.assertEqual(share.direction, "from_me")
978
self.assertEqual(share.other_username, self.usr1.username)
979
self.assertEqual(share.access_level, "View")
981
share = [s for s in resp.shares if s.name == "share 2"][0]
982
self.assertEqual(share.direction, "from_me")
983
self.assertEqual(share.other_username, self.usr2.username)
984
self.assertEqual(share.access_level, "Modify")
986
share = [s for s in resp.shares if s.name == "share 3"][0]
987
self.assertEqual(share.direction, "to_me")
988
self.assertEqual(share.other_username, self.usr1.username)
989
self.assertEqual(share.access_level, "View")
992
# authenticate and create a root to share
993
d = client.dummy_authenticate("open sesame")
994
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
995
d.addCallback(self.save_req, 'root_id')
997
# create one share from me with View
999
lambda r: client.create_share(
1000
self._state.root_id, self.usr1.username,
1001
u"share 1", "View"),
1004
# create one share from me with Modify
1006
lambda r: client.create_share(
1007
self._state.root_id, self.usr2.username,
1008
u"share 2", "Modify"),
1011
# create the share to me
1012
d.addCallbacks(lambda _: createShare(), client.test_fail)
1014
# list the shares and check
1015
d.addCallbacks(lambda _: client.list_shares(), client.test_fail)
1016
d.addCallbacks(check, client.test_fail)
1017
d.addCallbacks(client.test_done, client.test_fail)
1019
return self.callback_test(auth)
1021
def test_share_offer(self):
1022
"""Test a share offer which has no to_user."""
1024
def createShareOffer():
1026
self.usr0.root.make_shareoffer(u"me@example.com", u"share Offer",
1030
self.assertEqual(len(resp.shares), 1)
1032
share = [s for s in resp.shares if s.name == "share Offer"][0]
1033
self.assertEqual(share.direction, "from_me")
1034
self.assertEqual(share.other_username, u"")
1035
self.assertEqual(share.access_level, "View")
1038
# authenticate and create a root to share
1039
d = client.dummy_authenticate("open sesame")
1040
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
1041
d.addCallback(self.save_req, 'root_id')
1043
# create the share offer to me
1044
d.addCallbacks(lambda _: createShareOffer(), client.test_fail)
1046
# list the shares and check
1047
d.addCallbacks(lambda _: client.list_shares(), client.test_fail)
1048
d.addCallbacks(check, client.test_fail)
1049
d.addCallbacks(client.test_done, client.test_fail)
1051
return self.callback_test(auth)
1053
def test_dead_share(self):
1054
"""Test that Dead shares are not included in the list. """
1057
share = self.usr1.root.share(self.usr0.id,
1058
u"another dead share", readonly=True)
1062
self.assertEqual(len(resp.shares), 0, resp.shares)
1065
# authenticate and create a root to share
1066
d = client.dummy_authenticate("open sesame")
1067
d.addCallbacks(lambda r: client.get_root(), client.test_fail)
1068
d.addCallback(self.save_req, 'root_id')
1071
d.addCallbacks(lambda _: createShare(), client.test_fail)
1072
# list the shares and check
1073
d.addCallbacks(lambda _: client.list_shares(), client.test_fail)
1074
d.addCallbacks(check, client.test_fail)
1075
d.addCallbacks(client.test_done, client.test_fail)
1077
return self.callback_test(auth)
1079
def test_share_in_root_from_me_with_volume_id(self):
1080
"""List a share from_me and check that we have the node volume_id."""
1082
@defer.inlineCallbacks
1084
# authenticate and create a root to share
1085
yield client.dummy_authenticate("open sesame")
1086
root_id = yield client.get_root()
1087
yield client.create_share(root_id, self.usr1.username,
1089
# list the shares and check
1090
resp = yield client.list_shares()
1091
self.assertEqual(len(resp.shares), 1)
1092
share = resp.shares[0]
1093
self.assertEqual(share.direction, "from_me")
1094
self.assertEqual(share.other_username, u"usr1")
1095
self.assertEqual(share.name, "n1")
1096
self.assertEqual(share.access_level, "View")
1097
self.assertEqual(share.subtree, uuid.UUID(root_id))
1098
self.assertEqual(share.subtree_volume_id, None) # the root
1100
return self.callback_test(auth, add_default_callbacks=True)
1102
def test_share_in_udf_from_me_with_volume_id(self):
1103
"""List a share from_me and check that we have the node volume_id."""
1105
@defer.inlineCallbacks
1107
# authenticate and create a root to share
1108
yield client.dummy_authenticate("open sesame")
1109
yield client.get_root()
1110
udf = yield client.create_udf(u"~/myudf", u"foo")
1111
yield client.create_share(udf.node_id, self.usr1.username,
1113
# list the shares and check
1114
resp = yield client.list_shares()
1115
self.assertEqual(len(resp.shares), 1)
1116
share = resp.shares[0]
1117
self.assertEqual(share.direction, "from_me")
1118
self.assertEqual(share.other_username, u"usr1")
1119
self.assertEqual(share.name, "n1")
1120
self.assertEqual(share.access_level, "View")
1121
self.assertEqual(share.subtree, uuid.UUID(udf.node_id))
1122
self.assertEqual(share.subtree_volume_id, uuid.UUID(udf.volume_id))
1124
return self.callback_test(auth, add_default_callbacks=True)
1126
def test_share_to_me_without_volume_id(self):
1127
"""List a share to_me, check that we don't have the node volume_id."""
1129
@defer.inlineCallbacks
1131
# authenticate and create a root to share
1132
yield client.dummy_authenticate("open sesame")
1133
self._create_share(None, accepted=False)
1134
# list the shares and check
1135
resp = yield client.list_shares()
1136
self.assertEqual(len(resp.shares), 1)
1137
share = resp.shares[0]
1138
self.assertEqual(share.direction, "to_me")
1139
self.assertFalse(hasattr(share, 'subtree_volume_id'))
1141
return self.callback_test(auth, add_default_callbacks=True)
1144
class TestSharesNotified(TestWithDatabase):
1145
"""Test that notifications are sent to share receivers."""
1147
def test_notify_shared_node(self):
1148
"""The notif should be sent to other users if the node is shared."""
1151
self._state.client1 = client
1152
d = client.dummy_authenticate("open sesame") # for user #0
1153
d.addCallback(lambda _: new_client())
1154
d.addCallback(make_notification)
1158
self._state.client2 = client
1159
client.set_volume_new_generation_callback(on_notification)
1160
d = client.dummy_authenticate("friend") # for user #1
1161
d.addCallback(done_auth)
1164
factory = FactoryHelper(login1)
1165
factory2 = FactoryHelper(login2)
1166
d1 = defer.Deferred()
1167
d2 = defer.Deferred()
1168
timeout = reactor.callLater(3, d1.errback, Exception("timeout"))
1171
"""add the second client"""
1172
reactor.connectTCP('localhost', self.port, factory2)
1175
def on_notification(volume, generation):
1176
"""notification arrived, check and cleanup"""
1178
self.assertEqual(volume, self._state.share_id)
1181
factory.timeout.cancel()
1182
factory2.timeout.cancel()
1185
self._state.client1.transport.loseConnection()
1186
self._state.client2.transport.loseConnection()
1188
def done_auth(result):
1189
"""authentication done for client2, we can start making changes"""
1193
# mark the share as accepted by hand, so we don't
1194
# have to emulate the whole process just for this test
1195
shares = [s for s in self.usr1.get_shared_to()
1196
if s.shared_by_id == 0]
1199
# also store it in the state for further control
1200
self._state.share_id = share.id
1202
def make_notification(_):
1203
"""create a change that should create a notification"""
1205
d = self._state.client1.get_root()
1206
d.addCallback(self.save_req, 'root_id')
1210
lambda r: self._state.client1.create_share(
1211
r, self.usr1.username, u"name", "View"))
1212
d.addCallback(mark_share)
1214
# create a file in the root
1216
lambda _: self._state.client1.make_file(
1217
request.ROOT, self._state.root_id, "hola"))
1219
reactor.connectTCP('localhost', self.port, factory)
1222
def test_share_node_deleted(self):
1223
'''Remove the node that was shared.'''
1226
self._state.client1 = client
1227
d = client.dummy_authenticate("open sesame") # for user #0
1228
d.addCallback(new_client)
1229
d.addCallback(make_notification)
1233
self._state.client2 = client
1234
client.set_share_change_callback(notif1)
1235
d = client.dummy_authenticate("friend") # for user #1
1236
d.addCallback(done_auth)
1239
factory = FactoryHelper(login1)
1240
factory2 = FactoryHelper(login2)
1241
d1 = defer.Deferred()
1242
d2 = defer.Deferred()
1243
timeout = reactor.callLater(3, d1.errback, Exception("timeout"))
1246
"""add the second client"""
1247
reactor.connectTCP('localhost', self.port, factory2)
1250
def notif1(share_info):
1251
'''First notification, for the created share.'''
1252
self._state.client2.set_share_delete_callback(notif2)
1254
def notif2(share_id):
1255
'''Second notification, the tested one.'''
1256
# find the real share
1257
self.assertRaises(errors.DoesNotExist,
1258
self.usr1.get_share, self._state.share_id)
1261
factory.timeout.cancel()
1262
factory2.timeout.cancel()
1264
d1.callback((share_id))
1265
self._state.client1.transport.loseConnection()
1266
self._state.client2.transport.loseConnection()
1268
def done_auth(result):
1269
"""authentication done for client2, we can start making changes"""
1273
# mark the share as accepted by hand, so we don't
1274
# have to emulate the whole process just for this test
1275
share = self.usr1.get_shared_to(accepted=False)[0]
1277
# also store it in the state for further control
1278
self._state.share_id = share.id
1280
def make_notification(_):
1281
"""create a change that should create a notification"""
1283
client1 = self._state.client1
1284
d = client1.get_root()
1285
d.addCallback(self.save_req, 'root_id')
1287
# create the dir to share
1288
d.addCallback(lambda r: client1.make_dir(request.ROOT, r, "hi"))
1289
d.addCallback(self.save_req, "req")
1293
lambda _: client1.create_share(
1294
self._state.req.new_id, self.usr1.username,
1296
d.addCallback(mark_share)
1298
# remove the shared node
1300
lambda _: client1.unlink(request.ROOT, self._state.req.new_id))
1302
reactor.connectTCP('localhost', self.port, factory)
1304
test_share_node_deleted.skip = 'LP: #766088'
1306
def test_share_node_overwritten_with_move(self):
1307
'''Move something else over the node that was shared.'''
1310
self._state.client1 = client
1311
d = client.dummy_authenticate("open sesame") # for user #0
1312
d.addCallback(new_client)
1313
d.addCallback(make_notification)
1317
self._state.client2 = client
1318
client.set_share_change_callback(notif1)
1319
d = client.dummy_authenticate("friend") # for user #1
1320
d.addCallback(done_auth)
1323
factory = FactoryHelper(login1)
1324
factory2 = FactoryHelper(login2)
1325
d1 = defer.Deferred()
1326
d2 = defer.Deferred()
1327
timeout = reactor.callLater(3, d1.errback, Exception("timeout"))
1330
"""add the second client"""
1331
reactor.connectTCP('localhost', self.port, factory2)
1334
def notif1(share_id):
1335
'''First notification, for the created share.'''
1336
self._state.client2.set_share_delete_callback(notif2)
1338
def notif2(share_id):
1339
'''Second notification, the tested one.'''
1340
# find the real share
1341
self.assertRaises(errors.DoesNotExist,
1342
self.usr1.get_share, self._state.share_id)
1345
factory.timeout.cancel()
1346
factory2.timeout.cancel()
1348
d1.callback((share_id))
1349
self._state.client1.transport.loseConnection()
1350
self._state.client2.transport.loseConnection()
1352
def done_auth(result):
1353
"""authentication done for client2, we can start making changes"""
1357
# mark the share as accepted by hand, so we don't
1358
# have to emulate the whole process just for this test
1359
share = self.usr1.get_shared_to(accepted=False)[0]
1361
# also store it in the state for further control
1362
self._state.share_id = share.id
1364
def make_notification(_):
1365
"""create a change that should create a notification"""
1367
client1 = self._state.client1
1368
d = client1.get_root()
1369
d.addCallback(self.save_req, 'root_id')
1371
# create the dir to share
1372
d.addCallback(lambda r: client1.make_dir(request.ROOT, r, "prnt"))
1373
d.addCallback(self.save_req, 'parent')
1375
lambda r: client1.make_dir(request.ROOT, r.new_id, "hi"))
1376
d.addCallback(self.save_req, 'node')
1378
# create another dir, to be used as source
1380
lambda _: client1.make_dir(
1381
request.ROOT, self._state.root_id, "hi"))
1382
d.addCallback(self.save_req, 'srcdir')
1386
lambda _: client1.create_share(
1387
self._state.node.new_id, self.usr1.username,
1389
d.addCallback(mark_share)
1391
# move the source over the shared node
1393
lambda _: client1.move(request.ROOT, self._state.srcdir.new_id,
1394
self._state.parent.new_id, "hi"))
1396
reactor.connectTCP('localhost', self.port, factory)