Merge lp:~chipaca/u1db/u1db-client_resolve into lp:u1db

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 311
Merged at revision: 313
Proposed branch: lp:~chipaca/u1db/u1db-client_resolve
Merge into: lp:u1db
Diff against target: 134 lines (+99/-0)
2 files modified
u1db/commandline/client.py (+38/-0)
u1db/tests/commandline/test_client.py (+61/-0)
To merge this branch: bzr merge lp:~chipaca/u1db/u1db-client_resolve
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve
Review via email: mp+107270@code.launchpad.net

Commit message

a first pass at resolve-doc for u1db-client

Description of the change

a first pass at resolve-doc for u1db-client

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Looks good. I wonder if we need a test that passes in bogus revision numbers, or is that planned for a subsequent branch?

review: Approve
Revision history for this message
John Lenton (chipaca) wrote :

On Thu, May 24, 2012 at 8:54 PM, Eric Casteleijn
<email address hidden> wrote:
>
> Review: Approve
>
> Looks good. I wonder if we need a test that passes in bogus revision numbers, or is that planned for a subsequent branch?

that's going to happen inside resolve_doc in a future branch (bugĀ 1004004)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'u1db/commandline/client.py'
2--- u1db/commandline/client.py 2012-05-24 14:50:19 +0000
3+++ u1db/commandline/client.py 2012-05-24 19:25:41 +0000
4@@ -227,6 +227,44 @@
5 client_commands.register(CmdPut)
6
7
8+class CmdResolve(OneDbCmd):
9+ """Resolve a conflicted document"""
10+
11+ name = 'resolve-doc'
12+
13+ @classmethod
14+ def _populate_subparser(cls, parser):
15+ parser.add_argument('database',
16+ help='The local or remote database to update',
17+ metavar='database-path-or-url'),
18+ parser.add_argument('doc_id', help='The conflicted document id')
19+ parser.add_argument('doc_revs', metavar="doc-rev", nargs="+",
20+ help='The revisions that the new content supersedes')
21+ parser.add_argument('--infile', nargs='?', default=None,
22+ help='The filename of the document that will be used for content',
23+ type=argparse.FileType('rb'))
24+
25+ def run(self, database, doc_id, doc_revs, infile):
26+ if infile is None:
27+ infile = self.stdin
28+ try:
29+ db = self._open(database, create=False)
30+ except errors.DatabaseDoesNotExist:
31+ self.stderr.write("Database does not exist.\n")
32+ return 1
33+ doc = db.get_doc(doc_id)
34+ if doc is None:
35+ self.stderr.write("Document does not exist.\n")
36+ return 1
37+ doc.set_json(infile.read())
38+ db.resolve_doc(doc, doc_revs)
39+ self.stderr.write("rev: %s\n" % db.get_doc(doc_id).rev)
40+ if doc.has_conflicts:
41+ self.stderr.write("Document still has conflicts.\n")
42+
43+client_commands.register(CmdResolve)
44+
45+
46 class CmdSync(command.Command):
47 """Synchronize two databases"""
48
49
50=== modified file 'u1db/tests/commandline/test_client.py'
51--- u1db/tests/commandline/test_client.py 2012-05-24 14:50:19 +0000
52+++ u1db/tests/commandline/test_client.py 2012-05-24 19:25:41 +0000
53@@ -24,6 +24,7 @@
54 errors,
55 open as u1db_open,
56 tests,
57+ vectorclock,
58 )
59 from u1db.commandline import (
60 client,
61@@ -159,6 +160,14 @@
62 self.assertEqual('db', args.database)
63 self.assertEqual('doc-id', args.doc_id)
64
65+ def test_resolve(self):
66+ args = self.parse_args(['resolve-doc', 'db', 'doc-id', 'rev:1', 'other:1'])
67+ self.assertEqual(client.CmdResolve, args.subcommand)
68+ self.assertEqual('db', args.database)
69+ self.assertEqual('doc-id', args.doc_id)
70+ self.assertEqual(['rev:1', 'other:1'], args.doc_revs)
71+ self.assertEqual(None, args.infile)
72+
73
74 class TestCaseWithDB(tests.TestCase):
75 """These next tests are meant to have one class per Command.
76@@ -339,6 +348,58 @@
77 cmd.stderr.getvalue())
78
79
80+class TestCmdResolve(TestCaseWithDB):
81+
82+ def setUp(self):
83+ super(TestCmdResolve, self).setUp()
84+ self.doc1 = self.db.create_doc(tests.simple_doc, doc_id='my-doc')
85+ self.doc2 = self.make_document('my-doc', 'other:1', '{}', False)
86+ self.db._put_doc_if_newer(self.doc2, save_conflict=True)
87+
88+ def test_resolve_simple(self):
89+ self.assertTrue(self.db.get_doc('my-doc').has_conflicts)
90+ cmd = self.make_command(client.CmdResolve)
91+ inf = cStringIO.StringIO(tests.nested_doc)
92+ cmd.run(self.db_path, 'my-doc', [self.doc1.rev, self.doc2.rev], inf)
93+ doc = self.db.get_doc('my-doc')
94+ vec = vectorclock.VectorClockRev(doc.rev)
95+ self.assertTrue(vec.is_newer(vectorclock.VectorClockRev(self.doc1.rev)))
96+ self.assertTrue(vec.is_newer(vectorclock.VectorClockRev(self.doc2.rev)))
97+ self.assertGetDoc(self.db, 'my-doc', doc.rev, tests.nested_doc, False)
98+ self.assertEqual('', cmd.stdout.getvalue())
99+ self.assertEqual('rev: %s\n' % (doc.rev,),
100+ cmd.stderr.getvalue())
101+
102+ def test_resolve_double(self):
103+ moar = '{"x": 42}'
104+ doc3 = self.make_document('my-doc', 'third:1', moar, False)
105+ self.db._put_doc_if_newer(doc3, save_conflict=True)
106+ cmd = self.make_command(client.CmdResolve)
107+ inf = cStringIO.StringIO(tests.nested_doc)
108+ cmd.run(self.db_path, 'my-doc', [self.doc1.rev, self.doc2.rev], inf)
109+ doc = self.db.get_doc('my-doc')
110+ self.assertGetDoc(self.db, 'my-doc', doc.rev, moar, True)
111+ self.assertEqual('', cmd.stdout.getvalue())
112+ self.assertEqual('rev: %s\nDocument still has conflicts.\n' % (doc.rev,),
113+ cmd.stderr.getvalue())
114+
115+ def test_resolve_no_db(self):
116+ cmd = self.make_command(client.CmdResolve)
117+ retval = cmd.run(self.db_path + "__DOES_NOT_EXIST", "my-doc", [], None)
118+ self.assertEqual(retval, 1)
119+ self.assertEqual(cmd.stdout.getvalue(), '')
120+ self.assertEqual(cmd.stderr.getvalue(), 'Database does not exist.\n')
121+
122+ def test_resolve_no_doc(self):
123+ cmd = self.make_command(client.CmdResolve)
124+ retval = cmd.run(self.db_path, "foo", [], None)
125+ self.assertEqual(retval, 1)
126+ self.assertEqual(cmd.stdout.getvalue(), '')
127+ self.assertEqual(cmd.stderr.getvalue(), 'Document does not exist.\n')
128+
129+
130+
131+
132 class TestCmdSync(TestCaseWithDB):
133
134 def setUp(self):

Subscribers

People subscribed via source and target branches