public void TestAcceptSelfSignedCertWithPinnedCertificate()
        {
            _listener = CreateListener();
            _listener.TlsIdentity.Should()
            .NotBeNull("because otherwise the TLS identity was not created for the listener");
            _listener.TlsIdentity.Certs.Should().HaveCount(1,
                                                           "because otherwise bogus certs were used");

            // listener = cert1; replicator.pin = cert2; acceptSelfSigned = true => fail
            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,              //authenticator
                true,              //accept only self signed server cert
                DefaultServerCert, //server cert
                (int)CouchbaseLiteError.TLSCertUnknownRoot,
                CouchbaseLiteErrorType.CouchbaseLite
                );

            // listener = cert1; replicator.pin = cert1; acceptSelfSigned = false => pass
            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                false,                          //accept only self signed server cert
                _listener.TlsIdentity.Certs[0], //server cert
                0,
                0
                );

            _listener.Stop();
        }
        public void TestTLSIdentity()
        {
            // TLS is disabled
            _listener = CreateListener(false);
            _listener.TlsIdentity.Should().BeNull();
            _listener.Stop();
            _listener.TlsIdentity.Should().BeNull();

            // Anonymous Identity
            _listener = CreateListener(true);
            _listener.TlsIdentity.Should().NotBeNull();
            _listener.Stop();
            _listener.TlsIdentity.Should().BeNull();

            // User Identity
            TLSIdentity.DeleteIdentity(_store, ServerCertLabel, null);
            var id = TLSIdentity.CreateIdentity(false,
                                                new Dictionary <string, string>()
            {
                { Certificate.CommonNameAttribute, "CBL-Server" }
            },
                                                null,
                                                _store,
                                                ServerCertLabel,
                                                null);
            var config = CreateListenerConfig(true, true, null, id);

            _listener = new URLEndpointListener(config);
            _listener.TlsIdentity.Should().BeNull();
            _listener.Start();
            _listener.TlsIdentity.Should().NotBeNull();
            _listener.TlsIdentity.Should().BeEquivalentTo(config.TlsIdentity);
            _listener.Stop();
            _listener.TlsIdentity.Should().BeNull();
        }
        public void TestAcceptOnlySelfSignedCertMode()
        {
            _listener = CreateListener();
            _listener.TlsIdentity.Should()
            .NotBeNull("because otherwise the TLS identity was not created for the listener");
            _listener.TlsIdentity.Certs.Should().HaveCount(1,
                                                           "because otherwise bogus certs were used");

            DisableDefaultServerCertPinning = true;

            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                false,//accept only self signed server cert
                null,
                //TODO: Need to handle Linux throwing different error TLSCertUntrusted (5008)
                (int)CouchbaseLiteError.TLSCertUnknownRoot,
                CouchbaseLiteErrorType.CouchbaseLite
                );

            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                true, //accept only self signed server cert
                null,
                0,
                0
                );

            _listener.Stop();
        }
        public void TestMultipleListenersOnSameDatabase()
        {
            _listener = CreateListener();
            var _listener2 = CreateNewListener();

            using (var doc1 = new MutableDocument("doc1"))
                using (var doc2 = new MutableDocument("doc2")) {
                    doc1.SetString("name", "Sam");
                    Db.Save(doc1);
                    doc2.SetString("name", "Mary");
                    OtherDb.Save(doc2);
                }

            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                false, //accept only self signed server cert
                _listener.TlsIdentity.Certs[0],
                0,
                0
                );

            _listener.Stop();
            _listener2.Stop();

            OtherDb.Count.Should().Be(2);
        }
Beispiel #5
0
        public void TestEmptyNetworkInterface()
        {
            var config = CreateListenerConfig(tls: false, networkInterface: "0.0.0.0");

            _listener = Listen(config, 0, 0);
            _listener.Stop();
        }
        private URLEndpointListener Listen(URLEndpointListenerConfiguration config,
                                           int expectedErrCode = 0, CouchbaseLiteErrorType expectedErrDomain = 0)
        {
            _listener?.Stop();

            _listener = new URLEndpointListener(config);

            _listener.Port.Should().Be(0, "Listener's port should be 0 because the listener has not yet started.");
            _listener.Urls.Count.Should().Be(0, "Listener's Urls count should be 0 because the listener has not yet started.");
            _listener.TlsIdentity.Should().BeNull("Listener's TlsIdentity should be null because the listener has not yet started.");
            _listener.Status.ConnectionCount.Should().Be(0, "Listener's connection count should be 0 because the listener has not yet started.");
            _listener.Status.ActiveConnectionCount.Should().Be(0, "Listener's active connection count should be 0 because the listener has not yet started.");

            try {
                _listener.Start();
            } catch (CouchbaseLiteException e) {
                if (expectedErrCode == 0)
                {
                    throw;
                }

                e.Domain.Should().Be(expectedErrDomain);
                e.Error.Should().Be(expectedErrCode);
            } catch (CouchbaseNetworkException ne) {
                if (expectedErrCode == 0)
                {
                    throw;
                }

                ne.Domain.Should().Be(expectedErrDomain);
                ne.Error.Should().Be(expectedErrCode);
            }

            return(_listener);
        }
        public void TestUrls()
        {
            _listener = CreateListener(false);

            _listener.Urls.Count.Should().NotBe(0);
            _listener.Stop();
            _listener.Urls.Count.Should().Be(0);
        }
Beispiel #8
0
        private URLEndpointListener CreateNewListener()
        {
            var config   = new URLEndpointListenerConfiguration(OtherDb);
            var listener = new URLEndpointListener(config);

            listener.Start();
            return(_listener);
        }
        public void TestEmptyNetworkInterface()
        {
            var config = CreateListenerConfig(false);

            config.NetworkInterface = "0.0.0.0";
            _listener = Listen(config, 0, 0);
            _listener.Stop();
        }
Beispiel #10
0
        internal void CreateListener()
        {
            //tag::InitListener[]
            var listenerConfig = new URLEndpointListenerConfiguration(_db); // <1>

            listenerConfig.NetworkInterface = GetLocalIPv4(NetworkInterfaceType.Wireless80211) ?? GetLocalIPv4(NetworkInterfaceType.Ethernet);
            listenerConfig.Port             = 0; // Dynamic port

            switch (CoreApp.ListenerTLSMode)     // <2>
            //tag::TLSDisabled[]
            {
            case LISTENER_TLS_MODE.DISABLED:
                listenerConfig.DisableTLS  = true;
                listenerConfig.TlsIdentity = null;
                //end::TLSDisabled[]
                break;

            //tag::TLSWithAnonymousAuth[]
            case LISTENER_TLS_MODE.WITH_ANONYMOUS_AUTH:
                listenerConfig.DisableTLS  = false;    // Use with anonymous self signed cert if TlsIdentity is null
                listenerConfig.TlsIdentity = null;
                //end::TLSWithAnonymousAuth[]
                break;

            //tag::TLSWithBundledCert[]
            case LISTENER_TLS_MODE.WITH_BUNDLED_CERT:
                listenerConfig.DisableTLS  = false;
                listenerConfig.TlsIdentity = ImportTLSIdentityFromPkc12(ListenerCertLabel);
                //end::TLSWithBundledCert[]
                break;

            //tag::TLSWithGeneratedSelfSignedCert[]
            case LISTENER_TLS_MODE.WITH_GENERATED_SELF_SIGNED_CERT:
                listenerConfig.DisableTLS  = false;
                listenerConfig.TlsIdentity = CreateIdentityWithCertLabel(ListenerCertLabel);
                //end::TLSWithGeneratedSelfSignedCert[]
                break;
            }

            listenerConfig.EnableDeltaSync = true; // <3>

            if (CoreApp.RequiresUserAuth)          // <4>
            {
                listenerConfig.Authenticator = new ListenerPasswordAuthenticator((sender, username, password) =>
                {
                    // ** This is only a sample app to use an existing users credential shared cross platforms.
                    //    Developers should use SecureString password properly.
                    var found = CoreApp.AllowedUsers.Where(u => username == u.Username && new NetworkCredential(string.Empty, password).Password == u.Password).SingleOrDefault();
                    return(found != null);
                });
            }

            _urlEndpointListener = new URLEndpointListener(listenerConfig);
            //end::InitListener[]
        }
        public void TestMultipleReplicatorsToListener()
        {
            _listener = Listen(CreateListenerConfig()); // writable listener

            // save a doc on listenerDB
            using (var doc = new MutableDocument()) {
                OtherDb.Save(doc);
            }

            ValidateMultipleReplicationsTo(ReplicatorType.PushAndPull);
        }
        public void TestBusyPort()
        {
            _listener = CreateListener(false);
            //listener1 uses the same port as listener
            var config    = CreateListenerConfig(false);
            var listener1 = Listen(config, PosixBase.GetCode(nameof(PosixWindows.EADDRINUSE)), CouchbaseLiteErrorType.POSIX);

            _listener.Stop();
            listener1.Stop();
            listener1.Dispose();
        }
 public void TestPort()
 {
     //init and start a listener
     _listener = CreateListener(false);
     //In order to get the test to pass on Linux, temp modify to this:
     _listener.Port.Should().BeGreaterThan(0);
     //_listener.Port.Should().Be(WsPort);
     //stop the listener
     _listener.Stop();
     _listener.Port.Should().Be(0, "Listener's port should be 0 because the listener is stopped.");
 }
        public static Uri LocalUrl(this URLEndpointListener listener)
        {
            Debug.Assert(listener.Port > 0);
            var builder = new UriBuilder(
                listener.Config.DisableTLS ? "ws" : "wss",
                "localhost",
                listener.Port,
                $"/{listener.Config.Database.Name}"
                );

            return(builder.Uri);
        }
        private URLEndpointListener CreateNewListener()
        {
            var config = new URLEndpointListenerConfiguration(OtherDb);

            config.Port       = 0;
            config.DisableTLS = false;

            var listener = new URLEndpointListener(config);

            listener.Start();
            return(_listener);
        }
        private void WithActiveReplicatorAndURLEndpointListeners(bool isCloseNotDelete)
        {
            WaitAssert waitIdleAssert1    = new WaitAssert();
            WaitAssert waitStoppedAssert1 = new WaitAssert();

            _listener = CreateListener();
            var _listener2 = CreateNewListener();

            _listener.Config.Database.ActiveStoppables.Count.Should().Be(2);
            _listener2.Config.Database.ActiveStoppables.Count.Should().Be(2);

            using (var doc1 = new MutableDocument("doc1"))
                using (var doc2 = new MutableDocument("doc2")) {
                    doc1.SetString("name", "Sam");
                    Db.Save(doc1);
                    doc2.SetString("name", "Mary");
                    OtherDb.Save(doc2);
                }

            var target  = new DatabaseEndpoint(Db);
            var config1 = CreateConfig(target, ReplicatorType.PushAndPull, true, sourceDb: OtherDb);
            var repl1   = new Replicator(config1);

            repl1.AddChangeListener((sender, args) => {
                waitIdleAssert1.RunConditionalAssert(() => {
                    return(args.Status.Activity == ReplicatorActivityLevel.Idle);
                });

                waitStoppedAssert1.RunConditionalAssert(() => {
                    return(args.Status.Activity == ReplicatorActivityLevel.Stopped);
                });
            });

            repl1.Start();

            waitIdleAssert1.WaitForResult(TimeSpan.FromSeconds(10));
            OtherDb.ActiveStoppables.Count.Should().Be(3);

            if (isCloseNotDelete)
            {
                OtherDb.Close();
            }
            else
            {
                OtherDb.Delete();
            }

            OtherDb.ActiveStoppables.Count.Should().Be(0);
            OtherDb.IsClosedLocked.Should().Be(true);

            waitStoppedAssert1.WaitForResult(TimeSpan.FromSeconds(30));
        }
Beispiel #17
0
        //[Fact] //Mac OS 1-6-21
        public void TestMultipleReplicatorsOnReadOnlyListener()
        {
            var config = CreateListenerConfig(readOnly: true);

            _listener = Listen(config);

            // save a doc on listener DB
            using (var doc = new MutableDocument()) {
                OtherDb.Save(doc);
            }

            ValidateMultipleReplicationsTo(ReplicatorType.Pull);
        }
        public void TestBusyPort()
        {
            _listener = CreateListener(false, false);
            _listener.Start();

            //listener1 uses the same port as listener
            var config    = CreateListenerConfig(false, false, stopListener: false);
            var listener1 = Listen(config, GetEADDRINUSECode(), CouchbaseLiteErrorType.POSIX, stopListener: false);

            _listener.Stop();
            listener1.Stop();
            listener1.Dispose();
        }
        public void TestEmptyPort()
        {
            //init and start a listener
            var config = CreateListenerConfig(false);

            _listener = Listen(config, 0, 0);

            _listener.Port.Should().NotBe(0, "Because the port is dynamically assigned.");

            //stop the listener
            _listener.Stop();
            _listener.Port.Should().Be(0, "Listener's port should be 0 because the listener is stopped.");
        }
        //[Fact] Looks like MSBuild doesn't understand RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
        public void TestMultipleReplicatorsOnReadOnlyListener()
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) //Mac OS 8-23-21 hang with LiteCore Commit: 5d9539fae43e9282787c2b68772bb85ecbc00b5c [5d9539f]
            {
                var config = CreateListenerConfig();
                config.ReadOnly = true;
                _listener       = Listen(config);

                // save a doc on listener DB
                using (var doc = new MutableDocument()) {
                    OtherDb.Save(doc);
                }

                ValidateMultipleReplicationsTo(ReplicatorType.Pull);
            }
        }
Beispiel #21
0
        public void TestPasswordAuthenticator()
        {
            var auth = new ListenerPasswordAuthenticator((sender, username, password) =>
            {
                return(username == "daniel" && new NetworkCredential(string.Empty, password).Password == "123");
            });

            _listener = CreateListener(false, true, auth);

            // Replicator - No authenticator
            var targetEndpoint = _listener.LocalEndpoint();
            var config         = new ReplicatorConfiguration(Db, targetEndpoint);

            RunReplication(config, (int)CouchbaseLiteError.HTTPAuthRequired, CouchbaseLiteErrorType.CouchbaseLite);
            var          pw                  = "123";
            var          wrongPw             = "456";
            SecureString pwSecureString      = null;
            SecureString wrongPwSecureString = null;

            unsafe
            {
                fixed(char *pw_ = pw)
                fixed(char *wrongPw_ = wrongPw)
                {
                    pwSecureString      = new SecureString(pw_, pw.Length);
                    wrongPwSecureString = new SecureString(wrongPw_, wrongPw.Length);
                }
            }

            // Replicator - Wrong Credentials
            config = new ReplicatorConfiguration(Db, targetEndpoint)
            {
                Authenticator = new BasicAuthenticator("daniel", wrongPwSecureString)
            };
            RunReplication(config, (int)CouchbaseLiteError.HTTPAuthRequired, CouchbaseLiteErrorType.CouchbaseLite);

            // Replicator - Success
            config = new ReplicatorConfiguration(Db, targetEndpoint)
            {
                Authenticator = new BasicAuthenticator("daniel", pwSecureString)
            };
            RunReplication(config, 0, 0);

            _listener.Stop();
            pwSecureString.Dispose();
            wrongPwSecureString.Dispose();
        }
Beispiel #22
0
 //[Fact] //CouchbaseLiteException (POSIXDomain / 101): The requested address is not valid in its context.
 public void TestNetworkInterfaceName()
 {
     foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
     {
         if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
             ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
         {
             foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
             {
                 if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                 {
                     var config = CreateListenerConfig(tls: false, networkInterface: ip.Address.ToString());
                     _listener = Listen(config, 0, 0);
                     _listener.Stop();
                 }
             }
         }
     }
 }
        public void TestListenerWithImportIdentity()
        {
            byte[] serverData = null;
            using (var stream = typeof(URLEndpointListenerTest).Assembly.GetManifestResourceStream("client.p12"))
                using (var reader = new BinaryReader(stream)) {
                    serverData = reader.ReadBytes((int)stream.Length);
                }

            // Cleanup
            TLSIdentity.DeleteIdentity(_store, ClientCertLabel, null);

            // Import identity
            var id = TLSIdentity.ImportIdentity(_store, serverData, "123", ServerCertLabel, null);

            // Create listener and start
            var config = CreateListenerConfig(true, true, null, id);

            _listener = Listen(config);

            _listener.TlsIdentity.Should().NotBeNull();

            using (var doc1 = new MutableDocument("doc1")) {
                doc1.SetString("name", "Sam");
                Db.Save(doc1);
            }

            OtherDb.Count.Should().Be(0);

            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,                           //authenticator
                false,                          //accept only self signed server cert
                _listener.TlsIdentity.Certs[0], //server cert
                0,
                0
                );

            OtherDb.Count.Should().Be(1);

            _listener.Stop();
        }
        public void TestStopListener()
        {
            ManualResetEventSlim waitIdleAssert    = new ManualResetEventSlim();
            ManualResetEventSlim waitStoppedAssert = new ManualResetEventSlim();

            var config = CreateListenerConfig(false);

            _listener = Listen(config);

            var target  = _listener.LocalEndpoint();
            var config1 = CreateConfig(target, ReplicatorType.PushAndPull, true,
                                       serverCert: null);

            using (var repl = new Replicator(config1)) {
                var token = repl.AddChangeListener((sender, args) =>
                {
                    if (args.Status.Activity == ReplicatorActivityLevel.Idle)
                    {
                        waitIdleAssert.Set();
                        // Stop listener aka server
                        _listener.Stop();
                    }
                    else if (args.Status.Activity == ReplicatorActivityLevel.Stopped)
                    {
                        waitStoppedAssert.Set();
                    }
                });

                repl.Start();

                // Wait until idle then stop the listener
                waitIdleAssert.Wait(TimeSpan.FromSeconds(15)).Should().BeTrue();

                // Wait for the replicator to be stopped
                waitStoppedAssert.Wait(TimeSpan.FromSeconds(20)).Should().BeTrue();

                // Check error
                var error = repl.Status.Error.As <CouchbaseWebsocketException>();
                error.Error.Should().Be((int)CouchbaseLiteError.WebSocketGoingAway);
            }
        }
        public void TestClientCertAuthRootCertsError()
        {
            byte[] caData;
            using (var stream = typeof(URLEndpointListenerTest).Assembly.GetManifestResourceStream("client-ca.der"))
                using (var reader = new BinaryReader(stream)) {
                    caData = reader.ReadBytes((int)stream.Length);
                }

            var rootCert = new X509Certificate2(caData);
            var auth     = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert));

            _listener = CreateListener(true, true, auth);

            TLSIdentity.DeleteIdentity(_store, ClientCertLabel, null);
            // Create wrong client identity
            var id = TLSIdentity.CreateIdentity(false,
                                                new Dictionary <string, string>()
            {
                { Certificate.CommonNameAttribute, "daniel" }
            },
                                                null,
                                                _store,
                                                ClientCertLabel,
                                                null);

            id.Should().NotBeNull();
            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                new ClientCertificateAuthenticator(id),
                true,
                _listener.TlsIdentity.Certs[0],
                (int)CouchbaseLiteError.TLSHandshakeFailed,  //not TLSClientCertRejected as mac has..
                CouchbaseLiteErrorType.CouchbaseLite
                );

            TLSIdentity.DeleteIdentity(_store, ClientCertLabel, null);
            _listener.Stop();
        }
Beispiel #26
0
        private void PrintListener(URLEndpointListener listener)
        {
            Debug.WriteLine("Listener Config TLSIdentity info: ");
            if (listener?.Config.TlsIdentity == null)
            {
                Debug.WriteLine("No TLSIdentity to print.");
            }
            else
            {
                PrintTLSIdentity(listener.Config.TlsIdentity);
            }

            Debug.WriteLine("Listener TLSIdentity info: ");
            if (listener?.TlsIdentity == null)
            {
                Debug.WriteLine("No TLSIdentity to print.");
            }
            else
            {
                PrintTLSIdentity(listener.TlsIdentity);
            }
        }
        public void TestClientCertAuthenticatorRootCerts()
        {
            byte[] caData, clientData;
            using (var stream = typeof(URLEndpointListenerTest).Assembly.GetManifestResourceStream("client-ca.der"))
                using (var reader = new BinaryReader(stream)) {
                    caData = reader.ReadBytes((int)stream.Length);
                }

            using (var stream = typeof(URLEndpointListenerTest).Assembly.GetManifestResourceStream("client.p12"))
                using (var reader = new BinaryReader(stream)) {
                    clientData = reader.ReadBytes((int)stream.Length);
                }

            var rootCert = new X509Certificate2(caData);
            var auth     = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert));

            _listener = CreateListener(true, true, auth);
            var serverCert = _listener.TlsIdentity.Certs[0];

            // Cleanup
            TLSIdentity.DeleteIdentity(_store, ClientCertLabel, null);

            // Create client identity
            var id = TLSIdentity.ImportIdentity(_store, clientData, "123", ClientCertLabel, null);

            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                new ClientCertificateAuthenticator(id),
                true,
                serverCert,
                0,
                0
                );

            TLSIdentity.DeleteIdentity(_store, ClientCertLabel, null);
            _listener.Stop();
        }
        public void TestDoNotAcceptSelfSignedMode() //aka testPinnedServerCertificate in iOS
        {
            _listener = CreateListener();
            _listener.TlsIdentity.Should()
            .NotBeNull("because otherwise the TLS identity was not created for the listener");
            _listener.TlsIdentity.Certs.Should().HaveCount(1,
                                                           "because otherwise bogus certs were used");

            DisableDefaultServerCertPinning = true;

            // Replicator - TLS Error
            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                false, //accept only self signed server cert
                null,
                (int)CouchbaseLiteError.TLSCertUnknownRoot,
                CouchbaseLiteErrorType.CouchbaseLite
                );

            // Replicator - Success
            RunReplication(
                _listener.LocalEndpoint(),
                ReplicatorType.PushAndPull,
                false,
                null,
                false, //accept only self signed server cert
                _listener.TlsIdentity.Certs[0],
                0,
                0
                );

            _listener.Stop();
        }
        private void WithActiveReplicationsAndURLEndpointListener(bool isCloseNotDelete)
        {
            var waitIdleAssert1    = new ManualResetEventSlim();
            var waitIdleAssert2    = new ManualResetEventSlim();
            var waitStoppedAssert1 = new ManualResetEventSlim();
            var waitStoppedAssert2 = new ManualResetEventSlim();

            using (var doc = new MutableDocument()) {
                OtherDb.Save(doc);
            }

            _listener = CreateListener();
            _listener.Config.Database.ActiveStoppables.Count.Should().Be(1);

            using (var doc1 = new MutableDocument()) {
                Db.Save(doc1);
            }

            var target  = new DatabaseEndpoint(Db);
            var config1 = CreateConfig(target, ReplicatorType.PushAndPull, true, sourceDb: OtherDb);
            var repl1   = new Replicator(config1);

            Database.Delete("urlepTestDb", Directory);
            var urlepTestDb = OpenDB("urlepTestDb");

            using (var doc2 = new MutableDocument()) {
                urlepTestDb.Save(doc2);
            }

            var config2 = CreateConfig(_listener.LocalEndpoint(), ReplicatorType.PushAndPull, true,
                                       serverCert: _listener.TlsIdentity.Certs[0], sourceDb: urlepTestDb);
            var repl2 = new Replicator(config2);

            EventHandler <ReplicatorStatusChangedEventArgs> changeListener = (sender, args) =>
            {
                if (args.Status.Activity == ReplicatorActivityLevel.Idle && args.Status.Progress.Completed ==
                    args.Status.Progress.Total)
                {
                    if (sender == repl1)
                    {
                        waitIdleAssert1.Set();
                    }
                    else
                    {
                        waitIdleAssert2.Set();
                    }
                }
                else if (args.Status.Activity == ReplicatorActivityLevel.Stopped)
                {
                    if (sender == repl1)
                    {
                        waitStoppedAssert1.Set();
                    }
                    else
                    {
                        waitStoppedAssert2.Set();
                    }
                }
            };

            repl1.AddChangeListener(changeListener);
            repl2.AddChangeListener(changeListener);
            repl1.Start();
            repl2.Start();

            WaitHandle.WaitAll(new[] { waitIdleAssert1.WaitHandle, waitIdleAssert2.WaitHandle }, _timeout)
            .Should().BeTrue();

            OtherDb.ActiveStoppables.Count.Should().Be(2);
            urlepTestDb.ActiveStoppables.Count.Should().Be(1);

            if (isCloseNotDelete)
            {
                urlepTestDb.Close();
                OtherDb.Close();
            }
            else
            {
                urlepTestDb.Delete();
                OtherDb.Delete();
            }

            OtherDb.ActiveStoppables.Count.Should().Be(0);
            urlepTestDb.ActiveStoppables.Count.Should().Be(0);
            OtherDb.IsClosedLocked.Should().Be(true);
            urlepTestDb.IsClosedLocked.Should().Be(true);

            WaitHandle.WaitAll(new[] { waitStoppedAssert1.WaitHandle, waitStoppedAssert2.WaitHandle }, TimeSpan.FromSeconds(20))
            .Should().BeTrue();

            waitIdleAssert1.Dispose();
            waitIdleAssert2.Dispose();
            waitStoppedAssert1.Dispose();
            waitStoppedAssert2.Dispose();

            Thread.Sleep(500);
        }
 public static URLEndpoint LocalEndpoint(this URLEndpointListener listener)
 => new URLEndpoint(LocalUrl(listener));