Skip to content

Commit 6195aa2

Browse files
Merge pull request #197 from sailpoint-oss/fix/customizerCommands
fix cuztomizer and write tests
2 parents 114de31 + 42990c6 commit 6195aa2

14 files changed

+1302
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ cmd/transform/test_data/test_create.json
2121
cmd/transform/test_data/test_update.json
2222
example_transforms
2323
config.json
24+
gotest.log

cmd/api/test_data/test_create.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"attributes":{"accountFilter":"!(nativeIdentity.startsWith(\"*DELETED*\"))","accountPropertyFilter":"(groups.containsAll({'Admin'}) || location == 'Austin')","accountReturnFirstLink":false,"accountSortAttribute":"created","accountSortDescending":false,"attributeName":"DEPARTMENT","input":{"attributes":{"attributeName":"first_name","sourceName":"Source"},"type":"accountAttribute"},"requiresPeriodicRefresh":false,"sourceName":"Workday"},"name":"DvFMubXmxzqopJQh","type":"dateFormat"}
1+
{"attributes":{"accountFilter":"!(nativeIdentity.startsWith(\"*DELETED*\"))","accountPropertyFilter":"(groups.containsAll({'Admin'}) || location == 'Austin')","accountReturnFirstLink":false,"accountSortAttribute":"created","accountSortDescending":false,"attributeName":"DEPARTMENT","input":{"attributes":{"attributeName":"first_name","sourceName":"Source"},"type":"accountAttribute"},"requiresPeriodicRefresh":false,"sourceName":"Workday"},"name":"xQTfLofnWtrDsrbX","type":"dateFormat"}

cmd/api/test_data/test_update.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"attributes":{"accountFilter":"!(nativeIdentity.startsWith(\"*DELETED*\"))","accountPropertyFilter":"(groups.containsAll({'Admin'}) || location == 'Austin')","accountReturnFirstLink":false,"accountSortAttribute":"created","accountSortDescending":false,"attributeName":"UPDATED_DEPARTMENT","input":{"attributes":{"attributeName":"first_name","sourceName":"Source"},"type":"accountAttribute"},"requiresPeriodicRefresh":false,"sourceName":"Workday"},"name":"DvFMubXmxzqopJQh","type":"dateFormat"}
1+
{"attributes":{"accountFilter":"!(nativeIdentity.startsWith(\"*DELETED*\"))","accountPropertyFilter":"(groups.containsAll({'Admin'}) || location == 'Austin')","accountReturnFirstLink":false,"accountSortAttribute":"created","accountSortDescending":false,"attributeName":"UPDATED_DEPARTMENT","input":{"attributes":{"attributeName":"first_name","sourceName":"Source"},"type":"accountAttribute"},"requiresPeriodicRefresh":false,"sourceName":"Workday"},"name":"xQTfLofnWtrDsrbX","type":"dateFormat"}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// connector/create_test.go
2+
package connector
3+
4+
import (
5+
"bytes"
6+
"context"
7+
"encoding/json"
8+
"fmt"
9+
"io"
10+
"net/http"
11+
"strings"
12+
"testing"
13+
14+
"github.com/golang/mock/gomock"
15+
"github.com/sailpoint-oss/sailpoint-cli/internal/mocks"
16+
"github.com/sailpoint-oss/sailpoint-cli/internal/util"
17+
)
18+
19+
func TestNewCustomizerCreateCmd_missingArg(t *testing.T) {
20+
ctrl := gomock.NewController(t)
21+
defer ctrl.Finish()
22+
23+
mockClient := mocks.NewMockClient(ctrl)
24+
// Post should never be called when arg is missing
25+
mockClient.
26+
EXPECT().
27+
Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
28+
Times(0)
29+
30+
cmd := newCustomizerCreateCmd(mockClient)
31+
cmd.SetArgs([]string{}) // no <customizer-name>
32+
33+
if err := cmd.Execute(); err == nil {
34+
t.Error("expected error when customizer name arg is missing")
35+
}
36+
}
37+
38+
func TestNewCustomizerCreateCmd_success(t *testing.T) {
39+
ctrl := gomock.NewController(t)
40+
defer ctrl.Finish()
41+
42+
// prepare JSON input and response
43+
input := customizer{Name: "MyCustom"}
44+
rawIn, _ := json.Marshal(input)
45+
created := customizer{ID: "c-123", Name: "MyCustom"}
46+
rawOut, _ := json.Marshal(created)
47+
48+
mockClient := mocks.NewMockClient(ctrl)
49+
mockClient.
50+
EXPECT().
51+
Post(
52+
gomock.Any(),
53+
gomock.Eq(util.ResourceUrl(connectorCustomizersEndpoint)),
54+
gomock.Eq("application/json"),
55+
gomock.Any(), // reader matching rawIn
56+
gomock.Nil(),
57+
).
58+
DoAndReturn(func(ctx context.Context, url, cType string, body io.Reader, headers map[string]string) (*http.Response, error) {
59+
// verify body content
60+
got, _ := io.ReadAll(body)
61+
if !bytes.Equal(got, rawIn) {
62+
return nil, fmt.Errorf("unexpected request body: %s", got)
63+
}
64+
return &http.Response{
65+
StatusCode: http.StatusOK,
66+
Status: http.StatusText(http.StatusOK),
67+
Body: io.NopCloser(bytes.NewReader(rawOut)),
68+
}, nil
69+
}).
70+
Times(1)
71+
72+
cmd := newCustomizerCreateCmd(mockClient)
73+
var outBuf bytes.Buffer
74+
cmd.SetOut(&outBuf)
75+
cmd.SetArgs([]string{"MyCustom"})
76+
77+
if err := cmd.Execute(); err != nil {
78+
t.Fatalf("unexpected Execute error: %v", err)
79+
}
80+
81+
out := outBuf.String()
82+
// should contain returned ID
83+
if !strings.Contains(out, "c-123") {
84+
t.Errorf("output does not contain ID, got:\n%s", out)
85+
}
86+
// headers are uppercased by tablewriter
87+
for _, col := range customizerColumns {
88+
if !strings.Contains(out, strings.ToUpper(col)) {
89+
t.Errorf("output missing header %s, got:\n%s", col, out)
90+
}
91+
}
92+
}
93+
94+
func TestNewCustomizerCreateCmd_httpError(t *testing.T) {
95+
ctrl := gomock.NewController(t)
96+
defer ctrl.Finish()
97+
98+
mockClient := mocks.NewMockClient(ctrl)
99+
mockClient.
100+
EXPECT().
101+
Post(
102+
gomock.Any(),
103+
gomock.Eq(util.ResourceUrl(connectorCustomizersEndpoint)),
104+
gomock.Any(), gomock.Any(), gomock.Any(),
105+
).
106+
Return(&http.Response{
107+
StatusCode: http.StatusBadRequest,
108+
Status: http.StatusText(http.StatusBadRequest),
109+
Body: io.NopCloser(strings.NewReader("oops")),
110+
}, nil).
111+
Times(1)
112+
113+
cmd := newCustomizerCreateCmd(mockClient)
114+
cmd.SetErr(&bytes.Buffer{})
115+
cmd.SetArgs([]string{"MyCustom"})
116+
117+
err := cmd.Execute()
118+
if err == nil {
119+
t.Fatal("expected error on HTTP status != 200")
120+
}
121+
if !strings.Contains(err.Error(), "create customizer failed") {
122+
t.Errorf("unexpected error: %v", err)
123+
}
124+
}
125+
126+
func TestNewCustomizerCreateCmd_jsonError(t *testing.T) {
127+
ctrl := gomock.NewController(t)
128+
defer ctrl.Finish()
129+
130+
mockClient := mocks.NewMockClient(ctrl)
131+
mockClient.
132+
EXPECT().
133+
Post(
134+
gomock.Any(),
135+
gomock.Eq(util.ResourceUrl(connectorCustomizersEndpoint)),
136+
gomock.Any(), gomock.Any(), gomock.Any(),
137+
).
138+
Return(&http.Response{
139+
StatusCode: http.StatusOK,
140+
Status: http.StatusText(http.StatusOK),
141+
Body: io.NopCloser(strings.NewReader("not-json")),
142+
}, nil).
143+
Times(1)
144+
145+
cmd := newCustomizerCreateCmd(mockClient)
146+
cmd.SetErr(&bytes.Buffer{})
147+
cmd.SetArgs([]string{"MyCustom"})
148+
149+
err := cmd.Execute()
150+
if err == nil {
151+
t.Fatal("expected JSON decode error")
152+
}
153+
if !strings.Contains(err.Error(), "invalid character") {
154+
t.Errorf("unexpected error: %v", err)
155+
}
156+
}

cmd/connector/customizer_create_version.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func newCustomizerCreateVersionCmd(client client.Client) *cobra.Command {
2929
if err != nil {
3030
return err
3131
}
32+
defer f.Close()
3233

3334
info, err := f.Stat()
3435
if err != nil {

0 commit comments

Comments
 (0)