Repository: clouditor/clouditor
Pull request #472 (2022-01-28 14:59:28)

Separate DB access with interface
- [x] #467 

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> Merging [#472](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (a7a1501) into [main](https://codecov.io/gh/clouditor/clouditor/commit/e4bdd8398a1d315decb3bef8e7bcdda728e63d18?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (e4bdd83) will **increase** coverage by `0.14%`.
> The diff coverage is `71.68%`.

[![Impacted file tree graph](https://codecov.io/gh/clouditor/clouditor/pull/472/graphs/tree.svg?width=650&height=150&src=pr&token=syrXsw5LTX&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)

```diff
@@            Coverage Diff             @@
##             main     #472      +/-   ##
==========================================
+ Coverage   76.89%   77.04%   +0.14%     
==========================================
  Files          44       45       +1     
  Lines        3869     3920      +51     
==========================================
+ Hits         2975     3020      +45     
- Misses        754      760       +6     
  Partials      140      140              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `77.04% <71.68%> (+0.14%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [cli/commands/login/login.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-Y2xpL2NvbW1hbmRzL2xvZ2luL2xvZ2luLmdv) | `72.34% <ø> (ø)` | |
| [cmd/engine/engine.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-Y21kL2VuZ2luZS9lbmdpbmUuZ28=) | `23.37% <0.00%> (-0.16%)` | :arrow_down: |
| [service/auth/auth.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9hdXRoL2F1dGguZ28=) | `76.72% <66.66%> (-4.70%)` | :arrow_down: |
| [persistence/db.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-cGVyc2lzdGVuY2UvZGIuZ28=) | `61.29% <70.21%> (+24.92%)` | :arrow_up: |
| [service/orchestrator/cloud\_service.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvY2xvdWRfc2VydmljZS5nbw==) | `77.17% <75.00%> (+11.95%)` | :arrow_up: |
| [api/orchestrator/orchestrator.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-YXBpL29yY2hlc3RyYXRvci9vcmNoZXN0cmF0b3IuZ28=) | `100.00% <100.00%> (ø)` | |
| [service/orchestrator/orchestrator.go](https://codecov.io/gh/clouditor/clouditor/pull/472/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3Ivb3JjaGVzdHJhdG9yLmdv) | `84.24% <100.00%> (-0.11%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Last update [e4bdd83...a7a1501](https://codecov.io/gh/clouditor/clouditor/pull/472?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor).

lebogg: Some comments (see also TODOs in code):
- For RPC, should we check if reuest is nil? Can it even be nil?
- DeepSource Bug about coupling: Should we do 2 separate methods for init DBs or even 2 interface implementations for gorm?
- Add DB initiatlization for each service in the respective `NewService` as part of `opts`?
- Adding tests to auth?
- In `UpdateXXX` RPCs, should we add ID as separate field as in `UpdateCloudService`. Is this some convention? For the REST case we can add it via option anyway - I would omit it.
- I added validation methods for requests if needed in the respective API file: Maybe we can introduce this as a convention within our project for RPCs?
lebogg: TODOs:
- Improve test coverage
- Look at auth testing
lebogg: Still some work (mainly testing) to do. In particular, testing the functionality of Update/Save of GORM.

Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 499573a3..fd99e282 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -9,12 +9,14 @@
         "bufnet",
         "Clouditor",
         "Debugf",
+        "dialector",
         "emptypb",
         "Equalf",
         "gocron",
         "GOPATH",
         "gorm",
         "Infof",
+        "inmemory",
         "Kube",
         "Kubernetes",
         "oxisto",
diff --git a/persistence/db.go b/api/orchestrator/orchestrator.go
similarity index 51%
rename from persistence/db.go
rename to api/orchestrator/orchestrator.go
index 30246efb..93860ae0 100644
--- a/persistence/db.go
+++ b/api/orchestrator/orchestrator.go
@@ -1,4 +1,4 @@
-// Copyright 2016-2020 Fraunhofer AISEC
+// Copyright 2022 Fraunhofer AISEC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -23,53 +23,15 @@
 //
 // This file is part of Clouditor Community Edition.
 
-package persistence
+package orchestrator
 
 import (
-	"fmt"
-
-	"clouditor.io/clouditor/api/auth"
-	"clouditor.io/clouditor/api/orchestrator"
-	"github.com/sirupsen/logrus"
-	"gorm.io/driver/postgres"
-	"gorm.io/driver/sqlite"
-	"gorm.io/gorm"
+	"errors"
 )
 
-var log *logrus.Entry
-var db *gorm.DB
-
-func init() {
-	log = logrus.WithField("component", "db")
-}
-
-func InitDB(inMemory bool, host string, port int16) (err error) {
-	if inMemory {
-		if db, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{}); err != nil {
-			return err
-		}
-
-		log.Println("Using in-memory DB")
-	} else {
-		if db, err = gorm.Open(postgres.Open(fmt.Sprintf("postgres://postgres@%s:%d/postgres?sslmode=disable", host, port)), &gorm.Config{}); err != nil {
-			return err
-		}
-
-		log.Printf("Using postgres DB @ %s", host)
-	}
-
-	if err = db.AutoMigrate(&auth.User{}); err != nil {
-		return fmt.Errorf("error during auto-migration: %w", err)
-	}
-
-	if err = db.AutoMigrate(&orchestrator.CloudService{}); err != nil {
-		return fmt.Errorf("error during auto-migration: %w", err)
-	}
-
-	return nil
-}
-
-// GetDatabase returns the database
-func GetDatabase() *gorm.DB {
-	return db
-}
+var (
+	ErrRequestIsNil  = errors.New("request is empty")
+	ErrServiceIsNil  = errors.New("service is empty")
+	ErrNameIsMissing = errors.New("service name is empty")
+	ErrIDIsMissing   = errors.New("service ID is empty")
+)
diff --git a/cli/commands/cloud/cloud.go b/cli/commands/cloud/cloud.go
index 39c1a6b7..85c13c46 100644
--- a/cli/commands/cloud/cloud.go
+++ b/cli/commands/cloud/cloud.go
@@ -108,8 +108,8 @@ func NewListCloudServicesCommand() *cobra.Command {
 	return cmd
 }
 
-// NewGetCloudServiceComand returns a cobra command for the `get` subcommand
-func NewGetCloudServiceComand() *cobra.Command {
+// NewGetCloudServiceCommand returns a cobra command for the `get` subcommand
+func NewGetCloudServiceCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "get [id]",
 		Short: "Retrieves a target cloud service by its ID",
@@ -277,7 +277,7 @@ func AddCommands(cmd *cobra.Command) {
 	cmd.AddCommand(
 		NewRegisterCloudServiceCommand(),
 		NewListCloudServicesCommand(),
-		NewGetCloudServiceComand(),
+		NewGetCloudServiceCommand(),
 		NewUpdateCloudServiceCommand(),
 		NewRemoveCloudServiceComand(),
 		NewGetMetricConfigurationCommand(),
diff --git a/cli/commands/login/login.go b/cli/commands/login/login.go
index 13fbcea7..ce5423d6 100644
--- a/cli/commands/login/login.go
+++ b/cli/commands/login/login.go
@@ -37,7 +37,6 @@ import (
 
 const (
 	// URLFlag is the viper flag for the server url
-	// TODO(oxisto): Unused constant?
 	URLFlag = "url"
 )
 
diff --git a/cmd/engine/engine.go b/cmd/engine/engine.go
index 3d12f276..10f8d3f9 100644
--- a/cmd/engine/engine.go
+++ b/cmd/engine/engine.go
@@ -26,6 +26,9 @@
 package main
 
 import (
+	"clouditor.io/clouditor/persistence"
+	"clouditor.io/clouditor/persistence/gorm"
+	"clouditor.io/clouditor/persistence/inmemory"
 	"context"
 	"errors"
 	"fmt"
@@ -47,7 +50,6 @@ import (
 	"clouditor.io/clouditor/api/evidence"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli"
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/rest"
 	"clouditor.io/clouditor/service"
 
@@ -97,14 +99,17 @@ const (
 	EnvPrefix = "CLOUDITOR"
 )
 
-var server *grpc.Server
-var authService *service_auth.Service
-var discoveryService discovery.DiscoveryServer
-var orchestratorService *service_orchestrator.Service
-var assessmentService assessment.AssessmentServer
-var evidenceStoreService evidence.EvidenceStoreServer
+var (
+	server               *grpc.Server
+	authService          *service_auth.Service
+	discoveryService     discovery.DiscoveryServer
+	orchestratorService  *service_orchestrator.Service
+	assessmentService    assessment.AssessmentServer
+	evidenceStoreService evidence.EvidenceStoreServer
+	db                   persistence.Storage
 
-var log *logrus.Entry
+	log *logrus.Entry
+)
 
 var engineCmd = &cobra.Command{
 	Use:   "engine",
@@ -182,13 +187,19 @@ func doCmd(_ *cobra.Command, _ []string) (err error) {
   \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
  `)
 
-	if err = persistence.InitDB(viper.GetBool(DBInMemoryFlag),
-		viper.GetString(DBHostFlag),
-		int16(viper.GetInt(DBPortFlag))); err != nil {
-		return fmt.Errorf("could not initialize DB: %w", err)
+	if viper.GetBool(DBInMemoryFlag) {
+		db, err = inmemory.NewStorage()
+	} else {
+		db, err = gorm.NewStorage(gorm.WithPostgres(viper.GetString(DBHostFlag), int16(viper.GetInt(DBPortFlag))))
+	}
+	if err != nil {
+		// We could also just log the error and forward db = nil which will result in inmemory storages
+		// for each service below
+		return fmt.Errorf("could not create storage: %w", err)
 	}
 
 	authService = service_auth.NewService(
+		service_auth.WithStorage(db),
 		service_auth.WithApiKeyPassword(viper.GetString(APIKeyPasswordFlag)),
 		service_auth.WithApiKeyPath(viper.GetString(APIKeyPathFlag)),
 		service_auth.WithApiKeySaveOnCreate(viper.GetBool(APIKeySaveOnCreateFlag)),
@@ -200,7 +211,7 @@ func doCmd(_ *cobra.Command, _ []string) (err error) {
 			viper.GetString(APIDefaultPasswordFlag),
 		),
 	)
-	orchestratorService = service_orchestrator.NewService()
+	orchestratorService = service_orchestrator.NewService(service_orchestrator.WithStorage(db))
 	assessmentService = service_assessment.NewService(
 		service_assessment.WithInternalAuthorizer(
 			service.DefaultInternalAuthorizerAddress,
@@ -219,7 +230,10 @@ func doCmd(_ *cobra.Command, _ []string) (err error) {
 	// evidenceStoreService.RegisterEvidenceHook(func(result *evidence.Evidence, err error) {})
 	// assessmentService.RegisterAssessmentResultHook(func(result *assessment.AssessmentResult, err error) {}
 
-	authService.CreateDefaultUser(viper.GetString(APIDefaultUserFlag), viper.GetString(APIDefaultPasswordFlag))
+	err = authService.CreateDefaultUser(viper.GetString(APIDefaultUserFlag), viper.GetString(APIDefaultPasswordFlag))
+	if err != nil {
+		log.Errorf("Could not create default user: %v", err)
+	}
 
 	if viper.GetBool(CreateDefaultTarget) {
 		_, err := orchestratorService.CreateDefaultTargetCloudService()
diff --git a/persistence/gorm/gorm.go b/persistence/gorm/gorm.go
new file mode 100644
index 00000000..16ffb6ab
--- /dev/null
+++ b/persistence/gorm/gorm.go
@@ -0,0 +1,149 @@
+// Copyright 2016-2022 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package gorm
+
+import (
+	"errors"
+	"fmt"
+
+	"clouditor.io/clouditor/api/auth"
+	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/persistence"
+	"github.com/sirupsen/logrus"
+	"gorm.io/driver/postgres"
+	"gorm.io/driver/sqlite"
+	"gorm.io/gorm"
+)
+
+var log *logrus.Entry
+
+type storage struct {
+	db *gorm.DB
+	// for options: (set default when not in opts)
+	dialector gorm.Dialector
+	config    gorm.Config
+}
+
+// StorageOption is a functional option type to configure the GORM storage. E.g. WithInMemory or WithPostgres
+type StorageOption func(*storage)
+
+// WithInMemory is an option to configure Storage to use an in memory DB
+func WithInMemory() StorageOption {
+	return func(s *storage) {
+		s.dialector = sqlite.Open(":memory:")
+	}
+}
+
+// WithPostgres is an option to configure Storage to use a Postgres DB
+func WithPostgres(host string, port int16) StorageOption {
+	return func(s *storage) {
+		s.dialector = postgres.Open(fmt.Sprintf("postgres://postgres@%s:%d/postgres?sslmode=disable", host, port))
+	}
+}
+
+func init() {
+	log = logrus.WithField("component", "storage")
+}
+
+// NewStorage creates a new storage using GORM (which DB to use depends on the StorageOption)
+func NewStorage(opts ...StorageOption) (s persistence.Storage, err error) {
+	g := &storage{}
+
+	// Init storage
+	log.Println("Creating storage")
+	for _, o := range opts {
+		o(g)
+	}
+	if g.dialector == nil {
+		WithInMemory()(g)
+	}
+
+	g.db, err = gorm.Open(g.dialector, &g.config)
+	if err != nil {
+		return nil, err
+	}
+
+	// After successful DB initialization, migrate the schema
+	// Migrate User
+	if err = g.db.AutoMigrate(&auth.User{}); err != nil {
+		err = fmt.Errorf("error during auto-migration: %w", err)
+		return
+	}
+	// Migrate CloudService
+	if err = g.db.AutoMigrate(&orchestrator.CloudService{}); err != nil {
+		err = fmt.Errorf("error during auto-migration: %w", err)
+		return
+	}
+
+	s = g
+	return
+}
+
+func (s *storage) Create(r interface{}) error {
+	return s.db.Create(r).Error
+}
+
+func (s *storage) Get(r interface{}, conds ...interface{}) (err error) {
+	err = s.db.First(r, conds...).Error
+	// if record is not found, use the error message defined in the persistence package
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		err = persistence.ErrRecordNotFound
+	}
+	return
+}
+
+func (s *storage) List(r interface{}, conds ...interface{}) error {
+	return s.db.Find(r, conds...).Error
+}
+
+func (s *storage) Count(r interface{}, conds ...interface{}) (count int64, err error) {
+	err = s.db.Model(r).Where(conds).Count(&count).Error
+	return
+}
+
+func (s *storage) Save(r interface{}, conds ...interface{}) error {
+	return s.db.Where(conds).Save(r).Error
+}
+
+// Update will update the record with non-zero fields. Note that to get the entire updated record you have to call Get
+func (s *storage) Update(r interface{}, query interface{}, args ...interface{}) error {
+	return s.db.Model(r).Where(query, args).Updates(r).Error
+}
+
+// Delete deletes record with given id. If no record was found, returns ErrRecordNotFound
+func (s *storage) Delete(r interface{}, conds ...interface{}) error {
+	// Remove record r with given ID
+	tx := s.db.Delete(r, conds...)
+	if err := tx.Error; err != nil { // db error
+		return err
+	}
+	// No record with given ID found
+	if tx.RowsAffected == 0 {
+		return persistence.ErrRecordNotFound
+	}
+
+	return nil
+}
diff --git a/persistence/inmemory/inmemory.go b/persistence/inmemory/inmemory.go
new file mode 100644
index 00000000..dc308f9c
--- /dev/null
+++ b/persistence/inmemory/inmemory.go
@@ -0,0 +1,13 @@
+package inmemory
+
+import (
+	"clouditor.io/clouditor/persistence"
+	"clouditor.io/clouditor/persistence/gorm"
+)
+
+// NewStorage creates a new in-memory storage. For now this uses the gorm provider
+// with the gorm.WithInMemory. In the future we want to supply our own independent
+// implementation.
+func NewStorage() (persistence.Storage, error) {
+	return gorm.NewStorage(gorm.WithInMemory())
+}
diff --git a/persistence/persistence.go b/persistence/persistence.go
new file mode 100644
index 00000000..cf944520
--- /dev/null
+++ b/persistence/persistence.go
@@ -0,0 +1,62 @@
+// Copyright 2016-2022 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package persistence
+
+import (
+	"errors"
+)
+
+var (
+	ErrRecordNotFound = errors.New("record not in the database")
+)
+
+// Storage comprises a database interface
+// TODO(all): I think with generics we could get sth. like `[T]Get(r T, id string) T` which is maybe more intuitive
+// then changing the parameter? Maybe its only my impression
+type Storage interface {
+
+	// Create creates a new object and put it into the DB
+	Create(r interface{}) error
+
+	// Save updates the record r (workaround with conds needed that e.g. user has no specific ID, and we can not touch
+	// the generated (gRPC) code s.t. user.username has primary key annotation)
+	Save(r interface{}, conds ...interface{}) error
+
+	// Update updates the record with given id of the DB with non-zero values in r
+	Update(r interface{}, query interface{}, args ...interface{}) error
+
+	// Get gets the record which meet the given conditions
+	Get(r interface{}, conds ...interface{}) error
+
+	// List lists all records in database which meet the (optionally) given conditions
+	List(r interface{}, conds ...interface{}) error
+
+	// Count counts the number of records which meet the (optionally) given conditions
+	Count(r interface{}, conds ...interface{}) (int64, error)
+
+	// Delete deletes the record with given id of the DB
+	Delete(r interface{}, conds ...interface{}) error
+}
diff --git a/service/auth.go b/service/auth.go
index 9feff038..36303458 100644
--- a/service/auth.go
+++ b/service/auth.go
@@ -171,7 +171,10 @@ func StartDedicatedAuthServer(address string, opts ...service_auth.ServiceOption
 	}
 
 	authService = service_auth.NewService(opts...)
-	authService.CreateDefaultUser("clouditor", "clouditor")
+	err = authService.CreateDefaultUser("clouditor", "clouditor")
+	if err != nil {
+		return nil, nil, nil, fmt.Errorf("could not create default user: %w", err)
+	}
 
 	authConfig := ConfigureAuth(WithPublicKey(authService.GetPublicKey()))
 
diff --git a/service/auth/auth.go b/service/auth/auth.go
index 394698b9..a13d0ee1 100644
--- a/service/auth/auth.go
+++ b/service/auth/auth.go
@@ -40,6 +40,8 @@ import (
 	"strings"
 	"time"
 
+	"clouditor.io/clouditor/persistence/inmemory"
+
 	"clouditor.io/clouditor/api/auth"
 	"clouditor.io/clouditor/persistence"
 	argon2 "github.com/alexedwards/argon2id"
@@ -48,7 +50,6 @@ import (
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/timestamppb"
-	"gorm.io/gorm"
 )
 
 const (
@@ -87,6 +88,8 @@ type Service struct {
 	}
 
 	apiKey *ecdsa.PrivateKey
+
+	storage persistence.Storage
 }
 
 // UserClaims extend jwt.StandardClaims with more detailed claims about a user
@@ -120,6 +123,13 @@ func WithApiKeySaveOnCreate(saveOnCreate bool) ServiceOption {
 	}
 }
 
+// WithStorage is an option to set the storage. If not set, NewService will use inmemory storage.
+func WithStorage(storage persistence.Storage) ServiceOption {
+	return func(s *Service) {
+		s.storage = storage
+	}
+}
+
 // NewService creates a new Service representing an authentication service.
 func NewService(opts ...ServiceOption) *Service {
 	var err error
@@ -141,6 +151,14 @@ func NewService(opts ...ServiceOption) *Service {
 		o(s)
 	}
 
+	// Default to an in-memory storage, if nothing was explicitly set
+	if s.storage == nil {
+		s.storage, err = inmemory.NewStorage()
+		if err != nil {
+			log.Errorf("Could not initialize in-memory storage: %v", err)
+		}
+	}
+
 	// Load the key
 	s.apiKey, err = loadApiKey(s.config.keyPath, []byte(s.config.keyPassword))
 
@@ -249,9 +267,9 @@ func (s *Service) recoverFromLoadApiKeyError(err error, defaultPath bool) {
 // Login handles a login request
 func (s Service) Login(_ context.Context, request *auth.LoginRequest) (response *auth.LoginResponse, err error) {
 	var result bool
-	var user *auth.User
+	var u *auth.User
 
-	if result, user, err = verifyLogin(request); err != nil {
+	if result, u, err = s.verifyLogin(request); err != nil {
 		// a returned error means, something has gone wrong
 		return nil, status.Errorf(codes.Internal, "error during login: %v", err)
 	}
@@ -265,7 +283,7 @@ func (s Service) Login(_ context.Context, request *auth.LoginRequest) (response
 
 	var expiry = time.Now().Add(time.Hour * 24)
 
-	if token, err = s.issueToken(user.Username, user.FullName, user.Email, expiry); err != nil {
+	if token, err = s.issueToken(u.Username, u.FullName, u.Email, expiry); err != nil {
 		return nil, status.Errorf(codes.Internal, "token issue failed: %v", err)
 	}
 
@@ -298,16 +316,14 @@ func (s Service) ListPublicKeys(_ context.Context, _ *auth.ListPublicKeysRequest
 // It will return an error in err only if a database error or something else is occurred, this should be
 // returned to the user as an internal server error. For security reasons, if authentication failed, only
 // the result will be set to false, but no detailed error will be returned to the user.
-func verifyLogin(request *auth.LoginRequest) (result bool, user *auth.User, err error) {
+func (s *Service) verifyLogin(request *auth.LoginRequest) (result bool, user *auth.User, err error) {
 	var match bool
 
-	db := persistence.GetDatabase()
-
 	user = new(auth.User)
 
-	err = db.Where("username = ?", request.Username).First(user).Error
+	err = s.storage.Get(user, "username = ?", request.Username)
 
-	if errors.Is(err, gorm.ErrRecordNotFound) {
+	if errors.Is(err, persistence.ErrRecordNotFound) {
 		// user not found, set result to false, but hide the error
 		return false, nil, nil
 	} else if err != nil {
@@ -341,25 +357,33 @@ func hashPassword(password string) (string, error) {
 }
 
 // CreateDefaultUser creates a default user in the database
-func (Service) CreateDefaultUser(username string, password string) {
-	db := persistence.GetDatabase()
-
-	var count int64
-	db.Model(&auth.User{}).Count(&count)
+func (s *Service) CreateDefaultUser(username string, password string) error {
+	var (
+		err   error
+		count int64
+	)
 
-	if count == 0 {
+	count, err = s.storage.Count(&auth.User{})
+	if err != nil {
+		return fmt.Errorf("db error: %w", err)
+	} else if count == 0 {
 		hash, _ := hashPassword(password)
 
-		user := auth.User{
+		u := auth.User{
 			Username: username,
 			FullName: username,
 			Password: string(hash),
 		}
 
-		log.Infof("Creating default user %s\n", user.Username)
+		log.Infof("Creating default user %s\n", u.Username)
 
-		db.Create(&user)
+		err = s.storage.Create(&u)
+		if err != nil {
+			return fmt.Errorf("db error: %w", err)
+		}
 	}
+
+	return nil
 }
 
 // issueToken issues a JWT-based token
@@ -386,9 +410,9 @@ func (s Service) GetPublicKey() *ecdsa.PublicKey {
 
 // AuthFuncOverride implements the ServiceAuthFuncOverride interface to override the AuthFunc for this service.
 // The reason is to actually disable authentication checking in the auth service, because functions such as login
-// need to be publically available.
+// need to be publicly available.
 func (Service) AuthFuncOverride(ctx context.Context, _ string) (context.Context, error) {
-	// No authentication needed for login functions, otherwise we could not login
+	// No authentication needed for login functions, otherwise we could not log in
 	return ctx, nil
 }
 
@@ -396,20 +420,20 @@ func (Service) AuthFuncOverride(ctx context.Context, _ string) (context.Context,
 // of the user
 func expandPath(path string) (out string, err error) {
 	var (
-		usr *user.User
+		u *user.User
 	)
 
 	// Fetch the current user home directory
-	usr, err = user.Current()
+	u, err = user.Current()
 	if err != nil {
 		return path, fmt.Errorf("could not find retrieve current user: %w", err)
 	}
 
 	if path == "~" {
-		return usr.HomeDir, nil
+		return u.HomeDir, nil
 	} else if strings.HasPrefix(path, "~") {
 		// We only allow ~ at the beginning of the path
-		return filepath.Join(usr.HomeDir, path[2:]), nil
+		return filepath.Join(u.HomeDir, path[2:]), nil
 	}
 
 	return path, nil
diff --git a/service/discovery/discovery.go b/service/discovery/discovery.go
index 7b91ac41..d1f6ae86 100644
--- a/service/discovery/discovery.go
+++ b/service/discovery/discovery.go
@@ -84,14 +84,14 @@ func init() {
 }
 
 const (
-	// DefaultEvidenceStoreAddress specifies the default gRPC address of the assessment service.
+	// DefaultAssessmentAddress specifies the default gRPC address of the assessment service.
 	DefaultAssessmentAddress = "localhost:9090"
 )
 
 // ServiceOption is a functional option type to configure the discovery service.
 type ServiceOption func(*Service)
 
-// WithEvidenceStoreAddress is an option to configure the assessment service gRPC address.
+// WithAssessmentAddress is an option to configure the assessment service gRPC address.
 func WithAssessmentAddress(address string) ServiceOption {
 	return func(s *Service) {
 		s.assessmentAddress = address
diff --git a/service/orchestrator/cloud_service.go b/service/orchestrator/cloud_service.go
index e67919be..449be466 100644
--- a/service/orchestrator/cloud_service.go
+++ b/service/orchestrator/cloud_service.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Fraunhofer AISEC
+// Copyright 2021-2022 Fraunhofer AISEC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -28,26 +28,32 @@ package orchestrator
 import (
 	"context"
 	"errors"
+	"fmt"
+
+	"clouditor.io/clouditor/persistence"
 
 	"clouditor.io/clouditor/api/orchestrator"
 	"github.com/google/uuid"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/emptypb"
-	"gorm.io/gorm"
 )
 
-const DefaultTargetCloudServiceId = "00000000-0000-0000-000000000000"
-const DefaultTargetCloudServiceName = "default"
-const DefaultTargetCloudServiceDescription = "The default target cloud service"
+const (
+	DefaultTargetCloudServiceId          = "00000000-0000-0000-000000000000"
+	DefaultTargetCloudServiceName        = "default"
+	DefaultTargetCloudServiceDescription = "The default target cloud service"
+)
 
 func (s *Service) RegisterCloudService(_ context.Context, req *orchestrator.RegisterCloudServiceRequest) (service *orchestrator.CloudService, err error) {
-	if req == nil || req.Service == nil {
-		return nil, status.Errorf(codes.InvalidArgument, "service is empty")
+	if req == nil {
+		return nil, status.Errorf(codes.InvalidArgument, orchestrator.ErrRequestIsNil.Error())
+	}
+	if req.Service == nil {
+		return nil, status.Errorf(codes.InvalidArgument, orchestrator.ErrServiceIsNil.Error())
 	}
-
 	if req.Service.Name == "" {
-		return nil, status.Errorf(codes.InvalidArgument, "service name is empty")
+		return nil, status.Errorf(codes.InvalidArgument, orchestrator.ErrNameIsMissing.Error())
 	}
 
 	service = new(orchestrator.CloudService)
@@ -58,16 +64,20 @@ func (s *Service) RegisterCloudService(_ context.Context, req *orchestrator.Regi
 	service.Description = req.Service.Description
 
 	// Persist the service in our database
-	s.db.Create(&service)
+	err = s.storage.Create(service)
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "could not add cloud service to the database: %v", err)
+	}
 
 	return
 }
 
+// ListCloudServices implements method for OrchestratorServer interface for listing all cloud services
 func (s *Service) ListCloudServices(_ context.Context, _ *orchestrator.ListCloudServicesRequest) (response *orchestrator.ListCloudServicesResponse, err error) {
 	response = new(orchestrator.ListCloudServicesResponse)
 	response.Services = make([]*orchestrator.CloudService, 0)
 
-	err = s.db.Find(&response.Services).Error
+	err = s.storage.List(&response.Services)
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %s", err)
 	}
@@ -75,14 +85,18 @@ func (s *Service) ListCloudServices(_ context.Context, _ *orchestrator.ListCloud
 	return response, nil
 }
 
+// GetCloudService implements method for OrchestratorServer interface for getting a cloud service with provided id
 func (s *Service) GetCloudService(_ context.Context, req *orchestrator.GetCloudServiceRequest) (response *orchestrator.CloudService, err error) {
-	if req == nil || req.ServiceId == "" {
-		return nil, status.Errorf(codes.InvalidArgument, "service id is empty")
+	if req == nil {
+		return nil, status.Errorf(codes.InvalidArgument, orchestrator.ErrRequestIsNil.Error())
+	}
+	if req.ServiceId == "" {
+		return nil, status.Errorf(codes.InvalidArgument, orchestrator.ErrIDIsMissing.Error())
 	}
 
 	response = new(orchestrator.CloudService)
-	err = s.db.First(&response, "Id = ?", req.ServiceId).Error
-	if errors.Is(err, gorm.ErrRecordNotFound) {
+	err = s.storage.Get(response, "Id = ?", req.ServiceId)
+	if errors.Is(err, persistence.ErrRecordNotFound) {
 		return nil, status.Errorf(codes.NotFound, "service not found")
 	} else if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %s", err)
@@ -91,6 +105,7 @@ func (s *Service) GetCloudService(_ context.Context, req *orchestrator.GetCloudS
 	return response, nil
 }
 
+// UpdateCloudService implements method for OrchestratorServer interface for updating a cloud service
 func (s *Service) UpdateCloudService(_ context.Context, req *orchestrator.UpdateCloudServiceRequest) (response *orchestrator.CloudService, err error) {
 	if req.Service == nil {
 		return nil, status.Errorf(codes.InvalidArgument, "service is empty")
@@ -100,32 +115,34 @@ func (s *Service) UpdateCloudService(_ context.Context, req *orchestrator.Update
 		return nil, status.Errorf(codes.InvalidArgument, "service id is empty")
 	}
 
-	var count int64
-	err = s.db.Model(&orchestrator.CloudService{}).Count(&count).Error
+	count, err := s.storage.Count(req.Service, "Id = ?", req.ServiceId)
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %s", err)
 	}
-
 	if count == 0 {
 		return nil, status.Error(codes.NotFound, "service not found")
 	}
 
-	req.Service.Id = req.ServiceId
-	err = s.db.Save(&req.Service).Error
+	// Add id to response because otherwise it will overwrite ID with empty string
+	response = req.Service
+	response.Id = req.ServiceId
+
+	// Since UpdateCloudService is a PUT method, we use storage.Save
+	err = s.storage.Save(response, "Id = ?", req.ServiceId)
 	if err != nil {
-		return nil, status.Errorf(codes.Internal, "database error: %s", err)
+		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
-
-	return req.Service, nil
+	return
 }
 
+// RemoveCloudService implements method for OrchestratorServer interface for removing a cloud service
 func (s *Service) RemoveCloudService(_ context.Context, req *orchestrator.RemoveCloudServiceRequest) (response *emptypb.Empty, err error) {
 	if req.ServiceId == "" {
 		return nil, status.Errorf(codes.InvalidArgument, "service id is empty")
 	}
 
-	err = s.db.Delete(&orchestrator.CloudService{Id: req.ServiceId}).Error
-	if errors.Is(err, gorm.ErrRecordNotFound) {
+	err = s.storage.Delete(&orchestrator.CloudService{Id: req.ServiceId})
+	if errors.Is(err, persistence.ErrRecordNotFound) {
 		return nil, status.Errorf(codes.NotFound, "service not found")
 	} else if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %s", err)
@@ -139,8 +156,12 @@ func (s *Service) RemoveCloudService(_ context.Context, req *orchestrator.Remove
 //
 // If a new target cloud service was created, it will be returned.
 func (s *Service) CreateDefaultTargetCloudService() (service *orchestrator.CloudService, err error) {
-	var count int64
-	s.db.Model(&orchestrator.CloudService{}).Count(&count)
+	log.Infof("Trying to create new default target cloud service...")
+
+	count, err := s.storage.Count(service)
+	if err != nil {
+		return nil, fmt.Errorf("storage error: %w", err)
+	}
 
 	if count == 0 {
 		// Create a default target cloud service
@@ -151,14 +172,13 @@ func (s *Service) CreateDefaultTargetCloudService() (service *orchestrator.Cloud
 				Description: DefaultTargetCloudServiceDescription,
 			}
 
-		// Save it directly into the database, so that we can set the ID
-		err = s.db.Create(&service).Error
-
+		// Save it in the database
+		err = s.storage.Create(service)
 		if err != nil {
-			log.Infof("Created new default target cloud service %s", service.Id)
+			return nil, fmt.Errorf("storage error: %w", err)
+		} else {
+			log.Infof("Created new default target cloud service: %s", service.Id)
 		}
-
-		return service, err
 	}
 
 	return
diff --git a/service/orchestrator/orchestrator.go b/service/orchestrator/orchestrator.go
index 5f8f1d64..7894b090 100644
--- a/service/orchestrator/orchestrator.go
+++ b/service/orchestrator/orchestrator.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Fraunhofer AISEC
+// Copyright 2016-2022 Fraunhofer AISEC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -34,6 +34,8 @@ import (
 	"io/ioutil"
 	"sync"
 
+	"clouditor.io/clouditor/persistence/inmemory"
+
 	"google.golang.org/protobuf/types/known/emptypb"
 
 	"clouditor.io/clouditor/api/assessment"
@@ -42,7 +44,6 @@ import (
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
-	"gorm.io/gorm"
 )
 
 //go:embed metrics.json
@@ -70,7 +71,7 @@ type Service struct {
 	// mu is used for (un)locking result hook calls
 	mu sync.Mutex
 
-	db *gorm.DB
+	storage persistence.Storage
 
 	metricsFile string
 }
@@ -89,7 +90,16 @@ func WithMetricsFile(file string) ServiceOption {
 	}
 }
 
+// WithStorage is an option to set the storage. If not set, NewService will use inmemory storage.
+func WithStorage(storage persistence.Storage) ServiceOption {
+	return func(s *Service) {
+		s.storage = storage
+	}
+}
+
+// NewService creates a new Orchestrator service
 func NewService(opts ...ServiceOption) *Service {
+	var err error
 	s := Service{
 		results:              make(map[string]*assessment.AssessmentResult),
 		metricConfigurations: make(map[string]map[string]*assessment.MetricConfiguration),
@@ -101,7 +111,15 @@ func NewService(opts ...ServiceOption) *Service {
 		o(&s)
 	}
 
-	if err := LoadMetrics(s.metricsFile); err != nil {
+	// Default to an in-memory storage, if nothing was explicitly set
+	if s.storage == nil {
+		s.storage, err = inmemory.NewStorage()
+		if err != nil {
+			log.Errorf("Could not initialize the storage: %v", err)
+		}
+	}
+
+	if err = LoadMetrics(s.metricsFile); err != nil {
 		log.Errorf("Could not load embedded metrics. Will continue with empty metric list: %v", err)
 	}
 
@@ -132,8 +150,6 @@ func NewService(opts ...ServiceOption) *Service {
 		defaultMetricConfigurations[m.Id] = &config
 	}
 
-	s.db = persistence.GetDatabase()
-
 	return &s
 }
 
@@ -192,7 +208,6 @@ func (s *Service) StoreAssessmentResult(_ context.Context, req *orchestrator.Sto
 		return resp, status.Errorf(codes.InvalidArgument, "invalid req: %v", err)
 	}
 
-	// TODO(all): We do not check ID in Validate. Therefore, I assume that we have to set ID here?
 	s.results[req.Result.Id] = req.Result
 
 	go s.informHook(req.Result, nil)


Test patch:
diff --git a/cli/commands/assessmentresult/assessment_result_test.go b/cli/commands/assessmentresult/assessment_result_test.go
index 770e9f65..f89a558c 100644
--- a/cli/commands/assessmentresult/assessment_result_test.go
+++ b/cli/commands/assessmentresult/assessment_result_test.go
@@ -35,13 +35,12 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 
 	"github.com/spf13/viper"
@@ -52,14 +51,14 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
-var sock net.Listener
-var server *grpc.Server
-
 func TestMain(m *testing.M) {
 	var (
+		sock                net.Listener
+		server              *grpc.Server
+		orchestratorService *service_orchestrator.Service
+
 		err error
 		dir string
-		s   *service_orchestrator.Service
 	)
 
 	err = os.Chdir("../../../")
@@ -67,20 +66,15 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_orchestrator.NewService()
+	orchestratorService = service_orchestrator.NewService()
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
 	// Store an assessment result that output of CMD 'list' is not empty
-	orchestrator.RegisterOrchestratorServer(server, s)
-	_, err = s.StoreAssessmentResult(context.TODO(), &orchestrator.StoreAssessmentResultRequest{
+	orchestrator.RegisterOrchestratorServer(server, orchestratorService)
+	_, err = orchestratorService.StoreAssessmentResult(context.TODO(), &orchestrator.StoreAssessmentResultRequest{
 		Result: &assessment.AssessmentResult{
 			Id:         "11111111-1111-1111-1111-111111111111",
 			MetricId:   "assessmentResultMetricID",
diff --git a/cli/commands/cloud/cloud_test.go b/cli/commands/cloud/cloud_test.go
index d4f8a4a8..100453d5 100644
--- a/cli/commands/cloud/cloud_test.go
+++ b/cli/commands/cloud/cloud_test.go
@@ -34,12 +34,11 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 
 	"github.com/spf13/viper"
@@ -49,54 +48,8 @@ import (
 	"google.golang.org/protobuf/types/known/emptypb"
 )
 
-var sock net.Listener
-var server *grpc.Server
-var s *service_orchestrator.Service
-var target *orchestrator.CloudService
-
 func TestMain(m *testing.M) {
-	var (
-		err error
-		dir string
-	)
-
-	err = os.Chdir("../../../")
-	if err != nil {
-		panic(err)
-	}
-
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_orchestrator.NewService()
-
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
-	if err != nil {
-		panic(err)
-	}
-	orchestrator.RegisterOrchestratorServer(server, s)
-
-	target, err = s.CreateDefaultTargetCloudService()
-	if err != nil {
-		panic(err)
-	}
-
-	defer sock.Close()
-	defer server.Stop()
-
-	dir, err = ioutil.TempDir(os.TempDir(), ".clouditor")
-	if err != nil {
-		panic(err)
-	}
-
-	viper.Set("username", "clouditor")
-	viper.Set("password", "clouditor")
-	viper.Set("session-directory", dir)
-
-	cmd := login.NewLoginCommand()
-	err = cmd.RunE(nil, []string{fmt.Sprintf("localhost:%d", sock.Addr().(*net.TCPAddr).Port)})
+	err := os.Chdir("../../../")
 	if err != nil {
 		panic(err)
 	}
@@ -112,9 +65,15 @@ func TestNewCloudCommand(t *testing.T) {
 }
 
 func TestRegisterCloudServiceCommand(t *testing.T) {
-	var err error
-	var b bytes.Buffer
-	var response orchestrator.CloudService
+	var (
+		response orchestrator.CloudService
+
+		err error
+		b   bytes.Buffer
+	)
+	_, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
 
 	cli.Output = &b
 
@@ -130,9 +89,18 @@ func TestRegisterCloudServiceCommand(t *testing.T) {
 }
 
 func TestListCloudServicesCommand(t *testing.T) {
-	var err error
-	var b bytes.Buffer
-	var response orchestrator.ListCloudServicesResponse
+	var (
+		response orchestrator.ListCloudServicesResponse
+
+		err error
+		b   bytes.Buffer
+	)
+	orchestratorService, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
+
+	_, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
 
 	cli.Output = &b
 
@@ -148,13 +116,26 @@ func TestListCloudServicesCommand(t *testing.T) {
 }
 
 func TestGetCloudServiceCommand(t *testing.T) {
-	var err error
-	var b bytes.Buffer
-	var response orchestrator.CloudService
+	var (
+		response orchestrator.CloudService
+		target   *orchestrator.CloudService
+
+		err error
+		b   bytes.Buffer
+	)
+	orchestratorService, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
+
+	target, err = orchestratorService.CreateDefaultTargetCloudService()
+	fmt.Println("target:", target)
+	// target should be non-nil since it has been newly created
+	assert.NotNil(t, target)
+	assert.NoError(t, err)
 
 	cli.Output = &b
 
-	cmd := NewGetCloudServiceComand()
+	cmd := NewGetCloudServiceCommand()
 	err = cmd.RunE(nil, []string{target.Id})
 
 	assert.NoError(t, err)
@@ -166,9 +147,19 @@ func TestGetCloudServiceCommand(t *testing.T) {
 }
 
 func TestRemoveCloudServicesCommand(t *testing.T) {
-	var err error
-	var b bytes.Buffer
-	var response emptypb.Empty
+	var (
+		response emptypb.Empty
+		target   *orchestrator.CloudService
+
+		err error
+		b   bytes.Buffer
+	)
+	orchestratorService, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
+
+	target, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
 
 	cli.Output = &b
 
@@ -182,20 +173,34 @@ func TestRemoveCloudServicesCommand(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Re-create default service
-	_, err = s.CreateDefaultTargetCloudService()
+	_, err = orchestratorService.CreateDefaultTargetCloudService()
 
 	assert.NoError(t, err)
 }
 
 func TestUpdateCloudServiceCommand(t *testing.T) {
-	var err error
-	var b bytes.Buffer
-	var response orchestrator.CloudService
+	var (
+		response orchestrator.CloudService
+		target   *orchestrator.CloudService
+
+		err error
+		b   bytes.Buffer
+	)
+	const (
+		notDefault = "not_default"
+	)
+	orchestratorService, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
+
+	target, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
 
 	cli.Output = &b
 
 	viper.Set("id", target.Id)
-	viper.Set("name", "not_default")
+	viper.Set("name", notDefault)
+	//viper.Set("description", "newD")
 
 	cmd := NewUpdateCloudServiceCommand()
 	err = cmd.RunE(nil, []string{})
@@ -206,20 +211,29 @@ func TestUpdateCloudServiceCommand(t *testing.T) {
 
 	assert.NoError(t, err)
 	assert.Equal(t, target.Id, response.Id)
-	assert.Equal(t, "not_default", response.Name)
+	assert.Equal(t, notDefault, response.Name)
 }
 
 func TestGetMetricConfiguration(t *testing.T) {
 	var (
-		err    error
-		b      bytes.Buffer
 		target *orchestrator.CloudService
+
+		err error
+		b   bytes.Buffer
 	)
+	orchestratorService, server, sock := startServer()
+	defer sock.Close()
+	defer server.Stop()
+
+	target, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	// target should be not nil since there are no stored cloud services yet
+	assert.NotNil(t, target)
 
 	cli.Output = &b
 
 	// create a new target service
-	target, err = s.RegisterCloudService(context.TODO(), &orchestrator.RegisterCloudServiceRequest{Service: &orchestrator.CloudService{Name: "myservice"}})
+	target, err = orchestratorService.RegisterCloudService(context.TODO(), &orchestrator.RegisterCloudServiceRequest{Service: &orchestrator.CloudService{Name: "myservice"}})
 
 	assert.NotNil(t, target)
 	assert.NoError(t, err)
@@ -229,3 +243,38 @@ func TestGetMetricConfiguration(t *testing.T) {
 
 	assert.NoError(t, err)
 }
+
+// startServer starts a gRPC server with an orchestrator and auth service. We don't do it in TestMain since you
+// can only register a service - once before server.serve(). And we do need to add new Orchestrator service because
+// the DB won't be reset otherwise.
+func startServer() (orchestratorService *service_orchestrator.Service, server *grpc.Server, sock net.Listener) {
+	var (
+		err error
+		dir string
+	)
+
+	orchestratorService = service_orchestrator.NewService()
+
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
+	if err != nil {
+		panic(err)
+	}
+	orchestrator.RegisterOrchestratorServer(server, orchestratorService)
+
+	dir, err = ioutil.TempDir(os.TempDir(), ".clouditor")
+	if err != nil {
+		panic(err)
+	}
+
+	viper.Set("username", "clouditor")
+	viper.Set("password", "clouditor")
+	viper.Set("session-directory", dir)
+
+	cmd := login.NewLoginCommand()
+	err = cmd.RunE(nil, []string{fmt.Sprintf("localhost:%d", sock.Addr().(*net.TCPAddr).Port)})
+	if err != nil {
+		panic(err)
+	}
+
+	return
+}
diff --git a/cli/commands/evidence/evidence_test.go b/cli/commands/evidence/evidence_test.go
index be7b4bbd..e841af77 100644
--- a/cli/commands/evidence/evidence_test.go
+++ b/cli/commands/evidence/evidence_test.go
@@ -35,12 +35,11 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/evidence"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_evidenceStore "clouditor.io/clouditor/service/evidence"
 
 	"clouditor.io/clouditor/voc"
@@ -52,14 +51,14 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
-var sock net.Listener
-var server *grpc.Server
-
 func TestMain(m *testing.M) {
 	var (
+		sock                 net.Listener
+		server               *grpc.Server
+		evidenceStoreService *service_evidenceStore.Service
+
 		err error
 		dir string
-		s   *service_evidenceStore.Service
 	)
 
 	err = os.Chdir("../../../")
@@ -67,19 +66,14 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_evidenceStore.NewService()
+	evidenceStoreService = service_evidenceStore.NewService()
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
-	evidence.RegisterEvidenceStoreServer(server, s)
-	_, err = s.StoreEvidence(context.TODO(), &evidence.StoreEvidenceRequest{Evidence: &evidence.Evidence{
+	evidence.RegisterEvidenceStoreServer(server, evidenceStoreService)
+	_, err = evidenceStoreService.StoreEvidence(context.TODO(), &evidence.StoreEvidenceRequest{Evidence: &evidence.Evidence{
 		Id:        "11111111-1111-1111-1111-111111111111",
 		ToolId:    "mock",
 		Timestamp: timestamppb.Now(),
diff --git a/cli/commands/login/login_test.go b/cli/commands/login/login_test.go
index fe679d0b..ce4edaac 100644
--- a/cli/commands/login/login_test.go
+++ b/cli/commands/login/login_test.go
@@ -32,27 +32,23 @@ import (
 	"os"
 	"testing"
 
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock   net.Listener
+	server *grpc.Server
+)
 
 func TestMain(m *testing.M) {
-	var err error
-
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	var (
+		err error
+	)
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
diff --git a/cli/commands/metric/metric_test.go b/cli/commands/metric/metric_test.go
index 87fca725..4ed7cae4 100644
--- a/cli/commands/metric/metric_test.go
+++ b/cli/commands/metric/metric_test.go
@@ -33,12 +33,11 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 
 	"github.com/spf13/viper"
@@ -47,14 +46,14 @@ import (
 	"google.golang.org/protobuf/encoding/protojson"
 )
 
-var sock net.Listener
-var server *grpc.Server
-
 func TestMain(m *testing.M) {
 	var (
+		sock                net.Listener
+		server              *grpc.Server
+		orchestratorService *service_orchestrator.Service
+
 		err error
 		dir string
-		s   *service_orchestrator.Service
 	)
 
 	err = os.Chdir("../../../")
@@ -62,15 +61,10 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_orchestrator.NewService()
+	orchestratorService = service_orchestrator.NewService()
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
-	orchestrator.RegisterOrchestratorServer(server, s)
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
+	orchestrator.RegisterOrchestratorServer(server, orchestratorService)
 
 	if err != nil {
 		panic(err)
diff --git a/cli/commands/resource/resource_test.go b/cli/commands/resource/resource_test.go
index 4c6054b6..0235f378 100644
--- a/cli/commands/resource/resource_test.go
+++ b/cli/commands/resource/resource_test.go
@@ -34,12 +34,11 @@ import (
 	"testing"
 	"time"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_discovery "clouditor.io/clouditor/service/discovery"
 
 	"clouditor.io/clouditor/voc"
@@ -49,14 +48,16 @@ import (
 	"google.golang.org/protobuf/encoding/protojson"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock             net.Listener
+	server           *grpc.Server
+	discoveryService *service_discovery.Service
+)
 
 func TestMain(m *testing.M) {
 	var (
 		err error
 		dir string
-		s   *service_discovery.Service
 	)
 
 	err = os.Chdir("../../../../")
@@ -64,19 +65,14 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_discovery.NewService()
-	s.StartDiscovery(mockDiscoverer{testCase: 2})
+	discoveryService = service_discovery.NewService()
+	discoveryService.StartDiscovery(mockDiscoverer{testCase: 2})
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
-	discovery.RegisterDiscoveryServer(server, s)
+	discovery.RegisterDiscoveryServer(server, discoveryService)
 
 	defer func(sock net.Listener) {
 		err = sock.Close()
diff --git a/cli/commands/service/discovery/discovery_test.go b/cli/commands/service/discovery/discovery_test.go
index 84460acc..c5adf76e 100644
--- a/cli/commands/service/discovery/discovery_test.go
+++ b/cli/commands/service/discovery/discovery_test.go
@@ -34,12 +34,11 @@ import (
 	"testing"
 	"time"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_discovery "clouditor.io/clouditor/service/discovery"
 
 	"clouditor.io/clouditor/voc"
@@ -49,14 +48,16 @@ import (
 	"google.golang.org/protobuf/encoding/protojson"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock             net.Listener
+	server           *grpc.Server
+	discoveryService *service_discovery.Service
+)
 
 func TestMain(m *testing.M) {
 	var (
 		err error
 		dir string
-		s   *service_discovery.Service
 	)
 
 	err = os.Chdir("../../../../")
@@ -64,19 +65,14 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_discovery.NewService()
-	s.StartDiscovery(mockDiscoverer{testCase: 2})
+	discoveryService = service_discovery.NewService()
+	discoveryService.StartDiscovery(mockDiscoverer{testCase: 2})
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
-	discovery.RegisterDiscoveryServer(server, s)
+	discovery.RegisterDiscoveryServer(server, discoveryService)
 
 	defer func(sock net.Listener) {
 		err = sock.Close()
diff --git a/cli/commands/service/orchestrator/orchestrator_test.go b/cli/commands/service/orchestrator/orchestrator_test.go
index 6bfa7a5d..97fa804a 100644
--- a/cli/commands/service/orchestrator/orchestrator_test.go
+++ b/cli/commands/service/orchestrator/orchestrator_test.go
@@ -35,13 +35,12 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 
 	"github.com/spf13/viper"
@@ -52,14 +51,16 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock                net.Listener
+	server              *grpc.Server
+	orchestratorService *service_orchestrator.Service
+)
 
 func TestMain(m *testing.M) {
 	var (
 		err error
 		dir string
-		s   *service_orchestrator.Service
 	)
 
 	err = os.Chdir("../../../../")
@@ -67,20 +68,15 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_orchestrator.NewService()
+	orchestratorService = service_orchestrator.NewService()
 
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
-	orchestrator.RegisterOrchestratorServer(server, s)
+	orchestrator.RegisterOrchestratorServer(server, orchestratorService)
 	// Store an assessment result that output of CMD 'list_results' is not empty
-	_, err = s.StoreAssessmentResult(context.TODO(), &orchestrator.StoreAssessmentResultRequest{
+	_, err = orchestratorService.StoreAssessmentResult(context.TODO(), &orchestrator.StoreAssessmentResultRequest{
 		Result: &assessment.AssessmentResult{
 			Id:         "11111111-1111-1111-1111-111111111111",
 			MetricId:   "assessmentResultMetricID",
diff --git a/cli/commands/tool/tool_test.go b/cli/commands/tool/tool_test.go
index ebc704eb..2c7c2377 100644
--- a/cli/commands/tool/tool_test.go
+++ b/cli/commands/tool/tool_test.go
@@ -32,11 +32,10 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/cli/commands/login"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 
 	"github.com/spf13/viper"
@@ -44,30 +43,26 @@ import (
 	"google.golang.org/grpc"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock                net.Listener
+	server              *grpc.Server
+	orchestratorService *service_orchestrator.Service
+)
 
 func TestMain(m *testing.M) {
 	var (
 		err error
 		dir string
-		s   *service_orchestrator.Service
 	)
 
 	err = os.Chdir("../../../")
 	if err != nil {
 		panic(err)
 	}
+	orchestratorService = service_orchestrator.NewService()
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	s = service_orchestrator.NewService()
-
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
-	orchestrator.RegisterOrchestratorServer(server, s)
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
+	orchestrator.RegisterOrchestratorServer(server, orchestratorService)
 
 	if err != nil {
 		panic(err)
diff --git a/cli/session_test.go b/cli/session_test.go
index 0820d4f4..1a0d64c2 100644
--- a/cli/session_test.go
+++ b/cli/session_test.go
@@ -33,12 +33,11 @@ import (
 	"os"
 	"testing"
 
+	"clouditor.io/clouditor/service"
+
 	"google.golang.org/protobuf/proto"
 
 	"clouditor.io/clouditor/api/auth"
-	"clouditor.io/clouditor/persistence"
-	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
@@ -46,18 +45,20 @@ import (
 	"google.golang.org/grpc/status"
 )
 
-var sock net.Listener
-var server *grpc.Server
+var (
+	sock   net.Listener
+	server *grpc.Server
+)
 
 func TestMain(m *testing.M) {
-	var err error
-
-	err = persistence.InitDB(true, "", 0)
+	var (
+		err error
+	)
+	err = os.Chdir("../")
 	if err != nil {
 		panic(err)
 	}
-
-	sock, server, _, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/engine/engine_test.go b/cmd/engine/engine_test.go
index 260e7941..003624fa 100644
--- a/cmd/engine/engine_test.go
+++ b/cmd/engine/engine_test.go
@@ -10,38 +10,58 @@ import (
 )
 
 func Test_doCmd(t *testing.T) {
-	viper.Set(DBInMemoryFlag, true)
-	viper.Set(APIHTTPPortFlag, 0)
-	viper.Set(APIgRPCPortFlag, 0)
-
 	type args struct {
 		in0 *cobra.Command
 		in1 []string
 	}
 	tests := []struct {
-		name    string
-		args    args
-		wantErr bool
+		name      string
+		prepViper func()
+		args      args
+		wantErr   bool
 	}{
 		{
 			name: "Launch with --db-in-memory",
+			prepViper: func() {
+				viper.Set(DBInMemoryFlag, true)
+				viper.Set(APIHTTPPortFlag, 0)
+				viper.Set(APIgRPCPortFlag, 0)
+			},
+		},
+		{
+			name: "Launch with invalid postgres port",
+			prepViper: func() {
+				viper.Set(DBPortFlag, 0)
+			},
+			wantErr: true,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			viper.Reset()
+			tt.prepViper()
+
 			go func() {
-				if err := doCmd(tt.args.in0, tt.args.in1); (err != nil) != tt.wantErr {
+				err := doCmd(tt.args.in0, tt.args.in1)
+				if (err != nil) != tt.wantErr {
 					t.Errorf("doCmd() error = %v, wantErr %v", err, tt.wantErr)
 				}
+
+				if err != nil {
+					// Signal that we are ready anyway, so that we fail properly
+					rest.GetReadyChannel() <- false
+				}
 			}()
 
-			<-rest.GetReadyChannel()
+			success := <-rest.GetReadyChannel()
 
-			assert.NotNil(t, server)
-			assert.NotNil(t, authService)
-			assert.NotNil(t, discoveryService)
-			assert.NotNil(t, assessmentService)
-			assert.NotNil(t, evidenceStoreService)
+			if success {
+				assert.NotNil(t, server)
+				assert.NotNil(t, authService)
+				assert.NotNil(t, discoveryService)
+				assert.NotNil(t, assessmentService)
+				assert.NotNil(t, evidenceStoreService)
+			}
 		})
 	}
 }
diff --git a/persistence/gorm/gorm_test.go b/persistence/gorm/gorm_test.go
new file mode 100644
index 00000000..1678cbd5
--- /dev/null
+++ b/persistence/gorm/gorm_test.go
@@ -0,0 +1,389 @@
+package gorm
+
+import (
+	"clouditor.io/clouditor/api/orchestrator"
+	"fmt"
+	"testing"
+
+	"clouditor.io/clouditor/api/auth"
+	"clouditor.io/clouditor/persistence"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestStorageOptions(t *testing.T) {
+	type args struct {
+		opts []StorageOption
+	}
+	tests := []struct {
+		name              string
+		args              args
+		wantDialectorType string
+		wantErr           assert.ErrorAssertionFunc
+	}{
+		{
+			name: "in memory with option",
+			args: args{
+				opts: []StorageOption{
+					WithInMemory(),
+				},
+			},
+			wantDialectorType: "*sqlite.Dialector",
+			wantErr:           nil,
+		},
+		{
+			name:              "in memory without option",
+			wantDialectorType: "*sqlite.Dialector",
+			wantErr:           nil,
+		},
+		{
+			name: "postgres with option - invalid port",
+			args: args{
+				opts: []StorageOption{
+					WithPostgres("", 0),
+				},
+			},
+			wantDialectorType: "",
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.Contains(t, err.Error(), "invalid port")
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s, err := NewStorage(tt.args.opts...)
+			if (err != nil) && tt.wantErr != nil {
+				tt.wantErr(t, err, tt.args.opts)
+				return
+			}
+
+			gorm, ok := s.(*storage)
+			assert.True(t, ok)
+			assert.NotNil(t, gorm)
+
+			assert.Equal(t, tt.wantDialectorType, fmt.Sprintf("%T", gorm.dialector))
+
+			// Test to create new user and get it again with respective 'Create' and 'Get'
+			// Create record via DB call
+			userInput := &auth.User{
+				Username: "SomeName",
+				Password: "SomePassword",
+				Email:    "SomeMail",
+				FullName: "SomeFullName",
+			}
+			err = s.Create(userInput)
+			assert.NoError(t, err)
+
+			// Get record via DB call
+			userOutput := &auth.User{}
+			err = s.Get(userOutput, "Username = ?", "SomeName")
+			assert.NoError(t, err)
+			assert.Equal(t, userInput, userOutput)
+		})
+	}
+}
+
+func Test_storage_Get(t *testing.T) {
+	var (
+		err  error
+		s    persistence.Storage
+		user *auth.User
+	)
+
+	user = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// Return error since no record in the DB yet
+	err = s.Get(&auth.User{})
+	assert.ErrorIs(t, err, persistence.ErrRecordNotFound)
+
+	// Create user
+	err = s.Create(user)
+	assert.NoError(t, err)
+
+	// Get user via passing entire record
+	gotUser := &auth.User{}
+	err = s.Get(gotUser)
+	assert.NoError(t, err)
+	assert.Equal(t, user, gotUser)
+
+	// Get user via username
+	gotUser2 := &auth.User{}
+	err = s.Get(gotUser2, "username = ?", user.Username)
+	assert.NoError(t, err)
+	assert.Equal(t, user, gotUser2)
+
+	// Get user via mail
+	gotUser3 := &auth.User{}
+	err = s.Get(gotUser3, "Email = ?", user.Email)
+	assert.NoError(t, err)
+	assert.Equal(t, user, gotUser3)
+
+}
+
+func Test_storage_List(t *testing.T) {
+	var (
+		err   error
+		s     persistence.Storage
+		user1 *auth.User
+		user2 *auth.User
+		users []auth.User
+	)
+
+	user1 = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	user2 = &auth.User{
+		Username: "SomeName2",
+		Password: "SomePassword2",
+		Email:    "SomeMail2",
+		FullName: "SomeFullName2",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// List should return empty list since no users are in DB yet
+	err = s.List(&users)
+	assert.ErrorIs(t, err, nil)
+	assert.Empty(t, users)
+
+	// List should return list of 2 users (user1 and user2)
+	err = s.Create(user1)
+	assert.NoError(t, err)
+	err = s.Create(user2)
+	assert.NoError(t, err)
+	err = s.List(&users)
+	assert.ErrorIs(t, err, nil)
+	assert.Equal(t, len(users), 2)
+
+	// Check if user with name "SomeName" (user1) is in the list
+	for i := range users {
+		if users[i].Username == user1.Username {
+			return
+		}
+	}
+	// If not, let the test fail
+	assert.FailNow(t, "user1 is not listed but should be.")
+
+}
+
+func Test_storage_Count(t *testing.T) {
+	var (
+		count int64
+		err   error
+		s     persistence.Storage
+		user  *auth.User
+		user2 *auth.User
+	)
+
+	user = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	user2 = &auth.User{
+		Username: "SomeName2",
+		Password: "SomePassword2",
+		Email:    "SomeMail2",
+		FullName: "SomeFullName2",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// Since no records in DB yet, count of users should be 0
+	count, err = s.Count(&auth.User{})
+	assert.NoError(t, err)
+	assert.Equal(t, int(count), 0)
+
+	// Create one user -> count of users should be 1
+	assert.ErrorIs(t, s.Create(user), nil)
+	count, err = s.Count(&auth.User{})
+	assert.NoError(t, err)
+	assert.Equal(t, int(count), 1)
+
+	// Add another one -> count of users should be 2
+	assert.ErrorIs(t, s.Create(user2), nil)
+	count, err = s.Count(&auth.User{})
+	assert.NoError(t, err)
+	assert.Equal(t, int(count), 2)
+
+	// Count of users with ID "SomeName2" should be 1
+	count, err = s.Count(&auth.User{}, "Id = ?", "SomeName2")
+	assert.NoError(t, err)
+	assert.Equal(t, int(count), 1)
+
+	// Calling s.Count() with unsupported record type should throw "unsupported" error
+	_, err = s.Count(nil)
+	assert.Error(t, err)
+	fmt.Println(err)
+	assert.Contains(t, err.Error(), "unsupported data type")
+}
+
+func Test_storage_Save(t *testing.T) {
+	var (
+		err     error
+		s       persistence.Storage
+		user    *auth.User
+		newUser *auth.User
+		gotUser *auth.User
+	)
+	user = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// Create user
+	err = s.Create(user)
+	assert.NoError(t, err)
+
+	err = s.Get(&auth.User{}, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	// Save new User: Change PW and delete email. Username and FullName remain unchanged
+	newUser = &auth.User{
+		Username: user.Username,
+		Password: "SomeNewPassword",
+		Email:    "",
+		FullName: user.FullName,
+	}
+	err = s.Save(newUser, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	gotUser = &auth.User{}
+	err = s.Get(gotUser, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	// UserName and FullName should be the same
+	assert.Equal(t, user.Username, gotUser.Username)
+	assert.Equal(t, user.Username, gotUser.Username)
+	// PW should be changed
+	assert.Equal(t, newUser.Password, gotUser.Password)
+	// Email should be zero
+	assert.Equal(t, "", gotUser.Email)
+
+}
+
+func Test_storage_Update(t *testing.T) {
+	var (
+		err  error
+		s    persistence.Storage
+		user *auth.User
+	)
+	user = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// Create user
+	err = s.Create(user)
+	assert.NoError(t, err)
+
+	err = s.Get(&auth.User{}, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	err = s.Update(&auth.User{FullName: "SomeNewName"}, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	gotUser := &auth.User{}
+	err = s.Get(gotUser, "username = ?", user.Username)
+	assert.NoError(t, err)
+
+	// UserName should be changed
+	assert.Equal(t, "SomeNewName", gotUser.FullName)
+
+	// Other properties should stay the same
+	assert.Equal(t, user.Username, gotUser.Username)
+	assert.Equal(t, user.Password, gotUser.Password)
+	assert.Equal(t, user.Email, gotUser.Email)
+
+	// Testing cloud service (A table test now would be better, probably)
+
+	// Create user
+	cloudService := orchestrator.CloudService{
+		Id:          "SomeId",
+		Name:        "SomeName",
+		Description: "SomeDescription",
+	}
+	err = s.Create(&cloudService)
+	assert.NoError(t, err)
+
+	err = s.Get(&orchestrator.CloudService{}, "Id = ?", cloudService.Id)
+	assert.NoError(t, err)
+
+	err = s.Update(&orchestrator.CloudService{Name: "SomeNewName", Description: ""}, "Id = ?", cloudService.Id)
+	assert.NoError(t, err)
+
+	gotCloudService := &orchestrator.CloudService{}
+	err = s.Get(gotCloudService, "Id = ?", cloudService.Id)
+	assert.NoError(t, err)
+
+	// Name should be changed
+	assert.Equal(t, "SomeNewName", gotCloudService.Name)
+	// Other properties should stay the same
+	assert.Equal(t, cloudService.Id, gotCloudService.Id)
+	assert.Equal(t, cloudService.Description, gotCloudService.Description)
+}
+
+func Test_storage_Delete(t *testing.T) {
+	var (
+		err  error
+		s    persistence.Storage
+		user *auth.User
+		//gotUser *auth.User
+	)
+	user = &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+
+	// Create storage
+	s, err = NewStorage()
+	assert.NoError(t, err)
+
+	// Create user
+	err = s.Create(user)
+	assert.NoError(t, err)
+
+	// Should return ErrRecordNotFound since there is no user "FakeUserName" in DB
+	assert.ErrorIs(t, s.Delete(&auth.User{}, "username = ?", "FakeUserName"), persistence.ErrRecordNotFound)
+
+	// Successful deletion
+	assert.Nil(t, s.Delete(&auth.User{}, "username = ?", user.Username))
+	// Check with s.Get that user is not in DB anymore
+	assert.ErrorIs(t, s.Get(&auth.User{}, "username = ?", user.Username), persistence.ErrRecordNotFound)
+
+	// Should return DB error since a non-supported type is passed (just a string instead of, e.g., &auth.User{})
+	assert.Contains(t, s.Delete("Unsupported Type").Error(), "unsupported data type")
+
+}
diff --git a/persistence/inmemory/inmemory_test.go b/persistence/inmemory/inmemory_test.go
new file mode 100644
index 00000000..92001dc2
--- /dev/null
+++ b/persistence/inmemory/inmemory_test.go
@@ -0,0 +1,30 @@
+package inmemory
+
+import (
+	"testing"
+
+	"clouditor.io/clouditor/api/auth"
+	"github.com/stretchr/testify/assert"
+)
+
+// TestNewStorage is a simple test for NewStorage. If we implement in-memory our own, add more (table) tests
+func TestNewStorage(t *testing.T) {
+	s, err := NewStorage()
+	assert.NoError(t, err)
+
+	// Test to create new user
+	userInput := &auth.User{
+		Username: "SomeName",
+		Password: "SomePassword",
+		Email:    "SomeMail",
+		FullName: "SomeFullName",
+	}
+	err = s.Create(userInput)
+	assert.NoError(t, err)
+
+	// Test if we get same user via its name
+	userOutput := &auth.User{}
+	err = s.Get(userOutput, "Username = ?", "SomeName")
+	assert.NoError(t, err)
+	assert.Equal(t, userInput, userOutput)
+}
diff --git a/rest/rest_test.go b/rest/rest_test.go
index ed57d17b..3186b432 100644
--- a/rest/rest_test.go
+++ b/rest/rest_test.go
@@ -37,9 +37,8 @@ import (
 	"testing"
 	"time"
 
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/service"
-	service_auth "clouditor.io/clouditor/service/auth"
+
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
 )
@@ -49,9 +48,7 @@ var (
 	methods = []string{"GET", "POST"}
 	headers = DefaultAllowedHeaders
 
-	authService *service_auth.Service
-
-	grpcPort int = 0
+	grpcPort = 0
 )
 
 func TestMain(m *testing.M) {
@@ -61,14 +58,8 @@ func TestMain(m *testing.M) {
 		sock   net.Listener
 	)
 
-	// A small embedded DB is needed for the server
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
 	// Start at least an authentication server, so that we have something to forward
-	sock, server, authService, err = service.StartDedicatedAuthServer(":0", service_auth.WithApiKeySaveOnCreate(false))
+	sock, server, _, err = service.StartDedicatedAuthServer(":0")
 	if err != nil {
 		panic(err)
 	}
diff --git a/service/assessment/assessment_test.go b/service/assessment/assessment_test.go
index 60d191a6..405ab66e 100644
--- a/service/assessment/assessment_test.go
+++ b/service/assessment/assessment_test.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Fraunhofer AISEC
+// Copyright 2021-2022 Fraunhofer AISEC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -39,7 +39,6 @@ import (
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/evidence"
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/voc"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
@@ -58,15 +57,12 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	// We need an embedded DB for the auth server
-	err = persistence.InitDB(true, "", 0)
+	server, authService, _, _ := startBufConnServer()
+	err = authService.CreateDefaultUser("clouditor", "clouditor")
 	if err != nil {
 		panic(err)
 	}
 
-	server, authService, _, _ := startBufConnServer()
-	authService.CreateDefaultUser("clouditor", "clouditor")
-
 	code := m.Run()
 
 	server.Stop()
diff --git a/service/auth/auth_test.go b/service/auth/auth_test.go
index 630791d7..c419814b 100644
--- a/service/auth/auth_test.go
+++ b/service/auth/auth_test.go
@@ -30,6 +30,7 @@ import (
 	"crypto/ecdsa"
 	"crypto/elliptic"
 	"crypto/rand"
+	"errors"
 	"io/ioutil"
 	"math/big"
 	"os"
@@ -37,6 +38,7 @@ import (
 	"testing"
 
 	"clouditor.io/clouditor/api/auth"
+	"clouditor.io/clouditor/persistence/inmemory"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/protobuf/proto"
 )
@@ -218,7 +220,7 @@ func TestService_loadApiKey(t *testing.T) {
 
 	// Save a key to it
 	err := saveApiKey(tmpKey, tmpFile.Name(), "tmp")
-	assert.ErrorIs(t, err, nil)
+	assert.NoError(t, err)
 
 	type args struct {
 		path     string
@@ -271,3 +273,222 @@ func TestService_loadApiKey(t *testing.T) {
 		})
 	}
 }
+
+func TestNewService(t *testing.T) {
+	var myStorage, err = inmemory.NewStorage()
+	assert.NoError(t, err)
+
+	type args struct {
+		opts []ServiceOption
+	}
+	tests := []struct {
+		name string
+		args args
+		want assert.ValueAssertionFunc
+	}{
+		{
+			name: "New service with database",
+			args: args{
+				opts: []ServiceOption{WithStorage(myStorage)},
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				service, ok := i1.(*Service)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				return assert.Equal(tt, myStorage, service.storage)
+			},
+		},
+		{
+			name: "New service with Api Key Path",
+			args: args{
+				opts: []ServiceOption{WithApiKeyPath("SomePath")},
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				service, ok := i1.(*Service)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				return assert.Equal(tt, "SomePath", service.config.keyPath)
+			},
+		},
+		{
+			name: "New service with Api Key Save on Create",
+			args: args{
+				opts: []ServiceOption{WithApiKeySaveOnCreate(true)},
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				service, ok := i1.(*Service)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				return assert.Equal(tt, true, service.config.keySaveOnCreate)
+			},
+		},
+		{
+			name: "New service with Api Key Save on Create",
+			args: args{
+				opts: []ServiceOption{WithApiKeySaveOnCreate(false)},
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				service, ok := i1.(*Service)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				return assert.Equal(tt, false, service.config.keySaveOnCreate)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := NewService(tt.args.opts...)
+
+			if tt.want != nil {
+				tt.want(t, got, tt.args.opts)
+			}
+		})
+	}
+}
+
+func TestService_CreateDefaultUser(t *testing.T) {
+	// Instantiate a mock storage per error
+	var mStorageCount = mockStorage{countError: errors.New("mock storage db error: count")}
+	var mStorageCreate = mockStorage{createError: errors.New("mock storage db error: create")}
+
+	type fields struct {
+		config struct {
+			keySaveOnCreate bool
+			keyPath         string
+			keyPassword     string
+		}
+		apiKey      *ecdsa.PrivateKey
+		mockStorage *mockStorage
+	}
+	type args struct {
+		username string
+		password string
+	}
+	tests := []struct {
+		name         string
+		fields       fields
+		args         args
+		preFunctions []func(s *Service)
+		wantErr      assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Storage count error",
+			args: args{
+				username: "",
+				password: "",
+			},
+			fields: fields{
+				mockStorage: &mStorageCount,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, mStorageCount.countError)
+			},
+		},
+		{
+			name: "Storage not empty - dont create default user",
+			args: args{
+				username: "",
+				password: "",
+			},
+			preFunctions: []func(s *Service){
+				func(s *Service) {
+					err := s.storage.Create(&auth.User{Username: "SomeName", Password: "SomePassword"})
+					assert.NoError(t, err)
+				},
+			},
+			fields: fields{
+				mockStorage: nil,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, nil)
+			},
+		},
+		{
+			name: "Storage not empty but storage create error",
+			args: args{
+				username: "SomeName",
+				password: "SomePassword",
+			},
+			fields: fields{
+				mockStorage: &mStorageCreate,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, mStorageCreate.createError)
+			},
+		},
+		{
+			name: "Storage empty - create default user",
+			args: args{
+				username: "SomeName",
+				password: "SomePassword",
+			},
+			fields: fields{
+				mockStorage: nil,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				assert.ErrorIs(t, err, nil)
+				var gotUser auth.User
+				err = i[0].(*Service).storage.Get(&gotUser, "username = ?", "SomeName")
+				return assert.ErrorIs(t, err, nil)
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := &Service{
+				config: tt.fields.config,
+				apiKey: tt.fields.apiKey,
+			}
+			if tt.fields.mockStorage != nil {
+				s.storage = tt.fields.mockStorage
+			} else {
+				s.storage, _ = inmemory.NewStorage()
+			}
+			for _, f := range tt.preFunctions {
+				f(s)
+			}
+			tt.wantErr(t, s.CreateDefaultUser(tt.args.username, tt.args.password), s)
+		})
+	}
+}
+
+// mockStorage is a mocked persistence.Storage implementation that returns errors at the specified
+// operations.
+//
+// TODO(lebogg): Extract this struct into our new internal/testutils package
+type mockStorage struct {
+	createError error
+	saveError   error
+	updateError error
+	getError    error
+	listError   error
+	countError  error
+	deleteError error
+}
+
+func (m mockStorage) Create(interface{}) error { return m.createError }
+
+func (m mockStorage) Save(interface{}, ...interface{}) error { return m.saveError }
+
+func (m mockStorage) Update(interface{}, interface{}, ...interface{}) error {
+	return m.updateError
+}
+
+func (m mockStorage) Get(interface{}, ...interface{}) error { return m.getError }
+
+func (m mockStorage) List(interface{}, ...interface{}) error { return m.listError }
+
+func (m mockStorage) Count(interface{}, ...interface{}) (int64, error) {
+	return 0, m.countError
+}
+
+func (m mockStorage) Delete(interface{}, ...interface{}) error { return m.deleteError }
diff --git a/service/auth_test.go b/service/auth_test.go
index e815cf33..3e7f37f7 100644
--- a/service/auth_test.go
+++ b/service/auth_test.go
@@ -32,14 +32,13 @@ import (
 	"net"
 	"net/http"
 	"os"
+	"reflect"
 	"testing"
 	"time"
 
 	"clouditor.io/clouditor/api/auth"
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/rest"
 	service_auth "clouditor.io/clouditor/service/auth"
-
 	"github.com/golang-jwt/jwt/v4"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
@@ -60,12 +59,6 @@ func TestMain(m *testing.M) {
 		sock   net.Listener
 	)
 
-	// A small embedded DB is needed for the server
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
 	// Start at least an authentication server, so that we have something to forward
 	sock, server, authService, err = StartDedicatedAuthServer(":0")
 	if err != nil {
@@ -126,11 +119,11 @@ func TestAuthConfig_AuthFunc(t *testing.T) {
 	}
 
 	port, err := rest.GetServerPort()
-	assert.ErrorIs(t, err, nil)
+	assert.NoError(t, err)
 
 	// Some pre-work to retrieve a valid token
 	loginResponse, err := authService.Login(context.TODO(), &auth.LoginRequest{Username: "clouditor", Password: "clouditor"})
-	assert.ErrorIs(t, err, nil)
+	assert.NoError(t, err)
 	assert.NotNil(t, loginResponse)
 
 	type configureArgs struct {
@@ -223,3 +216,79 @@ func TestAuthConfig_AuthFunc(t *testing.T) {
 		})
 	}
 }
+
+func TestStartDedicatedAuthServer(t *testing.T) {
+	type args struct {
+		address string
+		opts    []service_auth.ServiceOption
+	}
+	tests := []struct {
+		name            string
+		args            args
+		wantSock        net.Listener
+		wantServer      *grpc.Server
+		wantAuthService *service_auth.Service
+		wantErr         bool
+	}{
+		{
+			name: "Could not create default user",
+			args: args{
+				opts: []service_auth.ServiceOption{service_auth.WithStorage(mockStorage{
+					createError: errors.New("could not create"),
+				})},
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotSock, gotServer, gotAuthService, err := StartDedicatedAuthServer(tt.args.address, tt.args.opts...)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("StartDedicatedAuthServer() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(gotSock, tt.wantSock) {
+				t.Errorf("StartDedicatedAuthServer() gotSock = %v, want %v", gotSock, tt.wantSock)
+			}
+			if !reflect.DeepEqual(gotServer, tt.wantServer) {
+				t.Errorf("StartDedicatedAuthServer() gotServer = %v, want %v", gotServer, tt.wantServer)
+			}
+			if !reflect.DeepEqual(gotAuthService, tt.wantAuthService) {
+				t.Errorf("StartDedicatedAuthServer() gotAuthService = %v, want %v", gotAuthService, tt.wantAuthService)
+			}
+		})
+	}
+}
+
+// mockStorage is a mocked persistence.Storage implementation that returns errors at the specified
+// operations.
+//
+// TODO(oxisto): Extract this struct into our new internal/testutils package
+type mockStorage struct {
+	createError error
+	saveError   error
+	updateError error
+	getError    error
+	listError   error
+	countError  error
+	deleteError error
+}
+
+func (m mockStorage) Create(interface{}) error { return m.createError }
+
+func (m mockStorage) Save(interface{}, ...interface{}) error { return m.saveError }
+
+func (m mockStorage) Update(interface{}, interface{}, ...interface{}) error {
+	return m.updateError
+}
+
+func (m mockStorage) Get(interface{}, ...interface{}) error { return m.getError }
+
+func (m mockStorage) List(interface{}, ...interface{}) error { return m.listError }
+
+func (m mockStorage) Count(interface{}, ...interface{}) (int64, error) {
+	return 0, m.countError
+}
+
+func (m mockStorage) Delete(interface{}, ...interface{}) error { return m.deleteError }
diff --git a/service/discovery/discovery_test.go b/service/discovery/discovery_test.go
index c7d2f5e1..2498f310 100644
--- a/service/discovery/discovery_test.go
+++ b/service/discovery/discovery_test.go
@@ -37,7 +37,6 @@ import (
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/api/evidence"
-	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/voc"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc"
@@ -54,15 +53,12 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	// We need an embedded DB for the auth server
-	err = persistence.InitDB(true, "", 0)
+	server, authService, _ := startBufConnServer()
+	err = authService.CreateDefaultUser("clouditor", "clouditor")
 	if err != nil {
 		panic(err)
 	}
 
-	server, authService, _ := startBufConnServer()
-	authService.CreateDefaultUser("clouditor", "clouditor")
-
 	code := m.Run()
 
 	server.Stop()
diff --git a/service/orchestrator/cloud_service_test.go b/service/orchestrator/cloud_service_test.go
index d4f3ab37..018190db 100644
--- a/service/orchestrator/cloud_service_test.go
+++ b/service/orchestrator/cloud_service_test.go
@@ -1,3 +1,28 @@
+// Copyright 2022 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
 package orchestrator
 
 import (
@@ -22,13 +47,13 @@ func TestRegisterCloudService(t *testing.T) {
 			"missing service",
 			&orchestrator.RegisterCloudServiceRequest{},
 			nil,
-			status.Error(codes.InvalidArgument, "service is empty"),
+			status.Error(codes.InvalidArgument, orchestrator.ErrServiceIsNil.Error()),
 		},
 		{
 			"missing service name",
 			&orchestrator.RegisterCloudServiceRequest{Service: &orchestrator.CloudService{}},
 			nil,
-			status.Error(codes.InvalidArgument, "service name is empty"),
+			status.Error(codes.InvalidArgument, orchestrator.ErrNameIsMissing.Error()),
 		},
 		{
 			"valid",
@@ -37,10 +62,14 @@ func TestRegisterCloudService(t *testing.T) {
 			nil,
 		},
 	}
+	orchestratorService := NewService()
+	cloudService, err := orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.NotNil(t, cloudService)
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			res, err := service.RegisterCloudService(context.Background(), tt.req)
+			res, err := orchestratorService.RegisterCloudService(context.Background(), tt.req)
 
 			if tt.err == nil {
 				assert.Equal(t, err, tt.err)
@@ -66,6 +95,32 @@ func TestRegisterCloudService(t *testing.T) {
 	}
 }
 
+func TestService_ListCloudServices(t *testing.T) {
+	var (
+		listCloudServicesResponse *orchestrator.ListCloudServicesResponse
+		cloudService              *orchestrator.CloudService
+		err                       error
+	)
+
+	orchestratorService := NewService()
+	// 1st case: No services stored
+	listCloudServicesResponse, err = orchestratorService.ListCloudServices(context.Background(), &orchestrator.ListCloudServicesRequest{})
+	assert.NoError(t, err)
+	assert.NotNil(t, listCloudServicesResponse.Services)
+	assert.Empty(t, listCloudServicesResponse.Services)
+
+	// 2nd case: One service stored
+	cloudService, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.NotNil(t, cloudService)
+
+	listCloudServicesResponse, err = orchestratorService.ListCloudServices(context.Background(), &orchestrator.ListCloudServicesRequest{})
+	assert.NoError(t, err)
+	assert.NotNil(t, listCloudServicesResponse.Services)
+	assert.NotEmpty(t, listCloudServicesResponse.Services)
+	assert.Equal(t, len(listCloudServicesResponse.Services), 1)
+}
+
 func TestGetCloudService(t *testing.T) {
 	tests := []struct {
 		name string
@@ -74,26 +129,20 @@ func TestGetCloudService(t *testing.T) {
 		err  error
 	}{
 		{
-			"missing request",
+			"invalid request",
 			nil,
 			nil,
-			status.Error(codes.InvalidArgument, "service id is empty"),
-		},
-		{
-			"missing service id",
-			&orchestrator.GetCloudServiceRequest{},
-			nil,
-			status.Error(codes.InvalidArgument, "service id is empty"),
+			status.Error(codes.InvalidArgument, orchestrator.ErrRequestIsNil.Error()),
 		},
 		{
-			"invalid service id",
+			"cloud service not found",
 			&orchestrator.GetCloudServiceRequest{ServiceId: "does-not-exist"},
 			nil,
 			status.Error(codes.NotFound, "service not found"),
 		},
 		{
 			"valid",
-			&orchestrator.GetCloudServiceRequest{ServiceId: defaultTarget.Id},
+			&orchestrator.GetCloudServiceRequest{ServiceId: DefaultTargetCloudServiceId},
 			&orchestrator.CloudService{
 				Id:          DefaultTargetCloudServiceId,
 				Name:        DefaultTargetCloudServiceName,
@@ -102,13 +151,18 @@ func TestGetCloudService(t *testing.T) {
 			nil,
 		},
 	}
+	orchestratorService := NewService()
+
+	cloudService, err := orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.NotNil(t, cloudService)
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			res, err := service.GetCloudService(context.Background(), tt.req)
+			res, err := orchestratorService.GetCloudService(context.Background(), tt.req)
 
 			if tt.err == nil {
-				assert.Equal(t, err, tt.err)
+				assert.Equal(t, tt.err, err)
 			} else {
 				assert.EqualError(t, err, tt.err.Error())
 			}
@@ -121,3 +175,111 @@ func TestGetCloudService(t *testing.T) {
 		})
 	}
 }
+
+func TestService_UpdateCloudService(t *testing.T) {
+	var (
+		cloudService *orchestrator.CloudService
+		err          error
+	)
+	orchestratorService := NewService()
+
+	// 1st case: Service is nil
+	_, err = orchestratorService.UpdateCloudService(context.Background(), &orchestrator.UpdateCloudServiceRequest{})
+	assert.Equal(t, codes.InvalidArgument, status.Code(err))
+
+	// 2nd case: Service ID is nil
+	_, err = orchestratorService.UpdateCloudService(context.Background(), &orchestrator.UpdateCloudServiceRequest{
+		Service: &orchestrator.CloudService{},
+	})
+	assert.Equal(t, codes.InvalidArgument, status.Code(err))
+
+	// 3rd case: Service not found since there are no services yet
+	_, err = orchestratorService.UpdateCloudService(context.Background(), &orchestrator.UpdateCloudServiceRequest{
+		Service: &orchestrator.CloudService{
+			Name:        DefaultTargetCloudServiceName,
+			Description: DefaultTargetCloudServiceDescription,
+		},
+		ServiceId: DefaultTargetCloudServiceId,
+	})
+	assert.Equal(t, codes.NotFound, status.Code(err))
+	// 4th case: Service updated successfully
+	_, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	if err != nil {
+		return
+	}
+	cloudService, err = orchestratorService.UpdateCloudService(context.Background(), &orchestrator.UpdateCloudServiceRequest{
+		Service: &orchestrator.CloudService{
+			Name:        "NewName",
+			Description: "",
+		},
+		ServiceId: DefaultTargetCloudServiceId,
+	})
+	assert.NoError(t, err)
+	assert.NotNil(t, cloudService)
+	assert.Equal(t, "NewName", cloudService.Name)
+	// Description should be overwritten with empty string
+	assert.Equal(t, "", cloudService.Description)
+}
+
+func TestService_RemoveCloudService(t *testing.T) {
+	var (
+		cloudServiceResponse      *orchestrator.CloudService
+		err                       error
+		listCloudServicesResponse *orchestrator.ListCloudServicesResponse
+	)
+	orchestratorService := NewService()
+
+	// 1st case: Empty service ID error
+	_, err = orchestratorService.RemoveCloudService(context.Background(), &orchestrator.RemoveCloudServiceRequest{ServiceId: ""})
+	assert.Error(t, err)
+	assert.Equal(t, status.Code(err), codes.InvalidArgument)
+
+	// 2nd case: ErrRecordNotFound
+	_, err = orchestratorService.RemoveCloudService(context.Background(), &orchestrator.RemoveCloudServiceRequest{ServiceId: DefaultTargetCloudServiceId})
+	assert.Error(t, err)
+	assert.Equal(t, status.Code(err), codes.NotFound)
+
+	// 3rd case: Record removed successfully
+	cloudServiceResponse, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.NotNil(t, cloudServiceResponse)
+
+	// There is a record for cloud services in the DB (default one)
+	listCloudServicesResponse, err = orchestratorService.ListCloudServices(context.Background(), &orchestrator.ListCloudServicesRequest{})
+	assert.NoError(t, err)
+	assert.NotNil(t, listCloudServicesResponse.Services)
+	assert.NotEmpty(t, listCloudServicesResponse.Services)
+
+	// Remove record
+	_, err = orchestratorService.RemoveCloudService(context.Background(), &orchestrator.RemoveCloudServiceRequest{ServiceId: DefaultTargetCloudServiceId})
+	assert.NoError(t, err)
+
+	// There is a record for cloud services in the DB (default one)
+	listCloudServicesResponse, err = orchestratorService.ListCloudServices(context.Background(), &orchestrator.ListCloudServicesRequest{})
+	assert.NoError(t, err)
+	assert.NotNil(t, listCloudServicesResponse.Services)
+	assert.Empty(t, listCloudServicesResponse.Services)
+}
+
+func TestService_CreateDefaultTargetCloudService(t *testing.T) {
+	var (
+		cloudServiceResponse *orchestrator.CloudService
+		err                  error
+	)
+	orchestratorService := NewService()
+
+	// 1st case: No records for cloud services -> Default target service is created
+	cloudServiceResponse, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.Equal(t, &orchestrator.CloudService{
+		Id:          DefaultTargetCloudServiceId,
+		Name:        DefaultTargetCloudServiceName,
+		Description: DefaultTargetCloudServiceDescription,
+	}, cloudServiceResponse)
+
+	// 2nd case: There is already a record for service (the default target service) -> Nothing added and no error
+	cloudServiceResponse, err = orchestratorService.CreateDefaultTargetCloudService()
+	assert.NoError(t, err)
+	assert.Nil(t, cloudServiceResponse)
+}
diff --git a/service/orchestrator/metrics_test.go b/service/orchestrator/metrics_test.go
index 9f87a388..9e720683 100644
--- a/service/orchestrator/metrics_test.go
+++ b/service/orchestrator/metrics_test.go
@@ -90,6 +90,7 @@ func TestService_CreateMetric(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			service := NewService()
 			gotMetric, err := service.CreateMetric(tt.args.in0, tt.args.req)
 
 			if (err != nil) != tt.wantErr {
@@ -146,7 +147,7 @@ func TestService_UpdateMetric(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			service = NewService()
+			service := NewService()
 			gotMetric, err := service.UpdateMetric(tt.args.in0, tt.args.req)
 
 			if (err != nil) != tt.wantErr {
@@ -208,7 +209,7 @@ func TestService_GetMetric(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			service = NewService()
+			service := NewService()
 			gotMetric, err := service.GetMetric(tt.args.in0, tt.args.req)
 
 			if (err != nil) != tt.wantErr {
@@ -227,6 +228,7 @@ func TestService_ListMetrics(t *testing.T) {
 		response *orchestrator.ListMetricsResponse
 		err      error
 	)
+	service := NewService()
 
 	response, err = service.ListMetrics(context.TODO(), &orchestrator.ListMetricsRequest{})
 
diff --git a/service/orchestrator/orchestrator_test.go b/service/orchestrator/orchestrator_test.go
index d2c5e310..2c258549 100644
--- a/service/orchestrator/orchestrator_test.go
+++ b/service/orchestrator/orchestrator_test.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Fraunhofer AISEC
+// Copyright 2021-2022 Fraunhofer AISEC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ package orchestrator
 
 import (
 	"context"
+	"encoding/json"
 	"io"
 	"os"
 	"reflect"
@@ -38,20 +39,17 @@ import (
 	"google.golang.org/protobuf/types/known/emptypb"
 	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
-	"k8s.io/apimachinery/pkg/util/json"
 
 	"clouditor.io/clouditor/api/assessment"
-	"clouditor.io/clouditor/persistence"
-
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/persistence/inmemory"
 	"github.com/stretchr/testify/assert"
 )
 
-var service *Service
-var defaultTarget *orchestrator.CloudService
-
-const assessmentResultID1 = "11111111-1111-1111-1111-111111111111"
-const assessmentResultID2 = "11111111-1111-1111-1111-111111111112"
+const (
+	assessmentResultID1 = "11111111-1111-1111-1111-111111111111"
+	assessmentResultID2 = "11111111-1111-1111-1111-111111111112"
+)
 
 func TestMain(m *testing.M) {
 	err := os.Chdir("../../")
@@ -59,17 +57,6 @@ func TestMain(m *testing.M) {
 		panic(err)
 	}
 
-	err = persistence.InitDB(true, "", 0)
-	if err != nil {
-		panic(err)
-	}
-
-	service = NewService()
-	defaultTarget, err = service.CreateDefaultTargetCloudService()
-	if err != nil {
-		panic(err)
-	}
-
 	os.Exit(m.Run())
 }
 
@@ -167,13 +154,14 @@ func TestAssessmentResultHook(t *testing.T) {
 	}
 }
 
+// TODO(lebogg): Convert to table tests
 func TestListMetricConfigurations(t *testing.T) {
 	var (
 		response *orchestrator.ListMetricConfigurationResponse
 		err      error
 	)
-
-	response, err = service.ListMetricConfigurations(context.TODO(), &orchestrator.ListMetricConfigurationRequest{})
+	s := NewService()
+	response, err = s.ListMetricConfigurations(context.TODO(), &orchestrator.ListMetricConfigurationRequest{})
 
 	assert.NoError(t, err)
 	assert.NotEmpty(t, response.Configurations)
@@ -297,6 +285,7 @@ func (mockStreamer) SendAndClose(_ *emptypb.Empty) error {
 }
 
 func (m *mockStreamer) Recv() (*assessment.AssessmentResult, error) {
+
 	if m.counter == 0 {
 		m.counter++
 		return &assessment.AssessmentResult{
@@ -376,3 +365,42 @@ func toStruct(f float32) (s *structpb.Value) {
 
 	return
 }
+
+func TestNewService(t *testing.T) {
+	var myStorage, err = inmemory.NewStorage()
+	assert.NoError(t, err)
+
+	type args struct {
+		opts []ServiceOption
+	}
+	tests := []struct {
+		name string
+		args args
+		want assert.ValueAssertionFunc
+	}{
+		{
+			name: "New service with database",
+			args: args{
+				opts: []ServiceOption{WithStorage(myStorage)},
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				service, ok := i1.(*Service)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				return assert.Equal(tt, myStorage, service.storage)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := NewService(tt.args.opts...)
+
+			if tt.want != nil {
+				tt.want(t, got, tt.args.opts)
+			}
+		})
+	}
+}