Repository: clouditor/clouditor
Pull request #815 (2022-10-14 11:47:02)

Update GetMetricConfiguration description
- [ ] UpdateMetricConfiguration
    - [x] Add request validation for  metricId and cloudServiceId, Add tests!
    - [ ] isDefault is not updated, if not available in request -> set to false
    - [ ] grpc does not returns some fields (e.g., isDefault, Timestamp)
    - [ ] updatedAt is always set to 1970-01-01T00:00:00Z
- [ ] GetMetricConfiguration: MetricId and cloudServiceId not available after initialization (curl call)

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/815?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> Merging [#815](https://codecov.io/gh/clouditor/clouditor/pull/815?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (1541f3a) into [main](https://codecov.io/gh/clouditor/clouditor/commit/7bd3b89137995594f2424a471e06a69cb5fcf63a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (7bd3b89) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##             main     #815   +/-   ##
=======================================
  Coverage   81.11%   81.11%           
=======================================
  Files          71       71           
  Lines        6551     6551           
=======================================
  Hits         5314     5314           
  Misses        961      961           
  Partials      276      276           
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `81.11% <ø> (ø)` | |

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/815?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [service/orchestrator/metrics.go](https://codecov.io/gh/clouditor/clouditor/pull/815/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvbWV0cmljcy5nbw==) | `85.51% <ø> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)

anatheka: The following should be checked in an separate PR/Issue.

UpdateMetricConfiguration
- [ ] isDefault is not updated, if not available in request -> set to false
- [ ] grpc does not returns some fields (e.g., isDefault, Timestamp)
- [ ] updatedAt is always set to 1970-01-01T00:00:00Z

GetMetricConfiguration: 
- [ ] MetricId and cloudServiceId not available after initialization (curl call)

Patch:
diff --git a/api/assessment/metric.go b/api/assessment/metric.go
index 07adacf1..0c712434 100644
--- a/api/assessment/metric.go
+++ b/api/assessment/metric.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 assessment
 
 import (
@@ -8,11 +33,14 @@ import (
 	"fmt"
 
 	"clouditor.io/clouditor/persistence"
+	"github.com/google/uuid"
 )
 
 var (
-	ErrMetricNameMissing = errors.New("metric name is missing")
-	ErrMetricEmpty       = errors.New("metric is missing or empty")
+	ErrMetricNameMissing       = errors.New("metric name is missing")
+	ErrMetricEmpty             = errors.New("metric is missing or empty")
+	ErrCloudServiceIDIsMissing = errors.New("cloud service id is missing")
+	ErrCloudServiceIDIsInvalid = errors.New("cloud service id is invalid")
 )
 
 func (r *Range) UnmarshalJSON(b []byte) (err error) {
@@ -142,8 +170,53 @@ func (m *Metric) Validate(opts ...MetricValidationOption) (err error) {
 	return nil
 }
 
+// Validate validates the metric configuration
+func (c *MetricConfiguration) Validate() (err error) {
+
+	// isDefault does not need to be checked.
+	// UpdatedAt does not need to be checked.
+
+	if c == nil {
+		return ErrMetricConfigurationMissing
+	}
+
+	if c.Operator == "" {
+		return ErrMetricConfigurationOperatorMissing
+	}
+
+	if c.TargetValue == nil {
+		return ErrMetricConfigurationTargetValueMissing
+	}
+
+	if c.MetricId == "" {
+		return ErrMetricIdMissing
+	}
+
+	err = CheckCloudServiceID(c.CloudServiceId)
+	if err != nil {
+		return err
+	}
+
+	return
+}
+
 // Hash provides a simple string based hash for this metric configuration. It can be used
 // to provide a key for a map or a cache.
 func (x *MetricConfiguration) Hash() string {
 	return base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%v-%v", x.Operator, x.TargetValue)))
 }
+
+// CheckCloudServiceID checks if serviceID is available and in the valid UUID format.
+func CheckCloudServiceID(serviceID string) error {
+	// Check if ServiceId is missing
+	if serviceID == "" {
+		return ErrCloudServiceIDIsMissing
+	}
+
+	// Check if ServiceId is valid
+	if _, err := uuid.Parse(serviceID); err != nil {
+		return ErrCloudServiceIDIsInvalid
+	}
+
+	return nil
+}
diff --git a/api/orchestrator/orchestrator.go b/api/orchestrator/orchestrator.go
index b4125756..c727b4fe 100644
--- a/api/orchestrator/orchestrator.go
+++ b/api/orchestrator/orchestrator.go
@@ -28,6 +28,8 @@ package orchestrator
 import (
 	"context"
 	"errors"
+
+	"clouditor.io/clouditor/api/assessment"
 )
 
 type CloudServiceHookFunc func(ctx context.Context, cld *CloudService, err error)
@@ -43,6 +45,38 @@ var (
 	ErrToEIDIsMissing     = errors.New("toe ID is empty")
 )
 
+// Validate validates the UpdateMetricConfigurationRequest
+func (req *UpdateMetricConfigurationRequest) Validate() error {
+	// Check cloud service ID
+	err := assessment.CheckCloudServiceID(req.CloudServiceId)
+	if err != nil {
+		return err
+	}
+
+	// Check metric ID
+	if req.MetricId == "" {
+		return assessment.ErrMetricIdMissing
+	}
+
+	return nil
+}
+
+// Validate validates the GetMetricConfigurationRequest
+func (req *GetMetricConfigurationRequest) Validate() error {
+	// Check cloud service ID
+	err := assessment.CheckCloudServiceID(req.CloudServiceId)
+	if err != nil {
+		return err
+	}
+
+	// Check metric ID
+	if req.MetricId == "" {
+		return assessment.ErrMetricIdMissing
+	}
+
+	return nil
+}
+
 type CloudServiceRequest interface {
 	GetCloudServiceId() string
 }
diff --git a/api/orchestrator/orchestrator.proto b/api/orchestrator/orchestrator.proto
index 54723164..2161ccc3 100644
--- a/api/orchestrator/orchestrator.proto
+++ b/api/orchestrator/orchestrator.proto
@@ -192,7 +192,7 @@ service Orchestrator {
   }
 
   // Retrieves a metric configuration (target value and operator) for a specific
-  // service and metric ID
+  // service and metric ID.
   rpc GetMetricConfiguration(GetMetricConfigurationRequest)
       returns (MetricConfiguration) {
     option (google.api.http) = {
diff --git a/api/orchestrator/orchestrator_grpc.pb.go b/api/orchestrator/orchestrator_grpc.pb.go
index 3c256501..5af3e2ed 100644
--- a/api/orchestrator/orchestrator_grpc.pb.go
+++ b/api/orchestrator/orchestrator_grpc.pb.go
@@ -65,7 +65,7 @@ type OrchestratorClient interface {
 	// service and metric ID
 	UpdateMetricConfiguration(ctx context.Context, in *UpdateMetricConfigurationRequest, opts ...grpc.CallOption) (*assessment.MetricConfiguration, error)
 	// Retrieves a metric configuration (target value and operator) for a specific
-	// service and metric ID
+	// service and metric ID.
 	GetMetricConfiguration(ctx context.Context, in *GetMetricConfigurationRequest, opts ...grpc.CallOption) (*assessment.MetricConfiguration, error)
 	// Lists all a metric configurations (target value and operator) for a
 	// specific service ID
@@ -580,7 +580,7 @@ type OrchestratorServer interface {
 	// service and metric ID
 	UpdateMetricConfiguration(context.Context, *UpdateMetricConfigurationRequest) (*assessment.MetricConfiguration, error)
 	// Retrieves a metric configuration (target value and operator) for a specific
-	// service and metric ID
+	// service and metric ID.
 	GetMetricConfiguration(context.Context, *GetMetricConfigurationRequest) (*assessment.MetricConfiguration, error)
 	// Lists all a metric configurations (target value and operator) for a
 	// specific service ID
diff --git a/openapi/orchestrator/openapi.yaml b/openapi/orchestrator/openapi.yaml
index 241dc50f..0fe85583 100644
--- a/openapi/orchestrator/openapi.yaml
+++ b/openapi/orchestrator/openapi.yaml
@@ -818,7 +818,7 @@ paths:
                 - Orchestrator
             description: |-
                 Retrieves a metric configuration (target value and operator) for a specific
-                 service and metric ID
+                 service and metric ID.
             operationId: Orchestrator_GetMetricConfiguration
             parameters:
                 - name: cloudServiceId
diff --git a/service/orchestrator/metrics.go b/service/orchestrator/metrics.go
index c26ae914..b225d885 100644
--- a/service/orchestrator/metrics.go
+++ b/service/orchestrator/metrics.go
@@ -307,7 +307,7 @@ func (svc *Service) ListMetrics(_ context.Context, req *orchestrator.ListMetrics
 	return res, nil
 }
 
-// GetMetric retrieves a metric specified by req.MetridId
+// GetMetric retrieves a metric specified by req.MetricId.
 func (svc *Service) GetMetric(_ context.Context, req *orchestrator.GetMetricRequest) (metric *assessment.Metric, err error) {
 	err = svc.storage.Get(&metric, "id = ?", req.MetricId)
 	if errors.Is(err, persistence.ErrRecordNotFound) {
@@ -320,6 +320,11 @@ func (svc *Service) GetMetric(_ context.Context, req *orchestrator.GetMetricRequ
 }
 
 func (svc *Service) GetMetricConfiguration(ctx context.Context, req *orchestrator.GetMetricConfigurationRequest) (res *assessment.MetricConfiguration, err error) {
+	// Validate request
+	if err = req.Validate(); err != nil {
+		return nil, status.Errorf(codes.InvalidArgument, "%s", err)
+	}
+
 	// Check, if this request has access to the cloud service according to our authorization strategy.
 	if !svc.authz.CheckAccess(ctx, service.AccessRead, req) {
 		return nil, service.ErrPermissionDenied
@@ -344,7 +349,16 @@ func (svc *Service) GetMetricConfiguration(ctx context.Context, req *orchestrato
 
 // UpdateMetricConfiguration updates the configuration for a metric, specified by the identifier in req.MetricId.
 func (svc *Service) UpdateMetricConfiguration(ctx context.Context, req *orchestrator.UpdateMetricConfigurationRequest) (res *assessment.MetricConfiguration, err error) {
-	// TODO(oxisto): Validate the request
+	// Validate request
+	if err = req.Validate(); err != nil {
+		return nil, status.Errorf(codes.InvalidArgument, "%s", err)
+	}
+
+	// Validate request configuration
+	err = req.Configuration.Validate()
+	if err != nil {
+		return nil, status.Errorf(codes.InvalidArgument, "%s", err)
+	}
 
 	// Check, if this request has access to the cloud service according to our authorization strategy.
 	if !svc.authz.CheckAccess(ctx, service.AccessRead, req) {
@@ -410,6 +424,7 @@ func (svc *Service) ListMetricConfigurations(ctx context.Context, req *orchestra
 	return
 }
 
+// GetMetricImplementation retrieves a metric implementation specified by req.MetricId.
 func (svc *Service) GetMetricImplementation(_ context.Context, req *orchestrator.GetMetricImplementationRequest) (res *assessment.MetricImplementation, err error) {
 	res = new(assessment.MetricImplementation)
 


Test patch:
diff --git a/api/assessment/metric_test.go b/api/assessment/metric_test.go
index 38319499..8e7b3375 100644
--- a/api/assessment/metric_test.go
+++ b/api/assessment/metric_test.go
@@ -1,10 +1,38 @@
+// 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 assessment
 
 import (
+	"database/sql/driver"
 	"fmt"
 	"testing"
 
+	"clouditor.io/clouditor/persistence"
 	"github.com/stretchr/testify/assert"
+	"google.golang.org/protobuf/types/known/structpb"
 )
 
 func TestMetric_Validate(t *testing.T) {
@@ -79,3 +107,291 @@ func TestMetric_Validate(t *testing.T) {
 		})
 	}
 }
+
+func TestMetricConfiguration_Validate(t *testing.T) {
+	type fields struct {
+		MetricConfiguration *MetricConfiguration
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "MetricConfiguration is nil",
+			fields: fields{
+				MetricConfiguration: nil,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, ErrMetricConfigurationMissing, err)
+			},
+		},
+		{
+			name: "MetricConfiguration Operator is empty",
+			fields: fields{
+				MetricConfiguration: &MetricConfiguration{
+					TargetValue: &structpb.Value{
+						Kind: &structpb.Value_StringValue{
+							StringValue: "1111",
+						},
+					},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, ErrMetricConfigurationOperatorMissing, err)
+			},
+		},
+		{
+			name: "MetricConfiguration TargetValue is empty",
+			fields: fields{
+				MetricConfiguration: &MetricConfiguration{
+					Operator: "TestOperator",
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, ErrMetricConfigurationTargetValueMissing, err)
+			},
+		},
+		{
+			name: "Successful Validation",
+			fields: fields{
+				MetricConfiguration: &MetricConfiguration{
+					TargetValue: &structpb.Value{
+						Kind: &structpb.Value_StringValue{
+							StringValue: "1111",
+						},
+					},
+					Operator:       "TestOperator",
+					MetricId:       "TestMetric",
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.NoError(t, err)
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := tt.fields.MetricConfiguration
+			tt.wantErr(t, c.Validate())
+		})
+	}
+}
+
+func TestRange_GormDataType(t *testing.T) {
+	tests := []struct {
+		name string
+		want string
+	}{
+		{
+			name: "Works correctly",
+			want: "jsonb",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := &Range{}
+			if got := r.GormDataType(); got != tt.want {
+				t.Errorf("Range.GormDataType() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestRange_Value(t *testing.T) {
+	type fields struct {
+		Range *Range
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    driver.Value
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Error in Range",
+			fields: fields{
+				Range: &Range{},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not marshal JSON")
+			},
+		},
+		{
+			name: "Range is empty",
+			fields: fields{
+				Range: nil,
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.NoError(t, err)
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := tt.fields.Range
+			got, err := r.Value()
+			tt.wantErr(t, err)
+			if err == nil {
+				assert.Equal(t, got, tt.want)
+			}
+		})
+	}
+}
+
+func TestRange_Scan(t *testing.T) {
+	type fields struct {
+		Range *Range
+	}
+	type args struct {
+		value interface{}
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Value of wrong type",
+			fields: fields{
+				Range: &Range{},
+			},
+			args: args{
+				value: "wrongType",
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, persistence.ErrUnsupportedType.Error())
+			},
+		},
+		{
+			name: "Error at unmarshalling",
+			fields: fields{
+				Range: &Range{},
+			},
+			args: args{
+				value: []byte{},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not unmarshal JSON")
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := tt.fields.Range
+			err := r.Scan(tt.args.value)
+			tt.wantErr(t, err)
+		})
+	}
+}
+
+func TestRange_MarshalJSON(t *testing.T) {
+	type fields struct {
+		Range *Range
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Unknown range type",
+			fields: fields{
+				Range: &Range{},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, persistence.ErrUnsupportedType.Error())
+			},
+		},
+		{
+			name: "Correct range type Range_AllowedValues",
+			fields: fields{
+				Range: &Range{
+					Range: &Range_AllowedValues{},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.NoError(t, err)
+			},
+		},
+		{
+			name: "Correct range type Range_MinMax",
+			fields: fields{
+				Range: &Range{
+					Range: &Range_MinMax{},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.NoError(t, err)
+			},
+		},
+		{
+			name: "Correct range type Range_Order",
+			fields: fields{
+				Range: &Range{
+					Range: &Range_Order{},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.NoError(t, err)
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := tt.fields.Range
+			_, err := r.MarshalJSON()
+			tt.wantErr(t, err)
+		})
+	}
+}
+
+func TestRange_UnmarshalJSON(t *testing.T) {
+	type fields struct {
+		Range *Range
+	}
+	type args struct {
+		b []byte
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Empty input",
+			fields: fields{
+				Range: &Range{},
+			},
+			args: args{
+				b: []byte{},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "unexpected end of JSON input")
+			},
+		},
+		{
+			name: "Invalid input",
+			fields: fields{
+				Range: &Range{
+					Range: &Range_AllowedValues{},
+				},
+			},
+			args: args{
+				b: []byte("Error"),
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "invalid character")
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := tt.fields.Range
+			tt.wantErr(t, r.UnmarshalJSON(tt.args.b))
+		})
+	}
+}
diff --git a/api/orchestrator/orchestrator_test.go b/api/orchestrator/orchestrator_test.go
index 81a49c6c..10caed29 100644
--- a/api/orchestrator/orchestrator_test.go
+++ b/api/orchestrator/orchestrator_test.go
@@ -28,10 +28,10 @@ package orchestrator
 import (
 	"testing"
 
-	"github.com/stretchr/testify/assert"
-
 	"clouditor.io/clouditor/api"
 	"clouditor.io/clouditor/api/assessment"
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/protobuf/types/known/structpb"
 )
 
 func TestListCertificatesRequest_Validate(t *testing.T) {
@@ -202,3 +202,143 @@ func TestListMetricsRequest_Validate(t *testing.T) {
 		})
 	}
 }
+
+func TestUpdateMetricConfigurationRequest_Validate(t *testing.T) {
+	type fields struct {
+		Request *UpdateMetricConfigurationRequest
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Missing CloudServiceId",
+			fields: fields{
+				Request: &UpdateMetricConfigurationRequest{
+					MetricId: "TestMetric",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:    "<",
+						TargetValue: &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "11111"}},
+					},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsMissing.Error())
+			},
+		},
+		{
+			name: "Wrong CloudServiceId",
+			fields: fields{
+				Request: &UpdateMetricConfigurationRequest{
+					MetricId:       "TestMetric",
+					CloudServiceId: "00000000000000000000",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:    "<",
+						TargetValue: &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "11111"}},
+					},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsInvalid.Error())
+			},
+		},
+		{
+			name: "Missing MetricId",
+			fields: fields{
+				Request: &UpdateMetricConfigurationRequest{
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:    "<",
+						TargetValue: &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "11111"}},
+					},
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrMetricIdMissing.Error())
+			},
+		},
+		{
+			name: "Without error",
+			fields: fields{
+				Request: &UpdateMetricConfigurationRequest{
+					MetricId:       "TestMetric",
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:    "<",
+						TargetValue: &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "11111"}},
+					},
+				},
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := tt.fields.Request
+			tt.wantErr(t, req.Validate())
+		})
+	}
+}
+
+func TestGetMetricConfigurationRequest_Validate(t *testing.T) {
+	type fields struct {
+		Request *GetMetricConfigurationRequest
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Missing CloudServiceId",
+			fields: fields{
+				Request: &GetMetricConfigurationRequest{
+					MetricId: "TestMetric",
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsMissing.Error())
+			},
+		},
+		{
+			name: "Wrong CloudServiceId",
+			fields: fields{
+				Request: &GetMetricConfigurationRequest{
+					MetricId:       "TestMetric",
+					CloudServiceId: "00000000000000000000",
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsInvalid.Error())
+			},
+		},
+		{
+			name: "Missing MetricId",
+			fields: fields{
+				Request: &GetMetricConfigurationRequest{
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+				},
+			},
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrMetricIdMissing.Error())
+			},
+		},
+		{
+			name: "Without error",
+			fields: fields{
+				Request: &GetMetricConfigurationRequest{
+					MetricId:       "TestMetric",
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+				},
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := tt.fields.Request
+			tt.wantErr(t, req.Validate())
+		})
+	}
+}
diff --git a/service/assessment/assessment_test.go b/service/assessment/assessment_test.go
index 3b874341..8a454386 100644
--- a/service/assessment/assessment_test.go
+++ b/service/assessment/assessment_test.go
@@ -200,8 +200,9 @@ func TestService_AssessEvidence(t *testing.T) {
 			args: args{
 				in0: context.TODO(),
 				evidence: &evidence.Evidence{
-					Timestamp: timestamppb.Now(),
-					Resource:  toStruct(voc.VirtualMachine{}, t),
+					Timestamp:      timestamppb.Now(),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Resource:       toStruct(voc.VirtualMachine{}, t),
 				},
 			},
 			hasRPCConnection: true,
@@ -216,9 +217,10 @@ func TestService_AssessEvidence(t *testing.T) {
 			args: args{
 				in0: context.TODO(),
 				evidence: &evidence.Evidence{
-					Id:       "11111111-1111-1111-1111-111111111111",
-					ToolId:   "mock",
-					Resource: toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
 				},
 			},
 			hasRPCConnection: true,
@@ -233,10 +235,11 @@ func TestService_AssessEvidence(t *testing.T) {
 			args: args{
 				in0: context.TODO(),
 				evidence: &evidence.Evidence{
-					Id:        "11111111-1111-1111-1111-111111111111",
-					ToolId:    "mock",
-					Timestamp: timestamppb.Now(),
-					Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					Timestamp:      timestamppb.Now(),
+					Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
 				},
 			},
 			hasRPCConnection: true,
@@ -250,10 +253,11 @@ func TestService_AssessEvidence(t *testing.T) {
 			args: args{
 				in0: context.TODO(),
 				evidence: &evidence.Evidence{
-					Id:        "11111111-1111-1111-1111-111111111111",
-					ToolId:    "mock",
-					Timestamp: timestamppb.Now(),
-					Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					Timestamp:      timestamppb.Now(),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
 				},
 			},
 			hasRPCConnection: false,
@@ -318,8 +322,9 @@ func TestService_AssessEvidences(t *testing.T) {
 			args: args{
 				streamToServer: createMockAssessmentServerStream(&assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Timestamp: timestamppb.Now(),
-						Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
+						Timestamp:      timestamppb.Now(),
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
+						Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
 			},
 			wantErr: false,
 			wantRespMessage: &assessment.AssessEvidenceResponse{
@@ -334,9 +339,10 @@ func TestService_AssessEvidences(t *testing.T) {
 			args: args{
 				streamToServer: createMockAssessmentServerStream(&assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Timestamp: timestamppb.Now(),
-						ToolId:    "2134",
-						Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
+						Timestamp:      timestamppb.Now(),
+						ToolId:         "2134",
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
+						Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
 			},
 			wantErr: false,
 			wantRespMessage: &assessment.AssessEvidenceResponse{
@@ -351,9 +357,10 @@ func TestService_AssessEvidences(t *testing.T) {
 			args: args{
 				streamToServer: createMockAssessmentServerStream(&assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Timestamp: timestamppb.Now(),
-						ToolId:    "2134",
-						Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
+						Timestamp:      timestamppb.Now(),
+						ToolId:         "2134",
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
+						Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
 			},
 			wantErr: false,
 			wantRespMessage: &assessment.AssessEvidenceResponse{
@@ -366,9 +373,10 @@ func TestService_AssessEvidences(t *testing.T) {
 			args: args{
 				streamToClientWithSendErr: createMockAssessmentServerStreamWithSendErr(&assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Timestamp: timestamppb.Now(),
-						ToolId:    "2134",
-						Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
+						Timestamp:      timestamppb.Now(),
+						ToolId:         "2134",
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
+						Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
 			},
 			wantErr:        true,
 			wantErrMessage: "rpc error: code = Unknown desc = cannot send response to the client",
@@ -379,9 +387,10 @@ func TestService_AssessEvidences(t *testing.T) {
 			args: args{
 				streamToServerWithRecvErr: createMockAssessmentServerStreamWithRecvErr(&assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Timestamp: timestamppb.Now(),
-						ToolId:    "2134",
-						Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
+						Timestamp:      timestamppb.Now(),
+						ToolId:         "2134",
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
+						Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t)}}),
 			},
 			wantErr:        true,
 			wantErrMessage: "rpc error: code = Unknown desc = cannot receive stream request",
@@ -469,9 +478,10 @@ func TestService_AssessmentResultHooks(t *testing.T) {
 				in0: context.TODO(),
 				evidence: &assessment.AssessEvidenceRequest{
 					Evidence: &evidence.Evidence{
-						Id:        "11111111-1111-1111-1111-111111111111",
-						ToolId:    "mock",
-						Timestamp: timestamppb.Now(),
+						Id:             "11111111-1111-1111-1111-111111111111",
+						ToolId:         "mock",
+						Timestamp:      timestamppb.Now(),
+						CloudServiceId: "00000000-0000-0000-0000-000000000000",
 						Resource: toStruct(&voc.VirtualMachine{
 							Compute: &voc.Compute{
 								Resource: &voc.Resource{
@@ -968,9 +978,10 @@ func TestService_HandleEvidence(t *testing.T) {
 			},
 			args: args{
 				evidence: &evidence.Evidence{
-					Id:        "11111111-1111-1111-1111-111111111111",
-					ToolId:    "mock",
-					Timestamp: timestamppb.Now(),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					Timestamp:      timestamppb.Now(),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
 					Resource: toStruct(voc.VirtualMachine{
 						Compute: &voc.Compute{
 							Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}},
@@ -999,10 +1010,11 @@ func TestService_HandleEvidence(t *testing.T) {
 			},
 			args: args{
 				evidence: &evidence.Evidence{
-					Id:        "11111111-1111-1111-1111-111111111111",
-					ToolId:    "mock",
-					Timestamp: timestamppb.Now(),
-					Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{}}}}, t),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					Timestamp:      timestamppb.Now(),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					Resource:       toStruct(voc.VirtualMachine{Compute: &voc.Compute{Resource: &voc.Resource{ID: "my-resource-id", Type: []string{}}}}, t),
 				},
 				resourceId: "my-resource-id",
 			},
@@ -1021,9 +1033,10 @@ func TestService_HandleEvidence(t *testing.T) {
 			},
 			args: args{
 				evidence: &evidence.Evidence{
-					Id:        "11111111-1111-1111-1111-111111111111",
-					ToolId:    "mock",
-					Timestamp: timestamppb.Now(),
+					Id:             "11111111-1111-1111-1111-111111111111",
+					ToolId:         "mock",
+					Timestamp:      timestamppb.Now(),
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
 					Resource: toStruct(voc.VirtualMachine{
 						Compute: &voc.Compute{
 							Resource: &voc.Resource{ID: "my-resource-id", Type: []string{"VirtualMachine"}},
diff --git a/service/orchestrator/metrics_test.go b/service/orchestrator/metrics_test.go
index 992563bd..792f2d65 100644
--- a/service/orchestrator/metrics_test.go
+++ b/service/orchestrator/metrics_test.go
@@ -736,7 +736,8 @@ func TestService_GetMetricConfiguration(t *testing.T) {
 			},
 			args: args{
 				req: &orchestrator.GetMetricConfigurationRequest{
-					MetricId: "NotExists",
+					MetricId:       "NotExists",
+					CloudServiceId: DefaultTargetCloudServiceId,
 				},
 			},
 			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
@@ -883,7 +884,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 		wantErr assert.ErrorAssertionFunc
 	}{
 		{
-			name: "metric does not exist",
+			name: "metricId is missing in request",
 			fields: fields{
 				storage: testutil.NewInMemoryStorage(t),
 				authz:   &service.AuthorizationStrategyAllowAll{},
@@ -891,8 +892,157 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 			args: args{
 				req: &orchestrator.UpdateMetricConfigurationRequest{
 					CloudServiceId: DefaultTargetCloudServiceId,
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrMetricIdMissing.Error())
+			},
+		},
+		{
+			name: "cloudServiceID is missing in request",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					MetricId: "MyMetric",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsMissing.Error())
+			},
+		},
+		{
+			name: "cloudServiceID is invalid in request",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					CloudServiceId: "00000000-000000000000",
 					MetricId:       "MyMetric",
-					Configuration:  &assessment.MetricConfiguration{},
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsInvalid.Error())
+			},
+		},
+		{
+			name: "configuration is missing in request",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					MetricId:       "MyMetric",
+					CloudServiceId: DefaultTargetCloudServiceId,
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrMetricConfigurationMissing.Error())
+			},
+		},
+		{
+			name: "metricId is missing in configuration",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					MetricId:       "MyMetric",
+					CloudServiceId: DefaultTargetCloudServiceId,
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrMetricIdMissing.Error())
+			},
+		},
+		{
+			name: "cloudServiceId is missing in configuration",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					MetricId:       "MyMetric",
+					CloudServiceId: DefaultTargetCloudServiceId,
+					Configuration: &assessment.MetricConfiguration{
+						Operator:    "<",
+						TargetValue: &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:    "MyMetric",
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsMissing.Error())
+			},
+		},
+		{
+			name: "cloudServiceId is invalid in configuration",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					MetricId:       "MyMetric",
+					CloudServiceId: DefaultTargetCloudServiceId,
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: "00000000-000000000000",
+					},
+				},
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, assessment.ErrCloudServiceIDIsInvalid.Error())
+			},
+		},
+		{
+			name: "metric does not exist in storage",
+			fields: fields{
+				storage: testutil.NewInMemoryStorage(t),
+				authz:   &service.AuthorizationStrategyAllowAll{},
+			},
+			args: args{
+				req: &orchestrator.UpdateMetricConfigurationRequest{
+					CloudServiceId: DefaultTargetCloudServiceId,
+					MetricId:       "MyMetric",
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
 				},
 			},
 			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
@@ -900,7 +1050,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 			},
 		},
 		{
-			name: "service does not exist",
+			name: "cloudService does not exist in storage",
 			fields: fields{
 				storage: testutil.NewInMemoryStorage(t, func(s persistence.Storage) {
 					_ = s.Create(assessment.Metric{Id: "MyMetric"})
@@ -909,9 +1059,14 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 			},
 			args: args{
 				req: &orchestrator.UpdateMetricConfigurationRequest{
-					CloudServiceId: "MyService",
+					CloudServiceId: DefaultTargetCloudServiceId,
 					MetricId:       "MyMetric",
-					Configuration:  &assessment.MetricConfiguration{},
+					Configuration: &assessment.MetricConfiguration{
+						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
+						MetricId:       "MyMetric",
+						CloudServiceId: DefaultTargetCloudServiceId,
+					},
 				},
 			},
 			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
@@ -935,6 +1090,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 						CloudServiceId: DefaultTargetCloudServiceId,
 						MetricId:       MockMetricID,
 						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
 					},
 				},
 			},
@@ -975,6 +1131,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 						CloudServiceId: DefaultTargetCloudServiceId,
 						MetricId:       MockMetricID,
 						Operator:       "<",
+						TargetValue:    &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "1111"}},
 					},
 				},
 			},
diff --git a/service/orchestrator/orchestrator_test.go b/service/orchestrator/orchestrator_test.go
index 1ea0facc..2870985e 100644
--- a/service/orchestrator/orchestrator_test.go
+++ b/service/orchestrator/orchestrator_test.go
@@ -122,9 +122,11 @@ func TestAssessmentResultHook(t *testing.T) {
 						EvidenceId: "11111111-1111-1111-1111-111111111111",
 						Timestamp:  timestamppb.Now(),
 						MetricConfiguration: &assessment.MetricConfiguration{
-							TargetValue: toStruct(1.0),
-							Operator:    "operator",
-							IsDefault:   true,
+							TargetValue:    toStruct(1.0),
+							Operator:       "operator",
+							IsDefault:      true,
+							CloudServiceId: "00000000-0000-0000-0000-000000000000",
+							MetricId:       "TestMetric",
 						},
 						NonComplianceComments: "non_compliance_comment",
 						Compliant:             true,
@@ -184,9 +186,11 @@ func TestStoreAssessmentResult(t *testing.T) {
 						EvidenceId: "11111111-1111-1111-1111-111111111111",
 						Timestamp:  timestamppb.Now(),
 						MetricConfiguration: &assessment.MetricConfiguration{
-							TargetValue: toStruct(1.0),
-							Operator:    "operator",
-							IsDefault:   true,
+							TargetValue:    toStruct(1.0),
+							Operator:       "operator",
+							IsDefault:      true,
+							CloudServiceId: "00000000-0000-0000-0000-000000000000",
+							MetricId:       "TestMetric",
 						},
 						NonComplianceComments: "non_compliance_comment",
 						Compliant:             true,
@@ -210,9 +214,11 @@ func TestStoreAssessmentResult(t *testing.T) {
 						EvidenceId: "11111111-1111-1111-1111-111111111111",
 						Timestamp:  timestamppb.Now(),
 						MetricConfiguration: &assessment.MetricConfiguration{
-							TargetValue: toStruct(1.0),
-							Operator:    "operator",
-							IsDefault:   true,
+							TargetValue:    toStruct(1.0),
+							Operator:       "operator",
+							IsDefault:      true,
+							CloudServiceId: "00000000-0000-0000-0000-000000000000",
+							MetricId:       "TestMetric",
 						},
 						NonComplianceComments: "non_compliance_comment",
 						Compliant:             true,
@@ -390,9 +396,11 @@ func createStoreAssessmentResultRequestsMock(count int) []*orchestrator.StoreAss
 				EvidenceId: "11111111-1111-1111-1111-111111111111",
 				Timestamp:  timestamppb.Now(),
 				MetricConfiguration: &assessment.MetricConfiguration{
-					TargetValue: toStruct(1.0),
-					Operator:    fmt.Sprintf("operator%d", i),
-					IsDefault:   true,
+					TargetValue:    toStruct(1.0),
+					Operator:       fmt.Sprintf("operator%d", i),
+					IsDefault:      true,
+					CloudServiceId: "00000000-0000-0000-0000-000000000000",
+					MetricId:       "TestMetric",
 				},
 				NonComplianceComments: "non_compliance_comment",
 				Compliant:             true,
@@ -577,6 +585,34 @@ func TestNewService(t *testing.T) {
 				return assert.Equal(tt, myStorage, service.storage)
 			},
 		},
+		{
+			name: "New service with catalogs file",
+			args: args{
+				opts: []ServiceOption{WithCatalogsFile("catalogsfile.json")},
+			},
+			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, "catalogsfile.json", service.catalogsFile)
+			},
+		},
+		{
+			name: "New service with metrics file",
+			args: args{
+				opts: []ServiceOption{WithMetricsFile("metricsfile.json")},
+			},
+			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, "metricsfile.json", service.metricsFile)
+			},
+		},
 	}
 
 	for _, tt := range tests {