diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 812b8ab5e9fb1e08aaf1723e60651aa4a34a6fc9..f02b6972e4c0baf8b761ec97e6aac2c65350f0e6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -62,14 +62,14 @@ code_style_octave:
   stage: test
   script:
     - mh_style -v
-    - mh_style --octave ./src ./doc
+    - make style_octave
   allow_failure: true
 
 code_style_cpp:
   tags: [ docker ]
   stage: test
   script:
-    - clang-format-11 --dry-run --verbose --Werror $(find test/ src/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in")
+    - make style_cpp
   allow_failure: true
 
 unit_tests:
@@ -79,30 +79,21 @@ unit_tests:
     - octave -v
     - make test
 
-package_tests:
-  tags: [ docker ]
-  stage: test
-  script:
-    - octave -v
-    - make pkg-test
-
 # Linting with miss_hit
 linting_octave:
   tags: [ docker ]
   stage: test
   script:
-    - mh_lint --octave ./
+    - make linting_octave
+  allow_failure: true
 
 # linting with clang-tidy and include-what-you-use
 linting_cpp:
   tags: [ docker ]
   stage: test
   script:
-    - cd src
-    - ./configure
-    - cd ../build
-    - cmake -D LINTING=On ..
-    - cmake --build .
+    - make linting_cpp
+  allow_failure: true
 
 # trigger the integration tests
 trigger_inttest:
@@ -111,7 +102,7 @@ trigger_inttest:
   script:
 
     ## Determine the octaveinttest branch...
-    # ... use an f-branch if posible...
+    # ... sync'ed f-branch (f-bar on octavelib requires f-bar on octaveinttest)...
     - if echo "$CI_COMMIT_REF_NAME" | grep -c "^f-" ; then
         OCTAVEINT_REF=$CI_COMMIT_REF_NAME ;
       fi;
diff --git a/ChangeLog b/CHANGELOG.md
similarity index 86%
rename from ChangeLog
rename to CHANGELOG.md
index 112d008ea737b3aff591b06155dd37f31f09b3bb..585a63db0bbb212bc533b222ade04db9359aadbb 100644
--- a/ChangeLog
+++ b/CHANGELOG.md
@@ -23,4 +23,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
-- Everything.
+- Minimal working example with basic connection functionality.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8382a5160e698c43881afdce2a6659a807e61204..ac4b6f47b13f07cb0fe906c3501dddb9bbaee8f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,33 +47,81 @@ string(REGEX REPLACE ";grpc\\+?\\+?_unsecure" "" CONAN_PKG_LIBS
 #######################################################
 option(AUTOFORMATTING "call clang-format at configure time" ON)
 if(AUTOFORMATTING)
-    file(GLOB format_sources src/*.cpp)
-    execute_process(COMMAND clang-format -i --verbose ${format_sources}
+    find_program(clang_format NAMES clang-format-11 clang-format)
+    file(GLOB_RECURSE format_sources src/*.cpp)
+    execute_process(COMMAND ${clang_format} -i --verbose ${format_sources}
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
 endif()
 
 
 #######################################################
-### Compile *.mex files
+### Build utility library "maoxdb"
 #######################################################
-add_subdirectory(src)
-set(PKG_INST_DIR "${PROJECT_SOURCE_DIR}/inst")
+# The library will be called maoxdb until we find a better name.  The name is not very important
+# though, since the library is used only internally.
+
+# OCTINCLUDEDIR
+execute_process(COMMAND mkoctfile -p OCTINCLUDEDIR
+    OUTPUT_VARIABLE OCTINCLUDEDIR
+    )
+string(REGEX REPLACE "\n" "" OCTINCLUDEDIR "${OCTINCLUDEDIR}")
+# OCTLIBDIR
+execute_process(COMMAND mkoctfile -p OCTLIBDIR
+    OUTPUT_VARIABLE OCTLIBDIR
+    )
+string(REGEX REPLACE "\n" "" OCTLIBDIR "${OCTLIBDIR}")
+# OCTLIBS
+execute_process(COMMAND mkoctfile -p OCTAVE_LIBS
+    OUTPUT_VARIABLE OCTLIBS
+    )
+string(REGEX REPLACE "\n" "" OCTLIBS "${OCTLIBS}")
+string(REGEX REPLACE " " ";" OCTLIBS "${OCTLIBS}")
+
+set(MAOXDB_DIR "${PROJECT_SOURCE_DIR}/src/lib")
+add_library(maoxdb STATIC "${MAOXDB_DIR}/maoxdb.cpp")
+set_target_properties(maoxdb PROPERTIES PUBLIC_HEADER "${MAOXDB_DIR}/maoxdb.hpp")
+target_compile_options(maoxdb PRIVATE "-I${OCTINCLUDEDIR}" "-fPIC")
+get_property(_MAOX_LIB_DIR TARGET maoxdb PROPERTY LIBRARY_OUTPUT_DIRECTORY)
+# message("_MAOX_LIB_DIR: ${_MAOX_LIB_DIR}")
+
+#######################################################
+### Compile into *.mex files
+#######################################################
+# Only files in src/private will be compiled.  This is to separate the non-mex files in src/lib; if
+# this is to change, the GLOB_RECURSE code needs to be changed.
+
+# Absolute paths to the cpp files.
+file(GLOB_RECURSE OCTAVE_CAOSDB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/private/*.cpp)
+message("OCTAVE_CAOSDB_SRC: ${OCTAVE_CAOSDB_SRC}")
 
+set(PKG_INST_DIR "${PROJECT_SOURCE_DIR}/inst")
 file(MAKE_DIRECTORY ${PKG_INST_DIR})
-string(REGEX REPLACE ";" ";-I" _MKOCTFILE_INCLUDES "-I${CONAN_INCLUDE_DIRS}")
-string(REGEX REPLACE ";" ";-L" _MKOCTFILE_LIB_DIRS "-L${CONAN_LIB_DIRS}")
-string(REGEX REPLACE ";" ";-l" _MKOCTFILE_LIBS "-l${CONAN_LIBS}")
+
+# Options for mex compilation
+string(REGEX REPLACE ";" ";-I" _MKOCTFILE_INCLUDES "-I${CONAN_INCLUDE_DIRS};${MAOXDB_DIR}")
+string(REGEX REPLACE ";" ";-L" _MKOCTFILE_LIB_DIRS "-L${CONAN_LIB_DIRS};${_MAOX_LIB_DIR}")
+string(REGEX REPLACE ";" ";-l" _MKOCTFILE_LIBS "-l${CONAN_LIBS};maoxdb")
 string(REGEX REPLACE ";" ":" _MKOCTFILE_RPATH "${CONAN_LIB_DIRS}")
-set(_MKOCTFILE_OPTIONS "-o" "${PKG_INST_DIR}/caosdb.mex" "-Wl,-rpath,${_MKOCTFILE_RPATH}" "--mex" "-std=gnu++17"
+set(_MKOCTFILE_OPTIONS "-Wl,-rpath,${_MKOCTFILE_RPATH}" "--mex" "-std=gnu++17"
     "-L/usr/local/lib" ${_MKOCTFILE_INCLUDES} ${_MKOCTFILE_LIB_DIRS} ${_MKOCTFILE_LIBS})
 
-add_custom_command(OUTPUT ${PKG_INST_DIR}/caosdb.mex
-    COMMAND mkoctfile
-    ARGS ${_MKOCTFILE_OPTIONS} ${OCTAVE_CAOSDB_SRC}
-    DEPENDS ${OCTAVE_CAOSDB_SRC}
-    )
-add_custom_target(caosdb.mex ALL
-    SOURCES ${PKG_INST_DIR}/caosdb.mex)
+add_custom_target(mex ALL
+    DEPENDS maoxdb)
+file(GLOB_RECURSE _CPP_SOURCES RELATIVE "${PROJECT_SOURCE_DIR}/src" src/private/*.cpp)
+foreach(sourcefile ${_CPP_SOURCES})
+    string(REGEX REPLACE ".cpp$" ".mex" _mex_ext_file ${sourcefile})
+    string(REGEX REPLACE "/" "__" _target_name "${sourcefile}")
+    set(_mex_ext_file "${PKG_INST_DIR}/${_mex_ext_file}")
+    set(_mkoct_output "-o;${_mex_ext_file}")
+    set(_abs_source "${PROJECT_SOURCE_DIR}/src/${sourcefile}")
+    add_custom_command(OUTPUT ${_mex_ext_file}
+        COMMAND mkoctfile
+        ARGS ${_MKOCTFILE_OPTIONS} ${_mkoct_output} ${_abs_source}
+        MAIN_DEPENDENCY ${_abs_source}
+        )
+    add_custom_target(${_target_name} DEPENDS ${_mex_ext_file})
+    add_dependencies(mex ${_target_name})
+endforeach(sourcefile)
 
 
 #######################################################
@@ -81,29 +129,23 @@ add_custom_target(caosdb.mex ALL
 #######################################################
 option(LINTING "clang-tidy and iwyu" OFF)
 if(LINTING)
-    execute_process(COMMAND mkoctfile -p OCTINCLUDEDIR
-        OUTPUT_VARIABLE _OCTINCLUDEDIR
-        )
-    string(REGEX REPLACE "\n" "" _OCTINCLUDEDIR "${_OCTINCLUDEDIR}")
-
-    find_program(clang_tidy NAMES clang-tidy clang-tidy-11)
+    find_program(clang_tidy NAMES clang-tidy-11 clang-tidy)
     if(NOT clang_tidy)
         message(WARNING "clang-tidy: Not found")
     else()
         message(STATUS "clang-tidy: ${clang_tidy}")
         set(_CMAKE_CXX_CLANG_TIDY
             "--warnings-as-errors=*"
-            "--header-filter=caosdb/.*[^\\\(\.pb\.h\\\)]$"
             "--fix")
         set(_CMAKE_CXX_CLANG_TIDY_CHECKS
           "--checks=*,-fuchsia-*,-llvmlibc-*,-llvm-else-after-return,-readability-else-after-return,-cppcoreguidelines-pro-type-vararg,-hicpp-vararg,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cert-err58-cpp")
 
         add_custom_command(
-            TARGET caosdb.mex
+            TARGET mex
             COMMAND ${clang_tidy}
             ARGS ${_CMAKE_CXX_CLANG_TIDY} ${_CMAKE_CXX_CLANG_TIDY_CHECKS}
             ${OCTAVE_CAOSDB_SRC} "--" ${_MKOCTFILE_INCLUDES} "-I/usr/include"
-            "-I${_OCTINCLUDEDIR}" "-std=c++17"
+            "-I${OCTINCLUDEDIR}" "-std=c++17"
             DEPENDS ${OCTAVE_CAOSDB_SRC})
     endif()
 
@@ -119,9 +161,9 @@ if(LINTING)
             "-Xiwyu" "--cxx17ns")
 
         add_custom_command(
-            TARGET caosdb.mex
+            TARGET mex
             COMMAND ${iwyu}
-            ARGS ${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE} "-I${_OCTINCLUDEDIR}"
+            ARGS ${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE} "-I${OCTINCLUDEDIR}"
             "-std=c++17" "-I/usr/include" ${_MKOCTFILE_INCLUDES}
             ${OCTAVE_CAOSDB_SRC} "||" "true"
             DEPENDS ${OCTAVE_CAOSDB_SRC})
@@ -131,4 +173,55 @@ else()
     message(STATUS "LINTING is OFF")
 endif()
 
-
+###############################################################################
+#                                    Tests                                    #
+###############################################################################
+
+option(TEST "Unit test with gtest" OFF)
+if(TEST)
+    # cmake tests suffers from not being able to define dependencies, see
+    # https://stackoverflow.com/questions/733475, so we disable it for now.
+    # enable_testing()
+    add_custom_target(test
+        echo "[EE] \\`make test\\` does not exist, did you mean \\`make test_detailed\\`?"
+        COMMAND exit 1)
+    # append all the test cases here (file name without the ".cpp" suffix)
+    set(test_cases
+        test_utilities
+        )
+    # add special cmake functions for gtest, (although they are not used yet, see above)
+    include(GoogleTest)
+    # `make testfiles` just builds the tests, `make test_detailed` builds and runs the tests
+    add_custom_target(testfiles)
+    add_custom_target(test_detailed DEPENDS testfiles)
+    # loop over all test cases and add them to the test runner
+    list(LENGTH test_cases len_test_cases)
+    math(EXPR len_test_cases "${len_test_cases} - 1")
+    foreach (i RANGE "${len_test_cases}")
+        list(GET test_cases ${i} test_case_name)
+        # Once cmake 3.14 is available everywhere, use the new `foreach` instead.
+        # foreach (test_case_name "${test_cases}")
+        message(STATUS "test_case_name: ${test_case_name}")
+        add_executable(${test_case_name} EXCLUDE_FROM_ALL test/${test_case_name}.cpp)
+        add_custom_target("test_${test_case_name}" "${test_case_name}" DEPENDS "${test_case_name}")
+        add_dependencies(test_detailed "test_${test_case_name}")
+        # set(libcaosdb_TEST_SRC
+        #     "${CMAKE_CURRENT_SOURCE_DIR}/${test_case_name}.cpp ${libcaosdb_TEST_SRC}")
+        target_include_directories(${test_case_name} PUBLIC
+            ${OCTINCLUDEDIR}
+            ${MAOXDB_DIR})
+        target_link_libraries(${test_case_name} PUBLIC
+            maoxdb ${CONAN_LIBS_CAOSDB} ${CONAN_LIBS_GTEST} ${CONAN_LIBS_BOOST})
+        target_link_directories(${test_case_name} PUBLIC ${OCTLIBDIR})
+        target_link_options(${test_case_name} PUBLIC ${OCTLIBS})
+        message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
+        gtest_discover_tests(${test_case_name}
+            WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+            TEST_LIST test_list
+            )
+        message(STATUS "tests for ${test_case_name}: ${test_list}")
+        add_dependencies(testfiles ${test_case_name})
+    endforeach ()
+else()
+    message(STATUS "TEST is OFF")
+endif(TEST)
diff --git a/Makefile b/Makefile
index e3d9d8d9070566cf48463b6dc534db354daaff07..0ca33357d62d985e77167fd6ee39b6592faa2904 100644
--- a/Makefile
+++ b/Makefile
@@ -33,24 +33,63 @@ help:
 doc:
 	cd doc && $(MAKE) html
 
+###############################################################################
+#                                   Styling                                   #
+###############################################################################
+
+style: style_octave style_cpp
 .PHONY: style
-style:
-	mh_style --fix --octave src doc
 
+style_octave:
+	mh_style --octave src doc test || echo "You may want to run `make style_fix`."
+.PHONY: style_octave
+
+style_cpp:
+	clang-format-11 --dry-run --verbose --Werror $(shell find test/ src/ -type f -iname "*.cpp" -o -iname "*.hpp" -o -iname "*.h" -o -iname "*.h.in") || echo "You may want to run `make style_fix`."
+.PHONY: style_cpp
+
+style_fix:
+	mh_style --fix --octave src doc test
+	clang-format-11 -i --verbose --Werror $(shell find test/ src/ -type f -iname "*.cpp" -o -iname "*.hpp" -o -iname "*.h" -o -iname "*.h.in")
+.PHONY: style_fix
+
+###############################################################################
+#                                   Linting                                   #
+###############################################################################
+
+linting: linting_octave linting_cpp
+.PHONY: linting
+
+linting_octave:
+	mh_lint --octave ./
+.PHONY: linting_octave
+
+linting_cpp:
+	cd src                      \
+    && ./configure            \
+    && cd ../build            \
+    && cmake -D LINTING=On .. \
+    && cmake --build .
+.PHONY: linting_cpp
+
+###############################################################################
+#                                    Tests                                    #
+###############################################################################
+
+test: install
+	$(MAKE) -C test test
 .PHONY: test
-test:
-	cd test && octave Run_Test.m
 
-.PHONY: pkg-test
-pkg-test: install
-	octave --eval "pkg load caosdb; caosdb --version"
+###############################################################################
+#                          Packaging and Installation                         #
+###############################################################################
 
 .PHONY: pkg
 pkg: dist/caosdb.tar.gz
 
 .PHONY: install
 install: pkg
-	octave --eval "pkg -verbose install dist/caosdb.tar.gz"
+	octave --eval "pkg install -verbose dist/caosdb.tar.gz"
 
 .PHONY: caosdb.tar.gz
 dist/caosdb.tar.gz: dist/
diff --git a/README_SETUP.md b/README_SETUP.md
index 688d7c97a0f88a355b55e37b7e2094842ddf1770..32c9d1a8b038085056248fca7d50d3d4874d58a9 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -5,7 +5,8 @@
 This package requires the following software:
 
 - conan: `pip install conan`
-- libcaosdb: See https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib for further instructions.
+- libcaosdb installed with Conan: See https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib for
+  further instructions.
 - For running this library, a valid libcaosdb configuration is needed, for example a
   `.caosdb_client.json` file in your home directory or current working directory.  Please look at
   [`README_SETUP.md` in caosdb-cpplib](https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib/-/blob/main/README_SETUP.md) for more information.
@@ -21,11 +22,13 @@ This package requires the following software:
 
 ## Run Unit Tests
 
-We use the standard test framework of octave.
+We use the standard test framework of Octave.
 
-See [this articel](https://wiki.octave.org/Tests) for a primer and use
+See [this article](https://wiki.octave.org/Tests) for a primer and use
 [this](https://octave.org/doc/interpreter/Test-Functions.html) as a reference.
 
+Additionally we use the [MOxUnit](https://github.com/MOxUnit/MOxUnit) framework.
+
 ## Code Formatting
 
 Code formatting is done with the
@@ -50,21 +53,3 @@ static website. If you rather would like to use the native,
 - `sphinx`
 - `sphinx-autoapi`
 - `recommonmark`
-
-### Writing Documentation
-
-- Example for texinfo documentation:
-  https://github.com/gnu-octave/octave/blob/default/scripts/geometry/inpolygon.m
-- Extract documentation from file:  
-  `[txt, form] = get_help_text_from_file(make_absolute_filename('pkg/inst/some_function.m'))`
-- Generate HTML documentation from single file:  
-  ```octave
-pkg uninstall caosdb
-pkg install caosdb.tar.gz
-pkg load caosdb
-html_help_text('some_function', './htdocs/some.html', 'octave-forge', pkgname="caosdb")
-```
-- Generate HTML documentation for a package:  
-  ```octave
-generate_package_html('caosdb', 'htdocs', 'octave-forge')
-```
diff --git a/conanfile.txt b/conanfile.txt
index e89a593b5f6388ac539975c6ae0ed808a900ecc3..37cf6d6b0f6a357663ea5c06f847e5c84b0207d0 100644
--- a/conanfile.txt
+++ b/conanfile.txt
@@ -1,5 +1,5 @@
 [requires]
-caosdb/0.0.5
+caosdb/[>=0.0.6]
 
 [generators]
 cmake
diff --git a/doc/Development.rst b/doc/Development.rst
new file mode 100644
index 0000000000000000000000000000000000000000..fca47d741ad4cf19b66d31719f101e3d4225d875
--- /dev/null
+++ b/doc/Development.rst
@@ -0,0 +1,31 @@
+Development
+===========
+
+Structure
+---------
+
+The sources for functions and classes are in ``src/``.  Private functions (mostly C++ source files
+which are to be compiled into ``*.mex``) are implemented in ``private/_some_function.*``.
+
+
+
+Writing Documentation
+---------------------
+
+- Example for texinfo documentation:
+  https://github.com/gnu-octave/octave/blob/default/scripts/geometry/inpolygon.m
+- Extract documentation from file:
+  ``[txt, form] = get_help_text_from_file(make_absolute_filename('pkg/inst/some_function.m'))``
+- Generate HTML documentation from single file:
+
+    .. code-block:: octave
+
+       pkg uninstall caosdb
+       pkg install caosdb.tar.gz
+       pkg load caosdb
+       html_help_text('some_function', './htdocs/some.html', 'octave-forge', pkgname="caosdb")
+- Generate HTML documentation for a package:
+
+    .. code-block:: octave
+
+       generate_package_html('caosdb', 'htdocs', 'octave-forge')
diff --git a/doc/Makefile b/doc/Makefile
index f985ba9f353c51c2d399d4bef6a05f623af81ce2..5f1840a185541ad9699c4ad79cb721d1295a3a34 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -42,7 +42,7 @@ doc-help:
 
 # Catch-all target: route all unknown targets to Sphinx using the new
 # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile octavedoc
+%: Makefile
 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
 octavedoc:
diff --git a/doc/conf.py b/doc/conf.py
index b7c2ef8ec1d1614793f15cd97a2d74876318a9d1..514d7de89df4d982d8f25d114f5d7787a33843be 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -91,7 +91,7 @@ html_theme = "sphinx_rtd_theme"
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = [
-    '_static',
+    # '_static',
     '_build_octave',
 ]
 
diff --git a/doc/index.rst b/doc/index.rst
index 0b144d0adc5de915e739e7a12e778195947ada34..6ccd2939ebe461fa4cb92af557b63c8e430dfbdb 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -38,6 +38,7 @@ This is work in progress.
     :caption: Contents:
 
     Welcome <self>
+    Development
     api/index
 
 * :ref:`genindex`
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index 908c635c0eb62381f0a334549dda94bd3affe6bd..0000000000000000000000000000000000000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# This file is a part of the CaosDB Project.
-#
-# Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
-# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-#
-
-set(OCTAVE_CAOSDB_SRC
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb.cpp
-    )
-
-# pass variable to parent scope
-set(OCTAVE_CAOSDB_SRC ${OCTAVE_CAOSDB_SRC} PARENT_SCOPE)
diff --git a/src/Caosdb.m b/src/Caosdb.m
index 7345ca240266d0d738377d564a35dda6c4ce6434..a94720905d6e188423457373bf895ffdbd5615ff 100644
--- a/src/Caosdb.m
+++ b/src/Caosdb.m
@@ -1,8 +1,8 @@
-% (C) Copyright 2021 IndiScale GmbH <info@indiscale.com>
-% (C) Copyright 2021 Timm FItschen <t.fitschen@indiscale.com>
-%
 % This file is a part of the CaosDB Project.
 %
+% Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+% Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+%
 % This program is free software: you can redistribute it and/or modify
 % it under the terms of the GNU Affero General Public License as
 % published by the Free Software Foundation, either version 3 of the
@@ -17,8 +17,8 @@
 % along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 % -*- texinfo -*-
-% @deftypefn {Loadable Function} {} caosdb ([@var{OPTIONS}])
-% This is the main function of the CaosDB client for Octave.
+% @deftypefn {Class} {} Caosdb ()
+% This is the main class of the CaosDB client for Octave.
 %
 % Print usage help and return:
 % @example
@@ -40,3 +40,34 @@
 %!demo
 %! ## Print Help:
 %! caosdb --help;
+classdef Caosdb < handle
+  properties
+    connection = ""             % Will use the default connection
+  end
+
+  methods
+
+    function obj = Caosdb(connection)
+      if nargin == 0
+        connection = "";
+      end
+      obj.connection = connection;
+    end
+
+    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+    %                                     info                                    %
+    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+    function res = info(obj)
+      disp(["connection: >", obj.connection, "<"]);
+      try
+        info_result = maox_info(obj.connection);
+        res = info_result;
+      catch
+        % disp("some error!");
+        % disp(lasterror());
+        rethrow(lasterror());
+      end
+    end
+
+  end
+end
diff --git a/src/caosdb_exec.m b/src/caosdb_exec.m
new file mode 100644
index 0000000000000000000000000000000000000000..3bcf273b0ff908ff1197ec0e1b40223e726dfd5e
--- /dev/null
+++ b/src/caosdb_exec.m
@@ -0,0 +1,29 @@
+% This file is a part of the CaosDB Project.
+%
+% Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+% Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+%
+% This program is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Affero General Public License as
+% published by the Free Software Foundation, either version 3 of the
+% License, or (at your option) any later version.
+%
+% This program is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU Affero General Public License for more details.
+%
+% You should have received a copy of the GNU Affero General Public License
+% along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+% -*- texinfo -*-
+% @deftypefn {Function File} {@var{out} =} caosdb_exec (arg)
+% @cindex index term
+% Calls the equivalkent of the @code{caosdb} command line interface executable.  Mostly, this can
+% output the version of libcaosdb.
+%
+% Typical arguments are @code{"--version"} or @code{"--test-connection"}.
+% @end deftypefn
+function out = caosdb_exec(arg)
+  out = maox_caosdb(arg);
+end
diff --git a/src/configure b/src/configure
index 8574639dfbc8ebc2407bdd0b56dab904220ce8bb..5266b64232c56de3c21ca84025c79bf4cbab5646 100755
--- a/src/configure
+++ b/src/configure
@@ -9,6 +9,7 @@ INST_DIR="$(realpath ../inst)"
 echo "Octave CaosDB INST_DIR: $INST_DIR"
 rm -r "${INST_DIR}" || true
 mkdir -p "${INST_DIR}"
+mkdir -p "${INST_DIR}/private"
 
 BUILD_DIR="$(realpath ../build)"
 echo "Octave CaosDB BUILD_DIR: $BUILD_DIR"
diff --git a/src/lib/maoxdb.cpp b/src/lib/maoxdb.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c8ffce7d00402178dacd7e09ffcbce56b52d25a
--- /dev/null
+++ b/src/lib/maoxdb.cpp
@@ -0,0 +1,56 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+
+/**
+ * Mostly utility functions for MEX files interacting with libcaosdb.
+ */
+
+#include "maoxdb.hpp"
+#include "mex.h"
+#include "caosdb/status_code.h"
+
+namespace maoxdb {
+using std::string;
+
+/**
+ * Extract useful strings from an Exception: ID and description.
+ */
+std::pair<string, string>
+exceptionToMessage(const caosdb::exceptions::Exception &exc) {
+
+  string id = std::to_string(exc.GetCode());
+  string text =
+    caosdb::get_status_description(exc.GetCode()) + "\n" + exc.what();
+
+  return std::pair<string, string>(id, text);
+}
+
+/**
+ * Just throw the error.
+ */
+void throwOctException(const caosdb::exceptions::Exception &exc) {
+  std::pair<string, string> excContent = exceptionToMessage(exc);
+  mexErrMsgIdAndTxt(excContent.first.c_str(), excContent.second.c_str());
+}
+
+} // namespace maoxdb
diff --git a/src/lib/maoxdb.hpp b/src/lib/maoxdb.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2f0154bb8a5b29c6353f7fe932a9a3bc60edcf9
--- /dev/null
+++ b/src/lib/maoxdb.hpp
@@ -0,0 +1,72 @@
+#ifndef MAOXDB_H                // NOLINT
+#define MAOXDB_H
+
+#include "caosdb/exceptions.h"
+#include "caosdb/status_code.h"
+#include "mex.h"
+#include <string>
+
+// Macros /////////////////////////////////////////////////////////////////////
+
+
+// Utility functions //////////////////////////////////////////////////////////
+namespace maoxdb {
+
+using std::string;
+
+template <typename T> auto mxScalar(T value, mxClassID class_id) -> mxArray * {
+  mxArray *array = mxCreateNumericMatrix(1, 1, class_id, mxREAL);
+  auto *data = static_cast<T *>(mxGetData(array));
+  data[0] = value;
+  return array;
+}
+
+// Convenience shortcut functions
+
+inline auto mxScalarUINT64(UINT64_T value) -> mxArray * {
+  return mxScalar<UINT64_T>(value, mxUINT64_CLASS);
+}
+inline auto mxScalarINT64(INT64_T value) -> mxArray * {
+  return mxScalar<INT64_T>(value, mxINT64_CLASS);
+}
+inline auto mxScalarDOUBLE(double value) -> mxArray * {
+  return mxScalar<double>(value, mxDOUBLE_CLASS);
+}
+inline auto mxScalarLOGICAL(bool value) -> mxArray * {
+  return mxScalar<UINT8_T>(static_cast<unsigned char>(value), mxLOGICAL_CLASS);
+}
+
+/**
+ * @brief Handle a CaosDB Exception and transform into error code and message.
+ *
+ * @details This function does all the processing to transform the information
+ * in an Exception object into a form that can conveniently be handled by Octave
+ * via the mexErrMsgIdAndTxt function.
+ *
+ * @param exc The Exception object.
+ *
+ * @return pair<string msgId, string msgText> The message ID and message text
+ * which can be passed to Octave.
+ *
+ * @note Additional utility functions should be easy to implement when the
+ * libcaosdb error handling changes.
+ */
+auto
+exceptionToMessage(const caosdb::exceptions::Exception &exc) -> std::pair<string, string>;
+
+/**
+ * @brief Throw a CaosDB Exception inside Octave.
+ *
+ * @details This function does all the processing to transform the information
+ * in an Exception object into a form that is accepted by Octave's error
+ * handling.d
+ *
+ * Internally, this function uses the exceptionToMessage function.
+ *
+ * @param exc The Exception object.
+ */
+void throwOctException(const caosdb::exceptions::Exception &exc);
+
+} // namespace maoxdb
+
+#endif /* MAOXDB_H */
diff --git a/src/caosdb.cpp b/src/private/maox_caosdb.cpp
similarity index 91%
rename from src/caosdb.cpp
rename to src/private/maox_caosdb.cpp
index 5fbc38a7d49affa1f15553c636102a2f7f1e4372..88d408cc1d3b4576e49b0930dc1cdc253f8f3f53 100644
--- a/src/caosdb.cpp
+++ b/src/private/maox_caosdb.cpp
@@ -2,7 +2,6 @@
 #include "caosdb/constants.h"  // for LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VE...
 #include "caosdb/info.h"       // for VersionInfo
 #include "mex.h"               // for mxArray, mexFunction
-#include "mexproto.h"          // for mexPrintf, mxCreateString, mxGetChars
 #include <cstring>             // for strcmp
 #include <memory>              // for unique_ptr, __shared_ptr_access, shar...
 #include <string>              // for allocator, char_traits, operator+
@@ -16,7 +15,7 @@ void print_usage() {
   mexPrintf("\nUsage: caosdb [OPTIONS]\n\n");
   mexPrintf("Options:\n");
   mexPrintf("    --help              Print this help and return.\n");
-  mexPrintf("    --version           Print the version of OcataveCaosDB and "
+  mexPrintf("    --version           Print the version of OctaveCaosDB and "
             "libcaosdb and return.\n");
   mexPrintf(
     "    --test-connection   Test the default connection and return.\n");
@@ -47,7 +46,7 @@ void mexFunction(int /*nlhs*/, mxArray *plhs[], int nrhs,
                  const mxArray *prhs[]) {
 
   if (nrhs == 1) {
-    auto const *first_arg = mxGetChars(prhs[0]);
+    auto const *first_arg = mxArrayToString(prhs[0]);
     if (strcmp(first_arg, "--version") == 0) {
       const auto *version = print_version();
       plhs[0] = mxCreateString(version);
diff --git a/src/private/maox_info.cpp b/src/private/maox_info.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6712d9e310235570d2b3c21fe8115978a4b039e3
--- /dev/null
+++ b/src/private/maox_info.cpp
@@ -0,0 +1,70 @@
+#include "caosdb/connection.h" // for Connection, ConnectionManager
+#include "caosdb/constants.h"  // for LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VE...
+#include "caosdb/exceptions.h" // for all error handling
+#include "caosdb/info.h"       // for VersionInfo
+#include "maoxdb.hpp"          // caosDB utils for mex files
+#include "mex.h"               // for mxArray, mexFunction
+#include <cstring>             // for strcmp
+#include <memory>              // for unique_ptr, __shared_ptr_access, shar...
+#include <string>              // for allocator, char_traits, operator+
+
+using caosdb::connection::Connection;
+using caosdb::connection::ConnectionManager;
+using std::string;
+
+auto info(const string &connection_name) -> mxArray *;
+
+/**
+ * The implementation of the info retrieval.
+ */
+auto info(string const &connection_name) -> mxArray * {
+  std::shared_ptr<Connection> connection = nullptr;
+  if (connection_name.empty()) {
+    connection = ConnectionManager::GetDefaultConnection();
+  } else {
+    connection = ConnectionManager::GetConnection(connection_name);
+  }
+  const auto &version_info = connection->RetrieveVersionInfo();
+  const char *keys[] = {"major", "minor", "patch", "pre_release", // NOLINT
+                        "build"};
+  std::array<mwSize, 2> dims = {1, 1};
+  mxArray *info_struct = mxCreateStructArray(2, dims.data(), 5, keys); // NOLINT
+
+  mxSetField(info_struct, 0, "major",
+             maoxdb::mxScalarUINT64(version_info.GetMajor()));
+  mxSetField(info_struct, 0, "minor",
+             maoxdb::mxScalarUINT64(version_info.GetMinor()));
+  mxSetField(info_struct, 0, "patch",
+             maoxdb::mxScalarUINT64(version_info.GetPatch()));
+  mxSetField(info_struct, 0, "pre_release",
+             mxCreateString(version_info.GetPreRelease().c_str()));
+  mxSetField(info_struct, 0, "build",
+             mxCreateString(version_info.GetBuild().c_str()));
+  return info_struct;
+}
+
+/**
+ * @brief      Get info about the server
+ *
+ * @details    At the moment, this is just the version of the server.
+ *
+ * @param connection_name A string with the connection name. May be omitted or
+ * an empty string.
+ *
+ * @return     return type
+ */
+void mexFunction(int /*nlhs*/, mxArray *plhs[], int nrhs,
+                 const mxArray *prhs[]) {
+
+  string conn_name;
+  if (nrhs >= 1 && mxGetNumberOfElements(prhs[0]) > 0) {
+    conn_name = mxArrayToString(prhs[0]);
+  }
+  try {
+    auto *info_struct = info(conn_name);
+    plhs[0] = info_struct;
+  } catch (const caosdb::exceptions::Exception &exc) {
+    mexPrintf("Some exception!");
+    maoxdb::throwOctException(exc);
+  }
+}
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2993b098fd36c233b1404cc9137faf5453694404
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,61 @@
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+
+# This Makefile depends on an installation with the base dir's Makefile.
+#
+# Specifically, the libraries must have be installed with Conan.
+
+.PHONY: help
+help:
+	@echo "Targets:"
+	@echo "    pkg - create tar.gz archive of the octave package."
+	@echo "    style - auto-format the source files."
+	@echo "    test - run unit tests."
+	@echo "    doc - create the documentation."
+
+
+###############################################################################
+#                                    Tests                                    #
+###############################################################################
+
+test: test_octave test_cpp
+.PHONY: test
+
+test_octave:
+	octave Run_Test.m
+.PHONY: test_octave
+
+
+# && make test || echo "Test(s) failed."; exit 1
+test_cpp:
+	$(eval BUILD_DIR ::= $(shell mktemp -d build_XXXXXXXX))
+	cd $(BUILD_DIR)                                                \
+    && conan install ../.. -s "compiler.libcxx=libstdc++11"      \
+    && cmake -D TEST=On ../..                                    \
+    && make test_detailed                                        \
+    || (echo "Test(s) failed."; exit 1)
+#   Exit with error if tests fail.
+#    && make test || make test_detailed || echo "Test(s) failed." ; exit 1
+
+# Cleanup if everything went fine.
+	rm -r $(BUILD_DIR)
+.PHONY: test_cpp
+
diff --git a/test/Run_Test.m b/test/Run_Test.m
index a1b6c1b1905e66871dc92a4aa6af06c408b6d239..e72e71a9cbc169647ccd1de253d9c7f178d89be9 100644
--- a/test/Run_Test.m
+++ b/test/Run_Test.m
@@ -16,6 +16,8 @@
 % You should have received a copy of the GNU Affero General Public License
 % along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-pkg install ..;
 pkg load caosdb;
-moxunit_runtests -verbose test_caosdb.m
+test_result = moxunit_runtests("-verbose", "test_unittest.m");
+if not(test_result)
+  exit(1)
+end
diff --git a/test/test_caosdb.m b/test/test_caosdb.m
deleted file mode 100644
index 6ce8788081c534b8efc72bae0ff76aae8cce3cee..0000000000000000000000000000000000000000
--- a/test/test_caosdb.m
+++ /dev/null
@@ -1,10 +0,0 @@
-function test_suite=test_caosdb
-    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
-        test_functions=localfunctions();
-    catch % no problem; early Matlab versions can use initTestSuite fine
-    end
-    initTestSuite;
-
-function test_print_version
-    v = caosdb("--version")
-    assertEqual(v, "v0.1 (libcaosdb v0.0.5)")
diff --git a/test/test_unittest.m b/test/test_unittest.m
new file mode 100644
index 0000000000000000000000000000000000000000..a4e38bd63e3fe3a6009c69419d83b7542dd04a94
--- /dev/null
+++ b/test/test_unittest.m
@@ -0,0 +1,42 @@
+% This file is a part of the CaosDB Project.
+%
+% Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+% Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+%
+% This program is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Affero General Public License as
+% published by the Free Software Foundation, either version 3 of the
+% License, or (at your option) any later version.
+%
+% This program is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU Affero General Public License for more details.
+%
+% You should have received a copy of the GNU Affero General Public License
+% along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+function test_suite=test_unittest()
+  try % assignment of 'localfunctions' is necessary in Matlab >= 2016
+    test_functions=localfunctions();
+  catch % no problem; early Matlab versions can use initTestSuite fine
+  end
+  initTestSuite;
+end
+
+% Only tests which don't need a connection to a server.
+function test_local()
+  % default connection
+  c1 = Caosdb();
+  assertEqual(c1.connection, "")
+  % class with explicit connection
+  c2 = Caosdb("local-caosdb-admin");
+  assertEqual(c2.connection, "local-caosdb-admin");
+
+  % TODO(daniel) Re-write s.th. version >= 0.0.6 (or whatever minimal
+  % version will be valid then) is checked.
+
+  % Only get the local versions.
+  % version = caosdb_exec("--version");
+  % assertEqual(version, "v0.1 (libcaosdb v0.0.7)");
+end
diff --git a/test/test_utilities.cpp b/test/test_utilities.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c42a2c375d7d669b0767e8fa319b1f874615396
--- /dev/null
+++ b/test/test_utilities.cpp
@@ -0,0 +1,125 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "caosdb/exceptions.h"
+#include "maoxdb.hpp"
+#include "mex.h"
+// #include "mexproto.h"
+#include <gtest/gtest.h>
+#include <limits>
+#include <string>
+#include <vector>
+
+namespace maoxdb {
+
+///////////////////////////////////////////////////////////////////////////////
+//                              Helper functions                             //
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * These functions test if the value can be converted to a mex scalar.
+ */
+void test_scalar_uint64(UINT64_T value) {
+  mxArray *scalar = mxScalarUINT64(value);
+  EXPECT_TRUE(mxIsUint64(scalar));
+  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
+  EXPECT_EQ(*((UINT64_T *) mxGetData(scalar)), value);
+}
+
+void test_scalar_int64(INT64_T value) {
+  mxArray *scalar = mxScalarINT64(value);
+  EXPECT_TRUE(mxIsInt64(scalar));
+  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
+  EXPECT_EQ(*((INT64_T *) mxGetData(scalar)), value);
+}
+
+void test_scalar_double(double value) {
+  mxArray *scalar = mxScalarDOUBLE(value);
+  EXPECT_TRUE(mxIsDouble(scalar));
+  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
+  EXPECT_EQ(*((double *) mxGetData(scalar)), value);
+}
+
+void test_scalar_logical(bool value) {
+  mxArray *scalar = mxScalarLOGICAL(value);
+  EXPECT_TRUE(mxIsLogical(scalar));
+  EXPECT_EQ(mxGetNumberOfElements(scalar), 1);
+  EXPECT_EQ(*((bool *) mxGetData(scalar)), value);
+}
+
+/**
+ * Generate a vector with useful values for testing.
+ */
+template<typename T>
+auto values() -> std::vector<T> {
+  std::vector<T> values =
+    {0, 1,
+     std::numeric_limits<T>::max(),
+     std::numeric_limits<T>::min(),
+     std::numeric_limits<T>::lowest(),
+     std::numeric_limits<T>::epsilon() // 0 for integers, but who cares?
+    };
+  return values;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                Actual tests                               //
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Test if construction of scalar mex arrays works
+ */
+TEST(test_utilities, scalar_arrays) {
+  auto values_uint64 = values<UINT64_T>();
+  for (auto value : values_uint64) {
+    test_scalar_uint64(value);
+  }
+
+  auto values_int64 = values<INT64_T>();
+  for (auto value : values_int64) {
+    test_scalar_int64(value);
+  }
+
+  auto values_double = values<double>();
+  for (auto value : values_double) {
+    test_scalar_double(value);
+  }
+
+  std::vector<bool> values_logical = {true, false};
+  for (auto value : values_logical) {
+    test_scalar_logical(value);
+  }
+}
+
+/**
+ * Test exception handling
+ */
+
+TEST(test_utilities, exception_handling) {
+  caosdb::exceptions::AuthenticationError exc("Authentication failed.");
+  auto strings = exceptionToMessage(exc);
+  EXPECT_EQ(strings.first, std::string("16"));
+  EXPECT_EQ(strings.second,
+            std::string("The attempt to execute this transaction has not been "
+                        "executed at all because the authentication did not "
+                        "succeed.\nAuthentication failed."));
+}
+
+}  // maoxdb