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));
                }
            });
        }
Пример #2
0
        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));
                }
            });
        }