diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..8912295addf48290400ffe2073371e2e813cc390
--- /dev/null
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java
@@ -0,0 +1,30 @@
+package at.tuwien.api.database.table;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+
+@Getter
+@Schema
+public enum HistoryEventTypeDto {
+
+    @JsonProperty("read")
+    READ("read"),
+
+    @JsonProperty("write_own")
+    WRITE_OWN("write_own"),
+
+    @JsonProperty("write_all")
+    WRITE_ALL("write_all");
+
+    private String name;
+
+    HistoryEventTypeDto(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/lib/python/.gitignore b/lib/python/.gitignore
index c954a774008e444617bea9ec16df30b7dda3183b..1f4bb524a1c7dfe8ec62be60b755993e13f7dedc 100644
--- a/lib/python/.gitignore
+++ b/lib/python/.gitignore
@@ -6,6 +6,7 @@ dist/
 dbrepo.egg-info/
 build/
 htmlcov/
+tmp-*
 
 # debug
 debug.py
diff --git a/lib/python/Pipfile b/lib/python/Pipfile
index 62a93cc02a1d043464485a4506adcf65ca2160a2..662d60f59560b5a6fdc8e200af5845e2f85b171b 100644
--- a/lib/python/Pipfile
+++ b/lib/python/Pipfile
@@ -21,6 +21,8 @@ pytest = "*"
 requests-mock = "*"
 furo = "*"
 pytest-ordering = "*"
+testcontainers = "*"
+httpx = "*"
 
 [requires]
 python_version = "3.11"
diff --git a/lib/python/Pipfile.lock b/lib/python/Pipfile.lock
index 52b7202c3e6e17d9651d8851a66dc925f010a4df..f33975b8cccafdc33f6f6f115e47bdece97939d8 100644
--- a/lib/python/Pipfile.lock
+++ b/lib/python/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "01e7f752292f6f3d558a9418f7172696f9bd200d00c7eed2745b74d08ef27eb4"
+            "sha256": "598d2fb07f9f85270aa16f90dd8ef90b11618a241b5ba050a6dce3b2b250813e"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -18,93 +18,98 @@
     "default": {
         "aiohappyeyeballs": {
             "hashes": [
-                "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745",
-                "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"
+                "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1",
+                "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"
             ],
-            "markers": "python_version >= '3.8'",
-            "version": "==2.4.4"
+            "markers": "python_version >= '3.9'",
+            "version": "==2.4.6"
         },
         "aiohttp": {
             "hashes": [
-                "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f",
-                "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33",
-                "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1",
-                "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665",
-                "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9",
-                "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e",
-                "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350",
-                "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226",
-                "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d",
-                "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a",
-                "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6",
-                "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add",
-                "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e",
-                "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8",
-                "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03",
-                "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e",
-                "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2",
-                "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1",
-                "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c",
-                "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538",
-                "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5",
-                "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e",
-                "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9",
-                "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3",
-                "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438",
-                "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12",
-                "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3",
-                "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853",
-                "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287",
-                "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2",
-                "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9",
-                "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c",
-                "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55",
-                "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c",
-                "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e",
-                "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1",
-                "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c",
-                "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194",
-                "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773",
-                "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e",
-                "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1",
-                "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d",
-                "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600",
-                "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34",
-                "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3",
-                "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8",
-                "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8",
-                "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2",
-                "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff",
-                "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62",
-                "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac",
-                "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef",
-                "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28",
-                "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab",
-                "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104",
-                "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76",
-                "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e",
-                "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d",
-                "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a",
-                "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5",
-                "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745",
-                "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4",
-                "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99",
-                "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43",
-                "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da",
-                "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231",
-                "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd",
-                "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d",
-                "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87",
-                "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886",
-                "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2",
-                "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b",
-                "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d",
-                "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f",
-                "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204",
-                "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"
+                "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef",
+                "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae",
+                "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462",
+                "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a",
+                "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5",
+                "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0",
+                "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6",
+                "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb",
+                "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb",
+                "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1",
+                "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce",
+                "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a",
+                "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42",
+                "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58",
+                "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204",
+                "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed",
+                "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9",
+                "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c",
+                "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3",
+                "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7",
+                "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1",
+                "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff",
+                "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802",
+                "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957",
+                "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73",
+                "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78",
+                "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef",
+                "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e",
+                "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798",
+                "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0",
+                "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804",
+                "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b",
+                "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e",
+                "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5",
+                "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5",
+                "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854",
+                "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420",
+                "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb",
+                "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55",
+                "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65",
+                "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6",
+                "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1",
+                "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df",
+                "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460",
+                "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6",
+                "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933",
+                "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b",
+                "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7",
+                "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259",
+                "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5",
+                "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0",
+                "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9",
+                "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9",
+                "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484",
+                "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f",
+                "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8",
+                "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb",
+                "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9",
+                "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d",
+                "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94",
+                "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72",
+                "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259",
+                "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f",
+                "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9",
+                "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df",
+                "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f",
+                "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788",
+                "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0",
+                "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c",
+                "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16",
+                "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d",
+                "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250",
+                "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a",
+                "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2",
+                "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1",
+                "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf",
+                "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd",
+                "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e",
+                "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00",
+                "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4",
+                "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287"
             ],
             "markers": "python_version >= '3.9'",
-            "version": "==3.11.11"
+            "version": "==3.11.12"
         },
         "aiosignal": {
             "hashes": [
@@ -124,19 +129,19 @@
         },
         "attrs": {
             "hashes": [
-                "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff",
-                "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"
+                "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e",
+                "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==24.3.0"
+            "version": "==25.1.0"
         },
         "certifi": {
             "hashes": [
-                "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56",
-                "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"
+                "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651",
+                "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==2024.12.14"
+            "version": "==2025.1.31"
         },
         "charset-normalizer": {
             "hashes": [
@@ -442,64 +447,64 @@
         },
         "numpy": {
             "hashes": [
-                "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f",
-                "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0",
-                "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd",
-                "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2",
-                "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4",
-                "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648",
-                "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be",
-                "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb",
-                "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160",
-                "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd",
-                "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a",
-                "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84",
-                "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e",
-                "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748",
-                "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825",
-                "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60",
-                "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957",
-                "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715",
-                "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317",
-                "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e",
-                "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283",
-                "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278",
-                "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9",
-                "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de",
-                "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369",
-                "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb",
-                "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189",
-                "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014",
-                "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323",
-                "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e",
-                "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49",
-                "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50",
-                "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d",
-                "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37",
-                "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39",
-                "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576",
-                "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a",
-                "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba",
-                "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7",
-                "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826",
-                "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467",
-                "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495",
-                "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc",
-                "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391",
-                "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0",
-                "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97",
-                "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c",
-                "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac",
-                "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369",
-                "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8",
-                "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2",
-                "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff",
-                "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a",
-                "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df",
-                "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"
+                "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52",
+                "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d",
+                "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693",
+                "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d",
+                "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8",
+                "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027",
+                "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304",
+                "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5",
+                "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5",
+                "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50",
+                "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a",
+                "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94",
+                "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021",
+                "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e",
+                "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe",
+                "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d",
+                "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890",
+                "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8",
+                "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe",
+                "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1",
+                "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e",
+                "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b",
+                "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb",
+                "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b",
+                "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094",
+                "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea",
+                "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c",
+                "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636",
+                "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4",
+                "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba",
+                "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a",
+                "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d",
+                "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95",
+                "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2",
+                "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b",
+                "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f",
+                "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1",
+                "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532",
+                "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082",
+                "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2",
+                "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0",
+                "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71",
+                "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787",
+                "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef",
+                "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d",
+                "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3",
+                "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b",
+                "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf",
+                "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020",
+                "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76",
+                "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716",
+                "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9",
+                "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb",
+                "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610",
+                "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b"
             ],
             "markers": "python_version == '3.11'",
-            "version": "==2.2.2"
+            "version": "==2.2.3"
         },
         "paho-mqtt": {
             "hashes": [
@@ -655,11 +660,11 @@
         },
         "pydantic": {
             "hashes": [
-                "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff",
-                "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"
+                "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584",
+                "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"
             ],
             "index": "pypi",
-            "version": "==2.10.5"
+            "version": "==2.10.6"
         },
         "pydantic-core": {
             "hashes": [
@@ -777,10 +782,10 @@
         },
         "pytz": {
             "hashes": [
-                "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a",
-                "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"
+                "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57",
+                "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"
             ],
-            "version": "==2024.2"
+            "version": "==2025.1"
         },
         "requests": {
             "hashes": [
@@ -936,13 +941,21 @@
             "markers": "python_version >= '3.10'",
             "version": "==1.0.0"
         },
+        "anyio": {
+            "hashes": [
+                "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a",
+                "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==4.8.0"
+        },
         "babel": {
             "hashes": [
-                "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b",
-                "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"
+                "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d",
+                "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==2.16.0"
+            "version": "==2.17.0"
         },
         "backports.tarfile": {
             "hashes": [
@@ -954,11 +967,11 @@
         },
         "beautifulsoup4": {
             "hashes": [
-                "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051",
-                "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"
+                "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b",
+                "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"
             ],
-            "markers": "python_full_version >= '3.6.0'",
-            "version": "==4.12.3"
+            "markers": "python_full_version >= '3.7.0'",
+            "version": "==4.13.3"
         },
         "build": {
             "hashes": [
@@ -970,11 +983,11 @@
         },
         "certifi": {
             "hashes": [
-                "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56",
-                "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"
+                "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651",
+                "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==2024.12.14"
+            "version": "==2025.1.31"
         },
         "cffi": {
             "hashes": [
@@ -1149,106 +1162,117 @@
         },
         "coverage": {
             "hashes": [
-                "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9",
-                "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f",
-                "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273",
-                "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994",
-                "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e",
-                "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50",
-                "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e",
-                "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e",
-                "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c",
-                "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853",
-                "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8",
-                "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8",
-                "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe",
-                "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165",
-                "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb",
-                "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59",
-                "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609",
-                "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18",
-                "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098",
-                "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd",
-                "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3",
-                "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43",
-                "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d",
-                "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359",
-                "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90",
-                "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78",
-                "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a",
-                "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99",
-                "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988",
-                "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2",
-                "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0",
-                "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694",
-                "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377",
-                "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d",
-                "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23",
-                "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312",
-                "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf",
-                "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6",
-                "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b",
-                "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c",
-                "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690",
-                "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a",
-                "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f",
-                "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4",
-                "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25",
-                "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd",
-                "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852",
-                "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0",
-                "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244",
-                "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315",
-                "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078",
-                "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0",
-                "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27",
-                "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132",
-                "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5",
-                "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247",
-                "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022",
-                "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b",
-                "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3",
-                "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18",
-                "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5",
-                "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"
+                "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95",
+                "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9",
+                "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe",
+                "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0",
+                "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924",
+                "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574",
+                "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702",
+                "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3",
+                "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b",
+                "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2",
+                "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea",
+                "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f",
+                "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3",
+                "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674",
+                "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9",
+                "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0",
+                "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e",
+                "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef",
+                "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb",
+                "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87",
+                "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1",
+                "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2",
+                "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703",
+                "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e",
+                "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd",
+                "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3",
+                "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4",
+                "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45",
+                "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa",
+                "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31",
+                "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8",
+                "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86",
+                "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6",
+                "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288",
+                "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf",
+                "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929",
+                "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc",
+                "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985",
+                "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3",
+                "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd",
+                "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e",
+                "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879",
+                "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57",
+                "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a",
+                "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad",
+                "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba",
+                "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d",
+                "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750",
+                "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c",
+                "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c",
+                "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f",
+                "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015",
+                "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558",
+                "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f",
+                "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d",
+                "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d",
+                "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425",
+                "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3",
+                "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953",
+                "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827",
+                "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c",
+                "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f",
+                "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"
             ],
             "index": "pypi",
-            "version": "==7.6.10"
+            "version": "==7.6.12"
         },
         "cryptography": {
             "hashes": [
-                "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7",
-                "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731",
-                "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b",
-                "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc",
-                "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543",
-                "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385",
-                "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c",
-                "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591",
-                "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede",
-                "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb",
-                "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f",
-                "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123",
-                "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c",
-                "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba",
-                "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c",
-                "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285",
-                "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd",
-                "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092",
-                "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa",
-                "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289",
-                "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02",
-                "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64",
-                "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053",
-                "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417",
-                "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e",
-                "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e",
-                "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7",
-                "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756",
-                "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"
+                "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7",
+                "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3",
+                "sha256:1f9a92144fa0c877117e9748c74501bea842f93d21ee00b0cf922846d9d0b183",
+                "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69",
+                "sha256:24979e9f2040c953a94bf3c6782e67795a4c260734e5264dceea65c8f4bae64a",
+                "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62",
+                "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911",
+                "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7",
+                "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a",
+                "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41",
+                "sha256:5fed5cd6102bb4eb843e3315d2bf25fede494509bddadb81e03a859c1bc17b83",
+                "sha256:610a83540765a8d8ce0f351ce42e26e53e1f774a6efb71eb1b41eb01d01c3d12",
+                "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864",
+                "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf",
+                "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c",
+                "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2",
+                "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b",
+                "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0",
+                "sha256:94f99f2b943b354a5b6307d7e8d19f5c423a794462bde2bf310c770ba052b1c4",
+                "sha256:9b336599e2cb77b1008cb2ac264b290803ec5e8e89d618a5e978ff5eb6f715d9",
+                "sha256:a2d8a7045e1ab9b9f803f0d9531ead85f90c5f2859e653b61497228b18452008",
+                "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862",
+                "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009",
+                "sha256:d9c5b9f698a83c8bd71e0f4d3f9f839ef244798e5ffe96febfa9714717db7af7",
+                "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f",
+                "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026",
+                "sha256:e403f7f766ded778ecdb790da786b418a9f2394f36e8cc8b796cc056ab05f44f",
+                "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd",
+                "sha256:f4daefc971c2d1f82f03097dc6f216744a6cd2ac0f04c68fb935ea2ba2a0d420",
+                "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14",
+                "sha256:fd0ee90072861e276b0ff08bd627abec29e32a53b2be44e41dbcdf87cbee2b00"
             ],
             "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
-            "version": "==44.0.0"
+            "version": "==44.0.1"
+        },
+        "docker": {
+            "hashes": [
+                "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c",
+                "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==7.1.0"
         },
         "docutils": {
             "hashes": [
@@ -1266,6 +1290,30 @@
             "index": "pypi",
             "version": "==2024.8.6"
         },
+        "h11": {
+            "hashes": [
+                "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
+                "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==0.14.0"
+        },
+        "httpcore": {
+            "hashes": [
+                "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c",
+                "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.0.7"
+        },
+        "httpx": {
+            "hashes": [
+                "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc",
+                "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"
+            ],
+            "index": "pypi",
+            "version": "==0.28.1"
+        },
         "id": {
             "hashes": [
                 "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d",
@@ -1532,6 +1580,14 @@
             "index": "pypi",
             "version": "==0.6"
         },
+        "python-dotenv": {
+            "hashes": [
+                "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca",
+                "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.0.1"
+        },
         "pyyaml": {
             "hashes": [
                 "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
@@ -1655,6 +1711,14 @@
             "index": "pypi",
             "version": "==75.8.0"
         },
+        "sniffio": {
+            "hashes": [
+                "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
+                "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==1.3.1"
+        },
         "snowballstemmer": {
             "hashes": [
                 "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1",
@@ -1734,6 +1798,14 @@
             "markers": "python_version >= '3.9'",
             "version": "==2.0.0"
         },
+        "testcontainers": {
+            "hashes": [
+                "sha256:315fb94b42a383872df530aa45319745278ef0cc18b9cfcdc231a75d14afa5a0",
+                "sha256:37fe9a222549ddb788463935965b16f91809e9a8d654f437d6a59eac9b77f76f"
+            ],
+            "index": "pypi",
+            "version": "==4.9.1"
+        },
         "twine": {
             "hashes": [
                 "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384",
@@ -1742,6 +1814,14 @@
             "index": "pypi",
             "version": "==6.1.0"
         },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+                "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==4.12.2"
+        },
         "urllib3": {
             "hashes": [
                 "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
@@ -1750,6 +1830,91 @@
             "markers": "python_version >= '3.9'",
             "version": "==2.3.0"
         },
+        "wrapt": {
+            "hashes": [
+                "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f",
+                "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c",
+                "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a",
+                "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b",
+                "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555",
+                "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c",
+                "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b",
+                "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6",
+                "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8",
+                "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662",
+                "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061",
+                "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998",
+                "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb",
+                "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62",
+                "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984",
+                "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392",
+                "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2",
+                "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306",
+                "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7",
+                "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3",
+                "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9",
+                "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6",
+                "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192",
+                "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317",
+                "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f",
+                "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda",
+                "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563",
+                "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a",
+                "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f",
+                "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d",
+                "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9",
+                "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8",
+                "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82",
+                "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9",
+                "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845",
+                "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82",
+                "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125",
+                "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504",
+                "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b",
+                "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7",
+                "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc",
+                "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6",
+                "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40",
+                "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a",
+                "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3",
+                "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a",
+                "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72",
+                "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681",
+                "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438",
+                "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae",
+                "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2",
+                "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb",
+                "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5",
+                "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a",
+                "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3",
+                "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8",
+                "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2",
+                "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22",
+                "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72",
+                "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061",
+                "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f",
+                "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9",
+                "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04",
+                "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98",
+                "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9",
+                "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f",
+                "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b",
+                "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925",
+                "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6",
+                "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0",
+                "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9",
+                "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c",
+                "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991",
+                "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6",
+                "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000",
+                "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb",
+                "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119",
+                "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b",
+                "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.17.2"
+        },
         "zipp": {
             "hashes": [
                 "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4",
diff --git a/lib/python/data.csv b/lib/python/data.csv
deleted file mode 100644
index f776784b3750ee8d183efec700935bb0cbea570e..0000000000000000000000000000000000000000
Binary files a/lib/python/data.csv and /dev/null differ
diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py
index 54d377079d4e8f32304f05652c8d9da01857bdd0..368e3886be85abe57daab86692f9b4a2672aad77 100644
--- a/lib/python/dbrepo/RestClient.py
+++ b/lib/python/dbrepo/RestClient.py
@@ -1,7 +1,6 @@
 import logging
 import os
 import sys
-import time
 
 import requests
 from pandas import DataFrame
@@ -9,7 +8,7 @@ from pydantic import TypeAdapter
 
 from dbrepo.UploadClient import UploadClient
 from dbrepo.api.dto import *
-from dbrepo.api.exceptions import ResponseCodeError, UsernameExistsError, EmailExistsError, NotExistsError, \
+from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, \
     ForbiddenError, MalformedError, NameExistsError, QueryStoreError, ExternalSystemError, \
     AuthenticationError, FormatNotAvailable, RequestError, ServiceError, ServiceConnectionError
 
@@ -78,71 +77,6 @@ class RestClient:
         return requests.request(method=method, url=url, auth=auth, verify=self.secure,
                                 json=payload, headers=headers, params=params, stream=stream)
 
-    def get_jwt_auth(self, username: str = None, password: str = None) -> JwtAuth:
-        """
-        Obtains a JWT auth object from the auth service containing e.g. the access token and refresh token.
-
-        :param username: The username used to authenticate with the auth service. Optional. Default: username from the `RestClient` constructor.
-        :param password: The password used to authenticate with the auth service. Optional. Default: password from the `RestClient` constructor.
-
-        :returns: JWT auth object from the auth service, if successful.
-
-        :raises MalformedError: If the payload was rejected by the service.
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises AuthenticationError: If something went wrong with the authentication.
-        :raises ServiceConnectionError: If something went wrong with connection to the auth service.
-        :raises ServiceError: If something went wrong with obtaining the information in the auth service.
-        :raises ResponseCodeError: If something went wrong with the authentication.
-        """
-        if username is None:
-            username = self.username
-        if password is None:
-            password = self.password
-        url = f'{self.endpoint}/api/user/token'
-        response = requests.post(url=url, json=dict({"username": username, "password": password}))
-        if response.status_code == 202:
-            body = response.json()
-            return JwtAuth.model_validate(body)
-        if response.status_code == 400:
-            raise MalformedError(f'Failed to get JWT: {response.text}')
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to get JWT: not allowed')
-        if response.status_code == 428:
-            raise AuthenticationError(f'Failed to get JWT: account not fully setup (requires password change?)')
-        if response.status_code == 502:
-            raise ServiceConnectionError(f'Failed to get JWT: failed to establish connection with auth service')
-        if response.status_code == 503:
-            raise ServiceError(f'Failed to get JWT: failed to get user in auth service')
-        raise ResponseCodeError(f'Failed to get JWT: response code: {response.status_code} is not '
-                                f'202 (ACCEPTED): {response.text}')
-
-    def refresh_jwt_auth(self, refresh_token: str) -> JwtAuth:
-        """
-        Refreshes a JWT auth object from the auth service containing e.g. the access token and refresh token.
-
-        :param refresh_token: The refresh token.
-
-        :returns: JWT auth object from the auth service, if successful.
-
-        :raises MalformedError: If the payload was rejected by the service.
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises ServiceConnectionError: If something went wrong with the connection to the auth service.
-        :raises ResponseCodeError: If something went wrong with the authentication.
-        """
-        url = f'{self.endpoint}/api/user/token'
-        response = requests.put(url=url, json=dict({"refresh_token": refresh_token}))
-        if response.status_code == 202:
-            body = response.json()
-            return JwtAuth.model_validate(body)
-        if response.status_code == 400:
-            raise MalformedError(f'Failed to refresh JWT: {response.text}')
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to refresh JWT: not allowed')
-        if response.status_code == 502:
-            raise ServiceConnectionError(f'Failed to refresh JWT: failed to establish connection with auth service')
-        raise ResponseCodeError(f'Failed to refresh JWT: response code: {response.status_code} is not '
-                                f'202 (ACCEPTED): {response.text}')
-
     def whoami(self) -> str | None:
         """
         Print the username.
@@ -209,48 +143,6 @@ class RestClient:
         raise ResponseCodeError(f'Failed to find user: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
-    def create_user(self, username: str, password: str, email: str) -> UserBrief:
-        """
-        Creates a new user.
-
-        :param username: The username of the new user. Must be unique.
-        :param password: The password of the new user.
-        :param email: The email of the new user. Must be unique.
-
-        :returns: The user, if successful.
-
-        :raises MalformedError: If the payload was rejected by the service.
-        :raises ForbiddenError: If the internal authentication to the auth service is invalid.
-        :raises UsernameExistsError: The username exists already.
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises NotExistsError: If the created user was not found in the auth service.
-        :raises EmailExistsError: The email exists already.
-        :raises ServiceConnectionError: If something went wrong with connection to the auth service.
-        :raises ServiceError: If something went wrong with obtaining the information in the auth service.
-        """
-        url = f'/api/user'
-        response = self._wrapper(method="post", url=url,
-                                 payload=CreateUser(username=username, password=password, email=email))
-        if response.status_code == 201:
-            body = response.json()
-            return UserBrief.model_validate(body)
-        if response.status_code == 400:
-            raise MalformedError(f'Failed to create user: {response.text}')
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to create user: internal authentication to the auth service is invalid')
-        if response.status_code == 404:
-            raise NotExistsError(f'Failed to create user: created user not found in auth service')
-        if response.status_code == 409:
-            raise UsernameExistsError(f'Failed to create user: user with username exists')
-        if response.status_code == 417:
-            raise EmailExistsError(f'Failed to create user: user with e-mail exists')
-        if response.status_code == 502:
-            raise ServiceConnectionError(f'Failed to create user: failed to establish connection with auth service')
-        if response.status_code == 503:
-            raise ServiceError(f'Failed to create user: failed to create in auth service')
-        raise ResponseCodeError(f'Failed to create user: response code: {response.status_code} is not '
-                                f'201 (CREATED): {response.text}')
-
     def update_user(self, user_id: str, theme: str, language: str, firstname: str = None, lastname: str = None,
                     affiliation: str = None, orcid: str = None) -> UserBrief:
         """
@@ -287,38 +179,6 @@ class RestClient:
         raise ResponseCodeError(f'Failed to update user: response code: {response.status_code} is not '
                                 f'202 (ACCEPTED): {response.text}')
 
-    def update_user_password(self, user_id: str, password: str) -> None:
-        """
-        Updates the password of a user with given user id.
-
-        :param user_id: The user id of the user that should be updated.
-        :param password: The updated user password.
-
-        :raises MalformedError: If the payload was rejected by the service.
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises NotExistsError: If the user does not exist.
-        :raises ServiceConnectionError: If something went wrong with connection to the auth service.
-        :raises ServiceError: If something went wrong with obtaining the information in the auth service.
-        :raises ResponseCodeError: If something went wrong with the update.
-        """
-        url = f'/api/user/{user_id}/password'
-        response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateUserPassword(password=password))
-        if response.status_code == 202:
-            return None
-        if response.status_code == 400:
-            raise MalformedError(f'Failed to update user password: {response.text}')
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to update user password: not allowed')
-        if response.status_code == 404:
-            raise NotExistsError(f'Failed to update user password: not found')
-        if response.status_code == 502:
-            raise ServiceConnectionError(
-                f'Failed to update user password: failed to establish connection with auth service')
-        if response.status_code == 503:
-            raise ServiceError(f'Failed to update user password: failed to update in auth service')
-        raise ResponseCodeError(f'Failed to update user theme: response code: {response.status_code} is not '
-                                f'202 (ACCEPTED): {response.text}')
-
     def get_containers(self) -> List[ContainerBrief]:
         """
         Get all containers.
@@ -393,8 +253,6 @@ class RestClient:
         :returns: The database, if successful.
 
         :raises NotExistsError: If the container does not exist.
-        :raises ServiceConnectionError: If something went wrong with connection to the broker service.
-        :raises ServiceError: If something went wrong with obtaining the information in the broker service.
         :raises ResponseCodeError: If something went wrong with the retrieval.
         """
         url = f'/api/database/{database_id}'
@@ -402,12 +260,10 @@ class RestClient:
         if response.status_code == 200:
             body = response.json()
             return Database.model_validate(body)
+        if response.status_code == 403:
+            raise ForbiddenError(f'Failed to find database: not allowed')
         if response.status_code == 404:
             raise NotExistsError(f'Failed to find database: not found')
-        if response.status_code == 502:
-            raise ServiceConnectionError(f'Failed to find database: failed to establish connection with broker service')
-        if response.status_code == 503:
-            raise ServiceError(f'Failed to find database: failed to obtain queue metadata from broker service')
         raise ResponseCodeError(f'Failed to find database: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
@@ -453,9 +309,8 @@ class RestClient:
         raise ResponseCodeError(f'Failed to create database: response code: {response.status_code} is not '
                                 f'201 (CREATED): {response.text}')
 
-    def create_container(self, name: str, host: str, image_id: int, sidecar_host: str, sidecar_port: int,
-                         privileged_username: str, privileged_password: str, port: int = None, ui_host: str = None,
-                         ui_port: int = None) -> Container:
+    def create_container(self, name: str, host: str, image_id: int, privileged_username: str, privileged_password: str,
+                         port: int = None, ui_host: str = None, ui_port: int = None) -> Container:
         """
         Register a container instance executing a given container image. Note that this does not create a container,
         but only saves it in the metadata database to be used within DBRepo. The container still needs to be created
@@ -464,8 +319,6 @@ class RestClient:
         :param name: The container name.
         :param host: The container hostname.
         :param image_id: The container image id.
-        :param sidecar_host: The container sidecar hostname.
-        :param sidecar_port: The container sidecar port.
         :param privileged_username: The container privileged user username.
         :param privileged_password: The container privileged user password.
         :param port: The container port bound to the host. Optional.
@@ -483,7 +336,6 @@ class RestClient:
         url = f'/api/container'
         response = self._wrapper(method="post", url=url, force_auth=True,
                                  payload=CreateContainer(name=name, host=host, image_id=image_id,
-                                                         sidecar_host=sidecar_host, sidecar_port=sidecar_port,
                                                          privileged_username=privileged_username,
                                                          privileged_password=privileged_password, port=port,
                                                          ui_host=ui_host, ui_port=ui_port))
@@ -574,7 +426,7 @@ class RestClient:
         raise ResponseCodeError(
             f'Failed to update database visibility: response code: {response.status_code} is not 202 (ACCEPTED)')
 
-    def update_database_schema(self, database_id: int) -> Database:
+    def update_database_schema(self, database_id: int) -> DatabaseBrief:
         """
         Updates the database table and view metadata of a database with given database id.
 
@@ -597,7 +449,7 @@ class RestClient:
             response = self._wrapper(method="put", url=url, force_auth=True)
             if response.status_code == 200:
                 body = response.json()
-                return Database.model_validate(body)
+                return DatabaseBrief.model_validate(body)
         if response.status_code == 400:
             raise MalformedError(f'Failed to update database schema: {response.text}')
         if response.status_code == 403:
@@ -693,8 +545,6 @@ class RestClient:
 
         :raises ForbiddenError: If something went wrong with the authorization.
         :raises NotExistsError: If the table does not exist.
-        :raises ServiceConnectionError: If something went wrong with connection to the metadata service.
-        :raises ServiceError: If something went wrong with obtaining the information in the metadata service.
         :raises ResponseCodeError: If something went wrong with the retrieval.
         """
         url = f'/api/database/{database_id}/table/{table_id}'
@@ -706,10 +556,6 @@ class RestClient:
             raise ForbiddenError(f'Failed to find table: not allowed')
         if response.status_code == 404:
             raise NotExistsError(f'Failed to find table: not found')
-        if response.status_code == 502:
-            raise ServiceConnectionError(f'Failed to find table: failed to establish connection to broker service')
-        if response.status_code == 503:
-            raise ServiceError(f'Failed to find table: failed to obtain queue information from broker service')
         raise ResponseCodeError(f'Failed to find table: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
@@ -770,29 +616,7 @@ class RestClient:
         raise ResponseCodeError(f'Failed to delete container: response code: {response.status_code} is not '
                                 f'202 (ACCEPTED): {response.text}')
 
-    def get_table_metadata(self, database_id: int) -> Database:
-        """
-        Generate metadata of all system-versioned tables in a database with given id.
-
-        :param database_id: The database id.
-
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises NotExistsError: If the table does not exist.
-        :raises ResponseCodeError: If something went wrong with the retrieval.
-        """
-        url = f'/api/database/{database_id}/metadata/table'
-        response = self._wrapper(method="put", url=url, force_auth=True)
-        if response.status_code == 200:
-            body = response.json()
-            return Database.model_validate(body)
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to get tables metadata: not allowed')
-        if response.status_code == 404:
-            raise NotExistsError(f'Failed to get tables metadata: not found')
-        raise ResponseCodeError(f'Failed to get tables metadata: response code: {response.status_code} is not '
-                                f'200 (OK): {response.text}')
-
-    def get_table_history(self, database_id: int, table_id: int, size: int = 100) -> Database:
+    def get_table_history(self, database_id: int, table_id: int, size: int = 100) -> [History]:
         """
         Get the table history of insert/delete operations.
 
@@ -807,10 +631,10 @@ class RestClient:
         :raises ResponseCodeError: If something went wrong with the retrieval.
         """
         url = f'/api/database/{database_id}/table/{table_id}/history?size={size}'
-        response = self._wrapper(method="get", url=url, force_auth=True)
+        response = self._wrapper(method="get", url=url)
         if response.status_code == 200:
             body = response.json()
-            return Database.model_validate(body)
+            return TypeAdapter(List[History]).validate_python(body)
         if response.status_code == 400:
             raise MalformedError(f'Failed to get table history: {response.text}')
         if response.status_code == 403:
@@ -868,13 +692,14 @@ class RestClient:
         raise ResponseCodeError(f'Failed to find view: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
-    def update_view(self, database_id: int, view_id: int, is_public: bool) -> ViewBrief:
+    def update_view(self, database_id: int, view_id: int, is_public: bool, is_schema_public: bool) -> ViewBrief:
         """
         Get a view of a database with given database id and view id.
 
         :param database_id: The database id.
         :param view_id: The view id.
-        :param is_public: If set to `True`, the view is publicly visible.
+        :param is_public: If set to `True`, the view data is publicly visible.
+        :param is_schema_public: If set to `True`, the view schema is publicly visible.
 
         :returns: The view, if successful.
 
@@ -883,7 +708,8 @@ class RestClient:
         :raises ResponseCodeError: If something went wrong with the retrieval.
         """
         url = f'/api/database/{database_id}/view/{view_id}'
-        response = self._wrapper(method="put", url=url, payload=UpdateView(is_public=is_public))
+        response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateView(is_public=is_public,
+                                                                                            is_schema_public=is_schema_public))
         if response.status_code == 202:
             body = response.json()
             return ViewBrief.model_validate(body)
@@ -1010,28 +836,6 @@ class RestClient:
         raise ResponseCodeError(f'Failed to get view data: response code: {response.status_code} is not '
                                 f'200 (OK):{response.text}')
 
-    def get_views_metadata(self, database_id: int) -> Database:
-        """
-        Generate metadata of all views in a database with given id.
-
-        :param database_id: The database id.
-
-        :raises ForbiddenError: If something went wrong with the authorization.
-        :raises NotExistsError: If the container does not exist.
-        :raises ResponseCodeError: If something went wrong with the retrieval.
-        """
-        url = f'/api/database/{database_id}/metadata/view'
-        response = self._wrapper(method="put", url=url, force_auth=True)
-        if response.status_code == 200:
-            body = response.json()
-            return Database.model_validate(body)
-        if response.status_code == 403:
-            raise ForbiddenError(f'Failed to get views metadata: not allowed')
-        if response.status_code == 404:
-            raise NotExistsError(f'Failed to get views metadata: not found')
-        raise ResponseCodeError(f'Failed to get views metadata: response code: {response.status_code} is not '
-                                f'200 (OK): {response.text}')
-
     def get_table_data(self, database_id: int, table_id: int, page: int = 0, size: int = 10,
                        timestamp: datetime.datetime = None) -> DataFrame:
         """
@@ -1103,9 +907,7 @@ class RestClient:
         raise ResponseCodeError(f'Failed to insert table data: response code: {response.status_code} is not '
                                 f'201 (CREATED): {response.text}')
 
-    def import_table_data(self, database_id: int, table_id: int, file_name_or_data_frame: str | DataFrame,
-                          separator: str = ",", quote: str = "\"", header: bool = False,
-                          line_encoding: str = "\n") -> None:
+    def import_table_data(self, database_id: int, table_id: int, dataframe: DataFrame) -> None:
         """
         Import a csv dataset from a file into a table in a database with given database id and table id. ATTENTION:
         the import is column-ordering sensitive! The csv dataset must have the same columns in the same order as the
@@ -1113,11 +915,7 @@ class RestClient:
 
         :param database_id: The database id.
         :param table_id: The table id.
-        :param file_name_or_data_frame: The path of the file that is imported on the storage service or pandas dataframe.
-        :param separator: The csv column separator. Optional.
-        :param quote: The column data quotation character. Optional.
-        :param header: If `True`, the first line contains column names, otherwise the first line is data. Optional. Default: `False`.
-        :param line_encoding: The encoding of the line termination. Optional. Default: CR (Windows).
+        :param dataframe: The pandas dataframe.
 
         :raises MalformedError: If the payload is rejected by the service (e.g. LOB could not be imported).
         :raises ForbiddenError: If something went wrong with the authorization.
@@ -1125,18 +923,12 @@ class RestClient:
         :raises ServiceError: If something went wrong with obtaining the information in the metadata service.
         :raises ResponseCodeError: If something went wrong with the insert.
         """
-        client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files")
-        if type(file_name_or_data_frame) is DataFrame:
-            file_path: str = f"./tmp-{time.time()}"
-            df: DataFrame = file_name_or_data_frame
-            df.to_csv(path_or_buf=file_path, index=False, header=False)
-        else:
-            file_path: str = file_name_or_data_frame
-        filename = client.upload(file_path=file_path)
+        client = UploadClient()
+        filename = client.upload(dataframe)
         url = f'/api/database/{database_id}/table/{table_id}/data/import'
         response = self._wrapper(method="post", url=url, force_auth=True,
-                                 payload=Import(location=filename, separator=separator, quote=quote,
-                                                header=header, line_termination=line_encoding))
+                                 payload=Import(location=filename, separator=',', quote='"', header=True,
+                                                line_termination='\n'))
         if response.status_code == 202:
             return
         if response.status_code == 400:
@@ -1151,17 +943,13 @@ class RestClient:
         raise ResponseCodeError(f'Failed to import table data: response code: {response.status_code} is not '
                                 f'202 (ACCEPTED): {response.text}')
 
-    def analyse_datatypes(self, file_path: str, separator: str, enum: bool = None,
-                          enum_tol: int = None, upload: bool = True) -> DatatypeAnalysis:
+    def analyse_datatypes(self, dataframe: DataFrame, enum: bool = None, enum_tol: int = None) -> DatatypeAnalysis:
         """
         Import a csv dataset from a file and analyse it for the possible enums, line encoding and column data types.
 
-        :param file_path: The path of the file that is imported on the storage service.
-        :param separator: The csv column separator.
+        :param dataframe: The dataframe.
         :param enum: If set to true, enumerations should be guessed, otherwise no guessing. Optional.
         :param enum_tol: The tolerance for guessing enumerations (ignored if enum=False). Optional.
-        :param upload: If set to true, the file from file_path will be uploaded, otherwise no upload will be performed \
-            and the file_path will be treated as S3 filename and analysed instead. Optional. Default: true.
 
         :returns: The determined data types, if successful.
 
@@ -1169,14 +957,11 @@ class RestClient:
         :raises NotExistsError: If the file was not found by the Analyse Service.
         :raises ResponseCodeError: If something went wrong with the analysis.
         """
-        if upload:
-            client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files")
-            filename = client.upload(file_path=file_path)
-        else:
-            filename = file_path
+        client = UploadClient()
+        filename = client.upload(dataframe)
         params = [
             ('filename', filename),
-            ('separator', separator),
+            ('separator', ','),
             ('enum', enum),
             ('enum_tol', enum_tol)
         ]
@@ -1192,14 +977,11 @@ class RestClient:
         raise ResponseCodeError(f'Failed to analyse data types: response code: {response.status_code} is not '
                                 f'202 (ACCEPTED): {response.text}')
 
-    def analyse_keys(self, file_path: str, separator: str, upload: bool = True) -> KeyAnalysis:
+    def analyse_keys(self, dataframe: DataFrame) -> KeyAnalysis:
         """
         Import a csv dataset from a file and analyse it for the possible primary key.
 
-        :param file_path: The path of the file that is imported on the storage service.
-        :param separator: The csv column separator.
-        :param upload: If set to true, the file from file_path will be uploaded, otherwise no upload will be performed \
-            and the file_path will be treated as S3 filename and analysed instead. Optional. Default: `True`.
+        :param dataframe: The dataframe.
 
         :returns: The determined ranking of the primary key candidates, if successful.
 
@@ -1207,14 +989,11 @@ class RestClient:
         :raises NotExistsError: If the file was not found by the Analyse Service.
         :raises ResponseCodeError: If something went wrong with the analysis.
         """
-        if upload:
-            client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files")
-            filename = client.upload(file_path=file_path)
-        else:
-            filename = file_path
+        client = UploadClient()
+        filename = client.upload(dataframe=dataframe)
         params = [
             ('filename', filename),
-            ('separator', separator),
+            ('separator', ','),
         ]
         url = f'/api/analyse/keys'
         response = self._wrapper(method="get", url=url, params=params)
@@ -1253,7 +1032,7 @@ class RestClient:
         if response.status_code == 404:
             raise NotExistsError(f'Failed to analyse table statistics: separator error')
         if response.status_code == 502:
-            raise NotExistsError(
+            raise ServiceConnectionError(
                 f'Failed to analyse table statistics: data service failed to establish connection to metadata service')
         if response.status_code == 503:
             raise ServiceError(f'Failed to analyse table statistics: failed to save statistic in search service')
diff --git a/lib/python/dbrepo/UploadClient.py b/lib/python/dbrepo/UploadClient.py
index b0bc90fc00aa1810d4e80873b9e8583ff1a4e5be..cb4dc3a3c534cd9534d28863c7a504233b7551ab 100644
--- a/lib/python/dbrepo/UploadClient.py
+++ b/lib/python/dbrepo/UploadClient.py
@@ -2,8 +2,13 @@ import logging
 import os
 import re
 import sys
+from io import BytesIO
+
+from pandas import DataFrame
 from tusclient import client
 
+from dbrepo.api.exceptions import UploadError
+
 logging.basicConfig(format='%(asctime)s %(name)-12s %(levelname)-6s %(message)s', level=logging.INFO,
                     stream=sys.stdout)
 
@@ -21,19 +26,15 @@ class UploadClient:
     def __init__(self, endpoint: str = 'http://gateway-service/api/upload/files') -> None:
         self.endpoint = os.environ.get('REST_UPLOAD_ENDPOINT', endpoint)
 
-    def upload(self, file_path: str) -> str:
-        """
-        Imports a file through the Upload Service into the Storage Service.
-
-        :param file_path: The file path on the local machine.
-
-        :returns: Filename on the Storage Service, if successful.
-        """
-        logging.debug(f"upload file to endpoint: {self.endpoint}")
+    def upload(self, dataframe: DataFrame) -> str:
+        logging.debug(f"upload to endpoint: {self.endpoint}")
         tus_client = client.TusClient(url=self.endpoint)
-        uploader = tus_client.uploader(file_path=file_path)
+        buffer: BytesIO = BytesIO(dataframe.to_csv(index=False, header=False).encode('utf-8'))
+        uploader = tus_client.uploader(file_stream=buffer)
         uploader.upload()
-        m = re.search('\\/([a-f0-9]+)\\+', uploader.url)
+        m = re.search('\\/([a-f0-9]+)$', uploader.url)
         filename = m.group(0)[1:-1]
-        logging.info(f'Uploaded file {file_path} to storage service with key: {filename}')
+        if filename is None:
+            raise UploadError('Failed to upload file: no filename')
+        logging.info(f'Uploaded to storage service with key: {filename}')
         return filename
diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py
index 582f40c9b16a8fe60fd7b2f4a14901958a7a7b3b..27801a948238953ffdd66eb35c049e44d4a2bd5d 100644
--- a/lib/python/dbrepo/api/dto.py
+++ b/lib/python/dbrepo/api/dto.py
@@ -12,18 +12,6 @@ Timestamp = Annotated[
 ]
 
 
-class JwtAuth(BaseModel):
-    access_token: str
-    refresh_token: str
-    id_token: str
-    expires_in: int
-    refresh_expires_in: int
-    not_before_policy: int
-    scope: str
-    session_state: str
-    token_type: str
-
-
 class Image(BaseModel):
     id: int
     registry: str
@@ -52,6 +40,7 @@ class CreateDatabase(BaseModel):
 
 class UpdateView(BaseModel):
     is_public: bool
+    is_schema_public: bool
 
 
 class CreateContainer(BaseModel):
@@ -134,6 +123,7 @@ class TableBrief(BaseModel):
 
 class UserAttributes(BaseModel):
     theme: str
+    language: str
     orcid: Optional[str] = None
     affiliation: Optional[str] = None
 
@@ -690,6 +680,12 @@ class CreateView(BaseModel):
     is_schema_public: bool
 
 
+class History(BaseModel):
+    event: HistoryEventType
+    total: int
+    timestamp: Timestamp
+
+
 class ViewBrief(BaseModel):
     id: int
     database_id: int
@@ -759,6 +755,14 @@ class TitleType(str, Enum):
     OTHER = "Other"
 
 
+class HistoryEventType(str, Enum):
+    """
+    Enumeration of history event types.
+    """
+    INSERT = "insert"
+    DELETE = "delete"
+
+
 class RelatedIdentifierType(str, Enum):
     """
     Enumeration of related identifier types.
diff --git a/lib/python/tests/conftest.py b/lib/python/tests/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1a41ca922c870f37fb12f2b9a68de5d1ff68286
--- /dev/null
+++ b/lib/python/tests/conftest.py
@@ -0,0 +1,31 @@
+import logging
+import os
+
+import pytest
+from testcontainers.generic import ServerContainer
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+@pytest.fixture(scope="session", autouse=True)
+def session(request):
+    """
+    Create one TUSd container per test run only
+    :param request: /
+    :return: The TUSd container
+    """
+    logging.debug("[fixture] creating container")
+    container = ServerContainer(port=8080, image='tusproject/tusd:v2.4.0').with_command("-base-path=/api/upload/files/")
+    logging.debug("[fixture] starting container")
+    container.start()
+    # set the environment for the client
+    endpoint = f"http://{container.get_container_host_ip()}:{container.get_exposed_port(8080)}/api/upload/files"
+    logging.debug(f'set upload endpoint {endpoint}')
+    os.environ['REST_UPLOAD_ENDPOINT'] = endpoint
+
+    # destructor
+    def stop_tusd():
+        container.stop()
+
+    request.addfinalizer(stop_tusd)
+    return container
diff --git a/lib/python/tests/test_integration_table.py b/lib/python/tests/test_integration_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8ca294779e8d93559514e533820ccd3e773566d
--- /dev/null
+++ b/lib/python/tests/test_integration_table.py
@@ -0,0 +1,83 @@
+import os
+import unittest
+
+import requests_mock
+from pandas import DataFrame
+
+from dbrepo.RestClient import RestClient
+from dbrepo.api.exceptions import ForbiddenError, MalformedError, NotExistsError, ServiceError, ResponseCodeError
+
+
+class TableUnitTest(unittest.TestCase):
+
+    def test_import_table_data_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=202)
+            # test
+            RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                     dataframe=DataFrame())
+
+    def test_import_table_data_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=400)
+            try:
+                RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                         dataframe=DataFrame())
+            except MalformedError:
+                pass
+
+    def test_import_table_data_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=403)
+            # test
+            try:
+                RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                         dataframe=DataFrame())
+            except ForbiddenError:
+                pass
+
+    def test_import_table_data_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=404)
+            # test
+            try:
+                RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                         dataframe=DataFrame())
+            except NotExistsError:
+                pass
+
+    def test_import_table_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=503)
+            # test
+            try:
+                RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                         dataframe=DataFrame())
+            except ServiceError:
+                pass
+
+    def test_import_table_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            # mock
+            mock.post('/api/database/1/table/9/data/import', status_code=200)
+            # test
+            try:
+                RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9,
+                                                                         dataframe=DataFrame())
+            except ResponseCodeError:
+                pass
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/lib/python/tests/test_unit_analyse.py b/lib/python/tests/test_unit_analyse.py
index 5ca2b8301cbb04f9e896436909c97b4873842612..fbbb342f790a19b9e930db8df899ae1d044ab590 100644
--- a/lib/python/tests/test_unit_analyse.py
+++ b/lib/python/tests/test_unit_analyse.py
@@ -1,9 +1,13 @@
+import os
+import re
 import unittest
+
 import requests_mock
+from pandas import DataFrame
 
 from dbrepo.RestClient import RestClient
-
-from dbrepo.api.dto import KeyAnalysis
+from dbrepo.api.dto import KeyAnalysis, DatatypeAnalysis, ColumnType
+from dbrepo.api.exceptions import NotExistsError, MalformedError, ResponseCodeError
 
 
 class AnalyseUnitTest(unittest.TestCase):
@@ -12,12 +16,89 @@ class AnalyseUnitTest(unittest.TestCase):
         with requests_mock.Mocker() as mock:
             exp = KeyAnalysis(keys={'id': 0, 'firstname': 1, 'lastname': 2})
             # mock
-            mock.get('/api/analyse/keys', json=exp.model_dump(), status_code=202)
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', '/api/analyse/keys', json=exp.model_dump(), status_code=202)
             # test
-            response = RestClient().analyse_keys(file_path='f705a7bd0cb2d5e37ab2b425036810a2', separator=',',
-                                                 upload=False)
+            response = RestClient().analyse_keys(dataframe=DataFrame())
             self.assertEqual(exp, response)
 
+    def test_analyse_keys_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', '/api/analyse/keys', status_code=400)
+            # test
+            try:
+                RestClient().analyse_keys(dataframe=DataFrame())
+            except MalformedError:
+                pass
+
+    def test_analyse_keys_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', '/api/analyse/keys', status_code=404)
+            # test
+            try:
+                RestClient().analyse_keys(dataframe=DataFrame())
+            except NotExistsError:
+                pass
+
+    def test_analyse_keys_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', '/api/analyse/keys', status_code=200)
+            # test
+            try:
+                RestClient().analyse_keys(dataframe=DataFrame())
+            except ResponseCodeError:
+                pass
+
+    def test_analyse_datatypes_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            exp = DatatypeAnalysis(separator=',',
+                                   columns={'id': ColumnType.SERIAL})
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), json=exp.model_dump(), status_code=202)
+            # test
+            response = RestClient().analyse_datatypes(dataframe=DataFrame())
+            self.assertEqual(exp, response)
+
+    def test_analyse_datatypes_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=400)
+            # test
+            try:
+                RestClient().analyse_datatypes(dataframe=DataFrame())
+            except MalformedError:
+                pass
+
+    def test_analyse_datatypes_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=404)
+            # test
+            try:
+                RestClient().analyse_datatypes(dataframe=DataFrame())
+            except NotExistsError:
+                pass
+
+    def test_analyse_datatypes_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True)
+            mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=200)
+            # test
+            try:
+                RestClient().analyse_datatypes(dataframe=DataFrame())
+            except ResponseCodeError:
+                pass
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/lib/python/tests/test_unit_container.py b/lib/python/tests/test_unit_container.py
index b7a35e6a4a714f38d3cb06841541b182a0c769e2..2edc65bf1983aa836eae6475918a990ae19611a6 100644
--- a/lib/python/tests/test_unit_container.py
+++ b/lib/python/tests/test_unit_container.py
@@ -1,15 +1,123 @@
 import unittest
 
 import requests_mock
-import datetime
 
 from dbrepo.RestClient import RestClient
 from dbrepo.api.dto import Container, Image, ContainerBrief, ImageBrief, DataType
-from dbrepo.api.exceptions import ResponseCodeError, NotExistsError
+from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, AuthenticationError, MalformedError, \
+    ForbiddenError, NameExistsError
 
 
 class ContainerUnitTest(unittest.TestCase):
 
+    def test_create_container_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            exp = Container(id=1,
+                            name="MariaDB 10.11.3",
+                            internal_name="mariadb_10_11_3",
+                            host="data-db",
+                            port=12345,
+                            image=Image(id=1,
+                                        registry="docker.io",
+                                        name="mariadb",
+                                        version="10.11.3",
+                                        default_port=3306,
+                                        dialect="org.hibernate.dialect.MariaDBDialect",
+                                        driver_class="org.mariadb.jdbc.Driver",
+                                        jdbc_method="mariadb",
+                                        data_types=[
+                                            DataType(display_name="SERIAL", value="serial",
+                                                     documentation="https://mariadb.com/kb/en/bigint/",
+                                                     is_quoted=False, is_buildable=True)]))
+            # mock
+            mock.post('/api/container', json=exp.model_dump(), status_code=201)
+            # test
+            response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                   host='data-db2', image_id=1,
+                                                                                   privileged_username='root',
+                                                                                   privileged_password='dbrepo',
+                                                                                   port=3306)
+            self.assertEqual(exp, response)
+
+    def test_create_container_anonymous_fails(self):
+        # test
+        try:
+            response = RestClient().create_container(name='MariaDB 10.11.3', host='data-db2', image_id=1,
+                                                     privileged_username='root', privileged_password='dbrepo',
+                                                     port=3306)
+        except AuthenticationError:
+            pass
+
+    def test_create_container_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/container', status_code=400)
+            # test
+            try:
+                response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                       host='data-db2', image_id=1,
+                                                                                       privileged_username='root',
+                                                                                       privileged_password='dbrepo',
+                                                                                       port=3306)
+            except MalformedError:
+                pass
+
+    def test_create_container_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/container', status_code=403)
+            # test
+            try:
+                response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                       host='data-db2', image_id=1,
+                                                                                       privileged_username='root',
+                                                                                       privileged_password='dbrepo',
+                                                                                       port=3306)
+            except ForbiddenError:
+                pass
+
+    def test_create_container_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/container', status_code=404)
+            # test
+            try:
+                response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                       host='data-db2', image_id=1,
+                                                                                       privileged_username='root',
+                                                                                       privileged_password='dbrepo',
+                                                                                       port=3306)
+            except NotExistsError:
+                pass
+
+    def test_create_container_409_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/container', status_code=409)
+            # test
+            try:
+                response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                       host='data-db2', image_id=1,
+                                                                                       privileged_username='root',
+                                                                                       privileged_password='dbrepo',
+                                                                                       port=3306)
+            except NameExistsError:
+                pass
+
+    def test_create_container_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/container', status_code=200)
+            # test
+            try:
+                response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3',
+                                                                                       host='data-db2', image_id=1,
+                                                                                       privileged_username='root',
+                                                                                       privileged_password='dbrepo',
+                                                                                       port=3306)
+            except ResponseCodeError:
+                pass
+
     def test_get_containers_empty_succeeds(self):
         with requests_mock.Mocker() as mock:
             # mock
@@ -52,11 +160,8 @@ class ContainerUnitTest(unittest.TestCase):
             exp = Container(id=1,
                             name="MariaDB 10.11.3",
                             internal_name="mariadb_10_11_3",
-                            running=True,
                             host="data-db",
                             port=12345,
-                            sidecar_host="data-db-sidecar",
-                            sidecar_port=3305,
                             image=Image(id=1,
                                         registry="docker.io",
                                         name="mariadb",
@@ -68,15 +173,14 @@ class ContainerUnitTest(unittest.TestCase):
                                         data_types=[
                                             DataType(display_name="SERIAL", value="serial",
                                                      documentation="https://mariadb.com/kb/en/bigint/",
-                                                     is_quoted=False, is_buildable=True)]),
-                            hash="f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50")
+                                                     is_quoted=False, is_buildable=True)]))
             # mock
             mock.get('/api/container/1', json=exp.model_dump())
             # test
             response = RestClient().get_container(container_id=1)
             self.assertEqual(exp, response)
 
-    def test_get_container_not_found_fails(self):
+    def test_get_container_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/container/1', status_code=404)
@@ -86,6 +190,70 @@ class ContainerUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_get_container_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/container/1', status_code=401)
+            # test
+            try:
+                response = RestClient().get_container(container_id=1)
+            except ResponseCodeError:
+                pass
+
+    def test_delete_container_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/container/1', status_code=202)
+            # test
+            RestClient(username='foo', password='bar').delete_container(container_id=1)
+
+    def test_delete_container_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/container/1', status_code=400)
+            # test
+            try:
+                RestClient(username='foo', password='bar').delete_container(container_id=1)
+            except MalformedError:
+                pass
+
+    def test_delete_container_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/container/1', status_code=403)
+            # test
+            try:
+                RestClient(username='foo', password='bar').delete_container(container_id=1)
+            except ForbiddenError:
+                pass
+
+    def test_delete_container_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/container/1', status_code=404)
+            # test
+            try:
+                RestClient(username='foo', password='bar').delete_container(container_id=1)
+            except NotExistsError:
+                pass
+
+    def test_delete_container_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/container/1', status_code=200)
+            # test
+            try:
+                RestClient(username='foo', password='bar').delete_container(container_id=1)
+            except ResponseCodeError:
+                pass
+
+    def test_delete_container_anonymous_fails(self):
+        # test
+        try:
+            RestClient().delete_container(container_id=1)
+        except AuthenticationError:
+            pass
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py
index 203c296b9c86d9bca0fce4f3b433af298f6cd251..0eb4675433d509c22ee77512af392b014c6e85aa 100644
--- a/lib/python/tests/test_unit_database.py
+++ b/lib/python/tests/test_unit_database.py
@@ -1,12 +1,12 @@
 import unittest
 
 import requests_mock
-from pydantic_core import ValidationError
 
 from dbrepo.RestClient import RestClient
 from dbrepo.api.dto import Database, DatabaseAccess, AccessType, DatabaseBrief, UserBrief, \
     ContainerBrief, ImageBrief
-from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, ForbiddenError, MalformedError, AuthenticationError
+from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, ForbiddenError, MalformedError, \
+    AuthenticationError, QueryStoreError, ServiceConnectionError, ServiceError
 
 
 class DatabaseUnitTest(unittest.TestCase):
@@ -28,18 +28,7 @@ class DatabaseUnitTest(unittest.TestCase):
                 contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                 internal_name='test_abcd',
                 is_public=True,
-                is_schema_public=True,
-                container=ContainerBrief(
-                    id=1,
-                    name='MariaDB Galera 11.1.3',
-                    internal_name='mariadb',
-                    image=ImageBrief(
-                        id=1,
-                        name='mariadb',
-                        version='11.2.2',
-                        jdbc_method='mariadb'
-                    )
-                )
+                is_schema_public=True
             )
         ]
         with requests_mock.Mocker() as mock:
@@ -49,6 +38,34 @@ class DatabaseUnitTest(unittest.TestCase):
             response = RestClient().get_databases()
             self.assertEqual(exp, response)
 
+    def test_get_databases_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database', status_code=401)
+            # test
+            try:
+                RestClient().get_databases()
+            except ResponseCodeError:
+                pass
+
+    def test_get_databases_count_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.head('/api/database', headers={'X-Count': '100'})
+            # test
+            response = RestClient().get_databases_count()
+            self.assertEqual(100, response)
+
+    def test_get_databases_count_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.head('/api/database', status_code=401)
+            # test
+            try:
+                RestClient().get_databases_count()
+            except ResponseCodeError:
+                pass
+
     def test_get_database_succeeds(self):
         exp = Database(
             id=1,
@@ -78,7 +95,17 @@ class DatabaseUnitTest(unittest.TestCase):
             response = RestClient().get_database(1)
             self.assertEqual(exp, response)
 
-    def test_get_database_not_found_fails(self):
+    def test_get_database_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1', status_code=403)
+            # test
+            try:
+                response = RestClient().get_database(1)
+            except ForbiddenError as e:
+                pass
+
+    def test_get_database_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1', status_code=404)
@@ -88,16 +115,10 @@ class DatabaseUnitTest(unittest.TestCase):
             except NotExistsError as e:
                 pass
 
-    def test_get_database_invalid_dto_fails(self):
-        try:
-            exp = Database()
-        except ValidationError as e:
-            pass
-
-    def test_get_database_unauthorized_fails(self):
+    def test_get_database_unknown_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.get('/api/database/1', status_code=401)
+            mock.get('/api/database/1', status_code=202)
             # test
             try:
                 response = RestClient().get_database(1)
@@ -131,32 +152,88 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.post('/api/database', json=exp.model_dump(), status_code=201)
             # test
             client = RestClient(username="a", password="b")
-            response = client.create_database(name='test', container_id=1, is_public=True)
+            response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                              is_public=True)
             self.assertEqual(response.name, 'test')
 
-    def test_create_database_not_allowed_fails(self):
+    def test_create_database_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database', status_code=400)
+            # test
+            try:
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
+            except MalformedError as e:
+                pass
+
+    def test_create_database_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_database(name='test', container_id=1, is_public=True)
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
             except ForbiddenError as e:
                 pass
 
-    def test_create_database_not_found_fails(self):
+    def test_create_database_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_database(name='test', container_id=1, is_public=True)
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
             except NotExistsError as e:
                 pass
 
-    def test_create_database_not_auth_fails(self):
+    def test_create_database_409_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database', status_code=409)
+            # test
+            try:
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
+            except QueryStoreError as e:
+                pass
+
+    def test_create_database_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database', status_code=502)
+            # test
+            try:
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
+            except ServiceConnectionError as e:
+                pass
+
+    def test_create_database_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database', status_code=503)
+            # test
+            try:
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
+            except ServiceError as e:
+                pass
+
+    def test_create_database_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database', status_code=202)
+            # test
+            try:
+                response = RestClient(username="a", password="b").create_database(name='test', container_id=1,
+                                                                                  is_public=True)
+            except ResponseCodeError as e:
+                pass
+
+    def test_create_database_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database', status_code=404)
@@ -193,41 +270,89 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.put('/api/database/1/visibility', json=exp.model_dump(), status_code=202)
             # test
             client = RestClient(username="a", password="b")
-            response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True)
+            response = RestClient(username="a", password="b").update_database_visibility(database_id=1, is_public=True,
+                                                                                         is_schema_public=True)
             self.assertEqual(response.is_public, True)
 
-    def test_update_database_visibility_not_allowed_fails(self):
+    def test_update_database_visibility_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/visibility', status_code=400)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
+            except MalformedError:
+                pass
+
+    def test_update_database_visibility_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/visibility', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True)
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
             except ForbiddenError:
                 pass
 
-    def test_update_database_visibility_not_found_fails(self):
+    def test_update_database_visibility_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/visibility', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True)
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
             except NotExistsError:
                 pass
 
-    def test_update_database_visibility_not_auth_fails(self):
+    def test_update_database_visibility_502_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('/api/database/1', status_code=404)
+            mock.put('/api/database/1/visibility', status_code=502)
             # test
             try:
-                response = RestClient().update_database_visibility(database_id=1, is_public=True, is_schema_public=True)
-            except AuthenticationError:
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
+            except ServiceConnectionError:
+                pass
+
+    def test_update_database_visibility_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/visibility', status_code=503)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
+            except ServiceError:
+                pass
+
+    def test_update_database_visibility_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/visibility', status_code=200)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_visibility(database_id=1,
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True)
+            except ResponseCodeError:
                 pass
 
+    def test_update_database_visibility_anonymous_fails(self):
+        # test
+        try:
+            response = RestClient().update_database_visibility(database_id=1, is_public=True, is_schema_public=True)
+        except AuthenticationError:
+            pass
+
     def test_update_database_owner_succeeds(self):
         exp = Database(
             id=1,
@@ -255,34 +380,77 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.put('/api/database/1/owner', json=exp.model_dump(), status_code=202)
             # test
             client = RestClient(username="a", password="b")
-            response = client.update_database_owner(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                    user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             self.assertEqual(response.owner.id, 'abdbf897-e599-4e5a-a3f0-7529884ea011')
 
-    def test_update_database_owner_not_allowed_fails(self):
+    def test_update_database_owner_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/owner', status_code=400)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            except MalformedError:
+                pass
+
+    def test_update_database_owner_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/owner', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_owner(database_id=1,
-                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except ForbiddenError:
                 pass
 
-    def test_update_database_owner_not_found_fails(self):
+    def test_update_database_owner_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/owner', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_owner(database_id=1,
-                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except NotExistsError:
                 pass
 
-    def test_update_database_owner_not_auth_fails(self):
+    def test_update_database_owner_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/owner', status_code=502)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            except ServiceConnectionError:
+                pass
+
+    def test_update_database_owner_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/owner', status_code=503)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            except ServiceError:
+                pass
+
+    def test_update_database_owner_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/owner', status_code=200)
+            # test
+            try:
+                response = RestClient(username="a", password="b").update_database_owner(database_id=1,
+                                                                                        user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            except ResponseCodeError:
+                pass
+
+    def test_update_database_owner_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/owner', status_code=404)
@@ -293,6 +461,157 @@ class DatabaseUnitTest(unittest.TestCase):
             except AuthenticationError:
                 pass
 
+    def test_update_database_schema_succeeds(self):
+        exp = DatabaseBrief(
+            id=1,
+            name='test',
+            owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
+            contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
+            internal_name='test_abcd',
+            is_public=True,
+            is_schema_public=True
+        )
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json=exp.model_dump())
+            mock.put('/api/database/1/metadata/view', json=exp.model_dump())
+            # test
+            response = RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            self.assertEqual(exp, response)
+
+    def test_update_database_schema_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=400)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except MalformedError:
+                pass
+
+    def test_update_database_schema_view_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=400)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except MalformedError:
+                pass
+
+    def test_update_database_schema_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=403)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ForbiddenError:
+                pass
+
+    def test_update_database_schema_view_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=403)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ForbiddenError:
+                pass
+
+    def test_update_database_schema_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=404)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except NotExistsError:
+                pass
+
+    def test_update_database_schema_view_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=404)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except NotExistsError:
+                pass
+
+    def test_update_database_schema_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=502)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ServiceConnectionError:
+                pass
+
+    def test_update_database_schema_view_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=502)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ServiceConnectionError:
+                pass
+
+    def test_update_database_schema_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=503)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ServiceError:
+                pass
+
+    def test_update_database_schema_view_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=503)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ServiceError:
+                pass
+
+    def test_update_database_schema_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', status_code=202)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ResponseCodeError:
+                pass
+
+    def test_update_database_schema_view_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/metadata/table', json={}, status_code=200)
+            mock.put('/api/database/1/metadata/view', status_code=202)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_database_schema(database_id=1)
+            except ResponseCodeError:
+                pass
+
+    def test_update_database_schema_anonymous_fails(self):
+        # test
+        try:
+            RestClient().update_database_schema(database_id=1)
+        except AuthenticationError:
+            pass
+
     def test_get_database_access_succeeds(self):
         exp = DatabaseAccess(type=AccessType.READ,
                              user=UserBrief(id='abdbf897-e599-4e5a-a3f0-7529884ea011', username='other'))
@@ -303,7 +622,7 @@ class DatabaseUnitTest(unittest.TestCase):
             response = RestClient().get_database_access(database_id=1)
             self.assertEqual(response, AccessType.READ)
 
-    def test_get_database_access_not_allowed_fails(self):
+    def test_get_database_access_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/access', status_code=403)
@@ -313,7 +632,7 @@ class DatabaseUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_database_access_not_found_fails(self):
+    def test_get_database_access_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/access', status_code=404)
@@ -331,24 +650,24 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', json=exp.model_dump(),
                       status_code=202)
             # test
-            client = RestClient(username="a", password="b")
-            response = client.create_database_access(database_id=1, type=AccessType.READ,
-                                                     user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            response = RestClient(username="a", password="b").create_database_access(database_id=1,
+                                                                                     type=AccessType.READ,
+                                                                                     user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             self.assertEqual(response, exp.type)
 
-    def test_create_database_access_malformed_fails(self):
+    def test_create_database_access_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").create_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except MalformedError:
                 pass
 
-    def test_create_database_access_not_auth_fails(self):
+    def test_create_database_access_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400)
@@ -359,27 +678,27 @@ class DatabaseUnitTest(unittest.TestCase):
             except AuthenticationError:
                 pass
 
-    def test_create_database_access_not_allowed_fails(self):
+    def test_create_database_access_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").create_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except ForbiddenError:
                 pass
 
-    def test_create_database_access_not_found_fails(self):
+    def test_create_database_access_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").create_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except NotExistsError:
                 pass
 
@@ -391,48 +710,48 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', json=exp.model_dump(),
                      status_code=202)
             # test
-            client = RestClient(username="a", password="b")
-            response = client.update_database_access(database_id=1, type=AccessType.READ,
-                                                     user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            response = RestClient(username="a", password="b").update_database_access(database_id=1,
+                                                                                     type=AccessType.READ,
+                                                                                     user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             self.assertEqual(response, exp.type)
 
-    def test_update_database_access_malformed_fails(self):
+    def test_update_database_access_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").update_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except MalformedError:
                 pass
 
-    def test_update_database_access_not_allowed_fails(self):
+    def test_update_database_access_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").update_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except ForbiddenError:
                 pass
 
-    def test_update_database_access_not_found_fails(self):
+    def test_update_database_access_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_database_access(database_id=1, type=AccessType.READ,
-                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                response = RestClient(username="a", password="b").update_database_access(database_id=1,
+                                                                                         type=AccessType.READ,
+                                                                                         user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except NotExistsError:
                 pass
 
-    def test_update_database_access_not_auth_fails(self):
+    def test_update_database_access_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404)
@@ -449,42 +768,43 @@ class DatabaseUnitTest(unittest.TestCase):
             mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=202)
             # test
             client = RestClient(username="a", password="b")
-            client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+            RestClient(username="a", password="b").delete_database_access(database_id=1,
+                                                                          user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
 
-    def test_delete_database_access_malformed_fails(self):
+    def test_delete_database_access_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                RestClient(username="a", password="b").delete_database_access(database_id=1,
+                                                                              user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except MalformedError:
                 pass
 
-    def test_delete_database_access_not_allowed_fails(self):
+    def test_delete_database_access_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                RestClient(username="a", password="b").delete_database_access(database_id=1,
+                                                                              user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except ForbiddenError:
                 pass
 
-    def test_delete_database_access_not_found_fails(self):
+    def test_delete_database_access_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
+                RestClient(username="a", password="b").delete_database_access(database_id=1,
+                                                                              user_id='abdbf897-e599-4e5a-a3f0-7529884ea011')
             except NotExistsError:
                 pass
 
-    def test_delete_database_access_not_auth_fails(self):
+    def test_delete_database_access_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404)
diff --git a/lib/python/tests/test_unit_identifier.py b/lib/python/tests/test_unit_identifier.py
index 8726d05ef23108c2a0a8fb9e8c4cd65052e19b1e..137f26aab22ca87c04aca8bb2d58d3293b67351b 100644
--- a/lib/python/tests/test_unit_identifier.py
+++ b/lib/python/tests/test_unit_identifier.py
@@ -47,7 +47,7 @@ class IdentifierUnitTest(unittest.TestCase):
                 creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')])
             self.assertEqual(exp, response)
 
-    def test_create_identifier_malformed_fails(self):
+    def test_create_identifier_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/identifier', status_code=400)
@@ -63,7 +63,7 @@ class IdentifierUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_create_identifier_not_allowed_fails(self):
+    def test_create_identifier_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/identifier', status_code=403)
@@ -79,7 +79,7 @@ class IdentifierUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_create_identifier_not_found_fails(self):
+    def test_create_identifier_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/identifier', status_code=404)
@@ -95,7 +95,7 @@ class IdentifierUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_create_identifier_not_auth_fails(self):
+    def test_create_identifier_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/identifier', status_code=503)
diff --git a/lib/python/tests/test_unit_query.py b/lib/python/tests/test_unit_query.py
index 6dff04867e33062f09ea151b8651f4362ce06bf8..31b4182a4b0fec18e22ac70cf3192e6ca8596793 100644
--- a/lib/python/tests/test_unit_query.py
+++ b/lib/python/tests/test_unit_query.py
@@ -25,7 +25,7 @@ class QueryUnitTest(unittest.TestCase):
                                             query="SELECT id, username FROM some_table WHERE id IN (1,2)")
             self.assertTrue(DataFrame.equals(df, response))
 
-    def test_create_subset_malformed_fails(self):
+    def test_create_subset_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/subset', status_code=400)
@@ -37,7 +37,7 @@ class QueryUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_create_subset_not_allowed_fails(self):
+    def test_create_subset_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/subset', status_code=403)
@@ -49,7 +49,7 @@ class QueryUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_create_subset_not_found_fails(self):
+    def test_create_subset_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/subset', status_code=404)
@@ -61,7 +61,7 @@ class QueryUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_create_subset_not_auth_succeeds(self):
+    def test_create_subset_anonymous_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}]
             df = DataFrame.from_records(json.dumps(exp))
@@ -94,7 +94,7 @@ class QueryUnitTest(unittest.TestCase):
             response = RestClient().get_subset(database_id=1, subset_id=6)
             self.assertEqual(exp, response)
 
-    def test_find_query_not_allowed_fails(self):
+    def test_find_query_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset/6', status_code=403)
@@ -104,7 +104,7 @@ class QueryUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_find_query_not_found_fails(self):
+    def test_find_query_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset/6', status_code=404)
@@ -143,7 +143,7 @@ class QueryUnitTest(unittest.TestCase):
             response = RestClient().get_queries(database_id=1)
             self.assertEqual(exp, response)
 
-    def test_get_queries_not_allowed_fails(self):
+    def test_get_queries_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset', status_code=403)
@@ -153,7 +153,7 @@ class QueryUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_queries_not_found_fails(self):
+    def test_get_queries_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset', status_code=404)
@@ -184,7 +184,7 @@ class QueryUnitTest(unittest.TestCase):
             self.assertEqual(df.shape, response.shape)
             self.assertTrue(DataFrame.equals(df, response))
 
-    def test_get_subset_data_not_allowed_fails(self):
+    def test_get_subset_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset/6/data', status_code=403)
@@ -194,7 +194,7 @@ class QueryUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_subset_data_not_found_fails(self):
+    def test_get_subset_data_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/subset/6/data', status_code=404)
@@ -213,7 +213,7 @@ class QueryUnitTest(unittest.TestCase):
             response = RestClient().get_subset_data_count(database_id=1, subset_id=6)
             self.assertEqual(exp, response)
 
-    def test_get_subset_data_count_not_allowed_fails(self):
+    def test_get_subset_data_count_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/subset/6/data', status_code=403)
@@ -223,7 +223,7 @@ class QueryUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_subset_data_count_not_found_fails(self):
+    def test_get_subset_data_count_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/subset/6/data', status_code=404)
diff --git a/lib/python/tests/test_unit_rest_client.py b/lib/python/tests/test_unit_rest_client.py
index 61a9432ce41cb36a0ea2f4295c7d6136e3a7a696..0c52cfd33ec211aa130d9771e97fc2e718253adf 100644
--- a/lib/python/tests/test_unit_rest_client.py
+++ b/lib/python/tests/test_unit_rest_client.py
@@ -1,99 +1,26 @@
 import os
 import unittest
 
-import requests_mock
-
 from dbrepo.RestClient import RestClient
-from dbrepo.api.dto import JwtAuth
-from dbrepo.api.exceptions import MalformedError, ServiceConnectionError, ServiceError, ForbiddenError, \
-    AuthenticationError, ResponseCodeError
 
 
 class RestClientUnitTest(unittest.TestCase):
 
     def test_constructor_succeeds(self):
-        with requests_mock.Mocker() as mock:
-            # test
-            os.environ['REST_API_SECURE'] = 'True'
-            response = RestClient()
-            self.assertTrue(response.secure)
-
-    def test_get_jwt_auth_empty_succeeds(self):
-        with requests_mock.Mocker() as mock:
-            exp = JwtAuth(access_token='ey123',
-                          refresh_token='ey456',
-                          id_token='ey789',
-                          expires_in=3600,
-                          scope='scope',
-                          token_type='Bearer',
-                          not_before_policy=0,
-                          session_state='session_state',
-                          refresh_expires_in=7200)
-            # mock
-            mock.post('/api/user/token', json=exp.model_dump(), status_code=202)
-            # test
-            response = RestClient().get_jwt_auth()
-            self.assertEqual(exp, response)
-
-    def test_get_jwt_auth_400_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=400)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except MalformedError:
-                pass
-
-    def test_get_jwt_auth_403_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=403)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except ForbiddenError:
-                pass
-
-    def test_get_jwt_auth_428_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=428)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except AuthenticationError:
-                pass
-
-    def test_get_jwt_auth_502_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=502)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except ServiceConnectionError:
-                pass
-
-    def test_get_jwt_auth_503_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=503)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except ServiceError:
-                pass
-
-    def test_get_jwt_auth_unknown_fails(self):
-        with requests_mock.Mocker() as mock:
-            # mock
-            mock.post('/api/user/token', status_code=418)
-            # test
-            try:
-                response = RestClient().get_jwt_auth()
-            except ResponseCodeError:
-                pass
+        # test
+        os.environ['REST_API_SECURE'] = 'True'
+        response = RestClient()
+        self.assertTrue(response.secure)
+
+    def test_whoami_anonymous_succeeds(self):
+        # test
+        response = RestClient().whoami()
+        self.assertIsNone(response)
+
+    def test_whoami_succeeds(self):
+        # test
+        response = RestClient(username="foobar").whoami()
+        self.assertEqual("foobar", response)
 
 
 if __name__ == "__main__":
diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py
index eccf8c2a59ee45c3341c4116678301e438f0089b..998a1bc4f0ae96b8517eac4c90b5a7c50c008444 100644
--- a/lib/python/tests/test_unit_table.py
+++ b/lib/python/tests/test_unit_table.py
@@ -1,3 +1,4 @@
+import datetime
 import json
 import unittest
 
@@ -6,9 +7,9 @@ from pandas import DataFrame
 
 from dbrepo.RestClient import RestClient
 from dbrepo.api.dto import Table, CreateTableConstraints, Column, Constraints, ColumnType, ConceptBrief, UnitBrief, \
-    TableStatistics, ColumnStatistic, PrimaryKey, ColumnBrief, TableBrief, UserBrief
+    TableStatistics, ColumnStatistic, PrimaryKey, ColumnBrief, TableBrief, UserBrief, History, HistoryEventType
 from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, NameExistsError, \
-    AuthenticationError, ExternalSystemError
+    AuthenticationError, ExternalSystemError, ServiceError, ServiceConnectionError, ResponseCodeError
 
 
 class TableUnitTest(unittest.TestCase):
@@ -33,7 +34,7 @@ class TableUnitTest(unittest.TestCase):
                                            constraints=CreateTableConstraints())
             self.assertEqual(exp, response)
 
-    def test_create_table_malformed_fails(self):
+    def test_create_table_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table', status_code=400)
@@ -46,20 +47,20 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_create_table_not_allowed_fails(self):
+    def test_create_table_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[],
-                                               is_public=True, is_schema_public=True,
-                                               constraints=CreateTableConstraints())
+                RestClient(username="a", password="b").create_table(database_id=1, name="Test",
+                                                                    description="Test Table", columns=[],
+                                                                    is_public=True, is_schema_public=True,
+                                                                    constraints=CreateTableConstraints())
             except ForbiddenError:
                 pass
 
-    def test_create_table_not_allowed_fails(self):
+    def test_create_table_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table', status_code=404)
@@ -72,7 +73,7 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_create_table_name_exists_fails(self):
+    def test_create_table_409_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table', status_code=409)
@@ -85,7 +86,46 @@ class TableUnitTest(unittest.TestCase):
             except NameExistsError:
                 pass
 
-    def test_create_table_not_auth_fails(self):
+    def test_create_table_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table', status_code=502)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[],
+                                               is_public=True, is_schema_public=True,
+                                               constraints=CreateTableConstraints())
+            except ServiceConnectionError:
+                pass
+
+    def test_create_table_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[],
+                                               is_public=True, is_schema_public=True,
+                                               constraints=CreateTableConstraints())
+            except ServiceError:
+                pass
+
+    def test_create_table_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[],
+                                               is_public=True, is_schema_public=True,
+                                               constraints=CreateTableConstraints())
+            except ResponseCodeError:
+                pass
+
+    def test_create_table_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table', status_code=409)
@@ -122,6 +162,36 @@ class TableUnitTest(unittest.TestCase):
             response = RestClient().get_tables(database_id=1)
             self.assertEqual(exp, response)
 
+    def test_get_tables_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table', status_code=403)
+            # test
+            try:
+                RestClient().get_tables(database_id=1)
+            except ForbiddenError:
+                pass
+
+    def test_get_tables_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table', status_code=404)
+            # test
+            try:
+                RestClient().get_tables(database_id=1)
+            except NotExistsError:
+                pass
+
+    def test_get_tables_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table', status_code=202)
+            # test
+            try:
+                RestClient().get_tables(database_id=1)
+            except ResponseCodeError:
+                pass
+
     def test_get_table_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = Table(id=2,
@@ -159,10 +229,7 @@ class TableUnitTest(unittest.TestCase):
                                         database_id=1,
                                         table_id=2,
                                         internal_name="id",
-                                        auto_generated=True,
-                                        is_primary_key=True,
                                         type=ColumnType.BIGINT,
-                                        is_public=True,
                                         is_null_allowed=False)])
             # mock
             mock.get('/api/database/1/table/2', json=exp.model_dump())
@@ -170,7 +237,7 @@ class TableUnitTest(unittest.TestCase):
             response = RestClient().get_table(database_id=1, table_id=2)
             self.assertEqual(exp, response)
 
-    def test_get_table_not_allowed_fails(self):
+    def test_get_table_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/table/2', status_code=403)
@@ -180,7 +247,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_table_not_found_fails(self):
+    def test_get_table_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/table/2', status_code=404)
@@ -190,6 +257,16 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_get_table_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/2', status_code=202)
+            # test
+            try:
+                response = RestClient().get_table(database_id=1, table_id=2)
+            except ResponseCodeError:
+                pass
+
     def test_delete_table_succeeds(self):
         with requests_mock.Mocker() as mock:
             # mock
@@ -198,7 +275,18 @@ class TableUnitTest(unittest.TestCase):
             client = RestClient(username="a", password="b")
             client.delete_table(database_id=1, table_id=2)
 
-    def test_delete_table_not_allowed_fails(self):
+    def test_delete_table_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/2', status_code=400)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table(database_id=1, table_id=2)
+            except MalformedError:
+                pass
+
+    def test_delete_table_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/2', status_code=403)
@@ -209,7 +297,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_delete_table_not_found_fails(self):
+    def test_delete_table_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/2', status_code=404)
@@ -220,7 +308,40 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_delete_table_not_auth_fails(self):
+    def test_delete_table_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/2', status_code=502)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table(database_id=1, table_id=2)
+            except ServiceConnectionError:
+                pass
+
+    def test_delete_table_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/2', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table(database_id=1, table_id=2)
+            except ServiceError:
+                pass
+
+    def test_delete_table_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/2', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table(database_id=1, table_id=2)
+            except ResponseCodeError:
+                pass
+
+    def test_delete_table_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/2', status_code=404)
@@ -230,6 +351,67 @@ class TableUnitTest(unittest.TestCase):
             except AuthenticationError:
                 pass
 
+    def test_get_table_history_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            exp = [History(event=HistoryEventType.INSERT,
+                           total=2,
+                           timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, datetime.timezone.utc))]
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', json=[exp[0].model_dump()])
+            # test
+            response = RestClient().get_table_history(database_id=1, table_id=9)
+            self.assertEqual(exp, response)
+
+    def test_get_table_history_400_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', status_code=400)
+            # test
+            try:
+                RestClient().get_table_history(database_id=1, table_id=9)
+            except MalformedError:
+                pass
+
+    def test_get_table_history_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', status_code=403)
+            # test
+            try:
+                RestClient().get_table_history(database_id=1, table_id=9)
+            except ForbiddenError:
+                pass
+
+    def test_get_table_history_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', status_code=404)
+            # test
+            try:
+                RestClient().get_table_history(database_id=1, table_id=9)
+            except NotExistsError:
+                pass
+
+    def test_get_table_history_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', status_code=503)
+            # test
+            try:
+                RestClient().get_table_history(database_id=1, table_id=9)
+            except ServiceError:
+                pass
+
+    def test_get_table_history_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/history?size=100', status_code=202)
+            # test
+            try:
+                RestClient().get_table_history(database_id=1, table_id=9)
+            except ResponseCodeError:
+                pass
+
     def test_get_table_data_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}]
@@ -237,7 +419,9 @@ class TableUnitTest(unittest.TestCase):
             # mock
             mock.get('/api/database/1/table/9/data', json=json.dumps(exp))
             # test
-            response = RestClient().get_table_data(database_id=1, table_id=9)
+            response = RestClient().get_table_data(database_id=1, table_id=9,
+                                                   timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0,
+                                                                               datetime.timezone.utc))
             self.assertTrue(DataFrame.equals(df, response))
 
     def test_get_table_data_dataframe_succeeds(self):
@@ -251,7 +435,7 @@ class TableUnitTest(unittest.TestCase):
             self.assertEqual(df.shape, response.shape)
             self.assertTrue(DataFrame.equals(df, response))
 
-    def test_get_table_data_malformed_fails(self):
+    def test_get_table_data_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/table/9/data', status_code=400)
@@ -261,7 +445,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_get_table_data_not_allowed_fails(self):
+    def test_get_table_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/table/9/data', status_code=403)
@@ -271,7 +455,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_table_data_not_found_fails(self):
+    def test_get_table_data_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/table/9/data', status_code=404)
@@ -281,6 +465,26 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_get_table_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/data', status_code=503)
+            # test
+            try:
+                response = RestClient().get_table_data(database_id=1, table_id=9)
+            except ServiceError:
+                pass
+
+    def test_get_table_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/table/9/data', status_code=202)
+            # test
+            try:
+                response = RestClient().get_table_data(database_id=1, table_id=9)
+            except ResponseCodeError:
+                pass
+
     def test_get_table_data_count_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = 2
@@ -290,7 +494,7 @@ class TableUnitTest(unittest.TestCase):
             response = RestClient().get_table_data_count(database_id=1, table_id=9)
             self.assertEqual(exp, response)
 
-    def test_get_table_data_count_malformed_fails(self):
+    def test_get_table_data_count_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/table/9/data', status_code=400)
@@ -300,7 +504,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_get_table_data_count_not_allowed_fails(self):
+    def test_get_table_data_count_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/table/9/data', status_code=403)
@@ -310,7 +514,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_table_data_count_not_found_fails(self):
+    def test_get_table_data_count_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/table/9/data', status_code=404)
@@ -339,7 +543,7 @@ class TableUnitTest(unittest.TestCase):
             client.create_table_data(database_id=1, table_id=9,
                                      data={'name': 'Josiah', 'age': 45, 'gender': 'male'})
 
-    def test_create_table_data_malformed_fails(self):
+    def test_create_table_data_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table/9/data', status_code=400)
@@ -351,7 +555,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_create_table_data_not_allowed_fails(self):
+    def test_create_table_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table/9/data', status_code=403)
@@ -363,7 +567,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_create_table_data_not_found_fails(self):
+    def test_create_table_data_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/table/9/data', status_code=404)
@@ -375,6 +579,41 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_create_table_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table/9/data', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.create_table_data(database_id=1, table_id=9,
+                                         data={'name': 'Josiah', 'age': 45, 'gender': 'male'})
+            except ServiceError:
+                pass
+
+    def test_create_table_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table/9/data', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.create_table_data(database_id=1, table_id=9,
+                                         data={'name': 'Josiah', 'age': 45, 'gender': 'male'})
+            except ResponseCodeError:
+                pass
+
+    def test_create_table_data_anonymous_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/table/9/data', status_code=503)
+            # test
+            try:
+                RestClient().create_table_data(database_id=1, table_id=9,
+                                               data={'name': 'Josiah', 'age': 45, 'gender': 'male'})
+            except AuthenticationError:
+                pass
+
     def test_update_table_data_succeeds(self):
         with requests_mock.Mocker() as mock:
             # mock
@@ -385,7 +624,7 @@ class TableUnitTest(unittest.TestCase):
                                      data={'name': 'Josiah', 'age': 45, 'gender': 'male'},
                                      keys={'id': 1})
 
-    def test_update_table_data_malformed_fails(self):
+    def test_update_table_data_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/9/data', status_code=400)
@@ -398,7 +637,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_update_table_data_not_allowed_fails(self):
+    def test_update_table_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/9/data', status_code=403)
@@ -411,7 +650,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_update_table_data_not_found_fails(self):
+    def test_update_table_data_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/9/data', status_code=404)
@@ -424,6 +663,32 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_update_table_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/table/9/data', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.update_table_data(database_id=1, table_id=9,
+                                         data={'name': 'Josiah', 'age': 45, 'gender': 'male'},
+                                         keys={'id': 1})
+            except ServiceError:
+                pass
+
+    def test_update_table_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/table/9/data', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.update_table_data(database_id=1, table_id=9,
+                                         data={'name': 'Josiah', 'age': 45, 'gender': 'male'},
+                                         keys={'id': 1})
+            except ResponseCodeError:
+                pass
+
     def test_delete_table_data_succeeds(self):
         with requests_mock.Mocker() as mock:
             # mock
@@ -432,7 +697,7 @@ class TableUnitTest(unittest.TestCase):
             client = RestClient(username="a", password="b")
             client.delete_table_data(database_id=1, table_id=9, keys={'id': 1})
 
-    def test_delete_table_data_malformed_fails(self):
+    def test_delete_table_data_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/9/data', status_code=400)
@@ -443,7 +708,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_delete_table_data_not_allowed_fails(self):
+    def test_delete_table_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/9/data', status_code=403)
@@ -454,7 +719,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_delete_table_data_not_found_fails(self):
+    def test_delete_table_data_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/9/data', status_code=404)
@@ -465,7 +730,29 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_delete_table_data_not_auth_fails(self):
+    def test_delete_table_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/9/data', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table_data(database_id=1, table_id=9, keys={'id': 1})
+            except ServiceError:
+                pass
+
+    def test_delete_table_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/table/9/data', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_table_data(database_id=1, table_id=9, keys={'id': 1})
+            except ResponseCodeError:
+                pass
+
+    def test_delete_table_data_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/table/9/data', status_code=404)
@@ -503,7 +790,7 @@ class TableUnitTest(unittest.TestCase):
                                                   concept_uri="http://dbpedia.org/page/Category:Precipitation")
             self.assertEqual(exp, response)
 
-    def test_update_table_column_malformed_fails(self):
+    def test_update_table_column_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/2/column/1', status_code=400)
@@ -516,7 +803,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_update_table_column_not_allowed_fails(self):
+    def test_update_table_column_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/2/column/1', status_code=403)
@@ -529,7 +816,7 @@ class TableUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_update_table_column_not_found_fails(self):
+    def test_update_table_column_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/2/column/1', status_code=404)
@@ -542,7 +829,7 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_update_table_column_not_auth_fails(self):
+    def test_update_table_column_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.put('/api/database/1/table/2/column/1', status_code=404)
@@ -564,7 +851,7 @@ class TableUnitTest(unittest.TestCase):
             response = RestClient().analyse_table_statistics(database_id=1, table_id=2)
             self.assertEqual(exp, response)
 
-    def test_analyse_table_statistics_malformed_fails(self):
+    def test_analyse_table_statistics_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/analyse/database/1/table/2/statistics', status_code=400)
@@ -574,7 +861,7 @@ class TableUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_analyse_table_statistics_not_found_fails(self):
+    def test_analyse_table_statistics_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/analyse/database/1/table/2/statistics', status_code=404)
@@ -584,6 +871,36 @@ class TableUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_analyse_table_statistics_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/analyse/database/1/table/2/statistics', status_code=502)
+            # test
+            try:
+                RestClient().analyse_table_statistics(database_id=1, table_id=2)
+            except ServiceConnectionError:
+                pass
+
+    def test_analyse_table_statistics_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/analyse/database/1/table/2/statistics', status_code=503)
+            # test
+            try:
+                RestClient().analyse_table_statistics(database_id=1, table_id=2)
+            except ServiceError:
+                pass
+
+    def test_analyse_table_statistics_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/analyse/database/1/table/2/statistics', status_code=200)
+            # test
+            try:
+                RestClient().analyse_table_statistics(database_id=1, table_id=2)
+            except ResponseCodeError:
+                pass
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/lib/python/tests/test_unit_unit.py b/lib/python/tests/test_unit_unit.py
new file mode 100644
index 0000000000000000000000000000000000000000..efea8b69bb335cf4618216e7822dd5a7d2a1e467
--- /dev/null
+++ b/lib/python/tests/test_unit_unit.py
@@ -0,0 +1,35 @@
+import unittest
+
+import requests_mock
+
+from dbrepo.RestClient import RestClient
+from dbrepo.api.dto import UnitBrief
+from dbrepo.api.exceptions import ResponseCodeError
+
+
+class UserUnitTest(unittest.TestCase):
+
+    def test_get_units_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            exp = [UnitBrief(id=1,
+                             uri='http://www.ontology-of-units-of-measure.org/resource/om-2/CelsiusTemperature',
+                             name='Celsius Temperature')]
+            # mock
+            mock.get('/api/unit', json=[exp[0].model_dump()])
+            # test
+            response = RestClient().get_units()
+            self.assertEqual(exp, response)
+
+    def test_get_units_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/unit', status_code=202)
+            # test
+            try:
+                RestClient().get_units()
+            except ResponseCodeError:
+                pass
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/lib/python/tests/test_unit_user.py b/lib/python/tests/test_unit_user.py
index 0e846b1b42e6ba935a2f204d09aa0920dce69626..213635634f4248ec32997b02966f79fd45c12663 100644
--- a/lib/python/tests/test_unit_user.py
+++ b/lib/python/tests/test_unit_user.py
@@ -3,9 +3,9 @@ import unittest
 import requests_mock
 
 from dbrepo.RestClient import RestClient
-from dbrepo.api.dto import User, UserAttributes, UserBrief
-from dbrepo.api.exceptions import ResponseCodeError, UsernameExistsError, EmailExistsError, NotExistsError, \
-    ForbiddenError, AuthenticationError, MalformedError, ServiceError
+from dbrepo.api.dto import UserAttributes, UserBrief, User, UnitBrief
+from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, \
+    ForbiddenError, AuthenticationError, ServiceError, MalformedError
 
 
 class UserUnitTest(unittest.TestCase):
@@ -22,101 +22,45 @@ class UserUnitTest(unittest.TestCase):
     def test_get_users_empty_succeeds(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.get('http://localhost/api/user', json=[])
+            mock.get('/api/user', json=[])
             # test
             response = RestClient().get_users()
             self.assertEqual([], response)
 
-    def test_get_user_succeeds(self):
+    def test_get_users_succeeds(self):
         with requests_mock.Mocker() as mock:
-            exp = [
-                User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise',
-                     attributes=UserAttributes(theme='dark'))
-            ]
+            exp = [UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')]
             # mock
-            mock.get('http://localhost/api/user', json=[exp[0].model_dump()])
+            mock.get('/api/user', json=[exp[0].model_dump()])
             # test
             response = RestClient().get_users()
             self.assertEqual(exp, response)
 
-    def test_get_user_fails(self):
+    def test_get_users_unknown_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.get('http://localhost/api/user', status_code=404)
+            mock.get('/api/user', status_code=404)
             # test
             try:
                 response = RestClient().get_users()
             except ResponseCodeError as e:
                 pass
 
-    def test_create_user_succeeds(self):
-        with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')
-            # mock
-            mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=201)
-            # test
-            response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com')
-            self.assertEqual(exp, response)
-
-    def test_create_user_bad_request_fails(self):
-        with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')
-            # mock
-            mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=400)
-            # test
-            try:
-                response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com')
-            except MalformedError as e:
-                pass
-
-    def test_create_user_username_exists_fails(self):
-        with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')
-            # mock
-            mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=409)
-            # test
-            try:
-                response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com')
-            except UsernameExistsError as e:
-                pass
-
-    def test_create_user_default_role_not_exists_fails(self):
-        with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')
-            # mock
-            mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=404)
-            # test
-            try:
-                response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com')
-            except NotExistsError as e:
-                pass
-
-    def test_create_user_emails_exists_fails(self):
-        with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')
-            # mock
-            mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=417)
-            # test
-            try:
-                response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com')
-            except EmailExistsError as e:
-                pass
-
     def test_get_user_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise',
-                       attributes=UserAttributes(theme='dark'))
+                       attributes=UserAttributes(theme='dark', language='en'))
             # mock
-            mock.get('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16',
+            mock.get('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16',
                      json=exp.model_dump())
             # test
             response = RestClient().get_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16')
             self.assertEqual(exp, response)
 
-    def test_get_user_not_found_fails(self):
+    def test_get_user_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.get('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404)
+            mock.get('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404)
             # test
             try:
                 response = RestClient().get_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16')
@@ -125,10 +69,9 @@ class UserUnitTest(unittest.TestCase):
 
     def test_update_user_succeeds(self):
         with requests_mock.Mocker() as mock:
-            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin',
-                            attributes=UserAttributes(theme='dark'))
+            exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin')
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=202,
+            mock.put('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=202,
                      json=exp.model_dump())
             # test
             client = RestClient(username="a", password="b")
@@ -136,95 +79,88 @@ class UserUnitTest(unittest.TestCase):
                                           language='en', theme='light')
             self.assertEqual(exp, response)
 
-    def test_update_user_not_allowed_fails(self):
+    def test_get_user_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=403)
+            mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin',
-                                              language='en', theme='light')
-            except ForbiddenError as e:
+                RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2')
+            except ForbiddenError:
                 pass
 
-    def test_update_user_not_found_fails(self):
+    def test_get_user_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404)
+            mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin',
-                                              language='en', theme='light')
-            except NotExistsError as e:
+                RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2')
+            except NotExistsError:
                 pass
 
-    def test_update_user_not_auth_fails(self):
+    def test_get_user_unknown_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=405)
+            mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=409)
             # test
             try:
-                response = RestClient().update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin',
-                                                    language='en', theme='light')
-            except AuthenticationError as e:
+                RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2')
+            except ResponseCodeError:
                 pass
 
-    def test_update_user_password_succeeds(self):
+    def test_update_user_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=202)
+            mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=202)
             # test
-            client = RestClient(username="a", password="b")
-            response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                                                   password='s3cr3t1n0rm4t10n')
+            try:
+                RestClient().update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', theme='dark', language='en')
+            except AuthenticationError:
+                pass
 
-    def test_update_user_password_not_allowed_fails(self):
+    def test_update_user_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=403)
+            mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=400)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                                                       password='s3cr3t1n0rm4t10n')
-            except ForbiddenError as e:
+                RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2',
+                                                                       theme='dark', language='en')
+            except MalformedError:
                 pass
 
-    def test_update_user_password_not_found_fails(self):
+    def test_update_user_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=404)
+            mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=403)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                                                       password='s3cr3t1n0rm4t10n')
-            except NotExistsError as e:
+                RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2',
+                                                                       theme='dark', language='en')
+            except ForbiddenError:
                 pass
 
-    def test_update_user_password_keycloak_fails(self):
+    def test_update_user_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=503)
+            mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=404)
             # test
             try:
-                client = RestClient(username="a", password="b")
-                response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                                                       password='s3cr3t1n0rm4t10n')
-            except ServiceError as e:
+                RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2',
+                                                                       theme='dark', language='en')
+            except NotExistsError:
                 pass
 
-    def test_update_user_password_not_auth_fails(self):
+    def test_update_user_unknown_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
-            mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=503)
+            mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=200)
             # test
             try:
-                response = RestClient().update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                                                             password='s3cr3t1n0rm4t10n')
-            except AuthenticationError as e:
+                RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2',
+                                                                       theme='dark', language='en')
+            except ResponseCodeError:
                 pass
 
 
diff --git a/lib/python/tests/test_unit_view.py b/lib/python/tests/test_unit_view.py
index 29fd6aac2f7942fa978c492a5b95ea31e724e0b9..842c2d7388b205590336ce38d722c437adb2ec40 100644
--- a/lib/python/tests/test_unit_view.py
+++ b/lib/python/tests/test_unit_view.py
@@ -6,7 +6,8 @@ from pandas import DataFrame
 
 from dbrepo.RestClient import RestClient
 from dbrepo.api.dto import View, ViewColumn, ColumnType, UserBrief, ViewBrief
-from dbrepo.api.exceptions import ForbiddenError, NotExistsError, MalformedError, AuthenticationError
+from dbrepo.api.exceptions import ForbiddenError, NotExistsError, MalformedError, AuthenticationError, \
+    ResponseCodeError, ExternalSystemError, ServiceError, ServiceConnectionError
 
 
 class ViewUnitTest(unittest.TestCase):
@@ -22,22 +23,22 @@ class ViewUnitTest(unittest.TestCase):
     def test_get_views_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = [ViewBrief(id=1,
-                        name="Data",
-                        internal_name="data",
-                        database_id=1,
-                        initial_view=False,
-                        query="SELECT id FROM mytable WHERE deg > 0",
-                        query_hash="94c74728b11a690e51d64719868824735f0817b7",
-                        owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16',
-                        is_public=True,
-                        is_schema_public=True)]
+                             name="Data",
+                             internal_name="data",
+                             database_id=1,
+                             initial_view=False,
+                             query="SELECT id FROM mytable WHERE deg > 0",
+                             query_hash="94c74728b11a690e51d64719868824735f0817b7",
+                             owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16',
+                             is_public=True,
+                             is_schema_public=True)]
             # mock
             mock.get('/api/database/1/view', json=[exp[0].model_dump()])
             # test
             response = RestClient().get_views(database_id=1)
             self.assertEqual(exp, response)
 
-    def test_get_views_not_found_fails(self):
+    def test_get_views_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/view', status_code=404)
@@ -47,6 +48,16 @@ class ViewUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_get_views_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view', status_code=202)
+            # test
+            try:
+                response = RestClient().get_views(database_id=1)
+            except ResponseCodeError:
+                pass
+
     def test_get_view_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = View(id=3,
@@ -73,7 +84,7 @@ class ViewUnitTest(unittest.TestCase):
             response = RestClient().get_view(database_id=1, view_id=3)
             self.assertEqual(exp, response)
 
-    def test_get_view_not_allowed_fails(self):
+    def test_get_view_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/view/3', status_code=403)
@@ -83,7 +94,7 @@ class ViewUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_get_views_not_found_fails(self):
+    def test_get_view_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/view/3', status_code=404)
@@ -93,6 +104,78 @@ class ViewUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
+    def test_get_view_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view/3', status_code=202)
+            # test
+            try:
+                response = RestClient().get_view(database_id=1, view_id=3)
+            except ResponseCodeError:
+                pass
+
+    def test_update_view_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            exp = ViewBrief(id=1,
+                            name="Data",
+                            internal_name="data",
+                            database_id=1,
+                            initial_view=False,
+                            query="SELECT id FROM mytable WHERE deg > 0",
+                            query_hash="94c74728b11a690e51d64719868824735f0817b7",
+                            owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16',
+                            is_public=False,
+                            is_schema_public=False)
+            # mock
+            mock.put('/api/database/1/view/1', json=exp.model_dump(), status_code=202)
+            # test
+            response = RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1,
+                                                                              is_public=False, is_schema_public=False)
+            self.assertEqual(exp, response)
+
+    def test_update_view_403_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/view/1', status_code=403)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False,
+                                                                       is_schema_public=False)
+            except ForbiddenError:
+                pass
+
+    def test_update_view_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/view/1', status_code=404)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False,
+                                                                       is_schema_public=False)
+            except NotExistsError:
+                pass
+
+    def test_update_view_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/view/1', status_code=200)
+            # test
+            try:
+                RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False,
+                                                                       is_schema_public=False)
+            except ResponseCodeError:
+                pass
+
+    def test_update_view_anonymous_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.put('/api/database/1/view/1', status_code=403)
+            # test
+            try:
+                RestClient().update_view(database_id=1, view_id=1, is_public=False, is_schema_public=False)
+            except AuthenticationError:
+                pass
+
     def test_create_view_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = View(id=3,
@@ -121,7 +204,7 @@ class ViewUnitTest(unittest.TestCase):
                                           query="SELECT id FROM mytable WHERE deg > 0")
             self.assertEqual(exp, response)
 
-    def test_create_view_malformed_fails(self):
+    def test_create_view_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/view', status_code=400)
@@ -133,7 +216,7 @@ class ViewUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_create_view_not_allowed_fails(self):
+    def test_create_view_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/view', status_code=403)
@@ -145,7 +228,7 @@ class ViewUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_create_view_not_found_fails(self):
+    def test_create_view_404_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/view', status_code=404)
@@ -157,7 +240,55 @@ class ViewUnitTest(unittest.TestCase):
             except NotExistsError:
                 pass
 
-    def test_create_view_not_auth_fails(self):
+    def test_create_view_423_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/view', status_code=423)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True,
+                                              query="SELECT id FROM mytable WHERE deg > 0")
+            except ExternalSystemError:
+                pass
+
+    def test_create_view_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/view', status_code=502)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True,
+                                              query="SELECT id FROM mytable WHERE deg > 0")
+            except ServiceConnectionError:
+                pass
+
+    def test_create_view_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/view', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True,
+                                              query="SELECT id FROM mytable WHERE deg > 0")
+            except ServiceError:
+                pass
+
+    def test_create_view_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('/api/database/1/view', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True,
+                                              query="SELECT id FROM mytable WHERE deg > 0")
+            except ResponseCodeError:
+                pass
+
+    def test_create_view_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.post('/api/database/1/view', status_code=404)
@@ -176,7 +307,7 @@ class ViewUnitTest(unittest.TestCase):
             client = RestClient(username="a", password="b")
             client.delete_view(database_id=1, view_id=3)
 
-    def test_delete_view_malformed_fails(self):
+    def test_delete_view_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/view/3', status_code=400)
@@ -187,7 +318,7 @@ class ViewUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_delete_view_not_allowed_fails(self):
+    def test_delete_view_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/view/3', status_code=403)
@@ -198,7 +329,62 @@ class ViewUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
-    def test_delete_view_not_auth_fails(self):
+    def test_delete_view_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/view/3', status_code=404)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_view(database_id=1, view_id=3)
+            except NotExistsError:
+                pass
+
+    def test_delete_view_423_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/view/3', status_code=423)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_view(database_id=1, view_id=3)
+            except ExternalSystemError:
+                pass
+
+    def test_delete_view_502_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/view/3', status_code=502)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_view(database_id=1, view_id=3)
+            except ServiceConnectionError:
+                pass
+
+    def test_delete_view_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/view/3', status_code=503)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_view(database_id=1, view_id=3)
+            except ServiceError:
+                pass
+
+    def test_delete_view_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.delete('/api/database/1/view/3', status_code=200)
+            # test
+            try:
+                client = RestClient(username="a", password="b")
+                client.delete_view(database_id=1, view_id=3)
+            except ResponseCodeError:
+                pass
+
+    def test_delete_view_anonymous_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.delete('/api/database/1/view/3', status_code=403)
@@ -229,7 +415,7 @@ class ViewUnitTest(unittest.TestCase):
             self.assertEqual(df.shape, response.shape)
             self.assertTrue(DataFrame.equals(df, response))
 
-    def test_get_view_data_malformed_fails(self):
+    def test_get_view_data_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/view/3/data', status_code=400)
@@ -239,7 +425,7 @@ class ViewUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_get_view_data_not_allowed_fails(self):
+    def test_get_view_data_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.get('/api/database/1/view/3/data', status_code=403)
@@ -249,6 +435,46 @@ class ViewUnitTest(unittest.TestCase):
             except ForbiddenError:
                 pass
 
+    def test_get_view_data_404_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view/3/data', status_code=404)
+            # test
+            try:
+                response = RestClient().get_view_data(database_id=1, view_id=3)
+            except NotExistsError:
+                pass
+
+    def test_get_view_data_409_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view/3/data', status_code=409)
+            # test
+            try:
+                response = RestClient().get_view_data(database_id=1, view_id=3)
+            except ExternalSystemError:
+                pass
+
+    def test_get_view_data_503_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view/3/data', status_code=503)
+            # test
+            try:
+                response = RestClient().get_view_data(database_id=1, view_id=3)
+            except ServiceError:
+                pass
+
+    def test_get_view_data_unknown_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.get('/api/database/1/view/3/data', status_code=202)
+            # test
+            try:
+                response = RestClient().get_view_data(database_id=1, view_id=3)
+            except ResponseCodeError:
+                pass
+
     def test_get_view_data_count_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = 844737
@@ -258,7 +484,7 @@ class ViewUnitTest(unittest.TestCase):
             response = RestClient().get_view_data_count(database_id=1, view_id=3)
             self.assertEqual(exp, response)
 
-    def test_get_view_data_count_malformed_fails(self):
+    def test_get_view_data_count_400_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/view/3/data', status_code=400)
@@ -268,7 +494,7 @@ class ViewUnitTest(unittest.TestCase):
             except MalformedError:
                 pass
 
-    def test_get_view_data_count_not_allowed_fails(self):
+    def test_get_view_data_count_403_fails(self):
         with requests_mock.Mocker() as mock:
             # mock
             mock.head('/api/database/1/view/3/data', status_code=403)