Skip to content

Commit 07b5b3c

Browse files
szwedmkyma-gopher-bot
authored andcommitted
Service Manager client ServiceOfferings test (#703)
Add unit test for Service Manager client and ServiceOfferings endpoint
1 parent f531026 commit 07b5b3c

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed

internal/service-manager/client.go

+4
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ func (c *Client) SetHTTPClient(httpClient *http.Client) {
131131
c.httpClient = httpClient
132132
}
133133

134+
func (c *Client) SetSMURL(smURL string) {
135+
c.smURL = smURL
136+
}
137+
134138
func (c *Client) ServiceOfferings() (*types.ServiceOfferings, error) {
135139
req, err := http.NewRequest(http.MethodGet, c.smURL+ServiceOfferingsPath, nil)
136140
if err != nil {
+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package servicemanager_test
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"log"
8+
"log/slog"
9+
"net/http"
10+
"net/http/httptest"
11+
"os"
12+
"testing"
13+
14+
servicemanager "github.com/kyma-project/btp-manager/internal/service-manager"
15+
"github.com/kyma-project/btp-manager/internal/service-manager/types"
16+
"github.com/stretchr/testify/assert"
17+
"github.com/stretchr/testify/require"
18+
corev1 "k8s.io/api/core/v1"
19+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+
)
21+
22+
const (
23+
serviceOfferingsJSONPath = "testdata/service_offerings.json"
24+
)
25+
26+
func TestClient(t *testing.T) {
27+
// given
28+
secretProvider := newFakeSecretProvider()
29+
srv, err := initFakeServer()
30+
require.NoError(t, err)
31+
32+
srv.Start()
33+
defer srv.Close()
34+
httpClient := srv.Client()
35+
url := srv.URL
36+
37+
t.Run("should get service offerings available for the default credentials", func(t *testing.T) {
38+
// given
39+
ctx := context.TODO()
40+
secretProvider.AddSecret(defaultSecret())
41+
smClient := servicemanager.NewClient(ctx, slog.Default(), secretProvider)
42+
43+
var expectedServiceOfferings types.ServiceOfferings
44+
soJSON, err := getResourcesFromJSONFile(serviceOfferingsJSONPath)
45+
require.NoError(t, err)
46+
47+
soBytes, err := json.Marshal(soJSON)
48+
require.NoError(t, err)
49+
50+
err = json.Unmarshal(soBytes, &expectedServiceOfferings)
51+
require.NoError(t, err)
52+
53+
// when
54+
err = smClient.Defaults(ctx)
55+
56+
// then
57+
require.NoError(t, err)
58+
59+
// given
60+
smClient.SetHTTPClient(httpClient)
61+
smClient.SetSMURL(url)
62+
63+
// when
64+
so, err := smClient.ServiceOfferings()
65+
66+
// then
67+
require.NoError(t, err)
68+
assert.Len(t, so.ServiceOfferings, 4)
69+
assert.ElementsMatch(t, expectedServiceOfferings.ServiceOfferings, so.ServiceOfferings)
70+
})
71+
}
72+
73+
func initFakeServer() (*httptest.Server, error) {
74+
smHandler, err := newFakeSMHandler()
75+
if err != nil {
76+
return nil, fmt.Errorf("while creating new fake SM handler: %w", err)
77+
}
78+
79+
mux := http.NewServeMux()
80+
mux.HandleFunc("GET /v1/service_offerings", smHandler.getServiceOfferings)
81+
82+
srv := httptest.NewUnstartedServer(mux)
83+
84+
return srv, nil
85+
}
86+
87+
type fakeSMHandler struct {
88+
serviceOfferings map[string]interface{}
89+
}
90+
91+
func newFakeSMHandler() (*fakeSMHandler, error) {
92+
so, err := getResourcesFromJSONFile(serviceOfferingsJSONPath)
93+
if err != nil {
94+
return nil, fmt.Errorf("while getting service offerings from JSON file: %w", err)
95+
}
96+
97+
return &fakeSMHandler{serviceOfferings: so}, nil
98+
}
99+
100+
func getResourcesFromJSONFile(jsonFilePath string) (map[string]interface{}, error) {
101+
var buf map[string]interface{}
102+
f, err := os.Open(jsonFilePath)
103+
defer f.Close()
104+
if err != nil {
105+
return nil, fmt.Errorf("while reading resources from JSON file: %w", err)
106+
}
107+
108+
d := json.NewDecoder(f)
109+
if err := d.Decode(&buf); err != nil {
110+
return nil, fmt.Errorf("while decoding resources JSON: %w", err)
111+
}
112+
return buf, nil
113+
}
114+
115+
func (h *fakeSMHandler) getServiceOfferings(w http.ResponseWriter, r *http.Request) {
116+
data, err := json.Marshal(h.serviceOfferings)
117+
if err != nil {
118+
log.Println("error while marshalling service offerings data: %w", err)
119+
w.WriteHeader(http.StatusInternalServerError)
120+
return
121+
}
122+
123+
w.WriteHeader(http.StatusOK)
124+
if _, err = w.Write(data); err != nil {
125+
w.WriteHeader(http.StatusBadRequest)
126+
log.Println("error while writing service offerings data: %w", err)
127+
return
128+
}
129+
}
130+
131+
type fakeSecretProvider struct {
132+
secrets []*corev1.Secret
133+
}
134+
135+
func newFakeSecretProvider() *fakeSecretProvider {
136+
return &fakeSecretProvider{secrets: make([]*corev1.Secret, 0)}
137+
}
138+
139+
func (p *fakeSecretProvider) AddSecret(secret *corev1.Secret) {
140+
p.secrets = append(p.secrets, secret)
141+
}
142+
143+
func (p *fakeSecretProvider) GetByNameAndNamespace(ctx context.Context, name, namespace string) (*corev1.Secret, error) {
144+
for _, secret := range p.secrets {
145+
if secret.Name == name && secret.Namespace == namespace {
146+
return secret, nil
147+
}
148+
}
149+
return nil, fmt.Errorf("secret not found")
150+
}
151+
152+
func (p *fakeSecretProvider) clean() {
153+
p.secrets = make([]*corev1.Secret, 0)
154+
}
155+
156+
func defaultSecret() *corev1.Secret {
157+
return &corev1.Secret{
158+
ObjectMeta: metav1.ObjectMeta{
159+
Name: "sap-btp-service-operator",
160+
Namespace: "kyma-system",
161+
},
162+
StringData: map[string]string{
163+
"clientid": "default-client-id",
164+
"clientsecret": "default-client-secret",
165+
"sm_url": "https://default-sm-url.local",
166+
"tokenurl": "https://default-token-url.local",
167+
"tokenurlsuffix": "/oauth/token",
168+
},
169+
}
170+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"num_items": 4,
3+
"items": [
4+
{
5+
"id": "fc26622b-aeb2-4f3c-95da-8eb337a26883",
6+
"ready": true,
7+
"name": "service1",
8+
"description": "Service 1 description",
9+
"bindable": true,
10+
"instances_retrievable": false,
11+
"bindings_retrievable": false,
12+
"plan_updateable": false,
13+
"allow_context_updates": false,
14+
"metadata": {
15+
"createBindingDocumentationUrl": "https://service1-test.local/create-binding.html",
16+
"discoveryCenterUrl": "https://discovery-center.local/serviceCatalog/service1",
17+
"displayName": "Service 1",
18+
"documentationUrl": "https://service1-test.local/documentation.html",
19+
"imageUrl": "data:image/svg+xml;base64,c2VydmljZTEK",
20+
"longDescription": "Service 1 long description",
21+
"serviceInventoryId": "SERVICE-1",
22+
"shareable": true,
23+
"supportUrl": "https://service1-test.local/support.html"
24+
},
25+
"broker_id": "9352e325-d62b-4cc9-bf73-b2db77a343aa",
26+
"catalog_id": "a5b7679a-36e6-4c85-8f4f-ab7a1ab1ac16",
27+
"catalog_name": "service1",
28+
"created_at": "2024-01-11T01:01:00",
29+
"updated_at": "2024-01-12T02:02:00"
30+
},
31+
{
32+
"id": "1d87e6e3-12b4-4a04-82bb-ddf7299d52c9",
33+
"ready": true,
34+
"name": "service2",
35+
"description": "Service 2 description",
36+
"bindable": true,
37+
"instances_retrievable": false,
38+
"bindings_retrievable": false,
39+
"plan_updateable": true,
40+
"allow_context_updates": false,
41+
"tags": [
42+
"service",
43+
"two"
44+
],
45+
"metadata": {
46+
"shareable": false,
47+
"displayName": "Service 2"
48+
},
49+
"broker_id": "c208c628-4600-4bfc-b92c-dc1e2bd8632b",
50+
"catalog_id": "fb4035a9-75ec-4049-9b5f-b9f19af48e9a",
51+
"catalog_name": "service2",
52+
"created_at": "2024-02-11T02:02:00",
53+
"updated_at": "2024-02-12T03:03:00"
54+
},
55+
{
56+
"id": "7df0a7a5-3bda-4af4-9fdf-763ad4c01bb7",
57+
"ready": true,
58+
"name": "service3",
59+
"description": "Service 3 description",
60+
"bindable": true,
61+
"instances_retrievable": true,
62+
"bindings_retrievable": false,
63+
"plan_updateable": false,
64+
"allow_context_updates": false,
65+
"tags": [
66+
"service-three"
67+
],
68+
"metadata": {
69+
"documentationUrl": "https://service3-test.local/documentation.html",
70+
"serviceInventoryId": "SERVICE-3",
71+
"displayName": "Service 3",
72+
"imageUrl": "data:image/png;base64,c2VydmljZTMK"
73+
},
74+
"broker_id": "f5ccc8b2-9723-4ae9-834a-46c00d177df7",
75+
"catalog_id": "service3-service-broker",
76+
"catalog_name": "service3",
77+
"created_at": "2024-03-11T03:03:00",
78+
"updated_at": "2024-03-12T04:04:00"
79+
},
80+
{
81+
"id": "69e313ac-6633-4fa9-a9d5-8aaf569bc966",
82+
"ready": true,
83+
"name": "service4",
84+
"description": "Service 4 description",
85+
"bindable": true,
86+
"instances_retrievable": false,
87+
"bindings_retrievable": false,
88+
"plan_updateable": false,
89+
"allow_context_updates": false,
90+
"tags": [
91+
"service",
92+
"four",
93+
"foursvc"
94+
],
95+
"metadata": {
96+
"longDescription": "Service 4 long description",
97+
"documentationUrl": "https://service4-test.local/documentation.html",
98+
"providerDisplayName": "GREAT COMPANY",
99+
"serviceInventoryId": "SERVICE-4",
100+
"displayName": "Service 4",
101+
"imageUrl": "data:image/svg+xml;base64,c2VydmljZTQK",
102+
"supportUrl": "https://service4-test.local/support.html"
103+
},
104+
"broker_id": "c94651ef-9997-485c-aa65-5ef5ed64e619",
105+
"catalog_id": "1277f478-9670-4d62-a068-039329ff086b",
106+
"catalog_name": "service4",
107+
"created_at": "2024-04-11T04:04:00",
108+
"updated_at": "2024-04-12T05:05:00"
109+
}
110+
]
111+
}

0 commit comments

Comments
 (0)