Skip to content

Commit 9c89f23

Browse files
authored
unit tests for notebook lifecycle instance (#113)
Description of changes: Sdk.go 93.1% coverage Custom code coverage >90% By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 4d25cfa commit 9c89f23

18 files changed

+526
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package notebook_instance_lifecycle_config
15+
16+
import (
17+
"errors"
18+
"fmt"
19+
20+
"path/filepath"
21+
"testing"
22+
23+
ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
24+
ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics"
25+
acktypes "github.com/aws-controllers-k8s/runtime/pkg/types"
26+
svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1"
27+
"github.com/aws-controllers-k8s/sagemaker-controller/pkg/testutil"
28+
mocksvcsdkapi "github.com/aws-controllers-k8s/sagemaker-controller/test/mocks/aws-sdk-go/sagemaker"
29+
svcsdk "github.com/aws/aws-sdk-go/service/sagemaker"
30+
"github.com/ghodss/yaml"
31+
"github.com/google/go-cmp/cmp"
32+
"github.com/google/go-cmp/cmp/cmpopts"
33+
"go.uber.org/zap/zapcore"
34+
ctrlrtzap "sigs.k8s.io/controller-runtime/pkg/log/zap"
35+
)
36+
37+
// provideResourceManagerWithMockSDKAPI accepts MockSageMakerAPI and returns pointer to resourceManager
38+
// the returned resourceManager is configured to use mockapi api.
39+
func provideResourceManagerWithMockSDKAPI(mockSageMakerAPI *mocksvcsdkapi.SageMakerAPI) *resourceManager {
40+
zapOptions := ctrlrtzap.Options{
41+
Development: true,
42+
Level: zapcore.InfoLevel,
43+
}
44+
fakeLogger := ctrlrtzap.New(ctrlrtzap.UseFlagOptions(&zapOptions))
45+
return &resourceManager{
46+
rr: nil,
47+
awsAccountID: "",
48+
awsRegion: "",
49+
sess: nil,
50+
sdkapi: mockSageMakerAPI,
51+
log: fakeLogger,
52+
metrics: ackmetrics.NewMetrics("sagemaker"),
53+
}
54+
}
55+
56+
// TestNotebookInstanceLifecycleConfigTestSuite runs the test suite for notebook instance lifecycle config
57+
func TestNotebookInstanceLifeCycleConfigTestSuite(t *testing.T) {
58+
var ts = testutil.TestSuite{}
59+
testutil.LoadFromFixture(filepath.Join("testdata", "test_suite.yaml"), &ts)
60+
var delegate = testRunnerDelegate{t: t}
61+
var runner = testutil.TestSuiteRunner{TestSuite: &ts, Delegate: &delegate}
62+
runner.RunTests()
63+
}
64+
65+
// testRunnerDelegate implements testutil.TestRunnerDelegate
66+
type testRunnerDelegate struct {
67+
t *testing.T
68+
}
69+
70+
func (d *testRunnerDelegate) ResourceDescriptor() acktypes.AWSResourceDescriptor {
71+
return &resourceDescriptor{}
72+
}
73+
74+
func (d *testRunnerDelegate) ResourceManager(mocksdkapi *mocksvcsdkapi.SageMakerAPI) acktypes.AWSResourceManager {
75+
return provideResourceManagerWithMockSDKAPI(mocksdkapi)
76+
}
77+
78+
func (d *testRunnerDelegate) GoTestRunner() *testing.T {
79+
return d.t
80+
}
81+
82+
func (d *testRunnerDelegate) EmptyServiceAPIOutput(apiName string) (interface{}, error) {
83+
if apiName == "" {
84+
return nil, errors.New("no API name specified")
85+
}
86+
//TODO: use reflection, template to auto generate this block/method.
87+
switch apiName {
88+
case "CreateNotebookInstanceLifecycleConfigWithContext":
89+
var output svcsdk.CreateNotebookInstanceLifecycleConfigOutput
90+
return &output, nil
91+
case "DescribeNotebookInstanceLifecycleConfigWithContext":
92+
var output svcsdk.DescribeNotebookInstanceLifecycleConfigOutput
93+
return &output, nil
94+
case "DeleteNotebookInstanceLifecycleConfigWithContext":
95+
var output svcsdk.DeleteNotebookInstanceLifecycleConfigOutput
96+
return &output, nil
97+
}
98+
return nil, errors.New(fmt.Sprintf("no matching API name found for: %s", apiName))
99+
}
100+
101+
func (d *testRunnerDelegate) Equal(a acktypes.AWSResource, b acktypes.AWSResource) bool {
102+
ac := a.(*resource)
103+
bc := b.(*resource)
104+
// Ignore LastTransitionTime since it gets updated each run.
105+
opts := []cmp.Option{cmpopts.EquateEmpty(), cmpopts.IgnoreFields(ackv1alpha1.Condition{}, "LastTransitionTime"),
106+
cmpopts.IgnoreFields(svcapitypes.NotebookInstanceLifecycleConfigStatus{}, "CreationTime"),
107+
cmpopts.IgnoreFields(svcapitypes.NotebookInstanceLifecycleConfigStatus{}, "LastModifiedTime")}
108+
109+
var specMatch = false
110+
if cmp.Equal(ac.ko.Spec, bc.ko.Spec, opts...) {
111+
specMatch = true
112+
} else {
113+
fmt.Printf("Difference ko.Spec (-expected +actual):\n\n")
114+
fmt.Println(cmp.Diff(ac.ko.Spec, bc.ko.Spec, opts...))
115+
specMatch = false
116+
}
117+
118+
var statusMatch = false
119+
if cmp.Equal(ac.ko.Status, bc.ko.Status, opts...) {
120+
statusMatch = true
121+
} else {
122+
fmt.Printf("Difference ko.Status (-expected +actual):\n\n")
123+
fmt.Println(cmp.Diff(ac.ko.Status, bc.ko.Status, opts...))
124+
statusMatch = false
125+
}
126+
127+
return statusMatch && specMatch
128+
}
129+
130+
// Checks to see if the given yaml file, with name stored as expectation,
131+
// matches the yaml marshal of the AWSResource stored as actual.
132+
func (d *testRunnerDelegate) YamlEqual(expectation string, actual acktypes.AWSResource) bool {
133+
// Build a tmp file for the actual yaml.
134+
actualResource := actual.(*resource)
135+
actualYamlByteArray, _ := yaml.Marshal(actualResource.ko)
136+
return testutil.IsYamlEqual(&expectation, &actualYamlByteArray)
137+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"NotebookInstanceLifecycleConfigArn": "arn:aws:sagemaker:us-west-2:123456789012:notebook-instance-lifecycle-config/unit-testing-notebook-instance-lifecycle-config"
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"CreationTime": "2021-09-22T06:13:00.841Z",
3+
"LastModifiedTime": "2021-09-22T06:13:00.841Z",
4+
"NotebookInstanceLifecycleConfigArn": "arn:aws:sagemaker:us-west-2:123456789012:notebook-instance-lifecycle-config/unit-testing-notebook-instance-lifecycle-config",
5+
"NotebookInstanceLifecycleConfigName": "unit-testing-notebook-instance-lifecycle-config",
6+
"OnCreate": [
7+
{
8+
"Content": "ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi"
9+
}
10+
],
11+
"OnStart": [
12+
{
13+
"Content": "ZWNobyAiRW50ZXJpbmcgb25TdGFydCI="
14+
}
15+
]
16+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
tests:
2+
- name: "Notebook instance lifecycle config create tests"
3+
description: "Testing create operation"
4+
scenarios:
5+
- name: "Create=InvalidInput"
6+
description: "Given one of the parameters is invalid, ko.Status shows a terminal condition"
7+
given:
8+
desired_state: "v1alpha1/create/desired/invalid_before_create.yaml"
9+
svc_api:
10+
- operation: CreateNotebookInstanceLifecycleConfigWithContext
11+
error:
12+
code: InvalidParameterValue
13+
message: "The notebook instance lifecycle config name must not include a special character."
14+
invoke: Create
15+
expect:
16+
latest_state: "v1alpha1/create/observed/invalid_create_attempted.yaml"
17+
error: resource is in terminal condition
18+
- name: "Create=Valid"
19+
description: "Create a new notebook instance lifecycle config successfully (ARN in status)."
20+
given:
21+
desired_state: "v1alpha1/create/desired/success_before_create.yaml"
22+
svc_api:
23+
- operation: CreateNotebookInstanceLifecycleConfigWithContext
24+
output_fixture: "sdkapi/create/create_success.json"
25+
invoke: Create
26+
expect:
27+
latest_state: "v1alpha1/create/observed/success_after_create.yaml"
28+
error: nil
29+
- name: "Notebook instance lifecycle config readOne tests"
30+
description: "Testing the readOne operation"
31+
scenarios:
32+
- name: "ReadOne=MissingRequiredField"
33+
description: "Testing readOne when required field is missing. No API call is made and returns error."
34+
given:
35+
desired_state: "v1alpha1/readone/desired/missing_required_field.yaml"
36+
invoke: ReadOne
37+
expect:
38+
error: "resource not found"
39+
- name: "ReadOne=SuccessClearsConditions"
40+
description: Testing a successful reconciliation clears conditions if terminal/recoverable condition were already set to true
41+
given:
42+
desired_state: "v1alpha1/readone/desired/error_conditions_true.yaml"
43+
svc_api:
44+
- operation: DescribeNotebookInstanceLifecycleConfigWithContext
45+
output_fixture: "sdkapi/describe/success_describe.json"
46+
invoke: ReadOne
47+
expect:
48+
latest_state: "v1alpha1/readone/observed/conditions_clear_on_success.yaml"
49+
- name: "ReadOne=NotFound"
50+
description: "Testing readOne when Describe fails to find the resource on SageMaker"
51+
given:
52+
desired_state: "v1alpha1/readone/desired/right_after_create.yaml"
53+
svc_api:
54+
- operation: DescribeNotebookInstanceLifecycleConfigWithContext
55+
error:
56+
code: ValidationException
57+
message: "Unable to describe Notebook Instance Lifecycle Config unit-testing-notebook-instance-lifecycle-config"
58+
invoke: ReadOne
59+
expect:
60+
error: "resource not found"
61+
- name: "ReadOne=Fail"
62+
description: "This test checks if the condition is updated if describe fails and readOne returns error"
63+
given:
64+
desired_state: "v1alpha1/readone/desired/right_after_create.yaml"
65+
svc_api:
66+
- operation: DescribeNotebookInstanceLifecycleConfigWithContext
67+
error:
68+
code: ServiceUnavailable
69+
message: "Server is down"
70+
invoke: ReadOne
71+
expect:
72+
latest_state: "v1alpha1/readone/observed/error_on_describe.yaml"
73+
error: "ServiceUnavailable: Server is down\n\tstatus code: 0, request id: "
74+
- name: "ReadOne=AfterCreate"
75+
description: "Testing readOne right after create"
76+
given:
77+
desired_state: "v1alpha1/readone/desired/right_after_create.yaml"
78+
svc_api:
79+
- operation: DescribeNotebookInstanceLifecycleConfigWithContext
80+
output_fixture: "sdkapi/describe/success_describe.json"
81+
invoke: ReadOne
82+
expect:
83+
latest_state: "v1alpha1/readone/observed/creating_after_describe.yaml"
84+
- name: "Notebook instance lifecycle config update tests"
85+
description: "Testing the Update operation"
86+
scenarios:
87+
- name: "Update=StatusUpdating"
88+
description: "This test checks if the notebook instance lifecycle config requeues after Updating "
89+
given:
90+
desired_state: "v1alpha1/update/desired/update_common.yaml"
91+
latest_state: "v1alpha1/update/desired/update_common.yaml"
92+
svc_api:
93+
- operation: UpdateNotebookInstanceLifecycleConfigWithContext
94+
invoke: Update
95+
expect:
96+
latest_state: "v1alpha1/update/observed/update_attempted_success.yaml"
97+
error: NotebookInstanceLifecycleConfig is updating.
98+
- name: "Update=Fail"
99+
description: "This test checks if the recoverable condition is updated if update fails and returns error"
100+
given:
101+
desired_state: "v1alpha1/update/desired/update_common.yaml"
102+
latest_state: "v1alpha1/readone/desired/right_after_create.yaml"
103+
svc_api:
104+
- operation: UpdateNotebookInstanceLifecycleConfigWithContext
105+
error:
106+
code: ServiceUnavailable
107+
message: "Server is down"
108+
invoke: Update
109+
expect:
110+
latest_state: "v1alpha1/update/observed/error_on_update.yaml"
111+
error: "ServiceUnavailable: Server is down\n\tstatus code: 0, request id: "
112+
- name: "Notebook instance lifecycle config delete tests"
113+
description: "Testing the delete operation"
114+
scenarios:
115+
- name: "Delete=Fail"
116+
description: "This test checks if the condition is updated if delete fails and returns error"
117+
given:
118+
desired_state: "v1alpha1/delete/desired/inservice_before_delete.yaml"
119+
svc_api:
120+
- operation: DeleteNotebookInstanceLifecycleConfigWithContext
121+
error:
122+
code: ServiceUnavailable
123+
message: "Server is down"
124+
invoke: Delete
125+
expect:
126+
error: "ServiceUnavailable: Server is down\n\tstatus code: 0, request id: "
127+
- name: "Delete=Successful"
128+
description: "This test checks if the notebook instance lifecycle config is deleted successfully"
129+
given:
130+
desired_state: "v1alpha1/delete/desired/inservice_before_delete.yaml"
131+
svc_api:
132+
- operation: DeleteNotebookInstanceLifecycleConfigWithContext
133+
invoke: Delete
134+
expect:
135+
error: nil
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
name: unit-testing-notebook-instance-lifecycle-config
5+
spec:
6+
notebookInstanceLifecycleConfigName: intentionally@invalid-name
7+
onStart:
8+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
9+
onCreate:
10+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
name: unit-testing-notebook-instance-lifecycle-config
5+
spec:
6+
notebookInstanceLifecycleConfigName: unit-testing-notebook-instance-lifecycle-config
7+
onStart:
8+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
9+
onCreate:
10+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
creationTimestamp: null
5+
name: unit-testing-notebook-instance-lifecycle-config
6+
spec:
7+
notebookInstanceLifecycleConfigName: intentionally@invalid-name
8+
onCreate:
9+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
10+
onStart:
11+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
12+
status:
13+
ackResourceMetadata:
14+
ownerAccountID: ""
15+
conditions:
16+
- message: "InvalidParameterValue: The notebook instance lifecycle config name must
17+
not include a special character.\n\tstatus code: 0, request id: "
18+
status: "True"
19+
type: ACK.Terminal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
creationTimestamp: null
5+
name: unit-testing-notebook-instance-lifecycle-config
6+
spec:
7+
notebookInstanceLifecycleConfigName: unit-testing-notebook-instance-lifecycle-config
8+
onCreate:
9+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
10+
onStart:
11+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
12+
status:
13+
ackResourceMetadata:
14+
arn: arn:aws:sagemaker:us-west-2:123456789012:notebook-instance-lifecycle-config/unit-testing-notebook-instance-lifecycle-config
15+
ownerAccountID: ""
16+
conditions: []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
creationTimestamp: null
5+
name: unit-testing-notebook-instance-lifecycle-config
6+
spec:
7+
notebookInstanceLifecycleConfigName: unit-testing-notebook-instance-lifecycle-config
8+
onCreate:
9+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
10+
onStart:
11+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
12+
status:
13+
ackResourceMetadata:
14+
arn: arn:aws:sagemaker:us-west-2:123456789012:notebook-instance-lifecycle-config/unit-testing-notebook-instance-lifecycle-config
15+
ownerAccountID: ""
16+
conditions:
17+
- status: "True"
18+
type: ACK.ResourceSynced
19+
creationTime: "0001-01-01T00:00:00Z"
20+
lastModifiedTime: "0001-01-01T00:00:00Z"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: sagemaker.services.k8s.aws/v1alpha1
2+
kind: NotebookInstanceLifecycleConfig
3+
metadata:
4+
creationTimestamp: null
5+
name: unit-testing-notebook-instance-lifecycle-config
6+
spec:
7+
notebookInstanceLifecycleConfigName: intentionally@invalid-name
8+
onCreate:
9+
- content: ZWNobyAiRW50ZXJpbmcgb25DcmVhdGUi
10+
onStart:
11+
- content: ZWNobyAiRW50ZXJpbmcgb25TdGFydCI=
12+
status:
13+
ackResourceMetadata:
14+
ownerAccountID: ""
15+
conditions:
16+
- message: "InvalidParameterValue: The notebook instance lifecycle config name must
17+
not include a special character.\n\tstatus code: 0, request id: "
18+
status: "True"
19+
type: ACK.Terminal

0 commit comments

Comments
 (0)