public void TestConflict() { RunTestVariants(() => { if (!IsRevTrees()) { return; } var body2 = C4Slice.Constant("{\"ok\":\"go\"}"); var body3 = C4Slice.Constant("{\"ubu\":\"roi\"}"); CreateRev(DocID.CreateString(), RevID, Body); CreateRev(DocID.CreateString(), Rev2ID, body2, C4RevisionFlags.KeepBody); CreateRev(DocID.CreateString(), C4Slice.Constant("3-aaaaaa"), body3); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { // "Pull" a conflicting revision: var history = new C4Slice[] { C4Slice.Constant("4-dddd"), C4Slice.Constant("3-ababab"), Rev2ID }; fixed(C4Slice * history_ = history) { var rq = new C4DocPutRequest { existingRevision = true, docID = DocID, history = history_, historyCount = 3, body = body3, save = true, remoteDBID = 1 }; C4Error error; var doc = Native.c4doc_put(Db, &rq, null, &error); ((IntPtr)doc).Should().NotBe(IntPtr.Zero); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "4-dddd").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "4-dddd", "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-ababab", "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "3-ababab").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, Rev2ID.CreateString(), "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", Rev2ID.CreateString()).Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); NativeRaw.c4doc_selectCommonAncestorRevision(doc, Rev2ID, Rev2ID).Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); } } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err)); LiteCoreBridge.Check(err => Native.c4doc_resolveConflict(doc, "4-dddd", "3-aaaaaa", Encoding.UTF8.GetBytes("{\"merged\":true}"), 0, err)); Native.c4doc_selectCurrentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("5-940fe7e020dbf8db0f82a5d764870c4b6c88ae99"); doc->selectedRev.body.CreateString().Should().Be("{\"merged\":true}"); Native.c4doc_selectParentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("4-dddd"); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err)); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err)); LiteCoreBridge.Check(err => Native.c4doc_resolveConflict(doc, "3-aaaaaa", "4-dddd", Encoding.UTF8.GetBytes("{\"merged\":true}"), 0, err)); Native.c4doc_selectCurrentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("4-333ee0677b5f1e1e5064b050d417a31d2455dc30"); doc->selectedRev.body.CreateString().Should().Be("{\"merged\":true}"); Native.c4doc_selectParentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("3-aaaaaa"); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err)); } }); }
public void TestGetForPut() { RunTestVariants(() => { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { // Creating doc given ID: var doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, DocID, C4Slice.Null, false, false, err)); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); doc->revID.Equals(C4Slice.Null).Should().BeTrue("because a rev ID has not been assigned yet"); ((int)doc->flags).Should().Be(0, "because the document has no flags yet"); doc->selectedRev.revID.Equals(C4Slice.Null).Should().BeTrue("because no rev ID has been assigned yet"); Native.c4doc_free(doc); // Creating doc, no ID: doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, C4Slice.Null, C4Slice.Null, false, false, err)); doc->docID.size.Should().BeGreaterOrEqualTo(20, "because the document should be assigned a random ID"); doc->revID.Equals(C4Slice.Null).Should().BeTrue("because the doc doesn't have a rev ID yet"); ((int)doc->flags).Should().Be(0, "because the document has no flags yet"); doc->selectedRev.revID.Equals(C4Slice.Null).Should().BeTrue("because no rev ID has been assigned yet"); Native.c4doc_free(doc); // Delete with no revID given C4Error error; doc = NativeRawPrivate.c4doc_getForPut(Db, C4Slice.Null, C4Slice.Null, true, false, &error); ((long)doc).Should().Be(0, "because the document does not exist"); error.code.Should().Be((int)LiteCoreError.NotFound, "because the correct error should be returned"); // Adding new rev of nonexistent doc: doc = NativeRawPrivate.c4doc_getForPut(Db, DocID, RevID, false, false, &error); ((long)doc).Should().Be(0, "because the document does not exist"); error.code.Should().Be((int)LiteCoreError.NotFound, "because the correct error should be returned"); // Adding new rev of existing doc: CreateRev(DocID.CreateString(), RevID, Body); doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, DocID, RevID, false, false, err)); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the correct rev ID"); doc->flags.Should().Be(C4DocumentFlags.Exists, "because the document has no flags yet"); doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the selected rev should have the correct rev ID"); Native.c4doc_free(doc); // Adding new rev, with nonexistent parent doc = NativeRawPrivate.c4doc_getForPut(Db, DocID, Rev2ID, false, false, &error); ((long)doc).Should().Be(0, "because the document does not exist"); error.code.Should().Be((int)LiteCoreError.Conflict, "because the correct error should be returned"); // Conflict -- try & fail to update non-current rev: var body2 = C4Slice.Constant("{\"ok\":\"go\"}"); CreateRev(DocID.CreateString(), Rev2ID, body2); doc = NativeRawPrivate.c4doc_getForPut(Db, DocID, RevID, false, false, &error); ((long)doc).Should().Be(0, "because the document does not exist"); error.code.Should().Be((int)LiteCoreError.Conflict, "because the correct error should be returned"); if (Versioning == C4DocumentVersioning.RevisionTrees) { // Conflict -- force an update of non-current rev: doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, DocID, RevID, false, true, err)); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the doc should have the correct rev ID"); Native.c4doc_free(doc); } // Deleting the doc: doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, DocID, Rev2ID, true, false, err)); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); doc->selectedRev.revID.Equals(Rev2ID).Should().BeTrue("because the doc should have the correct rev ID"); Native.c4doc_free(doc); // Actually delete it: CreateRev(DocID.CreateString(), Rev3ID, C4Slice.Null, C4RevisionFlags.Deleted); LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); // Re-creating the doc (no revID given): doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.c4doc_getForPut(Db, DocID, C4Slice.Null, false, false, err)); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); doc->selectedRev.revID.Equals(Rev3ID).Should().BeTrue("because the doc should have the correct rev ID"); doc->flags.Should().Be(C4DocumentFlags.Exists | C4DocumentFlags.Deleted, "because the document was deleted"); doc->selectedRev.revID.Equals(Rev3ID).Should().BeTrue("because the doc should have the correct rev ID"); Native.c4doc_free(doc); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } }); }
public void TestPurge() { RunTestVariants(() => { var body2 = C4Slice.Constant("{\"ok\":\"go\"}"); var body3 = C4Slice.Constant("{\"ubu\":\"roi\"}"); CreateRev(DocID.CreateString(), RevID, Body); CreateRev(DocID.CreateString(), Rev2ID, body2); CreateRev(DocID.CreateString(), Rev3ID, body3); var history = new[] { C4Slice.Constant("3-ababab"), Rev2ID }; fixed(C4Slice * history_ = history) { var rq = new C4DocPutRequest { existingRevision = true, docID = DocID, history = history_, historyCount = 2, body = body3, save = true }; C4Error error; C4Document *doc = null; LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { doc = Native.c4doc_put(Db, &rq, null, &error); ((IntPtr)doc).Should().NotBe(IntPtr.Zero); Native.c4doc_free(doc); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { LiteCoreBridge.Check(err => NativeRaw.c4db_purgeDoc(Db, DocID, err)); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } Native.c4db_getDocumentCount(Db).Should().Be(0UL); CreateRev(DocID.CreateString(), RevID, Body); CreateRev(DocID.CreateString(), Rev2ID, body2); CreateRev(DocID.CreateString(), Rev3ID, body3); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { doc = Native.c4doc_put(Db, &rq, null, &error); ((IntPtr)doc).Should().NotBe(IntPtr.Zero); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { LiteCoreBridge.Check(err => Native.c4doc_purgeRevision(doc, null, err)); LiteCoreBridge.Check(err => Native.c4doc_save(doc, 0, err)); } finally { Native.c4doc_free(doc); LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } Native.c4db_getDocumentCount(Db).Should().Be(0UL); } }); }
public void TestConflict() { RunTestVariants(() => { if (!IsRevTrees(Db)) { return; } var body2 = JSON2Fleece("{\"ok\":\"go\"}"); var body3 = JSON2Fleece("{\"ubu\":\"roi\"}"); CreateRev(DocID.CreateString(), RevID, FleeceBody); CreateRev(DocID.CreateString(), Rev2ID, (FLSlice)body2, C4RevisionFlags.KeepBody); CreateRev(DocID.CreateString(), FLSlice.Constant("3-aaaaaa"), (FLSlice)body3); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { // "Pull" a conflicting revision: var history = new FLSlice[] { FLSlice.Constant("4-dddd"), FLSlice.Constant("3-ababab"), Rev2ID }; fixed(FLSlice * history_ = history) { var rq = new C4DocPutRequest { existingRevision = true, docID = DocID, history = history_, historyCount = 3, allowConflict = true, body = (FLSlice)body3, save = true, remoteDBID = 1 }; C4Error error; var doc = Native.c4doc_put(Db, &rq, null, &error); ((IntPtr)doc).Should().NotBe(IntPtr.Zero); Native.FLSliceResult_Release(body2); Native.FLSliceResult_Release(body3); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "4-dddd").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "4-dddd", "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-ababab", "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "3-ababab").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, Rev2ID.CreateString(), "3-aaaaaa").Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", Rev2ID.CreateString()).Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); NativeRaw.c4doc_selectCommonAncestorRevision(doc, Rev2ID, Rev2ID).Should().BeTrue(); doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString()); } } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } var mergedBody = JSON2Fleece("{\"merged\":true}"); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, DocID.CreateString(), true, C4DocContentLevel.DocGetCurrentRev, err)); LiteCoreBridge.Check(err => NativeRaw.c4doc_resolveConflict(doc, FLSlice.Constant("4-dddd"), FLSlice.Constant("3-aaaaaa"), (FLSlice)mergedBody, 0, err)); Native.c4doc_selectCurrentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("5-79b2ecd897d65887a18c46cc39db6f0a3f7b38c4"); NativeRaw.c4doc_getRevisionBody(doc).Equals(mergedBody).Should().BeTrue(); Native.c4doc_selectParentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("4-dddd"); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err)); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, DocID.CreateString(), true, C4DocContentLevel.DocGetCurrentRev, err)); LiteCoreBridge.Check(err => NativeRaw.c4doc_resolveConflict(doc, FLSlice.Constant("3-aaaaaa"), FLSlice.Constant("4-dddd"), (FLSlice)mergedBody, 0, err)); Native.c4doc_selectCurrentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("4-1fa2dbcb66b5e0456f6d6fc4a90918d42f3dd302"); NativeRaw.c4doc_getRevisionBody(doc).Equals(mergedBody).Should().BeTrue(); Native.c4doc_selectParentRevision(doc); doc->selectedRev.revID.CreateString().Should().Be("3-aaaaaa"); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err)); } }); }