diff --git a/.gitignore b/.gitignore
index a5103ff..e66520c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ htmlcov/
 nosetests.xml
 coverage.xml
 *,cover
+.pytest_cache/
 
 # Translations
 *.mo
diff --git a/.travis.yml b/.travis.yml
index 4af490d..3a94266 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,10 +7,8 @@ before_install:
 - echo 'America/Los_Angeles' | sudo tee /etc/timezone
 - sudo dpkg-reconfigure --frontend noninteractive tzdata
 install:
-- if [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] ; then ./shared/dev-tasks/travis-install-ml.sh
-  release ; else (exit 0) ; fi
-- if [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] ; then ./shared/dev-tasks/setup-marklogic.sh
-  ; else (exit 0) ; fi
+- if [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] ; then ./shared/dev-tasks/travis-install-ml.sh ; else (exit 0) ; fi
+- if [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] ; then ./shared/dev-tasks/setup-marklogic.sh ; else (exit 0) ; fi
 script:
 - python setup.py test
 env:
diff --git a/examples/init-server.py b/examples/init-server.py
new file mode 100644
index 0000000..0c9b5ac
--- /dev/null
+++ b/examples/init-server.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python3
+#
+# Copyright 2018 MarkLogic Corporation
+#
+
+__author__ = 'ndw'
+
+import argparse
+import logging
+import json
+import logging
+from marklogic import MarkLogic
+
+class InitServer:
+    def __init__(self):
+        pass
+
+#logging.basicConfig(level=logging.INFO)
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--host", action='store', default="localhost",
+                    help="Management API host")
+parser.add_argument("--username", action='store', default="admin",
+                    help="User name")
+parser.add_argument("--password", action='store', default="admin",
+                    help="Password")
+parser.add_argument("--wallet", action='store', default="admin",
+                    help="Wallet password")
+parser.add_argument('--debug', action='store_true',
+                    help='Enable debug logging')
+args = parser.parse_args()
+
+if args.debug:
+    logging.basicConfig(level=logging.WARNING)
+    logging.getLogger("requests").setLevel(logging.WARNING)
+    logging.getLogger("marklogic").setLevel(logging.DEBUG)
+
+print("Initialize host {}".format(args.host))
+MarkLogic.instance_init(args.host)
+print("Initialize admin {}".format(args.host))
+MarkLogic.instance_admin(args.host, "public", args.username, args.password, args.wallet)
+
+print("finished")
diff --git a/examples/mldbmirror.py b/examples/mldbmirror.py
index fe38c64..6bc79f0 100644
--- a/examples/mldbmirror.py
+++ b/examples/mldbmirror.py
@@ -67,6 +67,27 @@ def connect(self, args):
         self.path = os.path.abspath(args['path'])
         self.loadconfig(self.path)
 
+        if args['hostname'] is None:
+            if 'host' in self.config:
+                self.hostname = self.config['host']
+                if 'port' in self.config:
+                    self.port = self.config['port']
+                else:
+                    self.port = 8000
+                if 'management-port' in self.config:
+                    self.management_port = self.config['management-port']
+                else:
+                    self.management_port = 8002
+        else:
+            parts = args['hostname'].split(":")
+            self.hostname = parts.pop(0)
+            self.management_port = 8002
+            self.port = 8000
+            if parts:
+                self.management_port = parts.pop(0)
+            if parts:
+                self.port = parts.pop(0)
+
         if args['credentials'] is not None:
             cred = args['credentials']
         else:
@@ -74,6 +95,11 @@ def connect(self, args):
                 cred = self.config['user'] + ":" + self.config['pass']
             else:
                 cred = None
+                key = self.hostname + ":" + str(self.management_port)
+                if key in self.config:
+                    obj = self.config[key]
+                    if 'user' in obj and 'pass' in obj:
+                        cred = obj['user'] + ":" + obj['pass']
 
         try:
             adminuser, adminpass = re.split(":", cred)
@@ -102,27 +128,6 @@ def connect(self, args):
         if self.root.endswith("/"):
             self.root = self.root[0:len(self.root)-1]
 
-        if args['hostname'] is None:
-            if 'host' in self.config:
-                self.hostname = self.config['host']
-                if 'port' in self.config:
-                    self.port = self.config['port']
-                else:
-                    self.port = 8000
-                if 'management-port' in self.config:
-                    self.management_port = self.config['management-port']
-                else:
-                    self.management_port = 8002
-        else:
-            parts = args['hostname'].split(":")
-            self.hostname = parts.pop(0)
-            self.management_port = 8002
-            self.port = 8000
-            if parts:
-                self.management_port = parts.pop(0)
-            if parts:
-                self.port = parts.pop(0)
-
         self.connection \
           = Connection(self.hostname, HTTPDigestAuth(adminuser, adminpass), \
                            port=self.port, management_port=self.management_port)
@@ -486,15 +491,17 @@ def _download_directory(self, trans):
         down_map = {}
         skip_list = []
         for uri in uris:
-            if not self.can_store_on_filesystem(uri):
-                raise RuntimeError("Cannot save URI:", uri)
-
             localfile = self.path + uri
             skip = False
-            if uri in stamps and os.path.exists(localfile):
-                statinfo = os.stat(localfile)
-                stamp = self._convert_timestamp(stamps[uri])
-                skip = statinfo.st_mtime >= stamp.timestamp()
+
+            if not self.can_store_on_filesystem(uri):
+                print("Skipping " + uri + ": cannot store on filesystem")
+                skip = True
+            else:
+                if uri in stamps and os.path.exists(localfile):
+                    statinfo = os.stat(localfile)
+                    stamp = self._convert_timestamp(stamps[uri])
+                    skip = statinfo.st_mtime >= stamp.timestamp()
 
             if skip:
                 skip_list.append(localfile)
@@ -709,7 +716,7 @@ def can_store_on_filesystem(self, filename):
         filesystem because if it's ever uploaded, it'll get a leading /.
         """
         if (not filename.startswith("/")) or ("//" in filename) \
-          or (":" in filename) or ('"' in filename) or ('"' in filename) \
+          or (":" in filename) or ('"' in filename) or ("'" in filename) \
           or ("\\" in filename):
             return False
         else:
diff --git a/examples/read-everything.py b/examples/read-everything.py
new file mode 100644
index 0000000..dfb490b
--- /dev/null
+++ b/examples/read-everything.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python3
+#
+# Copyright 2015 MarkLogic Corporation
+#
+# This script attempts to read all of the resource types on the cluster.
+# The point of this script is to make sure that we catch any new properties
+# that have been added by the server.
+
+__author__ = 'ndw'
+
+import argparse
+import logging
+import json
+import logging
+import sys
+from requests.auth import HTTPDigestAuth
+from marklogic.connection import Connection
+from marklogic.models.cluster import LocalCluster
+from marklogic.models.group import Group
+from marklogic.models.host import Host
+from marklogic.models.database import Database
+from marklogic.models.permission import Permission
+from marklogic.models.privilege import Privilege
+from marklogic.models.role import Role
+from marklogic.models.forest import Forest
+from marklogic.models.server import Server
+from marklogic.models.user import User
+
+class ReadEverything:
+    def __init__(self, connection):
+        self.databases = {}
+        self.forests = {}
+        self.servers = {}
+        self.users = {}
+        self.roles = {}
+        self.privileges = {}
+        self.connection = connection
+        pass
+
+    def readClass(self, kind, klass, max_read=sys.maxsize):
+        names = klass.list(self.connection)
+        for name in names:
+            if max_read > 0:
+                if name.find("|") > 0:
+                    parts = name.split("|")
+                    rsrc = klass.lookup(self.connection, parts[0], parts[1])
+                else:
+                    rsrc = klass.lookup(self.connection, name)
+                max_read = max_read - 1
+        print("{}: {}".format(kind, len(names)))
+
+    def readPrivileges(self):
+        names = Privilege.list(self.connection)
+        max_read = { "execute": 5, "uri": 5 }
+        counts = { "execute": 0, "uri": 0 }
+        for name in names:
+            parts = name.split("|")
+            kind = parts[0]
+            pname = parts[1]
+
+            counts[kind] = counts[kind] + 1
+
+            if max_read[kind] > 0:
+                rsrc = Privilege.lookup(self.connection, pname, kind)
+                max_read[kind] = max_read[kind] - 1
+
+        print("Execute privileges: {}".format(counts["execute"]))
+        print("URI privileges: {}".format(counts["uri"]))
+
+    def read(self):
+        conn = self.connection
+        cluster = LocalCluster(connection=conn).read()
+        print("Read local cluster: {}".format(cluster.cluster_name()))
+
+        self.readClass("Groups", Group, max_read=5)
+        self.readClass("Hosts", Host, max_read=5)
+        self.readClass("Databases", Database, max_read=5)
+        self.readClass("Forests", Forest, max_read=5)
+        self.readClass("Servers", Server)
+        self.readClass("Roles", Role, max_read=5)
+        self.readClass("Users", User, max_read=5)
+        self.readPrivileges()
+
+        return
+
+logging.basicConfig(level=logging.INFO)
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--host", action='store', default="localhost",
+                    help="Management API host")
+parser.add_argument("--username", action='store', default="admin",
+                    help="User name")
+parser.add_argument("--password", action='store', default="admin",
+                    help="Password")
+parser.add_argument('--debug', action='store_true',
+                    help='Enable debug logging')
+args = parser.parse_args()
+
+if args.debug:
+    logging.basicConfig(level=logging.WARNING)
+    logging.getLogger("requests").setLevel(logging.WARNING)
+    logging.getLogger("marklogic").setLevel(logging.DEBUG)
+
+conn = Connection(args.host, HTTPDigestAuth(args.username, args.password))
+read_everything = ReadEverything(conn)
+
+print("Reading all resources from {}".format(args.host))
+
+read_everything.read()
+
+print("Finished")
diff --git a/marklogic/__init__.py b/marklogic/__init__.py
index ed3de89..740a1ec 100644
--- a/marklogic/__init__.py
+++ b/marklogic/__init__.py
@@ -36,7 +36,7 @@
 from marklogic.models.server import OdbcServer, XdbcServer
 from marklogic.exceptions import InvalidAPIRequest, UnexpectedManagementAPIResponse
 
-__version__ = "0.0.14"
+__version__ = "0.0.17"
 
 class MarkLogic:
     """
@@ -446,7 +446,7 @@ def instance_init(cls, host):
         return Host(host)._set_just_initialized()
 
     @classmethod
-    def instance_admin(cls,host,realm,admin,password):
+    def instance_admin(cls,host,realm,admin,password,wallet_password=None):
         """
         Initializes the security database of a newly initialized server.
 
@@ -463,6 +463,9 @@ def instance_admin(cls,host,realm,admin,password):
             'realm': realm
             }
 
+        if wallet_password is not None:
+            payload["wallet-password"] = wallet_password
+
         uri = "{0}://{1}:8001/admin/v1/instance-admin".format(
             conn.protocol, conn.host)
 
diff --git a/marklogic/cli/manager/marklogic.py b/marklogic/cli/manager/marklogic.py
index a163fb2..455af67 100644
--- a/marklogic/cli/manager/marklogic.py
+++ b/marklogic/cli/manager/marklogic.py
@@ -152,7 +152,11 @@ def restart(self, args, config, connection):
                 cluster = LocalCluster(connection=connection).read()
                 print("Restarting cluster...")
                 cluster.restart()
-
+                # Make sure it's back up
+                status = self.status(args,config,connection,internal=True)
+                while status != 'up':
+                    time.sleep(2)
+                    status = self.status(args,config,connection,internal=True)
             else:
                 hostname = connection.host
                 if hostname == 'localhost':
@@ -160,6 +164,11 @@ def restart(self, args, config, connection):
                 host = Host(hostname,connection=connection).read()
                 print("Restarting host...")
                 host.restart()
+                # Make sure it's back up
+                status = self.status(args,config,connection,internal=True)
+                while status != 'up':
+                    time.sleep(2)
+                    status = self.status(args,config,connection,internal=True)
 
     def stop(self, args, config, connection):
         status = self.status(args, config, connection, internal=True)
@@ -174,6 +183,11 @@ def stop(self, args, config, connection):
                 cluster = LocalCluster(connection=connection).read()
                 print("Shutting down cluster...")
                 cluster.shutdown()
+                # Make sure it's all the way down
+                status = self.status(args,config,connection,internal=True)
+                while status != 'down':
+                    time.sleep(2)
+                    status = self.status(args,config,connection,internal=True)
             else:
                 hostname = connection.host
                 if hostname == 'localhost':
@@ -190,6 +204,11 @@ def stop(self, args, config, connection):
 
                 print("Shutting down host: " + host.host_name())
                 host.shutdown()
+                # Make sure it's all the way down
+                status = self.status(args,config,connection,internal=True)
+                while status != 'down':
+                    time.sleep(2)
+                    status = self.status(args,config,connection,internal=True)
 
             status = self.status(args,config,connection,internal=True)
             while status == 'up':
diff --git a/marklogic/cli/template.py b/marklogic/cli/template.py
index 2894e5d..ea647ea 100644
--- a/marklogic/cli/template.py
+++ b/marklogic/cli/template.py
@@ -638,6 +638,8 @@ def _make_parser(self, command, artifact, description=""):
                             help='Host on which to issue the request')
         parser.add_argument('--credentials', default='admin:admin',
                             help='Login credentials for request')
+        parser.add_argument('--https', action='store_true',
+                            help='Enable https')
         parser.add_argument('--debug', action='store_true',
                             help='Enable debug logging')
         return parser
diff --git a/marklogic/connection.py b/marklogic/connection.py
index 6b7dca4..e359f30 100644
--- a/marklogic/connection.py
+++ b/marklogic/connection.py
@@ -31,6 +31,7 @@
 from requests.exceptions import ReadTimeout
 from requests.packages.urllib3.exceptions import ProtocolError
 from requests.packages.urllib3.exceptions import ReadTimeoutError
+from requests.packages import urllib3
 
 """
 Connection related classes and method to connect to MarkLogic.
@@ -56,6 +57,9 @@ def __init__(self, host, auth,
         self.logger = logging.getLogger("marklogic.connection")
         self.payload_logger = logging.getLogger("marklogic.connection.payloads")
 
+        self.verify = False # Danger, Will Robinson!
+        urllib3.disable_warnings()
+
     # You'd expect parameters to be a dictionary, but then it couldn't
     # have repeated keys, so it's an array.
     def uri(self, relation, name=None,
@@ -104,7 +108,7 @@ def client_uri(self, path, protocol=None, host=None, port=None, version=None):
 
     def head(self, uri, accept="application/json"):
         self.logger.debug("HEAD {0}...".format(uri))
-        self.response = requests.head(uri, auth=self.auth)
+        self.response = requests.head(uri, auth=self.auth, verify=self.verify)
         return self._response()
 
     def get(self, uri, accept="application/json", headers=None):
@@ -117,7 +121,8 @@ def get(self, uri, accept="application/json", headers=None):
         self.payload_logger.debug("Headers:")
         self.payload_logger.debug(json.dumps(headers, indent=2))
 
-        self.response = requests.get(uri, auth=self.auth, headers=headers)
+        self.response = requests.get(uri, auth=self.auth, headers=headers,
+                                     verify=self.verify)
         return self._response()
 
     def post(self, uri, payload=None, etag=None, headers=None,
@@ -143,14 +148,17 @@ def post(self, uri, payload=None, etag=None, headers=None,
                 self.payload_logger.debug(payload)
 
         if payload is None:
-            self.response = requests.post(uri, auth=self.auth, headers=headers)
+            self.response = requests.post(uri, auth=self.auth, headers=headers,
+                                          verify=self.verify)
         else:
             if content_type == "application/json":
                 self.response = requests.post(uri, json=payload,
-                                              auth=self.auth, headers=headers)
+                                              auth=self.auth, headers=headers,
+                                              verify=self.verify)
             else:
                 self.response = requests.post(uri, data=payload,
-                                              auth=self.auth, headers=headers)
+                                              auth=self.auth, headers=headers,
+                                              verify=self.verify)
 
         return self._response()
 
@@ -173,14 +181,17 @@ def put(self, uri, payload=None, etag=None,
                 self.payload_logger.debug(payload)
 
         if payload is None:
-            self.response = requests.put(uri, auth=self.auth, headers=headers)
+            self.response = requests.put(uri, auth=self.auth, headers=headers,
+                                         verify=self.verify)
         else:
             if content_type == "application/json":
                 self.response = requests.put(uri, json=payload,
-                                             auth=self.auth, headers=headers)
+                                             auth=self.auth, headers=headers,
+                                             verify=self.verify)
             else:
                 self.response = requests.put(uri, data=payload,
-                                             auth=self.auth, headers=headers)
+                                             auth=self.auth, headers=headers,
+                                             verify=self.verify)
 
         return self._response()
 
@@ -203,10 +214,12 @@ def delete(self, uri, payload=None, etag=None,
                 self.payload_logger.debug(payload)
 
         if payload is None:
-            self.response = requests.delete(uri, auth=self.auth, headers=headers)
+            self.response = requests.delete(uri, auth=self.auth, headers=headers,
+                                            verify=self.verify)
         else:
             self.response = requests.delete(uri, json=payload,
-                                            auth=self.auth, headers=headers)
+                                            auth=self.auth, headers=headers,
+                                            verify=self.verify)
 
         return self._response()
 
@@ -250,7 +263,8 @@ def wait_for_restart(self, last_startup, timestamp_uri="/admin/v1/timestamp"):
                 self.logger.debug("Waiting for restart of {0}"
                                   .format(self.host))
                 response = requests.get(uri, auth=self.auth,
-                                        headers={'accept': 'application/json'})
+                                        headers={'accept': 'application/json'},
+                                        verify=self.verify)
                 done = (response.status_code == 200
                         and response.text != last_startup)
             except TypeError:
diff --git a/marklogic/mma.py b/marklogic/mma.py
index 8a4bec4..0db3f29 100644
--- a/marklogic/mma.py
+++ b/marklogic/mma.py
@@ -7,6 +7,7 @@
 import shlex
 import sys
 from requests.auth import HTTPDigestAuth
+from requests.auth import HTTPBasicAuth
 from marklogic.connection import Connection
 from marklogic.cli.template import Template
 
@@ -57,7 +58,7 @@ def run(self, argv):
                 optarg = False
             elif tok.startswith("-"):
                 options.append(tok)
-                if tok != "--debug":
+                if tok != "--debug" and tok != "--https":
                     optarg = True
             elif "=" in tok:
                 params.append(tok)
@@ -152,9 +153,16 @@ def run(self, argv):
                 mgmt_port = args['hostname'].split(":")[1]
             except IndexError:
                 mgmt_port = 8002
-            self.connection = Connection(host,
-                                         HTTPDigestAuth(username, password),
-                                         management_port=mgmt_port)
+
+            if args['https']:
+                self.connection = Connection(host,
+                                             HTTPBasicAuth(username, password),
+                                             protocol="https",
+                                             management_port=mgmt_port)
+            else:
+                self.connection = Connection(host,
+                                             HTTPDigestAuth(username, password),
+                                             management_port=mgmt_port)
 
         # do it!
         if command == 'run':
diff --git a/marklogic/models/database/__init__.py b/marklogic/models/database/__init__.py
index d97d003..689dddd 100644
--- a/marklogic/models/database/__init__.py
+++ b/marklogic/models/database/__init__.py
@@ -3755,7 +3755,7 @@ def unmarshal(cls, config, hostname=None,
                     olist.append(temp)
                 result._config['range-path-index'] = olist
             else:
-                logger.warn("Unexpected database property: " + key)
+                logger.warning("Unexpected database property: " + key)
 
         return result
 
diff --git a/marklogic/models/forest/__init__.py b/marklogic/models/forest/__init__.py
index f2c1513..f21c6b3 100644
--- a/marklogic/models/forest/__init__.py
+++ b/marklogic/models/forest/__init__.py
@@ -445,7 +445,7 @@ def unmarshal(cls, config, connection=None, save_connection=True):
                     olist.append(temp)
                 result._config['forest-replica'] = olist
             else:
-                logger.warn("Unexpected forest property: " + key)
+                logger.warning("Unexpected forest property: " + key)
 
         return result
 
diff --git a/marklogic/models/group/__init__.py b/marklogic/models/group/__init__.py
index 1c02a7c..f215e7e 100644
--- a/marklogic/models/group/__init__.py
+++ b/marklogic/models/group/__init__.py
@@ -154,7 +154,9 @@ def unmarshal(cls, config,
                   'opsdirector-log-level', 'opsdirector-metering',
                   'opsdirector-session-endpoint', 'telemetry-config',
                   'telemetry-log-level', 'telemetry-metering',
-                  'telemetry-session-endpoint'
+                  'telemetry-session-endpoint',
+                  'xdqp-ssl-disable-sslv3', 'xdqp-ssl-disable-tlsv1',
+                  'xdqp-ssl-disable-tlsv1-1', 'xdqp-ssl-disable-tlsv1-2'
                   }
 
         for key in result._config:
@@ -188,7 +190,7 @@ def unmarshal(cls, config,
                                                     r['audit-restriction-items'])
                             restrictions.append(rest)
                     else:
-                        logger.warn("Unexpected audit property: " + prop)
+                        logger.warning("Unexpected audit property: " + prop)
                 audit = Audit(enabled, keep, rotate, events, restrictions)
                 result._config[key] = audit
             elif key == 'event':
@@ -200,7 +202,7 @@ def unmarshal(cls, config,
                     schemas.append(schema)
                 result._config[key] = schemas
             else:
-                logger.warn("Unexpected group property: " + key)
+                logger.warning("Unexpected group property: " + key)
 
         return result
 
diff --git a/marklogic/utilities/validators.py b/marklogic/utilities/validators.py
index 1994734..b51c910 100644
--- a/marklogic/utilities/validators.py
+++ b/marklogic/utilities/validators.py
@@ -244,8 +244,8 @@ def validate_collation(index_type, collation):
         return
     if collation is None or collation == "":
         return
-    raise ValidationError('Collation cannot be {0} for an index of type {1}' \
-                          .format(index_type, collation))
+    raise ValidationError('Invalid collation for index of type {0}' \
+                          .format(index_type), repr(collation))
 
 def validate_type(raw_val, cls):
     """
diff --git a/setup.py b/setup.py
index 9ee37a6..a37b3a5 100644
--- a/setup.py
+++ b/setup.py
@@ -25,8 +25,8 @@ def read(*filenames, **kwargs):
     long_description=read('README.rst'),
     packages=find_packages(),
     install_requires=[
-        'requests>=2.8.0',
-        'requests_toolbelt>=0.6.0'
+        'requests>=2.21.0',
+        'requests_toolbelt>=0.9.1'
     ],
     include_package_data=True,
     platforms='any',
diff --git a/shared/dev-tasks/travis-install-ml.sh b/shared/dev-tasks/travis-install-ml.sh
index b008590..bd3fee0 100755
--- a/shared/dev-tasks/travis-install-ml.sh
+++ b/shared/dev-tasks/travis-install-ml.sh
@@ -49,7 +49,7 @@ else
   # if the user passed a day string as a param then use it instead
   test $1 && day=$1
   # make a version number out of the date
-  ver="8.0-$day"
+  ver="9.0-$day"
 
   echo "********* Downloading MarkLogic nightly $ver"
 
@@ -60,7 +60,7 @@ else
   suff="_amd64.deb"
   fnamedeb=$fnamedeb$suff
 
-  url="https://root.marklogic.com/nightly/builds/linux64/rh6-intel64-80-test-1.marklogic.com/b8_0/pkgs.$day/$fname"
+  url="https://root.marklogic.com/nightly/builds/linux64-rh7/rh7v-intel64-90-test-build.marklogic.com/b9_0/pkgs.20190313/$fname"
 
   status=$(curl -k --anyauth -u $MLBUILD_USER:$MLBUILD_PASSWORD --head --write-out %{http_code} --silent --output /dev/null $url)
   if [[ $status = 200 ]]; then
diff --git a/tests/test_group.py b/tests/test_group.py
new file mode 100644
index 0000000..31850f3
--- /dev/null
+++ b/tests/test_group.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2015 MarkLogic Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0#
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# File History
+# ------------
+#
+# Paul Hoehne       03/26/2015     Initial development
+# Norman Walsh      05/02/2018     Adapted from test_host.py
+#
+
+from mlconfig import MLConfig
+from marklogic.models.group import Group
+
+class TestGroup(MLConfig):
+    def group_list(self):
+        return Group.list(self.connection)
+
+    def test_list_groups(self):
+        groups = self.group_list()
+        assert len(groups) > 0
+        assert groups
+
+    def test_load(self):
+        group_name = self.group_list()[0]
+        group = Group(group_name)
+        assert group.read(self.connection) is not None
+        assert group.host_timeout() > 0
+