public void TestForceInsertEmptyHistory()
        {
            var rev           = new RevisionInternal("FakeDocId", "1-tango", false, database);
            var revProperties = new Dictionary <string, object>();

            revProperties.Put("_id", rev.GetDocId());
            revProperties.Put("_rev", rev.GetRevId());
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);

            IList <string> revHistory = null;

            database.ForceInsert(rev, revHistory, null);
        }
Example #2
0
        private DocumentChange ChangeWithNewRevision(RevisionInternal inRev, bool isWinningRev, C4Document *doc, Uri source)
        {
            var winningRevId = default(string);

            if (isWinningRev)
            {
                winningRevId = inRev.GetRevId();
            }
            else
            {
                winningRevId = (string)doc->revID;
            }

            return(new DocumentChange(inRev, winningRevId, doc->IsConflicted, source));
        }
        /// <exception cref="System.Exception"></exception>
        public virtual void TestCorrectWinningRevisionLongerBranch()
        {
            // Create a conflict on purpose
            Document      doc   = database.CreateDocument();
            SavedRevision rev1  = doc.CreateRevision().Save();
            SavedRevision rev2a = CreateRevisionWithRandomProps(rev1, false);
            SavedRevision rev2b = CreateRevisionWithRandomProps(rev1, true);
            SavedRevision rev3b = CreateRevisionWithRandomProps(rev2b, true);
            // rev3b should be picked as the winner since it has a longer branch
            SavedRevision    expectedWinner = rev3b;
            RevisionInternal revFound       = database.GetDocumentWithIDAndRev(doc.GetId(), null, EnumSet
                                                                               .NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(expectedWinner.GetId(), revFound.GetRevId());
        }
        internal SavedRevision GetRevisionFromRev(RevisionInternal internalRevision)
        {
            if (internalRevision == null)
            {
                return(null);
            }

            if (currentRevision != null && internalRevision.GetRevId().Equals(CurrentRevision.Id))
            {
                return(currentRevision);
            }
            else
            {
                return(new SavedRevision(this, internalRevision));
            }
        }
Example #5
0
            public IDictionary <string, object> Invoke(RevisionInternal source)
            {
                AtomicBoolean  hasAttachment = new AtomicBoolean(false);
                IList <string> attsSince     = database.GetPossibleAncestorRevisionIDs(source, Puller.
                                                                                       MaxNumberOfAttsSince, hasAttachment);

                if (!hasAttachment.Get() || attsSince.Count == 0)
                {
                    attsSince = null;
                }
                IDictionary <string, object> mapped = new Dictionary <string, object>();

                mapped.Put("id", source.GetDocId());
                mapped.Put("rev", source.GetRevId());
                mapped.Put("atts_since", attsSince);
                return(mapped);
            }
        internal void RevisionAdded(DocumentChange documentChange)
        {
            RevisionInternal rev = documentChange.GetWinningRevision();

            if (rev == null)
            {
                return;
            }
            // current revision didn't change
            if (currentRevision != null && !rev.GetRevId().Equals(currentRevision.GetId()))
            {
                currentRevision = new SavedRevision(this, rev);
            }
            foreach (Document.ChangeListener listener in changeListeners)
            {
                listener.Changed(new Document.ChangeEvent(this, documentChange));
            }
        }
Example #7
0
        private static void VerifyHistory(Database db, RevisionInternal rev, IList <string
                                                                                    > history)
        {
            RevisionInternal gotRev = db.GetDocumentWithIDAndRev(rev.GetDocId(), null, EnumSet
                                                                 .NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(rev, gotRev);
            NUnit.Framework.Assert.AreEqual(rev.Properties, gotRev.Properties);
            IList <RevisionInternal> revHistory = db.GetRevisionHistory(gotRev);

            NUnit.Framework.Assert.AreEqual(history.Count, revHistory.Count);
            for (int i = 0; i < history.Count; i++)
            {
                RevisionInternal hrev = revHistory[i];
                NUnit.Framework.Assert.AreEqual(rev.GetDocId(), hrev.GetDocId());
                NUnit.Framework.Assert.AreEqual(history[i], hrev.GetRevId());
                NUnit.Framework.Assert.IsFalse(rev.IsDeleted());
            }
        }
Example #8
0
        public RevisionInternal GetParentRevision(RevisionInternal rev)
        {
            var retVal = default(RevisionInternal);

            WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc =>
            {
                if (!Native.c4doc_selectParentRevision(doc))
                {
                    return;
                }

                ForestDBBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err));
                retVal = new RevisionInternal((string)doc->docID, (string)doc->selectedRev.revID, doc->selectedRev.IsDeleted);
                retVal.SetSequence((long)doc->selectedRev.sequence);
                retVal.SetBody(new Body(doc->selectedRev.body));
            });

            return(retVal);
        }
        /// <summary>https://github.com/couchbase/couchbase-lite-java-core/issues/135</summary>
        /// <exception cref="System.Exception"></exception>
        public virtual void TestCorrectWinningRevisionHighRevisionNumber()
        {
            // Create a conflict on purpose
            Document         doc      = database.CreateDocument();
            SavedRevision    rev1     = doc.CreateRevision().Save();
            SavedRevision    rev2a    = CreateRevisionWithRandomProps(rev1, false);
            SavedRevision    rev2b    = CreateRevisionWithRandomProps(rev1, true);
            SavedRevision    rev3b    = CreateRevisionWithRandomProps(rev2b, true);
            SavedRevision    rev4b    = CreateRevisionWithRandomProps(rev3b, true);
            SavedRevision    rev5b    = CreateRevisionWithRandomProps(rev4b, true);
            SavedRevision    rev6b    = CreateRevisionWithRandomProps(rev5b, true);
            SavedRevision    rev7b    = CreateRevisionWithRandomProps(rev6b, true);
            SavedRevision    rev8b    = CreateRevisionWithRandomProps(rev7b, true);
            SavedRevision    rev9b    = CreateRevisionWithRandomProps(rev8b, true);
            SavedRevision    rev10b   = CreateRevisionWithRandomProps(rev9b, true);
            RevisionInternal revFound = database.GetDocumentWithIDAndRev(doc.GetId(), null, EnumSet
                                                                         .NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(rev10b.GetId(), revFound.GetRevId());
        }
Example #10
0
        private void VerifyHistory(Database db, RevisionInternal rev, IList <string> history)
        {
            var gotRev = db.GetDocumentWithIDAndRev(rev.GetDocId(), null,
                                                    DocumentContentOptions.None);

            Assert.AreEqual(rev, gotRev);
            AssertPropertiesAreEqual(rev.GetProperties(), gotRev.GetProperties());

            var revHistory = db.GetRevisionHistory(gotRev);

            Assert.AreEqual(history.Count, revHistory.Count);

            for (int i = 0; i < history.Count; i++)
            {
                RevisionInternal hrev = revHistory[i];
                Assert.AreEqual(rev.GetDocId(), hrev.GetDocId());
                Assert.AreEqual(history[i], hrev.GetRevId());
                Assert.IsFalse(rev.IsDeleted());
            }
        }
Example #11
0
        public IList <RevisionInternal> GetRevisionHistory(RevisionInternal rev, ICollection <string> ancestorRevIds)
        {
            var history = new List <RevisionInternal>();

            WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc =>
            {
                var enumerator = new CBForestHistoryEnumerator(doc, false);
                foreach (var next in enumerator)
                {
                    if (ancestorRevIds != null && ancestorRevIds.Contains((string)next.SelectedRev.revID))
                    {
                        break;
                    }

                    var newRev = new RevisionInternal(next.GetDocument(), false);
                    newRev.SetMissing(!Native.c4doc_hasRevisionBody(next.GetDocument()));
                    history.Add(newRev);
                }
            });

            return(history);
        }
Example #12
0
        public void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread().ToString() + ": pullRemoteRevision with rev: "
                  + rev);
            Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskStarted()"
                  );
            AsyncTaskStarted();
            ++httpConnectionCount;
            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            StringBuilder path = new StringBuilder("/" + URLEncoder.Encode(rev.GetDocId()) +
                                                   "?rev=" + URLEncoder.Encode(rev.GetRevId()) + "&revs=true&attachments=true");
            IList <string> knownRevs = KnownCurrentRevIDs(rev);

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                Log.D(Database.Tag, this + "|" + Sharpen.Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskFinished()"
                      );
                AsyncTaskFinished(1);
                --httpConnectionCount;
                return;
            }
            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }
            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            string pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest("GET", pathInside, null, db, new _RemoteRequestCompletionBlock_336
                                                    (this, rev, pathInside));
        }
Example #13
0
 /// <summary>
 /// Creates a dictionary of metadata for one specific revision
 /// </summary>
 /// <returns>The metadata dictionary</returns>
 /// <param name="rev">The revision to examine</param>
 /// <param name="responseState">The current response state</param>
 public static IDictionary <string, object> ChangesDictForRev(RevisionInternal rev, DBMonitorCouchbaseResponseState responseState)
 {
     if (responseState.ChangesIncludeDocs)
     {
         var status = new Status();
         var rev2   = DocumentMethods.ApplyOptions(responseState.ContentOptions, rev, responseState.Context, responseState.Db, status);
         if (rev2 != null)
         {
             rev2.SetSequence(rev.GetSequence());
             rev = rev2;
         }
     }
     return(new NonNullDictionary <string, object> {
         { "seq", rev.GetSequence() },
         { "id", rev.GetDocId() },
         { "changes", new List <object> {
               new Dictionary <string, object> {
                   { "rev", rev.GetRevId() }
               }
           } },
         { "deleted", rev.IsDeleted() ? (object)true : null },
         { "doc", responseState.ChangesIncludeDocs ? rev.GetProperties() : null }
     });
 }
        /// <exception cref="System.Exception"></exception>
        public virtual void TestPusher()
        {
            CountDownLatch replicationDoneSignal = new CountDownLatch(1);
            Uri            remote         = GetReplicationURL();
            string         docIdTimestamp = System.Convert.ToString(Runtime.CurrentTimeMillis());
            // Create some documents:
            IDictionary <string, object> documentProperties = new Dictionary <string, object>();
            string doc1Id = string.Format("doc1-%s", docIdTimestamp);

            documentProperties.Put("_id", doc1Id);
            documentProperties.Put("foo", 1);
            documentProperties.Put("bar", false);
            Body             body   = new Body(documentProperties);
            RevisionInternal rev1   = new RevisionInternal(body, database);
            Status           status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            documentProperties.Put("_rev", rev1.GetRevId());
            documentProperties.Put("UPDATED", true);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
                                                                              , database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            documentProperties = new Dictionary <string, object>();
            string doc2Id = string.Format("doc2-%s", docIdTimestamp);

            documentProperties.Put("_id", doc2Id);
            documentProperties.Put("baz", 666);
            documentProperties.Put("fnord", true);
            database.PutRevision(new RevisionInternal(documentProperties, database), null, false
                                 , status);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            bool        continuous = false;
            Replication repl       = database.CreatePushReplication(remote);

            repl.SetContinuous(continuous);
            repl.SetCreateTarget(true);
            // Check the replication's properties:
            NUnit.Framework.Assert.AreEqual(database, repl.GetLocalDatabase());
            NUnit.Framework.Assert.AreEqual(remote, repl.GetRemoteUrl());
            NUnit.Framework.Assert.IsFalse(repl.IsPull());
            NUnit.Framework.Assert.IsFalse(repl.IsContinuous());
            NUnit.Framework.Assert.IsTrue(repl.ShouldCreateTarget());
            NUnit.Framework.Assert.IsNull(repl.GetFilter());
            NUnit.Framework.Assert.IsNull(repl.GetFilterParams());
            // TODO: CAssertNil(r1.doc_ids);
            // TODO: CAssertNil(r1.headers);
            // Check that the replication hasn't started running:
            NUnit.Framework.Assert.IsFalse(repl.IsRunning());
            NUnit.Framework.Assert.AreEqual(Replication.ReplicationStatus.ReplicationStopped,
                                            repl.GetStatus());
            NUnit.Framework.Assert.AreEqual(0, repl.GetCompletedChangesCount());
            NUnit.Framework.Assert.AreEqual(0, repl.GetChangesCount());
            NUnit.Framework.Assert.IsNull(repl.GetLastError());
            RunReplication(repl);
            // make sure doc1 is there
            // TODO: make sure doc2 is there (refactoring needed)
            Uri replicationUrlTrailing = new Uri(string.Format("%s/", remote.ToExternalForm()
                                                               ));
            Uri pathToDoc = new Uri(replicationUrlTrailing, doc1Id);

            Log.D(Tag, "Send http request to " + pathToDoc);
            CountDownLatch httpRequestDoneSignal = new CountDownLatch(1);
            BackgroundTask getDocTask            = new _BackgroundTask_122(pathToDoc, doc1Id, httpRequestDoneSignal
                                                                           );

            //Closes the connection.
            getDocTask.Execute();
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                httpRequestDoneSignal.Await(300, TimeUnit.Seconds);
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Sharpen.Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusher() finished");
        }
Example #15
0
        public static ICouchbaseResponseState RevsDiff(ICouchbaseListenerContext context)
        {
            // Collect all of the input doc/revision IDs as CBL_Revisions:
            var revs = new RevisionList();
            var body = context.BodyAs <Dictionary <string, object> >();

            if (body == null)
            {
                return(context.CreateResponse(StatusCode.BadJson).AsDefaultState());
            }

            foreach (var docPair in body)
            {
                var revIDs = docPair.Value.AsList <string>();
                if (revIDs == null)
                {
                    return(context.CreateResponse(StatusCode.BadParam).AsDefaultState());
                }

                foreach (var revID in revIDs)
                {
                    var rev = new RevisionInternal(docPair.Key, revID, false);
                    revs.Add(rev);
                }
            }

            return(PerformLogicWithDatabase(context, true, db =>
            {
                var response = context.CreateResponse();
                // Look them up, removing the existing ones from revs:
                db.Storage.FindMissingRevisions(revs);

                // Return the missing revs in a somewhat different format:
                IDictionary <string, object> diffs = new Dictionary <string, object>();
                foreach (var rev in revs)
                {
                    var docId = rev.GetDocId();
                    IList <string> missingRevs = null;
                    if (!diffs.ContainsKey(docId))
                    {
                        missingRevs = new List <string>();
                        diffs[docId] = new Dictionary <string, IList <string> > {
                            { "missing", missingRevs }
                        };
                    }
                    else
                    {
                        missingRevs = ((Dictionary <string, IList <string> >)diffs[docId])["missing"];
                    }

                    missingRevs.Add(rev.GetRevId());
                }

                // Add the possible ancestors for each missing revision:
                foreach (var docPair in diffs)
                {
                    IDictionary <string, IList <string> > docInfo = (IDictionary <string, IList <string> >)docPair.Value;
                    int maxGen = 0;
                    string maxRevID = null;
                    foreach (var revId in docInfo["missing"])
                    {
                        var parsed = RevisionInternal.ParseRevId(revId);
                        if (parsed.Item1 > maxGen)
                        {
                            maxGen = parsed.Item1;
                            maxRevID = revId;
                        }
                    }

                    var rev = new RevisionInternal(docPair.Key, maxRevID, false);
                    var ancestors = db.Storage.GetPossibleAncestors(rev, 0, false);
                    var ancestorList = ancestors == null ? null : ancestors.ToList();
                    if (ancestorList != null && ancestorList.Count > 0)
                    {
                        docInfo["possible_ancestors"] = ancestorList;
                    }
                }

                response.JsonBody = new Body(diffs);
                return response;
            }).AsDefaultState());
        }
Example #16
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        internal void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(Tag, "PullRemoteRevision with rev: {0}", rev);

            Log.D(Tag, "PullRemoteRevision() calling AsyncTaskStarted()");
            AsyncTaskStarted();

            httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + Uri.EscapeUriString(rev.GetDocId()) + "?rev=" + Uri.EscapeUriString(rev.GetRevId()) + "&revs=true&attachments=true");
            var knownRevs = KnownCurrentRevIDs(rev);

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                Log.D(Tag, "PullRemoteRevision() calling AsyncTaskFinished()");
                AsyncTaskFinished(1);

                httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                try
                {
                    // OK, now we've got the response revision:
                    Log.D(Tag, "PullRemoteRevision got response for rev: " + rev);

                    if (e != null)
                    {
                        Log.E(Tag, "Error pulling remote revision", e);
                        SetLastError(e);
                        RevisionFailed();
                        Log.D(Tag, "PullRemoteRevision updating completedChangesCount from " +
                              CompletedChangesCount + " -> " + (CompletedChangesCount + 1)
                              + " due to error pulling remote revision");
                        SafeIncrementCompletedChangesCount();
                    }
                    else
                    {
                        var properties = result.AsDictionary <string, object>();
                        var gotRev     = new PulledRevision(properties);
                        gotRev.SetSequence(rev.GetSequence());
                        AsyncTaskStarted();
                        Log.D(Tag, "PullRemoteRevision add rev: " + gotRev + " to batcher");

                        if (downloadsToInsert != null)
                        {
                            downloadsToInsert.QueueObject(gotRev);
                        }
                        else
                        {
                            Log.E(Tag, "downloadsToInsert is null");
                        }
                    }
                }
                finally
                {
                    Log.D(Tag, "PullRemoteRevision.onCompletion() calling AsyncTaskFinished()");
                    AsyncTaskFinished(1);
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --httpConnectionCount;
                PullRemoteRevisions();
            });
        }
        public void TestLocalDocs()
        {
            //create a document
            var documentProperties = new Dictionary <string, object>();

            documentProperties["_id"] = "_local/doc1";
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body);

            rev1 = database.PutLocalRevision(rev1, null);
            Log.V(Tag, "Created " + rev1);
            Assert.AreEqual("_local/doc1", rev1.GetDocId());
            Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));

            //read it back
            var readRev = database.GetLocalDocument(rev1.GetDocId(), null);

            Assert.IsNotNull(readRev);
            var readRevProps = readRev.GetProperties();

            Assert.AreEqual(rev1.GetDocId(), readRevProps.Get("_id"));
            Assert.AreEqual(rev1.GetRevId(), readRevProps.Get("_rev"));
            AssertPropertiesAreEqual(UserProperties(readRevProps),
                                     UserProperties(body.GetProperties()));

            //now update it
            documentProperties           = (Dictionary <string, object>)readRev.GetProperties();
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            var rev2      = new RevisionInternal(body);
            var rev2input = rev2;

            rev2 = database.PutLocalRevision(rev2, rev1.GetRevId());
            Log.V(Tag, "Updated " + rev1);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));

            //read it back
            readRev = database.GetLocalDocument(rev2.GetDocId(), null);
            Assert.IsNotNull(readRev);
            AssertPropertiesAreEqual(UserProperties(readRev.GetProperties()),
                                     UserProperties(body.GetProperties()));

            // Try to update the first rev, which should fail:
            var gotException = false;

            try
            {
                database.PutLocalRevision(rev2input, rev1.GetRevId());
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.Conflict, e.GetCBLStatus().GetCode());
                gotException = true;
            }
            Assert.IsTrue(gotException);

            // Delete it:
            var revD = new RevisionInternal(rev2.GetDocId(), null, true);

            gotException = false;
            try
            {
                var revResult = database.PutLocalRevision(revD, null);
                Assert.IsNull(revResult);
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.Conflict, e.GetCBLStatus().GetCode());
                gotException = true;
            }
            Assert.IsTrue(gotException);
            revD = database.PutLocalRevision(revD, rev2.GetRevId());

            // Delete nonexistent doc:
            gotException = false;
            var revFake = new RevisionInternal("_local/fake", null, true);

            try
            {
                database.PutLocalRevision(revFake, null);
            }
            catch (CouchbaseLiteException e)
            {
                Assert.AreEqual(StatusCode.NotFound, e.GetCBLStatus().GetCode());
                gotException = true;
            }
            Assert.IsTrue(gotException);

            // Read it back (should fail):
            readRev = database.GetLocalDocument(revD.GetDocId(), null);
            Assert.IsNull(readRev);
        }
 /// <summary>Creates and saves a new revision with the given properties.</summary>
 /// <remarks>
 /// Creates and saves a new <see cref="Couchbase.Lite.Revision"/> with the specified properties. To succeed the specified properties must include a '_rev' property whose value maches the current %Revision's% id.
 /// This will fail with a 412 error if the receiver is not the current revision of the document.
 /// </remarks>
 /// <returns>
 /// The new <see cref="Couchbase.Lite.SavedRevision"/>.
 /// </returns>
 /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
 public SavedRevision CreateRevision(IDictionary <String, Object> properties)
 {
     return(Document.PutProperties(properties, RevisionInternal.GetRevId(), allowConflict: false));
 }
Example #19
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestValidations()
        {
            Validator validator = new _Validator_19(this);

            database.SetValidation("hoopy", validator);
            // POST a valid new document:
            IDictionary <string, object> props = new Dictionary <string, object>();

            props.Put("name", "Zaphod Beeblebrox");
            props.Put("towel", "velvet");
            RevisionInternal rev    = new RevisionInternal(props, database);
            Status           status = new Status();

            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            // PUT a valid update:
            props.Put("head_count", 3);
            rev.SetProperties(props);
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            // PUT an invalid update:
            Sharpen.Collections.Remove(props, "towel");
            rev.SetProperties(props);
            validationCalled = false;
            bool gotExpectedError = false;

            try
            {
                rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // POST an invalid new document:
            props = new Dictionary <string, object>();
            props.Put("name", "Vogon");
            props.Put("poetry", true);
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // PUT a valid new document with an ID:
            props = new Dictionary <string, object>();
            props.Put("_id", "ford");
            props.Put("name", "Ford Prefect");
            props.Put("towel", "terrycloth");
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId());
            // DELETE a document:
            rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
            NUnit.Framework.Assert.IsTrue(rev.IsDeleted());
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            // PUT an invalid new document:
            props = new Dictionary <string, object>();
            props.Put("_id", "petunias");
            props.Put("name", "Pot of Petunias");
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
        }
        public virtual void TestPusher()
        {
            var replicationDoneSignal = new CountDownLatch(1);
            var remote         = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documents:
            var documentProperties = new Dictionary <string, object>();
            var doc1Id             = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body   = new Body(documentProperties);
            var rev1   = new RevisionInternal(body, database);
            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties.Put("_rev", rev1.GetRevId());
            documentProperties["UPDATED"] = true;
            var rev2 = database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties = new Dictionary <string, object>();
            var doc2Id = string.Format("doc2-{0}", docIdTimestamp);

            documentProperties["_id"]   = doc2Id;
            documentProperties["baz"]   = 666;
            documentProperties["fnord"] = true;

            database.PutRevision(new RevisionInternal(documentProperties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var continuous = false;
            var repl       = database.CreatePushReplication(remote);

            repl.Continuous = continuous;

            //repl.CreateTarget = false;

            // Check the replication's properties:
            Assert.AreEqual(database, repl.LocalDatabase);
            Assert.AreEqual(remote, repl.RemoteUrl);
            Assert.IsFalse(repl.IsPull);
            Assert.IsFalse(repl.Continuous);
            //Assert.IsTrue(repl.CreateTarget);
            Assert.IsNull(repl.Filter);
            Assert.IsNull(repl.FilterParams);
            // TODO: CAssertNil(r1.doc_ids);
            // TODO: CAssertNil(r1.headers);
            // Check that the replication hasn't started running:
            Assert.IsFalse(repl.IsRunning);
            Assert.AreEqual((int)repl.Status, (int)ReplicationStatus.Stopped);
            Assert.AreEqual(0, repl.CompletedChangesCount);
            Assert.AreEqual(0, repl.ChangesCount);
            Assert.IsNull(repl.LastError);
            RunReplication(repl);
            // make sure doc1 is there
            // TODO: make sure doc2 is there (refactoring needed)
            var replicationUrlTrailing = new Uri(string.Format("{0}/", remote));
            var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);

            Log.D(Tag, "Send http request to " + pathToDoc);
            var httpRequestDoneSignal = new CountDownLatch(1);
            var getDocTask            = Task.Factory.StartNew(() =>
            {
                var httpclient = new HttpClient();
                HttpResponseMessage response;
                string responseString = null;
                try
                {
                    var responseTask = httpclient.GetAsync(pathToDoc.ToString());
                    responseTask.Wait(TimeSpan.FromSeconds(10));
                    response       = responseTask.Result;
                    var statusLine = response.StatusCode;
                    NUnit.Framework.Assert.IsTrue(statusLine == HttpStatusCode.OK);
                    if (statusLine == HttpStatusCode.OK)
                    {
                        var responseStringTask = response.Content.ReadAsStringAsync();
                        responseStringTask.Wait(TimeSpan.FromSeconds(10));
                        responseString = responseStringTask.Result;
                        NUnit.Framework.Assert.IsTrue(responseString.Contains(doc1Id));
                        Log.D(ReplicationTest.Tag, "result: " + responseString);
                    }
                    else
                    {
                        var statusReason = response.ReasonPhrase;
                        response.Dispose();
                        throw new IOException(statusReason);
                    }
                }
                catch (ProtocolViolationException e)
                {
                    NUnit.Framework.Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                }
                catch (IOException e)
                {
                    NUnit.Framework.Assert.IsNull(e, "Got IOException: " + e.Message);
                }
                httpRequestDoneSignal.CountDown();
            });

            //Closes the connection.
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                var result = httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Assert.IsTrue(result, "Could not retrieve the new doc from the sync gateway.");
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Sharpen.Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusher() finished");
        }
Example #21
0
        /// <summary>Fetches the contents of a revision from the remote db, including its parent revision ID.
        ///     </summary>
        /// <remarks>
        /// Fetches the contents of a revision from the remote db, including its parent revision ID.
        /// The contents are stored into rev.properties.
        /// </remarks>
        internal void PullRemoteRevision(RevisionInternal rev)
        {
            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision with rev: " + rev);
            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskStarted()");

            AsyncTaskStarted();

            httpConnectionCount++;

            // Construct a query. We want the revision history, and the bodies of attachments that have
            // been added since the latest revisions we have locally.
            // See: http://wiki.apache.org/couchdb/HTTP_Document_API#Getting_Attachments_With_a_Document
            var path      = new StringBuilder("/" + HttpUtility.UrlEncode(rev.GetDocId()) + "?rev=" + HttpUtility.UrlEncode(rev.GetRevId()) + "&revs=true&attachments=true");
            var knownRevs = KnownCurrentRevIDs(rev);

            if (knownRevs == null)
            {
                //this means something is wrong, possibly the replicator has shut down
                Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision() calling asyncTaskFinished()");
                AsyncTaskFinished(1);
                httpConnectionCount--;
                return;
            }

            if (knownRevs.Count > 0)
            {
                path.Append("&atts_since=");
                path.Append(JoinQuotedEscaped(knownRevs));
            }

            //create a final version of this variable for the log statement inside
            //FIXME find a way to avoid this
            var pathInside = path.ToString();

            SendAsyncMultipartDownloaderRequest(HttpMethod.Get, pathInside, null, LocalDatabase, (result, e) =>
            {
                try
                {
                    // OK, now we've got the response revision:
                    Log.D(Tag, this + ": pullRemoteRevision got response for rev: " + rev);
                    if (result != null)
                    {
                        var properties = ((JObject)result).ToObject <IDictionary <string, object> >();
                        var history    = Database.ParseCouchDBRevisionHistory(properties);

                        if (history != null)
                        {
                            rev.SetProperties(properties);
                            // Add to batcher ... eventually it will be fed to -insertRevisions:.
                            var toInsert = new AList <object> ();
                            toInsert.AddItem(rev);
                            toInsert.AddItem(history);
                            Log.D(Tag, this + ": pullRemoteRevision add rev: " + rev + " to batcher");
                            downloadsToInsert.QueueObject(toInsert);
                            Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskStarted()");
                            AsyncTaskStarted();
                        }
                        else
                        {
                            Log.W(Tag, this + ": Missing revision history in response from " + pathInside);
                            CompletedChangesCount += 1;
                        }
                    }
                    else
                    {
                        if (e != null)
                        {
                            Log.E(Tag, "Error pulling remote revision", e);
                            LastError = e;
                        }
                        CompletedChangesCount += 1;
                    }
                }
                finally
                {
                    Log.D(Tag, this + "|" + Thread.CurrentThread() + ": pullRemoteRevision.onCompletion() calling asyncTaskFinished()");
                    AsyncTaskFinished(1);
                }

                // Note that we've finished this task; then start another one if there
                // are still revisions waiting to be pulled:
                --httpConnectionCount;
                PullRemoteRevisions();
            });
        }
Example #22
0
        public void TestAttachments()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");
            var props   = new Dictionary <string, object> {
                { "foo", 1 },
                { "bar", false },
                { "_attachments", CreateAttachmentsDict(attach1, testAttachmentName, "text/plain", false) }
            };

            Status           status = new Status();
            RevisionInternal rev1   = database.PutRevision(new RevisionInternal(props), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var att = database.GetAttachmentForRevision(rev1, testAttachmentName, status);

            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            var itemDict = new Dictionary <string, object> {
                { "content_type", "text/plain" },
                { "digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=" },
                { "length", 27 },
                { "stub", true },
                { "revpos", 1 }
            };
            var attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, itemDict }
            };
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.None);

            AssertDictionariesAreEqual(attachmentDict, gotRev1.GetAttachments());

            itemDict.Remove("stub");
            itemDict["data"] = Convert.ToBase64String(attach1);
            gotRev1          = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var expandedRev = gotRev1.CopyWithDocID(rev1.GetDocId(), rev1.GetRevId());

            Assert.IsTrue(database.ExpandAttachments(expandedRev, 0, false, true, status));
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Add a second revision that doesn't update the attachment:
            props = new Dictionary <string, object> {
                { "_id", rev1.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsStub(testAttachmentName) }
            };
            var rev2 = database.PutRevision(new RevisionInternal(props), rev1.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Add a third revision of the same document:
            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            props = new Dictionary <string, object> {
                { "_id", rev2.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsDict(attach2, testAttachmentName, "text/html", false) }
            };
            var rev3 = database.PutRevision(new RevisionInternal(props), rev2.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Check the second revision's attachment
            att = database.GetAttachmentForRevision(rev2, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev2.CopyWithDocID(rev2.GetDocId(), rev2.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            AssertDictionariesAreEqual(new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "stub", true },
                      { "revpos", 1 }
                  } }
            }, expandedRev.GetAttachments());

            // Check the 3rd revision's attachment:
            att = database.GetAttachmentForRevision(rev3, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach2, att.Content);
            Assert.AreEqual("text/html", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev3.CopyWithDocID(rev3.GetDocId(), rev3.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "content_type", "text/html" },
                      { "data", "PGh0bWw+QW5kIHRoaXMgaXMgYXR0YWNoMjwvaHRtbD4=" },
                      { "digest", "sha1-s14XRTXlwvzYfjo1t1u0rjB+ZUA=" },
                      { "length", 32 },
                      { "revpos", 3 }
                  } }
            };
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach1), BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
            database.Compact();
            Assert.AreEqual(1, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
        }
        public virtual void TestPusherDeletedDoc()
        {
            Assert.Fail(); // TODO.ZJG: Needs debugging, overflows stack.

            CountDownLatch replicationDoneSignal = new CountDownLatch(1);
            Uri            remote         = GetReplicationURL();
            string         docIdTimestamp = System.Convert.ToString(Runtime.CurrentTimeMillis());
            // Create some documentsConvert
            IDictionary <string, object> documentProperties = new Dictionary <string, object>();
            string doc1Id = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            Body             body   = new Body(documentProperties);
            RevisionInternal rev1   = new RevisionInternal(body, database);
            Status           status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
            documentProperties["_rev"]     = rev1.GetRevId();
            documentProperties["UPDATED"]  = true;
            documentProperties["_deleted"] = true;
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
                                                                              , database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.IsTrue((int)status.GetCode() >= 200 && (int)status.GetCode() < 300);
            var repl = database.CreatePushReplication(remote);

            ((Pusher)repl).CreateTarget = true;
            RunReplication(repl);
            // make sure doc1 is deleted
            Uri replicationUrlTrailing = new Uri(string.Format("{0}/", remote.ToString()
                                                               ));
            Uri pathToDoc = new Uri(replicationUrlTrailing, doc1Id);

            Log.D(Tag, "Send http request to " + pathToDoc);
            CountDownLatch httpRequestDoneSignal = new CountDownLatch(1);
            var            getDocTask            = Task.Factory.StartNew(() =>
            {
                var httpclient = new HttpClient();
                HttpResponseMessage response;
                string responseString = null;
                try
                {
                    var responseTask = httpclient.GetAsync(pathToDoc.ToString());
                    responseTask.Wait();
                    response       = responseTask.Result;
                    var statusLine = response.StatusCode;
                    Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                    Assert.AreEqual(HttpStatusCode.NotFound, statusLine.GetStatusCode());
                }
                catch (ProtocolViolationException e)
                {
                    NUnit.Framework.Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                }
                catch (IOException e)
                {
                    NUnit.Framework.Assert.IsNull(e, "Got IOException: " + e.Message);
                }
                finally
                {
                    httpRequestDoneSignal.CountDown();
                }
            });

            getDocTask.Start();
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Sharpen.Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusherDeletedDoc() finished");
        }
Example #24
0
        public virtual void TestPutAttachment()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            attachments.DeleteBlobs();
            Assert.AreEqual(0, attachments.Count());

            // Put a revision that includes an _attachments dict:
            var attach1    = Encoding.UTF8.GetBytes("This is the body of attach1");
            var base64     = Convert.ToBase64String(attach1);
            var attachment = new Dictionary <string, object>();

            attachment["content_type"] = "text/plain";
            attachment["data"]         = base64;

            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict[testAttachmentName] = attachment;
            var properties = new Dictionary <string, object>();

            properties["foo"]          = 1;
            properties["bar"]          = false;
            properties["_attachments"] = attachmentDict;

            var rev1 = database.PutRevision(new RevisionInternal(properties), null, false);

            // Examine the attachment store:
            Assert.AreEqual(1, attachments.Count());

            // Get the revision:
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(),
                                                           rev1.GetRevId(), DocumentContentOptions.None);
            var gotAttachmentDict = gotRev1.GetPropertyForKey("_attachments").AsDictionary <string, object>();

            var innerDict = new JObject();

            innerDict["content_type"] = "text/plain";
            innerDict["digest"]       = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=";
            innerDict["length"]       = 27;
            innerDict["stub"]         = true;
            innerDict["revpos"]       = 1;
            var expectAttachmentDict = new Dictionary <string, object>();

            expectAttachmentDict[testAttachmentName] = innerDict;
            Assert.AreEqual(expectAttachmentDict, gotAttachmentDict);

            // Update the attachment directly:
            var attachv2 = Encoding.UTF8.GetBytes("Replaced body of attach");
            var writer   = new BlobStoreWriter(database.Attachments);

            writer.AppendData(attachv2);
            writer.Finish();
            var gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, writer, "application/foo",
                                          AttachmentEncoding.None, rev1.GetDocId(), null);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;

            try
            {
                database.UpdateAttachment(testAttachmentName, new BlobStoreWriter(database.Attachments), "application/foo",
                                          AttachmentEncoding.None, rev1.GetDocId(), "1-bogus");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.Conflict);
            }

            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            RevisionInternal rev2 = null;

            try
            {
                rev2 = database.UpdateAttachment(testAttachmentName, writer, "application/foo",
                                                 AttachmentEncoding.None, rev1.GetDocId(), rev1.GetRevId());
            }
            catch (CouchbaseLiteException)
            {
                gotExpectedErrorCode = true;
            }
            Assert.IsFalse(gotExpectedErrorCode);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.AreEqual(2, rev2.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev2 = database.GetDocumentWithIDAndRev(rev2.GetDocId(), rev2
                                                                        .GetRevId(), DocumentContentOptions.None);

            attachmentDict            = gotRev2.GetProperties().Get("_attachments").AsDictionary <string, object>();
            innerDict                 = new JObject();
            innerDict["content_type"] = "application/foo";
            innerDict["digest"]       = "sha1-mbT3208HI3PZgbG4zYWbDW2HsPk=";
            innerDict["length"]       = 23;
            innerDict["stub"]         = true;
            innerDict["revpos"]       = 2;
            expectAttachmentDict[testAttachmentName] = innerDict;
            Assert.AreEqual(expectAttachmentDict, attachmentDict);
            // Delete the attachment:
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, "application/foo",
                                          AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId());
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            gotExpectedErrorCode = false;
            try
            {
                database.UpdateAttachment("nosuchattach", null, null,
                                          AttachmentEncoding.None, "nosuchdoc", "nosuchrev");
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedErrorCode = (e.GetCBLStatus().GetCode() == StatusCode.NotFound);
            }
            Assert.IsTrue(gotExpectedErrorCode);
            RevisionInternal rev3 = database.UpdateAttachment(testAttachmentName, null, null,
                                                              AttachmentEncoding.None, rev2.GetDocId(), rev2.GetRevId());

            Assert.AreEqual(rev2.GetDocId(), rev3.GetDocId());
            Assert.AreEqual(3, rev3.GetGeneration());
            // Get the updated revision:
            RevisionInternal gotRev3 = database.GetDocumentWithIDAndRev(rev3.GetDocId(), rev3
                                                                        .GetRevId(), DocumentContentOptions.None);

            attachmentDict = gotRev3.GetProperties().Get("_attachments").AsDictionary <string, object>();
            Assert.IsNull(attachmentDict);
            database.Close();
        }
Example #25
0
        public void TestPusher()
        {
            var remote         = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documents:
            var documentProperties = new Dictionary <string, object>();
            var doc1Id             = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body   = new Body(documentProperties);
            var rev1   = new RevisionInternal(body, database);
            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties.Put("_rev", rev1.GetRevId());
            documentProperties["UPDATED"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties = new Dictionary <string, object>();
            var doc2Id = string.Format("doc2-{0}", docIdTimestamp);

            documentProperties["_id"]   = doc2Id;
            documentProperties["baz"]   = 666;
            documentProperties["fnord"] = true;

            database.PutRevision(new RevisionInternal(documentProperties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var doc2             = database.GetDocument(doc2Id);
            var doc2UnsavedRev   = doc2.CreateRevision();
            var attachmentStream = GetAsset("attachment.png");

            doc2UnsavedRev.SetAttachment("attachment.png", "image/png", attachmentStream);
            var doc2Rev = doc2UnsavedRev.Save();

            Assert.IsNotNull(doc2Rev);

            const bool continuous = false;
            var        repl       = database.CreatePushReplication(remote);

            repl.Continuous = continuous;
            if (!IsSyncGateway(remote))
            {
                repl.CreateTarget = true;
            }

            // Check the replication's properties:
            Assert.AreEqual(database, repl.LocalDatabase);
            Assert.AreEqual(remote, repl.RemoteUrl);
            Assert.IsFalse(repl.IsPull);
            Assert.IsFalse(repl.Continuous);
            Assert.IsNull(repl.Filter);
            Assert.IsNull(repl.FilterParams);
            Assert.IsNull(repl.DocIds);
            // TODO: CAssertNil(r1.headers); still not null!
            // Check that the replication hasn't started running:
            Assert.IsFalse(repl.IsRunning);
            Assert.AreEqual((int)repl.Status, (int)ReplicationStatus.Stopped);
            Assert.AreEqual(0, repl.CompletedChangesCount);
            Assert.AreEqual(0, repl.ChangesCount);
            Assert.IsNull(repl.LastError);

            RunReplication(repl);

            // TODO: Verify the foloowing 2 asserts. ChangesCount and CompletedChangesCount
            // should already be reset when the replicator stopped.
            Assert.IsTrue(repl.ChangesCount >= 2);
            Assert.IsTrue(repl.CompletedChangesCount >= 2);
            Assert.IsNull(repl.LastError);

            VerifyRemoteDocExists(remote, doc1Id);

            // Add doc3
            documentProperties = new Dictionary <string, object>();
            var doc3Id = string.Format("doc3-{0}", docIdTimestamp);
            var doc3   = database.GetDocument(doc3Id);

            documentProperties["bat"] = 677;
            doc3.PutProperties(documentProperties);

            // re-run push replication
            var repl2 = database.CreatePushReplication(remote);

            repl2.Continuous = continuous;
            if (!IsSyncGateway(remote))
            {
                repl2.CreateTarget = true;
            }

            var repl2CheckedpointId = repl2.RemoteCheckpointDocID();

            RunReplication(repl2);

            Assert.IsNull(repl2.LastError);

            // make sure trhe doc has been added
            VerifyRemoteDocExists(remote, doc3Id);

            Assert.AreEqual(repl2.LastSequence, database.LastSequenceWithCheckpointId(repl2CheckedpointId));

            System.Threading.Thread.Sleep(2000);
            var json = GetRemoteDoc(remote, repl2CheckedpointId);
            var remoteLastSequence = (string)json["lastSequence"];

            Assert.AreEqual(repl2.LastSequence, remoteLastSequence);

            Log.D(Tag, "testPusher() finished");
        }
Example #26
0
        public void TestPusherDeletedDoc()
        {
            var remote         = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documentsConvert
            var documentProperties = new Dictionary <string, object>();
            var doc1Id             = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body   = new Body(documentProperties);
            var rev1   = new RevisionInternal(body, database);
            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties["_rev"]     = rev1.GetRevId();
            documentProperties["UPDATED"]  = true;
            documentProperties["_deleted"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.IsTrue((int)status.GetCode() >= 200 && (int)status.GetCode() < 300);

            var repl = database.CreatePushReplication(remote);

            if (!IsSyncGateway(remote))
            {
                ((Pusher)repl).CreateTarget = true;
            }

            RunReplication(repl);

            Assert.IsNull(repl.LastError);

            // make sure doc1 is deleted
            var replicationUrlTrailing = new Uri(string.Format("{0}/", remote));
            var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);

            Log.D(Tag, "Send http request to " + pathToDoc);
            var httpRequestDoneSignal = new CountDownLatch(1);
            var httpclient            = new HttpClient();

            try
            {
                var getDocResponse = httpclient.GetAsync(pathToDoc.ToString()).Result;
                var statusLine     = getDocResponse.StatusCode;
                Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                Assert.AreEqual(Couchbase.Lite.StatusCode.NotFound, statusLine.GetStatusCode());
            }
            catch (ProtocolViolationException e)
            {
                Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
            }
            catch (IOException e)
            {
                Assert.IsNull(e, "Got IOException: " + e.Message);
            }
            finally
            {
                httpRequestDoneSignal.CountDown();
            }
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusherDeletedDoc() finished");
        }
Example #27
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestViewIndex()
        {
            int numTimesMapFunctionInvoked     = 0;
            IDictionary <string, object> dict1 = new Dictionary <string, object>();

            dict1["key"] = "one";
            IDictionary <string, object> dict2 = new Dictionary <string, object>();

            dict2["key"] = "two";
            IDictionary <string, object> dict3 = new Dictionary <string, object>();

            dict3["key"] = "three";
            IDictionary <string, object> dictX = new Dictionary <string, object>();

            dictX["clef"] = "quatre";
            RevisionInternal rev1 = PutDoc(database, dict1);
            RevisionInternal rev2 = PutDoc(database, dict2);
            RevisionInternal rev3 = PutDoc(database, dict3);

            PutDoc(database, dictX);
            View view            = database.GetView("aview");
            var  numTimesInvoked = 0;

            MapDelegate mapBlock = (IDictionary <string, object> document, EmitDelegate emitter) =>
            {
                numTimesInvoked += 1;
                NUnit.Framework.Assert.IsNotNull(document["_id"]);
                NUnit.Framework.Assert.IsNotNull(document["_rev"]);
                if (document["key"] != null)
                {
                    emitter(document["key"], null);
                }
            };

            view.SetMap(mapBlock, "1");
            NUnit.Framework.Assert.AreEqual(1, view.GetViewId());
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            IList <IDictionary <string, object> > dumpResult = view.Dump();

            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"two\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(2, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"three\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(3, dumpResult[1]["seq"]);
            //no-op reindex
            NUnit.Framework.Assert.IsFalse(view.IsStale());
            view.UpdateIndex();
            // Now add a doc and update a doc:
            RevisionInternal threeUpdated = new RevisionInternal(rev3.GetDocId(), rev3.GetRevId
                                                                     (), false, database);

            numTimesMapFunctionInvoked = mapBlock.GetNumTimesInvoked();
            IDictionary <string, object> newdict3 = new Dictionary <string, object>();

            newdict3["key"] = "3hree";
            threeUpdated.SetProperties(newdict3);
            Status status = new Status();

            rev3 = database.PutRevision(threeUpdated, rev3.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            // Make sure the map function was only invoked one more time (for the document that was added)
            NUnit.Framework.Assert.AreEqual(mapBlock.GetNumTimesInvoked(), numTimesMapFunctionInvoked
                                            + 1);
            IDictionary <string, object> dict4 = new Dictionary <string, object>();

            dict4["key"] = "four";
            RevisionInternal rev4       = PutDoc(database, dict4);
            RevisionInternal twoDeleted = new RevisionInternal(rev2.GetDocId(), rev2.GetRevId
                                                                   (), true, database);

            database.PutRevision(twoDeleted, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            dumpResult = view.Dump();
            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"3hree\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(5, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"four\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(6, dumpResult[1]["seq"]);
            // Now do a real query:
            IList <QueryRow> rows = view.QueryWithOptions(null);

            NUnit.Framework.Assert.AreEqual(3, rows.Count);
            NUnit.Framework.Assert.AreEqual("one", rows[2].Key);
            NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rows[2].DocumentId);
            NUnit.Framework.Assert.AreEqual("3hree", rows[0].Key);
            NUnit.Framework.Assert.AreEqual(rev3.GetDocId(), rows[0].DocumentId);
            NUnit.Framework.Assert.AreEqual("four", rows[1].Key);
            NUnit.Framework.Assert.AreEqual(rev4.GetDocId(), rows[1].DocumentId);
            view.DeleteIndex();
        }
        public void TestValidations()
        {
            ValidateDelegate validator = (newRevision, context) =>
            {
                Assert.IsNotNull(newRevision);
                Assert.IsNotNull(context);
                Assert.IsTrue(newRevision.Properties != null || newRevision.IsDeletion);

                validationCalled = true;

                bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel") != null);
                Log.V(ValidationsTest.Tag, string.Format("--- Validating {0} --> {1}", newRevision.Properties, hoopy));
                if (!hoopy)
                {
                    context.Reject("Where's your towel?");
                }
                return(hoopy);
            };

            database.SetValidation("hoopy", validator);

            // POST a valid new document:
            IDictionary <string, object> props = new Dictionary <string, object>();

            props["name"]  = "Zaphod Beeblebrox";
            props["towel"] = "velvet";
            RevisionInternal rev    = new RevisionInternal(props, database);
            Status           status = new Status();

            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // PUT a valid update:
            props["head_count"] = 3;
            rev.SetProperties(props);
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // PUT an invalid update:
            Sharpen.Collections.Remove(props, "towel");
            rev.SetProperties(props);
            validationCalled = false;
            bool gotExpectedError = false;

            try
            {
                rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);

            // POST an invalid new document:
            props            = new Dictionary <string, object>();
            props["name"]    = "Vogon";
            props["poetry"]  = true;
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);

            // PUT a valid new document with an ID:
            props            = new Dictionary <string, object>();
            props["_id"]     = "ford";
            props["name"]    = "Ford Prefect";
            props["towel"]   = "terrycloth";
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            rev              = database.PutRevision(rev, null, false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual("ford", rev.GetDocId());

            // DELETE a document:
            rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
            Assert.IsTrue(rev.IsDeleted());
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            Assert.IsTrue(validationCalled);

            // PUT an invalid new document:
            props            = new Dictionary <string, object>();
            props["_id"]     = "petunias";
            props["name"]    = "Pot of Petunias";
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);
        }
Example #29
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestRevTree()
        {
            RevisionInternal             rev           = new RevisionInternal("MyDocId", "4-foxy", false, database);
            IDictionary <string, object> revProperties = new Dictionary <string, object>();

            revProperties.Put("_id", rev.GetDocId());
            revProperties.Put("_rev", rev.GetRevId());
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);
            IList <string> revHistory = new AList <string>();

            revHistory.AddItem(rev.GetRevId());
            revHistory.AddItem("3-thrice");
            revHistory.AddItem("2-too");
            revHistory.AddItem("1-won");
            database.ForceInsert(rev, revHistory, null);
            NUnit.Framework.Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, rev, revHistory);
            RevisionInternal conflict = new RevisionInternal("MyDocId", "5-epsilon", false, database
                                                             );
            IDictionary <string, object> conflictProperties = new Dictionary <string, object>();

            conflictProperties.Put("_id", conflict.GetDocId());
            conflictProperties.Put("_rev", conflict.GetRevId());
            conflictProperties["message"] = "yo";
            conflict.SetProperties(conflictProperties);
            IList <string> conflictHistory = new AList <string>();

            conflictHistory.AddItem(conflict.GetRevId());
            conflictHistory.AddItem("4-delta");
            conflictHistory.AddItem("3-gamma");
            conflictHistory.AddItem("2-too");
            conflictHistory.AddItem("1-won");
            database.ForceInsert(conflict, conflictHistory, null);
            NUnit.Framework.Assert.AreEqual(1, database.DocumentCount);
            VerifyHistory(database, conflict, conflictHistory);
            // Add an unrelated document:
            RevisionInternal other = new RevisionInternal("AnotherDocID", "1-ichi", false, database
                                                          );
            IDictionary <string, object> otherProperties = new Dictionary <string, object>();

            otherProperties["language"] = "jp";
            other.SetProperties(otherProperties);
            IList <string> otherHistory = new AList <string>();

            otherHistory.AddItem(other.GetRevId());
            database.ForceInsert(other, otherHistory, null);
            // Fetch one of those phantom revisions with no body:
            RevisionInternal rev2 = database.GetDocumentWithIDAndRev(rev.GetDocId(), "2-too",
                                                                     EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(rev.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.AreEqual("2-too", rev2.GetRevId());
            //Assert.assertNull(rev2.getContent());
            // Make sure no duplicate rows were inserted for the common revisions:
            NUnit.Framework.Assert.AreEqual(8, database.GetLastSequenceNumber());
            // Make sure the revision with the higher revID wins the conflict:
            RevisionInternal current = database.GetDocumentWithIDAndRev(rev.GetDocId(), null,
                                                                        EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(conflict, current);
            // Get the _changes feed and verify only the winner is in it:
            ChangesOptions options         = new ChangesOptions();
            RevisionList   changes         = database.ChangesSince(0, options, null);
            RevisionList   expectedChanges = new RevisionList();

            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            NUnit.Framework.Assert.AreEqual(changes, expectedChanges);
            options.SetIncludeConflicts(true);
            changes         = database.ChangesSince(0, options, null);
            expectedChanges = new RevisionList();
            expectedChanges.AddItem(rev);
            expectedChanges.AddItem(conflict);
            expectedChanges.AddItem(other);
            NUnit.Framework.Assert.AreEqual(changes, expectedChanges);
        }
Example #30
0
        public void TestRevTreeChangeNotification()
        {
            const string DOCUMENT_ID = "MyDocId";

            var rev           = new RevisionInternal(DOCUMENT_ID, "1-one", false, database);
            var revProperties = new Dictionary <string, object>();

            revProperties["_id"]     = rev.GetDocId();
            revProperties["_rev"]    = rev.GetRevId();
            revProperties["message"] = "hi";
            rev.SetProperties(revProperties);

            var revHistory = new List <string>();

            revHistory.Add(rev.GetRevId());

            EventHandler <DatabaseChangeEventArgs> handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(rev.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var current = database.GetDocument(change.DocumentId).CurrentRevision;
                Assert.AreEqual(rev.GetRevId(), current.Id);
            };

            database.Changed += handler;
            database.ForceInsert(rev, revHistory, null);
            database.Changed -= handler;

            // add two more revisions to the document
            var rev3           = new RevisionInternal(DOCUMENT_ID, "3-three", false, database);
            var rev3Properties = new Dictionary <string, object>();

            rev3Properties["_id"]     = rev3.GetDocId();
            rev3Properties["_rev"]    = rev3.GetRevId();
            rev3Properties["message"] = "hi again";
            rev3.SetProperties(rev3Properties);

            var rev3History = new List <string>();

            rev3History.Add(rev3.GetRevId());
            rev3History.Add("2-two");
            rev3History.Add(rev.GetRevId());

            handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var doc = database.GetDocument(change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), doc.CurrentRevisionId);
                try
                {
                    Assert.AreEqual(3, doc.RevisionHistory.ToList().Count);
                }
                catch (CouchbaseLiteException ex)
                {
                    Assert.Fail();
                }
            };

            database.Changed += handler;
            database.ForceInsert(rev3, rev3History, null);
            database.Changed -= handler;

            // add a conflicting revision, with the same history length as the last revision we
            // inserted. Since this new revision's revID has a higher ASCII sort, it should become the
            // new winning revision.
            var conflictRev        = new RevisionInternal(DOCUMENT_ID, "3-winner", false, database);
            var conflictProperties = new Dictionary <string, object>();

            conflictProperties["_id"]     = conflictRev.GetDocId();
            conflictProperties["_rev"]    = conflictRev.GetRevId();
            conflictProperties["message"] = "winner";
            conflictRev.SetProperties(conflictProperties);

            var conflictRevHistory = new List <string>();

            conflictRevHistory.Add(conflictRev.GetRevId());
            conflictRevHistory.Add("2-two");
            conflictRevHistory.Add(rev.GetRevId());

            handler = (sender, e) =>
            {
                var changes = e.Changes.ToList();
                Assert.AreEqual(1, changes.Count);
                var change = changes[0];
                Assert.AreEqual(DOCUMENT_ID, change.DocumentId);
                Assert.AreEqual(conflictRev.GetRevId(), change.RevisionId);
                Assert.IsTrue(change.IsCurrentRevision);
                Assert.IsFalse(change.IsConflict);

                var doc = database.GetDocument(change.DocumentId);
                Assert.AreEqual(rev3.GetRevId(), doc.CurrentRevisionId);
                try
                {
                    Assert.AreEqual(2, doc.ConflictingRevisions.ToList().Count);
                    Assert.AreEqual(3, doc.RevisionHistory.ToList().Count);
                }
                catch (CouchbaseLiteException ex)
                {
                    Assert.Fail();
                }
            };

            database.Changed += handler;
            database.ForceInsert(conflictRev, conflictRevHistory, null);
            database.Changed -= handler;
        }