Merge lp:~thisfred/u1db/validate_transaction_id_of_target into lp:u1db
- validate_transaction_id_of_target
- Merge into trunk
Proposed by
Eric Casteleijn
Status: | Merged |
---|---|
Approved by: | Eric Casteleijn |
Approved revision: | 339 |
Merged at revision: | 332 |
Proposed branch: | lp:~thisfred/u1db/validate_transaction_id_of_target |
Merge into: | lp:u1db |
Prerequisite: | lp:~thisfred/u1db/txid_in_sync_exchange |
Diff against target: |
538 lines (+253/-24) 12 files modified
include/u1db/u1db_internal.h (+11/-0) src/u1db.c (+92/-3) src/u1db_sync_target.c (+21/-13) u1db/backends/__init__.py (+18/-1) u1db/backends/inmemory.py (+12/-0) u1db/backends/sqlite_backend.py (+22/-0) u1db/sync.py (+8/-5) u1db/tests/c_backend_wrapper.pyx (+19/-0) u1db/tests/test_backends.py (+19/-0) u1db/tests/test_c_backend.py (+8/-0) u1db/tests/test_sqlite_backend.py (+5/-2) u1db/tests/test_sync.py (+18/-0) |
To merge this branch: | bzr merge lp:~thisfred/u1db/validate_transaction_id_of_target |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni | Approve | ||
Review via email: mp+111091@code.launchpad.net |
This proposal supersedes a proposal from 2012-06-15.
Commit message
Added a validation function that checks that the generation + txid the sync target knows about us is correct.
Description of the change
Added a validation function that checks that the generation + txid the sync target knows about us is correct.
To post a comment you must log in.
- 338. By Eric Casteleijn
-
merged trunk resolved conflicts
- 339. By Eric Casteleijn
-
merged txid branch
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'include/u1db/u1db_internal.h' |
2 | --- include/u1db/u1db_internal.h 2012-06-19 19:41:19 +0000 |
3 | +++ include/u1db/u1db_internal.h 2012-06-19 19:41:19 +0000 |
4 | @@ -233,6 +233,17 @@ |
5 | int u1db__get_generation(u1database *db, int *generation); |
6 | |
7 | /** |
8 | + * Internal API, Get the global database rev and transaction id. |
9 | + */ |
10 | +int u1db__get_generation_info(u1database *db, int *generation, |
11 | + char **trans_id); |
12 | + |
13 | +/** |
14 | + * Internal API, Validate generation and transaction id. |
15 | + */ |
16 | +int u1db_validate_gen_and_trans_id(u1database *db, int generation, |
17 | + const char *trans_id); |
18 | +/** |
19 | * Internal API, Allocate a new document id, for cases when callers do not |
20 | * supply their own. Callers of this API are expected to free the result. |
21 | */ |
22 | |
23 | === modified file 'src/u1db.c' |
24 | --- src/u1db.c 2012-06-19 19:16:35 +0000 |
25 | +++ src/u1db.c 2012-06-19 19:41:19 +0000 |
26 | @@ -900,8 +900,8 @@ |
27 | (stored_doc_rev != NULL)); |
28 | } |
29 | if (status == U1DB_OK && replica_uid != NULL) { |
30 | - status = u1db__set_sync_info(db, replica_uid, replica_gen, |
31 | - replica_trans_id); |
32 | + status = u1db__set_sync_info( |
33 | + db, replica_uid, replica_gen, replica_trans_id); |
34 | } |
35 | if (status == U1DB_OK && at_gen != NULL) { |
36 | status = u1db__get_generation(db, at_gen); |
37 | @@ -1264,14 +1264,26 @@ |
38 | status = U1DB_OK; |
39 | *gen = 0; |
40 | *trans_id = strdup(""); |
41 | + if (*trans_id == NULL) { |
42 | + status = U1DB_NOMEM; |
43 | + goto finish; |
44 | + } |
45 | } else if (status == SQLITE_ROW) { |
46 | status = U1DB_OK; |
47 | *gen = sqlite3_column_int(statement, 0); |
48 | tmp = (const char *)sqlite3_column_text(statement, 1); |
49 | if (tmp == NULL) { |
50 | *trans_id = strdup(""); |
51 | + if (*trans_id == NULL) { |
52 | + status = U1DB_NOMEM; |
53 | + goto finish; |
54 | + } |
55 | } else { |
56 | *trans_id = strdup(tmp); |
57 | + if (*trans_id == NULL) { |
58 | + status = U1DB_NOMEM; |
59 | + goto finish; |
60 | + } |
61 | } |
62 | } |
63 | finish: |
64 | @@ -1411,6 +1423,80 @@ |
65 | return status; |
66 | } |
67 | |
68 | +int |
69 | +u1db__get_generation_info(u1database *db, int *generation, char **trans_id) |
70 | +{ |
71 | + int status; |
72 | + const char *tmp; |
73 | + |
74 | + sqlite3_stmt *statement; |
75 | + if (db == NULL || generation == NULL) { |
76 | + return U1DB_INVALID_PARAMETER; |
77 | + } |
78 | + status = sqlite3_prepare_v2(db->sql_handle, |
79 | + "SELECT max(generation), transaction_id FROM transaction_log", -1, |
80 | + &statement, NULL); |
81 | + if (status != SQLITE_OK) { |
82 | + return status; |
83 | + } |
84 | + status = sqlite3_step(statement); |
85 | + if (status == SQLITE_DONE) { |
86 | + // No records, we are at rev 0 |
87 | + status = SQLITE_OK; |
88 | + *generation = 0; |
89 | + } else if (status == SQLITE_ROW) { |
90 | + status = SQLITE_OK; |
91 | + *generation = sqlite3_column_int(statement, 0); |
92 | + tmp = (const char *)sqlite3_column_text(statement, 1); |
93 | + if (tmp == NULL) { |
94 | + *trans_id = NULL; |
95 | + } else { |
96 | + *trans_id = strdup(tmp); |
97 | + if (*trans_id == NULL) { |
98 | + status = U1DB_NOMEM; |
99 | + } |
100 | + } |
101 | + } |
102 | + sqlite3_finalize(statement); |
103 | + return status; |
104 | +} |
105 | + |
106 | +int |
107 | +u1db_validate_gen_and_trans_id(u1database *db, int generation, |
108 | + const char *trans_id) |
109 | +{ |
110 | + int status = U1DB_OK; |
111 | + sqlite3_stmt *statement; |
112 | + |
113 | + if (generation == 0) |
114 | + return status; |
115 | + if (db == NULL) { |
116 | + return U1DB_INVALID_PARAMETER; |
117 | + } |
118 | + status = sqlite3_prepare_v2(db->sql_handle, |
119 | + "SELECT transaction_id FROM transaction_log WHERE generation = ?", -1, |
120 | + &statement, NULL); |
121 | + if (status != SQLITE_OK) { goto finish; } |
122 | + status = sqlite3_bind_int(statement, 1, generation); |
123 | + if (status != SQLITE_OK) { goto finish; } |
124 | + status = sqlite3_step(statement); |
125 | + if (status == SQLITE_DONE) { |
126 | + status = U1DB_INVALID_GENERATION; |
127 | + goto finish; |
128 | + } else if (status == SQLITE_ROW) { |
129 | + // Note: We may want to handle the column containing NULL |
130 | + if (strcmp(trans_id, |
131 | + (const char *)sqlite3_column_text(statement, 0)) == 0) { |
132 | + status = U1DB_OK; |
133 | + goto finish; |
134 | + } |
135 | + status = U1DB_INVALID_TRANSACTION_ID; |
136 | + } |
137 | +finish: |
138 | + sqlite3_finalize(statement); |
139 | + return status; |
140 | +} |
141 | + |
142 | char * |
143 | u1db__allocate_doc_id(u1database *db) |
144 | { |
145 | @@ -1544,7 +1630,10 @@ |
146 | // Note: We may want to handle the column containing NULL |
147 | tmp = (const char *)sqlite3_column_text(statement, 1); |
148 | if (tmp == NULL) { |
149 | - *trans_id = NULL; |
150 | + *trans_id = strdup(""); |
151 | + if (*trans_id == NULL) { |
152 | + status = U1DB_NOMEM; |
153 | + } |
154 | } else { |
155 | *trans_id = strdup(tmp); |
156 | if (*trans_id == NULL) { |
157 | |
158 | === modified file 'src/u1db_sync_target.c' |
159 | --- src/u1db_sync_target.c 2012-06-19 19:41:19 +0000 |
160 | +++ src/u1db_sync_target.c 2012-06-19 19:41:19 +0000 |
161 | @@ -24,7 +24,7 @@ |
162 | static int st_get_sync_info(u1db_sync_target *st, |
163 | const char *source_replica_uid, |
164 | const char **st_replica_uid, int *st_gen, int *source_gen, |
165 | - char **trans_id); |
166 | + char **source_trans_id); |
167 | |
168 | static int st_record_sync_info(u1db_sync_target *st, |
169 | const char *source_replica_uid, int source_gen, const char *trans_id); |
170 | @@ -106,7 +106,7 @@ |
171 | static int |
172 | st_get_sync_info(u1db_sync_target *st, const char *source_replica_uid, |
173 | const char **st_replica_uid, int *st_gen, int *source_gen, |
174 | - char **trans_id) |
175 | + char **source_trans_id) |
176 | { |
177 | int status = U1DB_OK; |
178 | u1database *db; |
179 | @@ -125,8 +125,8 @@ |
180 | db = (u1database *)st->implementation; |
181 | status = u1db_get_replica_uid(db, st_replica_uid); |
182 | if (status != U1DB_OK) { goto finish; } |
183 | - status = u1db__get_sync_gen_info(db, source_replica_uid, source_gen, |
184 | - trans_id); |
185 | + status = u1db__get_sync_gen_info( |
186 | + db, source_replica_uid, source_gen, source_trans_id); |
187 | if (status != U1DB_OK) { goto finish; } |
188 | status = u1db__get_generation(db, st_gen); |
189 | finish: |
190 | @@ -614,6 +614,9 @@ |
191 | status = target->get_sync_info(target, local_uid, &target_uid, &target_gen, |
192 | &local_gen_known_by_target, &local_trans_id_known_by_target); |
193 | if (status != U1DB_OK) { goto finish; } |
194 | + status = u1db_validate_gen_and_trans_id( |
195 | + db, local_gen_known_by_target, local_trans_id_known_by_target); |
196 | + if (status != U1DB_OK) { goto finish; } |
197 | status = u1db__get_sync_gen_info(db, target_uid, |
198 | &target_gen_known_by_local, &target_trans_id_known_by_local); |
199 | if (status != U1DB_OK) { goto finish; } |
200 | @@ -623,8 +626,9 @@ |
201 | // Before we start the sync exchange, get the list of doc_ids that we want |
202 | // to send. We have to do this first, so that local_gen_before_sync will |
203 | // match exactly the list of doc_ids we send |
204 | - status = u1db_whats_changed(db, &local_gen, &local_trans_id, |
205 | - (void*)&to_send_state, whats_changed_to_doc_ids); |
206 | + status = u1db_whats_changed( |
207 | + db, &local_gen, &local_trans_id, (void*)&to_send_state, |
208 | + whats_changed_to_doc_ids); |
209 | if (status != U1DB_OK) { goto finish; } |
210 | if (local_gen == local_gen_known_by_target |
211 | && target_gen == target_gen_known_by_local) |
212 | @@ -644,19 +648,23 @@ |
213 | &target_gen_known_by_local, &target_trans_id_known_by_local, |
214 | &return_doc_state, return_doc_to_insert_from_target); |
215 | if (status != U1DB_OK) { goto finish; } |
216 | - status = u1db__get_generation(db, &local_gen); |
217 | + if (local_trans_id != NULL) { |
218 | + free(local_trans_id); |
219 | + } |
220 | + status = u1db__get_generation_info(db, &local_gen, &local_trans_id); |
221 | if (status != U1DB_OK) { goto finish; } |
222 | // Now we successfully sent and received docs, make sure we record the |
223 | // current remote generation |
224 | - status = u1db__set_sync_info(db, target_uid, target_gen_known_by_local, |
225 | - "T-sid"); |
226 | + status = u1db__set_sync_info( |
227 | + db, target_uid, target_gen_known_by_local, |
228 | + target_trans_id_known_by_local); |
229 | if (status != U1DB_OK) { goto finish; } |
230 | if (return_doc_state.num_inserted > 0 && |
231 | - ((*local_gen_before_sync + return_doc_state.num_inserted) |
232 | - == local_gen)) |
233 | + ((*local_gen_before_sync + return_doc_state.num_inserted) |
234 | + == local_gen)) |
235 | { |
236 | - status = target->record_sync_info(target, local_uid, local_gen, |
237 | - "T-sid"); |
238 | + status = target->record_sync_info( |
239 | + target, local_uid, local_gen, local_trans_id); |
240 | if (status != U1DB_OK) { goto finish; } |
241 | } |
242 | finish: |
243 | |
244 | === modified file 'u1db/backends/__init__.py' |
245 | --- u1db/backends/__init__.py 2012-06-19 19:16:35 +0000 |
246 | +++ u1db/backends/__init__.py 2012-06-19 19:41:19 +0000 |
247 | @@ -22,7 +22,6 @@ |
248 | import u1db |
249 | from u1db import ( |
250 | errors, |
251 | - vectorclock, |
252 | ) |
253 | import u1db.sync |
254 | from u1db.vectorclock import VectorClockRev |
255 | @@ -54,8 +53,17 @@ |
256 | raise errors.InvalidDocId() |
257 | |
258 | def _get_generation(self): |
259 | + """Return the current generation. |
260 | + |
261 | + """ |
262 | raise NotImplementedError(self._get_generation) |
263 | |
264 | + def _get_generation_info(self): |
265 | + """Return the current generation and transaction id. |
266 | + |
267 | + """ |
268 | + raise NotImplementedError(self._get_generation_info) |
269 | + |
270 | def _get_doc(self, doc_id): |
271 | """Extract the document from storage. |
272 | |
273 | @@ -94,6 +102,15 @@ |
274 | result.append(doc) |
275 | return result |
276 | |
277 | + def validate_gen_and_trans_id(self, generation, trans_id): |
278 | + """Validate the generation and transaction id. |
279 | + |
280 | + Raises an InvalidGeneration when the generation does not exist, and an |
281 | + InvalidTransactionId when it does but with a different transaction id. |
282 | + |
283 | + """ |
284 | + raise NotImplementedError(self.validate_gen_and_trans_id) |
285 | + |
286 | def _validate_source(self, other_replica_uid, other_generation, |
287 | other_transaction_id, cur_vcr, other_vcr): |
288 | """Validate the new generation and transaction id. |
289 | |
290 | === modified file 'u1db/backends/inmemory.py' |
291 | --- u1db/backends/inmemory.py 2012-06-18 12:51:14 +0000 |
292 | +++ u1db/backends/inmemory.py 2012-06-19 19:41:19 +0000 |
293 | @@ -79,6 +79,18 @@ |
294 | def _get_generation(self): |
295 | return len(self._transaction_log) |
296 | |
297 | + def _get_generation_info(self): |
298 | + return len(self._transaction_log), self._transaction_log[-1][1] |
299 | + |
300 | + def validate_gen_and_trans_id(self, generation, trans_id): |
301 | + if generation == 0: |
302 | + return |
303 | + if generation > len(self._transaction_log): |
304 | + raise errors.InvalidGeneration |
305 | + if self._transaction_log[generation - 1][1] == trans_id: |
306 | + return |
307 | + raise errors.InvalidTransactionId |
308 | + |
309 | def put_doc(self, doc): |
310 | if doc.doc_id is None: |
311 | raise errors.InvalidDocId() |
312 | |
313 | === modified file 'u1db/backends/sqlite_backend.py' |
314 | --- u1db/backends/sqlite_backend.py 2012-06-18 12:51:14 +0000 |
315 | +++ u1db/backends/sqlite_backend.py 2012-06-19 19:41:19 +0000 |
316 | @@ -265,6 +265,28 @@ |
317 | return 0 |
318 | return val |
319 | |
320 | + def _get_generation_info(self): |
321 | + c = self._db_handle.cursor() |
322 | + c.execute( |
323 | + 'SELECT max(generation), transaction_id FROM transaction_log ') |
324 | + val = c.fetchone() |
325 | + if val[0] is None: |
326 | + return(0, val[1]) |
327 | + return val |
328 | + |
329 | + def validate_gen_and_trans_id(self, generation, trans_id): |
330 | + if generation == 0: |
331 | + return |
332 | + c = self._db_handle.cursor() |
333 | + c.execute( |
334 | + 'SELECT transaction_id FROM transaction_log WHERE generation = ?', |
335 | + (generation,)) |
336 | + val = c.fetchone() |
337 | + if val is None: |
338 | + raise errors.InvalidGeneration |
339 | + if val[0] != trans_id: |
340 | + raise errors.InvalidTransactionId |
341 | + |
342 | def _get_transaction_log(self): |
343 | c = self._db_handle.cursor() |
344 | c.execute("SELECT doc_id, transaction_id FROM transaction_log" |
345 | |
346 | === modified file 'u1db/sync.py' |
347 | --- u1db/sync.py 2012-06-19 19:41:19 +0000 |
348 | +++ u1db/sync.py 2012-06-19 19:41:19 +0000 |
349 | @@ -83,11 +83,11 @@ |
350 | record with the target that they are fully up to date with our |
351 | new generation. |
352 | """ |
353 | - cur_gen = self.source._get_generation() |
354 | + cur_gen, trans_id = self.source._get_generation_info() |
355 | if (cur_gen == start_generation + self.num_inserted |
356 | - and self.num_inserted > 0): |
357 | - self.sync_target.record_sync_info(self.source._replica_uid, |
358 | - cur_gen, 'T-sid') |
359 | + and self.num_inserted > 0): |
360 | + self.sync_target.record_sync_info( |
361 | + self.source._replica_uid, cur_gen, trans_id) |
362 | |
363 | def sync(self, callback=None): |
364 | """Synchronize documents between source and target.""" |
365 | @@ -98,6 +98,8 @@ |
366 | target_my_trans_id) = sync_target.get_sync_info( |
367 | self.source._replica_uid) |
368 | # what's changed since that generation and this current gen |
369 | + self.source.validate_gen_and_trans_id( |
370 | + target_my_gen, target_my_trans_id) |
371 | my_gen, _, changes = self.source.whats_changed(target_my_gen) |
372 | |
373 | # this source last-seen database generation for the target |
374 | @@ -121,7 +123,8 @@ |
375 | self.source._replica_uid, target_last_known_gen, |
376 | return_doc_cb=self._insert_doc_from_target) |
377 | # record target synced-up-to generation including applying what we sent |
378 | - self.source._set_sync_info(self.target_replica_uid, new_gen, 'T-sid') |
379 | + self.source._set_sync_info( |
380 | + self.target_replica_uid, new_gen, new_trans_id) |
381 | |
382 | # if gapless record current reached generation with target |
383 | self._record_sync_info_with_the_target(my_gen) |
384 | |
385 | === modified file 'u1db/tests/c_backend_wrapper.pyx' |
386 | --- u1db/tests/c_backend_wrapper.pyx 2012-06-19 19:41:19 +0000 |
387 | +++ u1db/tests/c_backend_wrapper.pyx 2012-06-19 19:41:19 +0000 |
388 | @@ -204,6 +204,9 @@ |
389 | |
390 | |
391 | int u1db__get_generation(u1database *, int *db_rev) |
392 | + int u1db__get_generation_info(u1database *, int *db_rev, char **trans_id) |
393 | + int u1db_validate_gen_and_trans_id(u1database *, int db_rev, |
394 | + const_char_ptr trans_id) |
395 | char *u1db__allocate_doc_id(u1database *) |
396 | int u1db__sql_close(u1database *) |
397 | int u1db__sql_is_open(u1database *) |
398 | @@ -1064,6 +1067,22 @@ |
399 | u1db__get_generation(self._db, &generation)) |
400 | return generation |
401 | |
402 | + def _get_generation_info(self): |
403 | + cdef int generation |
404 | + cdef char *trans_id |
405 | + handle_status("get_generation_info", |
406 | + u1db__get_generation_info(self._db, &generation, &trans_id)) |
407 | + raw_trans_id = None |
408 | + if trans_id != NULL: |
409 | + raw_trans_id = trans_id |
410 | + free(trans_id) |
411 | + return generation, raw_trans_id |
412 | + |
413 | + def validate_gen_and_trans_id(self, generation, trans_id): |
414 | + handle_status( |
415 | + "validate_gen_and_trans_id", |
416 | + u1db_validate_gen_and_trans_id(self._db, generation, trans_id)) |
417 | + |
418 | def _get_sync_gen_info(self, replica_uid): |
419 | cdef int generation, status |
420 | cdef char *trans_id = NULL |
421 | |
422 | === modified file 'u1db/tests/test_backends.py' |
423 | --- u1db/tests/test_backends.py 2012-06-19 19:41:19 +0000 |
424 | +++ u1db/tests/test_backends.py 2012-06-19 19:41:19 +0000 |
425 | @@ -437,6 +437,25 @@ |
426 | self.db._put_doc_if_newer, doc, save_conflict=False, |
427 | replica_uid='other', replica_gen=1, replica_trans_id='T-sad') |
428 | |
429 | + def test_validate_gen_and_trans_id(self): |
430 | + self.db.create_doc(simple_doc) |
431 | + gen, trans_id = self.db._get_generation_info() |
432 | + self.db.validate_gen_and_trans_id(gen, trans_id) |
433 | + |
434 | + def test_validate_gen_and_trans_id_invalid_txid(self): |
435 | + self.db.create_doc(simple_doc) |
436 | + gen, _ = self.db._get_generation_info() |
437 | + self.assertRaises( |
438 | + errors.InvalidTransactionId, |
439 | + self.db.validate_gen_and_trans_id, gen, 'wrong') |
440 | + |
441 | + def test_validate_gen_and_trans_id_invalid_txid(self): |
442 | + self.db.create_doc(simple_doc) |
443 | + gen, trans_id = self.db._get_generation_info() |
444 | + self.assertRaises( |
445 | + errors.InvalidGeneration, |
446 | + self.db.validate_gen_and_trans_id, gen + 1, trans_id) |
447 | + |
448 | def test_validate_source_gen_and_trans_id_same(self): |
449 | self.db._set_sync_info('other', 1, 'T-sid') |
450 | v1 = vectorclock.VectorClockRev('other:1|self:1') |
451 | |
452 | === modified file 'u1db/tests/test_c_backend.py' |
453 | --- u1db/tests/test_c_backend.py 2012-06-19 19:41:19 +0000 |
454 | +++ u1db/tests/test_c_backend.py 2012-06-19 19:41:19 +0000 |
455 | @@ -73,6 +73,14 @@ |
456 | db.create_doc(tests.simple_doc) |
457 | self.assertEqual(1, db._get_generation()) |
458 | |
459 | + def test__get_generation_info(self): |
460 | + db = c_backend_wrapper.CDatabase(':memory:') |
461 | + self.assertEqual((0, None), db._get_generation_info()) |
462 | + db.create_doc(tests.simple_doc) |
463 | + info = db._get_generation_info() |
464 | + self.assertEqual(1, info[0]) |
465 | + self.assertTrue(info[1].startswith('T-')) |
466 | + |
467 | def test__set_replica_uid(self): |
468 | db = c_backend_wrapper.CDatabase(':memory:') |
469 | self.assertIsNot(None, db._replica_uid) |
470 | |
471 | === modified file 'u1db/tests/test_sqlite_backend.py' |
472 | --- u1db/tests/test_sqlite_backend.py 2012-05-31 16:31:31 +0000 |
473 | +++ u1db/tests/test_sqlite_backend.py 2012-06-19 19:41:19 +0000 |
474 | @@ -152,6 +152,9 @@ |
475 | def test__get_generation(self): |
476 | self.assertEqual(0, self.db._get_generation()) |
477 | |
478 | + def test__get_generation_info(self): |
479 | + self.assertEqual((0, None), self.db._get_generation_info()) |
480 | + |
481 | def test_create_index(self): |
482 | self.db.create_index('test-idx', "key") |
483 | self.assertEqual([('test-idx', ["key"])], self.db.list_indexes()) |
484 | @@ -261,7 +264,7 @@ |
485 | def test__open_database_with_factory(self): |
486 | temp_dir = self.createTempDir(prefix='u1db-test-') |
487 | path = temp_dir + '/test.sqlite' |
488 | - db = sqlite_backend.SQLitePartialExpandDatabase(path) |
489 | + sqlite_backend.SQLitePartialExpandDatabase(path) |
490 | db2 = sqlite_backend.SQLiteDatabase._open_database( |
491 | path, document_factory=TestAlternativeDocument) |
492 | self.assertEqual(TestAlternativeDocument, db2._factory) |
493 | @@ -322,7 +325,7 @@ |
494 | def test_open_database_with_factory(self): |
495 | temp_dir = self.createTempDir(prefix='u1db-test-') |
496 | path = temp_dir + '/existing.sqlite' |
497 | - db = sqlite_backend.SQLitePartialExpandDatabase(path) |
498 | + sqlite_backend.SQLitePartialExpandDatabase(path) |
499 | db2 = sqlite_backend.SQLiteDatabase.open_database( |
500 | path, create=False, document_factory=TestAlternativeDocument) |
501 | self.assertEqual(TestAlternativeDocument, db2._factory) |
502 | |
503 | === modified file 'u1db/tests/test_sync.py' |
504 | --- u1db/tests/test_sync.py 2012-06-19 19:41:19 +0000 |
505 | +++ u1db/tests/test_sync.py 2012-06-19 19:41:19 +0000 |
506 | @@ -839,6 +839,12 @@ |
507 | doc1 = self.db1.create_doc('{"a": 1}', doc_id='the-doc') |
508 | db3 = self.create_database('test3') |
509 | self.sync(self.db2, self.db1) |
510 | + self.assertEqual( |
511 | + self.db1._get_generation_info(), |
512 | + self.db2._get_sync_gen_info(self.db1._replica_uid)) |
513 | + self.assertEqual( |
514 | + self.db2._get_generation_info(), |
515 | + self.db1._get_sync_gen_info(self.db2._replica_uid)) |
516 | self.sync(db3, self.db1) |
517 | # update on 2 |
518 | doc2 = self.make_document('the-doc', doc1.rev, '{"a": 2}') |
519 | @@ -872,7 +878,19 @@ |
520 | self.db2.create_doc('{"b": 1}', doc_id='the-doc') |
521 | db3.create_doc('{"c": 1}', doc_id='the-doc') |
522 | self.sync(db3, self.db1) |
523 | + self.assertEqual( |
524 | + self.db1._get_generation_info(), |
525 | + db3._get_sync_gen_info(self.db1._replica_uid)) |
526 | + self.assertEqual( |
527 | + db3._get_generation_info(), |
528 | + self.db1._get_sync_gen_info(db3._replica_uid)) |
529 | self.sync(db3, self.db2) |
530 | + self.assertEqual( |
531 | + self.db2._get_generation_info(), |
532 | + db3._get_sync_gen_info(self.db2._replica_uid)) |
533 | + self.assertEqual( |
534 | + db3._get_generation_info(), |
535 | + self.db2._get_sync_gen_info(db3._replica_uid)) |
536 | self.assertEqual(3, len(db3.get_doc_conflicts('the-doc'))) |
537 | doc1.set_json('{"a": 2}') |
538 | self.db1.put_doc(doc1) |
looks good, as we discussed we need integration tests showing that the validate cases trigger in appropriate sync situations