@credibledata/cred-cli 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.prettierignore +2 -1
  2. package/README.md +0 -2
  3. package/bun.lock +182 -283
  4. package/dist/client/api.js +804 -111
  5. package/dist/client/api.js.map +1 -1
  6. package/dist/clientconfiguration.js +55 -13
  7. package/dist/clientconfiguration.js.map +1 -1
  8. package/dist/clientconfiguration.spec.js +305 -16
  9. package/dist/clientconfiguration.spec.js.map +1 -1
  10. package/dist/commands/add.js +6 -3
  11. package/dist/commands/add.js.map +1 -1
  12. package/dist/commands/connection/createconnections.js +18 -3
  13. package/dist/commands/connection/createconnections.js.map +1 -1
  14. package/dist/commands/connection/createconnections.spec.js +219 -0
  15. package/dist/commands/connection/createconnections.spec.js.map +1 -0
  16. package/dist/commands/connection/deleteconnection.spec.js +130 -0
  17. package/dist/commands/connection/deleteconnection.spec.js.map +1 -0
  18. package/dist/commands/connection/listconnections.js +9 -3
  19. package/dist/commands/connection/listconnections.js.map +1 -1
  20. package/dist/commands/connection/listconnections.spec.js +117 -0
  21. package/dist/commands/connection/listconnections.spec.js.map +1 -0
  22. package/dist/commands/group/addmember.js +2 -0
  23. package/dist/commands/group/addmember.js.map +1 -1
  24. package/dist/commands/group/addmember.spec.js +148 -0
  25. package/dist/commands/group/addmember.spec.js.map +1 -0
  26. package/dist/commands/group/creategroup.spec.js +122 -0
  27. package/dist/commands/group/creategroup.spec.js.map +1 -0
  28. package/dist/commands/group/deletegroup.spec.js +104 -0
  29. package/dist/commands/group/deletegroup.spec.js.map +1 -0
  30. package/dist/commands/group/getgroup.spec.js +111 -0
  31. package/dist/commands/group/getgroup.spec.js.map +1 -0
  32. package/dist/commands/group/listgroups.spec.js +118 -0
  33. package/dist/commands/group/listgroups.spec.js.map +1 -0
  34. package/dist/commands/group/removemember.js +1 -0
  35. package/dist/commands/group/removemember.js.map +1 -1
  36. package/dist/commands/group/removemember.spec.js +138 -0
  37. package/dist/commands/group/removemember.spec.js.map +1 -0
  38. package/dist/commands/groupaccesstoken/createToken.js +6 -2
  39. package/dist/commands/groupaccesstoken/createToken.js.map +1 -1
  40. package/dist/commands/groupaccesstoken/createToken.spec.js +127 -0
  41. package/dist/commands/groupaccesstoken/createToken.spec.js.map +1 -0
  42. package/dist/commands/groupaccesstoken/deleteToken.js.map +1 -1
  43. package/dist/commands/groupaccesstoken/deleteToken.spec.js +103 -0
  44. package/dist/commands/groupaccesstoken/deleteToken.spec.js.map +1 -0
  45. package/dist/commands/groupaccesstoken/groupContainer.js.map +1 -1
  46. package/dist/commands/groupaccesstoken/listTokens.js.map +1 -1
  47. package/dist/commands/groupaccesstoken/listTokens.spec.js +115 -0
  48. package/dist/commands/groupaccesstoken/listTokens.spec.js.map +1 -0
  49. package/dist/commands/list.js +10 -3
  50. package/dist/commands/list.js.map +1 -1
  51. package/dist/commands/misc/login.js +146 -22
  52. package/dist/commands/misc/login.js.map +1 -1
  53. package/dist/commands/misc/login.spec.js +111 -6
  54. package/dist/commands/misc/login.spec.js.map +1 -1
  55. package/dist/commands/misc/logout.spec.js +30 -0
  56. package/dist/commands/misc/logout.spec.js.map +1 -0
  57. package/dist/commands/misc/loopbackOAuth.js +110 -0
  58. package/dist/commands/misc/loopbackOAuth.js.map +1 -0
  59. package/dist/commands/misc/set-access-token.js.map +1 -1
  60. package/dist/commands/misc/set-access-token.spec.js +65 -0
  61. package/dist/commands/misc/set-access-token.spec.js.map +1 -0
  62. package/dist/commands/package/deletepackage.spec.js +136 -0
  63. package/dist/commands/package/deletepackage.spec.js.map +1 -0
  64. package/dist/commands/package/listpackages.js +10 -5
  65. package/dist/commands/package/listpackages.js.map +1 -1
  66. package/dist/commands/package/listpackages.spec.js +129 -0
  67. package/dist/commands/package/listpackages.spec.js.map +1 -0
  68. package/dist/commands/package/publish.js +23 -0
  69. package/dist/commands/package/publish.js.map +1 -1
  70. package/dist/commands/package/publish.spec.js +511 -0
  71. package/dist/commands/package/publish.spec.js.map +1 -0
  72. package/dist/commands/package/updatepackage.spec.js +50 -0
  73. package/dist/commands/package/updatepackage.spec.js.map +1 -1
  74. package/dist/commands/project/createproject.js +11 -1
  75. package/dist/commands/project/createproject.js.map +1 -1
  76. package/dist/commands/project/createproject.spec.js +154 -0
  77. package/dist/commands/project/createproject.spec.js.map +1 -0
  78. package/dist/commands/project/deleteproject.spec.js +106 -0
  79. package/dist/commands/project/deleteproject.spec.js.map +1 -0
  80. package/dist/commands/project/getproject.spec.js +99 -0
  81. package/dist/commands/project/getproject.spec.js.map +1 -0
  82. package/dist/commands/project/listprojects.js +13 -7
  83. package/dist/commands/project/listprojects.js.map +1 -1
  84. package/dist/commands/project/listprojects.spec.js +111 -0
  85. package/dist/commands/project/listprojects.spec.js.map +1 -0
  86. package/dist/commands/project/setproject.spec.js +58 -0
  87. package/dist/commands/project/setproject.spec.js.map +1 -0
  88. package/dist/commands/project/updateproject.js +68 -0
  89. package/dist/commands/project/updateproject.js.map +1 -0
  90. package/dist/commands/project/updateproject.spec.js +129 -0
  91. package/dist/commands/project/updateproject.spec.js.map +1 -0
  92. package/dist/commands/rm.js.map +1 -1
  93. package/dist/commands/update.js +13 -0
  94. package/dist/commands/update.js.map +1 -1
  95. package/dist/commands/version/listversions.spec.js +121 -0
  96. package/dist/commands/version/listversions.spec.js.map +1 -0
  97. package/dist/common/UserGroupId.spec.js +92 -0
  98. package/dist/common/UserGroupId.spec.js.map +1 -0
  99. package/dist/constants.js +49 -13
  100. package/dist/constants.js.map +1 -1
  101. package/dist/index.js +40 -18
  102. package/dist/index.js.map +1 -1
  103. package/dist/logger.spec.js +125 -0
  104. package/dist/logger.spec.js.map +1 -0
  105. package/dist/polyfills/file.polyfill.js.map +1 -1
  106. package/dist/utils.js +59 -0
  107. package/dist/utils.js.map +1 -1
  108. package/dist/utils.spec.js +158 -0
  109. package/dist/utils.spec.js.map +1 -0
  110. package/docker/Dockerfile.ci +11 -3
  111. package/eslint.config.mjs +45 -0
  112. package/generate-client.sh +5 -0
  113. package/package.json +24 -16
  114. package/src/client/.openapi-generator/FILES +1 -0
  115. package/src/client/api.ts +1999 -696
  116. package/src/clientconfiguration.spec.ts +394 -20
  117. package/src/clientconfiguration.ts +74 -15
  118. package/src/commands/add.ts +15 -6
  119. package/src/commands/connection/createconnections.spec.ts +403 -0
  120. package/src/commands/connection/createconnections.ts +30 -5
  121. package/src/commands/connection/deleteconnection.spec.ts +189 -0
  122. package/src/commands/connection/listconnections.spec.ts +132 -0
  123. package/src/commands/connection/listconnections.ts +22 -3
  124. package/src/commands/group/addmember.spec.ts +257 -0
  125. package/src/commands/group/addmember.ts +2 -0
  126. package/src/commands/group/creategroup.spec.ts +146 -0
  127. package/src/commands/group/deletegroup.spec.ts +120 -0
  128. package/src/commands/group/getgroup.spec.ts +129 -0
  129. package/src/commands/group/listgroups.spec.ts +137 -0
  130. package/src/commands/group/removemember.spec.ts +224 -0
  131. package/src/commands/group/removemember.ts +1 -0
  132. package/src/commands/groupaccesstoken/createToken.spec.ts +211 -0
  133. package/src/commands/groupaccesstoken/createToken.ts +55 -51
  134. package/src/commands/groupaccesstoken/deleteToken.spec.ts +147 -0
  135. package/src/commands/groupaccesstoken/deleteToken.ts +16 -11
  136. package/src/commands/groupaccesstoken/groupContainer.ts +51 -51
  137. package/src/commands/groupaccesstoken/listTokens.spec.ts +147 -0
  138. package/src/commands/groupaccesstoken/listTokens.ts +16 -11
  139. package/src/commands/list.ts +50 -9
  140. package/src/commands/misc/login.spec.ts +91 -6
  141. package/src/commands/misc/login.ts +208 -25
  142. package/src/commands/misc/logout.spec.ts +32 -0
  143. package/src/commands/misc/loopbackOAuth.ts +127 -0
  144. package/src/commands/misc/set-access-token.spec.ts +95 -0
  145. package/src/commands/misc/set-access-token.ts +16 -16
  146. package/src/commands/package/deletepackage.spec.ts +164 -0
  147. package/src/commands/package/listpackages.spec.ts +147 -0
  148. package/src/commands/package/listpackages.ts +28 -5
  149. package/src/commands/package/publish.spec.ts +597 -0
  150. package/src/commands/package/publish.ts +32 -0
  151. package/src/commands/package/updatepackage.spec.ts +98 -0
  152. package/src/commands/project/createproject.spec.ts +247 -0
  153. package/src/commands/project/createproject.ts +12 -1
  154. package/src/commands/project/deleteproject.spec.ts +123 -0
  155. package/src/commands/project/getproject.spec.ts +109 -0
  156. package/src/commands/project/listprojects.spec.ts +121 -0
  157. package/src/commands/project/listprojects.ts +33 -7
  158. package/src/commands/project/setproject.spec.ts +63 -0
  159. package/src/commands/project/updateproject.spec.ts +217 -0
  160. package/src/commands/project/updateproject.ts +82 -0
  161. package/src/commands/rm.ts +1 -6
  162. package/src/commands/update.ts +24 -1
  163. package/src/commands/version/listversions.spec.ts +138 -0
  164. package/src/common/UserGroupId.spec.ts +117 -0
  165. package/src/constants.ts +59 -13
  166. package/src/index.ts +46 -20
  167. package/src/logger.spec.ts +158 -0
  168. package/src/polyfills/file.polyfill.ts +1 -0
  169. package/src/utils.spec.ts +135 -0
  170. package/src/utils.ts +81 -0
  171. package/tsconfig.json +15 -18
  172. package/.eslintrc +0 -33
@@ -0,0 +1,132 @@
1
+ import { expect } from "chai";
2
+ import sinon from "sinon";
3
+ import { ConnectionsApi } from "../../client/api";
4
+ import { ClientConfiguration } from "../../clientconfiguration";
5
+ import { logger } from "../../logger";
6
+ import { prettifyJson } from "../../utils";
7
+ import { listConnections } from "./listconnections";
8
+
9
+ describe("list-connections", () => {
10
+ let sandbox: sinon.SinonSandbox;
11
+ let listConnectionsStub: sinon.SinonStub;
12
+ let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
13
+ let consoleLogStub: sinon.SinonStub;
14
+
15
+ beforeEach(() => {
16
+ sandbox = sinon.createSandbox();
17
+ listConnectionsStub = sandbox.stub();
18
+ loggerStub = sandbox.stub(logger);
19
+ consoleLogStub = sandbox.stub(console, "log");
20
+
21
+ sandbox
22
+ .stub(ClientConfiguration.prototype, "getOrganization")
23
+ .returns("test-org");
24
+ sandbox
25
+ .stub(ClientConfiguration.prototype, "getProject")
26
+ .returns("test-project");
27
+ sandbox
28
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
29
+ .returns(true);
30
+ sandbox
31
+ .stub(ClientConfiguration.prototype, "getHeaders")
32
+ .returns({ Authorization: "Bearer test-token" });
33
+
34
+ sandbox
35
+ .stub(ConnectionsApi.prototype, "listConnections")
36
+ .callsFake(listConnectionsStub);
37
+ });
38
+
39
+ afterEach(() => {
40
+ sandbox.restore();
41
+ });
42
+
43
+ it("should list connections successfully", async () => {
44
+ const mockConnections = [
45
+ { name: "conn1", type: "postgres" },
46
+ { name: "conn2", type: "mysql" },
47
+ ];
48
+ const mockResponse = {
49
+ data: mockConnections,
50
+ headers: {},
51
+ };
52
+ listConnectionsStub.resolves(mockResponse);
53
+
54
+ await listConnections("http://test-server", false);
55
+
56
+ expect(listConnectionsStub.calledOnce).to.be.true;
57
+ expect(listConnectionsStub.firstCall.args[0]).to.equal("test-org");
58
+ expect(listConnectionsStub.firstCall.args[1]).to.equal("test-project");
59
+ expect(listConnectionsStub.firstCall.args[2]).to.equal(-1); // limit
60
+ expect(listConnectionsStub.firstCall.args[3]).to.equal(0); // offset
61
+ expect(listConnectionsStub.firstCall.args[4]).to.deep.include({
62
+ baseURL: "http://test-server",
63
+ headers: { Authorization: "Bearer test-token" },
64
+ });
65
+ expect(consoleLogStub.calledOnce).to.be.true;
66
+ const loggedValue = consoleLogStub.firstCall.args[0];
67
+ const parsed = JSON.parse(loggedValue);
68
+ expect(parsed).to.have.property("connections");
69
+ expect(parsed.connections).to.deep.equal(mockConnections);
70
+ });
71
+
72
+ it("should handle missing config", async () => {
73
+ sandbox.restore();
74
+ sandbox = sinon.createSandbox();
75
+ listConnectionsStub = sandbox.stub();
76
+ loggerStub = sandbox.stub(logger);
77
+ consoleLogStub = sandbox.stub(console, "log");
78
+
79
+ sandbox
80
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
81
+ .returns(false);
82
+ sandbox
83
+ .stub(ConnectionsApi.prototype, "listConnections")
84
+ .callsFake(listConnectionsStub);
85
+
86
+ await listConnections("http://test-server", false);
87
+
88
+ expect(listConnectionsStub.called).to.be.false;
89
+ expect(
90
+ loggerStub.error.calledWith(
91
+ sinon.match("Please, make sure that you are logged in"),
92
+ ),
93
+ ).to.be.true;
94
+ });
95
+
96
+ it("should handle missing project", async () => {
97
+ sandbox.restore();
98
+ sandbox = sinon.createSandbox();
99
+ listConnectionsStub = sandbox.stub();
100
+ loggerStub = sandbox.stub(logger);
101
+ consoleLogStub = sandbox.stub(console, "log");
102
+
103
+ sandbox
104
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
105
+ .returns(true);
106
+ sandbox.stub(ClientConfiguration.prototype, "getProject").returns("");
107
+ sandbox
108
+ .stub(ConnectionsApi.prototype, "listConnections")
109
+ .callsFake(listConnectionsStub);
110
+
111
+ await listConnections("http://test-server", false);
112
+
113
+ expect(listConnectionsStub.called).to.be.false;
114
+ expect(
115
+ loggerStub.error.calledWith(
116
+ sinon.match("Please, make sure to set a project"),
117
+ ),
118
+ ).to.be.true;
119
+ });
120
+
121
+ it("should handle API errors", async () => {
122
+ const mockError = new Error("API Error");
123
+ listConnectionsStub.rejects(mockError);
124
+
125
+ await listConnections("http://test-server", true);
126
+
127
+ expect(listConnectionsStub.calledOnce).to.be.true;
128
+ expect(
129
+ loggerStub.error.calledWith(sinon.match("Failed to list connections")),
130
+ ).to.be.true;
131
+ });
132
+ });
@@ -2,11 +2,21 @@ import { ConnectionsApi } from "../../client/api";
2
2
  import { Configuration } from "../../client/configuration";
3
3
  import { ClientConfiguration } from "../../clientconfiguration";
4
4
  import { logger } from "../../logger";
5
- import { prettifyJson, printOutError } from "../../utils";
5
+ import {
6
+ prettifyJson,
7
+ printOutError,
8
+ parsePaginationParams,
9
+ extractTotalCount,
10
+ } from "../../utils";
6
11
 
7
12
  const connectionsApi = new ConnectionsApi(new Configuration());
8
13
 
9
- export const listConnections = async (controlPlane: string, debug: boolean) => {
14
+ export const listConnections = async (
15
+ controlPlane: string,
16
+ debug: boolean,
17
+ page?: string | number,
18
+ pageSize?: string | number,
19
+ ) => {
10
20
  const clientConfig = new ClientConfiguration();
11
21
  const organizationName = clientConfig.getOrganization();
12
22
  const projectName = clientConfig.getProject();
@@ -22,16 +32,25 @@ export const listConnections = async (controlPlane: string, debug: boolean) => {
22
32
  }
23
33
 
24
34
  try {
35
+ const { limit, offset } = parsePaginationParams(page, pageSize);
25
36
  const response = await connectionsApi.listConnections(
26
37
  organizationName,
27
38
  projectName,
39
+ limit,
40
+ offset,
28
41
  {
29
42
  baseURL: controlPlane,
30
43
  headers: clientConfig.getHeaders(),
31
44
  },
32
45
  );
46
+ const totalCount = extractTotalCount(response.headers);
33
47
 
34
- console.log(prettifyJson(response.data));
48
+ const payload = {
49
+ connections: response.data,
50
+ totalCount,
51
+ };
52
+
53
+ console.log(prettifyJson(payload));
35
54
  } catch (e) {
36
55
  logger.error("Failed to list connections");
37
56
  printOutError(e, debug);
@@ -0,0 +1,257 @@
1
+ import { expect } from "chai";
2
+ import sinon from "sinon";
3
+ import { GroupsApi } from "../../client/api";
4
+ import { ClientConfiguration } from "../../clientconfiguration";
5
+ import { logger } from "../../logger";
6
+ import { addMember } from "./addmember";
7
+
8
+ describe("add-member", () => {
9
+ let sandbox: sinon.SinonSandbox;
10
+ let addGroupMembersStub: sinon.SinonStub;
11
+ let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
12
+ let processExitStub: sinon.SinonStub;
13
+
14
+ beforeEach(() => {
15
+ sandbox = sinon.createSandbox();
16
+ addGroupMembersStub = sandbox.stub();
17
+ loggerStub = sandbox.stub(logger);
18
+ processExitStub = sandbox.stub(process, "exit");
19
+
20
+ sandbox
21
+ .stub(ClientConfiguration.prototype, "getOrganization")
22
+ .returns("test-org");
23
+ sandbox
24
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
25
+ .returns(true);
26
+ sandbox
27
+ .stub(ClientConfiguration.prototype, "getAuthenticationHeader")
28
+ .returns("Bearer test-token");
29
+
30
+ sandbox
31
+ .stub(GroupsApi.prototype, "addGroupMembers")
32
+ .callsFake(addGroupMembersStub);
33
+ });
34
+
35
+ afterEach(() => {
36
+ sandbox.restore();
37
+ });
38
+
39
+ it("should add user member successfully", async () => {
40
+ const mockResponse = {
41
+ data: {},
42
+ };
43
+ addGroupMembersStub.resolves(mockResponse);
44
+
45
+ await addMember(
46
+ "http://test-server",
47
+ "test-group",
48
+ "user",
49
+ "test-user",
50
+ "admin",
51
+ false,
52
+ );
53
+
54
+ expect(addGroupMembersStub.calledOnce).to.be.true;
55
+ expect(addGroupMembersStub.firstCall.args[0]).to.equal("test-org");
56
+ expect(addGroupMembersStub.firstCall.args[1]).to.equal("test-group");
57
+ expect(addGroupMembersStub.firstCall.args[2]).to.deep.include({
58
+ members: [
59
+ {
60
+ userGroupId: "user:test-user",
61
+ status: "admin",
62
+ },
63
+ ],
64
+ });
65
+ expect(
66
+ loggerStub.info.calledWith(
67
+ sinon.match(
68
+ "Successfully added user test-user to group test-group with role admin",
69
+ ),
70
+ ),
71
+ ).to.be.true;
72
+ });
73
+
74
+ it("should add group member successfully", async () => {
75
+ const mockResponse = {
76
+ data: {},
77
+ };
78
+ addGroupMembersStub.resolves(mockResponse);
79
+
80
+ await addMember(
81
+ "http://test-server",
82
+ "test-group",
83
+ "group",
84
+ "test-subgroup",
85
+ "member",
86
+ false,
87
+ );
88
+
89
+ expect(addGroupMembersStub.firstCall.args[2]).to.deep.include({
90
+ members: [
91
+ {
92
+ userGroupId: "group:test-subgroup",
93
+ status: "member",
94
+ },
95
+ ],
96
+ });
97
+ expect(
98
+ loggerStub.info.calledWith(
99
+ sinon.match(
100
+ "Successfully added group test-subgroup to group test-group with role member",
101
+ ),
102
+ ),
103
+ ).to.be.true;
104
+ });
105
+
106
+ it("should handle case-insensitive member type", async () => {
107
+ const mockResponse = {
108
+ data: {},
109
+ };
110
+ addGroupMembersStub.resolves(mockResponse);
111
+
112
+ await addMember(
113
+ "http://test-server",
114
+ "test-group",
115
+ "USER",
116
+ "test-user",
117
+ "admin",
118
+ false,
119
+ );
120
+
121
+ expect(
122
+ addGroupMembersStub.firstCall.args[2].members[0].userGroupId,
123
+ ).to.equal("user:test-user");
124
+ });
125
+
126
+ it("should handle missing config", async () => {
127
+ sandbox.restore();
128
+ sandbox = sinon.createSandbox();
129
+ addGroupMembersStub = sandbox.stub();
130
+ loggerStub = sandbox.stub(logger);
131
+ processExitStub = sandbox.stub(process, "exit");
132
+
133
+ sandbox
134
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
135
+ .returns(false);
136
+ sandbox
137
+ .stub(GroupsApi.prototype, "addGroupMembers")
138
+ .callsFake(addGroupMembersStub);
139
+
140
+ await addMember(
141
+ "http://test-server",
142
+ "test-group",
143
+ "user",
144
+ "test-user",
145
+ "admin",
146
+ false,
147
+ );
148
+
149
+ expect(addGroupMembersStub.called).to.be.false;
150
+ expect(
151
+ loggerStub.error.calledWith(
152
+ sinon.match("Please, make sure that you are logged in"),
153
+ ),
154
+ ).to.be.true;
155
+ });
156
+
157
+ it("should handle missing organization", async () => {
158
+ sandbox.restore();
159
+ sandbox = sinon.createSandbox();
160
+ addGroupMembersStub = sandbox.stub();
161
+ loggerStub = sandbox.stub(logger);
162
+ processExitStub = sandbox.stub(process, "exit");
163
+
164
+ sandbox
165
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
166
+ .returns(true);
167
+ sandbox
168
+ .stub(ClientConfiguration.prototype, "getOrganization")
169
+ .returns(undefined);
170
+ sandbox
171
+ .stub(GroupsApi.prototype, "addGroupMembers")
172
+ .callsFake(addGroupMembersStub);
173
+
174
+ await addMember(
175
+ "http://test-server",
176
+ "test-group",
177
+ "user",
178
+ "test-user",
179
+ "admin",
180
+ false,
181
+ );
182
+
183
+ expect(addGroupMembersStub.called).to.be.false;
184
+ expect(
185
+ loggerStub.error.calledWith(
186
+ sinon.match(
187
+ "No organization set. Please login or set an organization.",
188
+ ),
189
+ ),
190
+ ).to.be.true;
191
+ });
192
+
193
+ it("should reject invalid member type", async () => {
194
+ await addMember(
195
+ "http://test-server",
196
+ "test-group",
197
+ "invalid",
198
+ "test-user",
199
+ "admin",
200
+ false,
201
+ );
202
+
203
+ expect(addGroupMembersStub.called).to.be.false;
204
+ expect(
205
+ loggerStub.error.calledWith(
206
+ sinon.match(
207
+ (value: string) =>
208
+ typeof value === "string" &&
209
+ value.includes("memberType") &&
210
+ value.includes("must be either 'user' or 'group'"),
211
+ ),
212
+ ),
213
+ ).to.be.true;
214
+ expect(processExitStub.calledWith(1)).to.be.true;
215
+ });
216
+
217
+ it("should reject invalid role", async () => {
218
+ await addMember(
219
+ "http://test-server",
220
+ "test-group",
221
+ "user",
222
+ "test-user",
223
+ "invalid-role",
224
+ false,
225
+ );
226
+
227
+ expect(addGroupMembersStub.called).to.be.false;
228
+ expect(
229
+ loggerStub.error.calledWith(
230
+ sinon.match(/role must be either 'admin' or 'member'/),
231
+ ),
232
+ ).to.be.true;
233
+ expect(processExitStub.calledWith(1)).to.be.true;
234
+ });
235
+
236
+ it("should handle API errors", async () => {
237
+ const mockError = new Error("API Error");
238
+ addGroupMembersStub.rejects(mockError);
239
+
240
+ await addMember(
241
+ "http://test-server",
242
+ "test-group",
243
+ "user",
244
+ "test-user",
245
+ "admin",
246
+ true,
247
+ );
248
+
249
+ expect(addGroupMembersStub.calledOnce).to.be.true;
250
+ expect(
251
+ loggerStub.error.calledWith(
252
+ sinon.match("Failed to add member to group"),
253
+ ),
254
+ ).to.be.true;
255
+ expect(processExitStub.calledWith(1)).to.be.true;
256
+ });
257
+ });
@@ -47,12 +47,14 @@ export async function addMember(
47
47
  `Error: memberType ${memberType} must be either 'user' or 'group'`,
48
48
  );
49
49
  process.exit(1);
50
+ return;
50
51
  }
51
52
 
52
53
  // Validate role
53
54
  if (role !== "admin" && role !== "member") {
54
55
  logger.error("Error: role must be either 'admin' or 'member'");
55
56
  process.exit(1);
57
+ return;
56
58
  }
57
59
 
58
60
  const groupMember: GroupMember = {
@@ -0,0 +1,146 @@
1
+ import { expect } from "chai";
2
+ import sinon from "sinon";
3
+ import { GroupsApi } from "../../client/api";
4
+ import { ClientConfiguration } from "../../clientconfiguration";
5
+ import { logger } from "../../logger";
6
+ import { createGroup } from "./creategroup";
7
+
8
+ describe("create-group", () => {
9
+ let sandbox: sinon.SinonSandbox;
10
+ let createGroupStub: sinon.SinonStub;
11
+ let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
12
+ let consoleLogStub: sinon.SinonStub;
13
+ let processExitStub: sinon.SinonStub;
14
+
15
+ beforeEach(() => {
16
+ sandbox = sinon.createSandbox();
17
+ createGroupStub = sandbox.stub();
18
+ loggerStub = sandbox.stub(logger);
19
+ consoleLogStub = sandbox.stub(console, "log");
20
+ processExitStub = sandbox.stub(process, "exit");
21
+
22
+ sandbox
23
+ .stub(ClientConfiguration.prototype, "getOrganization")
24
+ .returns("test-org");
25
+ sandbox
26
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
27
+ .returns(true);
28
+ sandbox
29
+ .stub(ClientConfiguration.prototype, "getAuthenticationHeader")
30
+ .returns("Bearer test-token");
31
+
32
+ sandbox
33
+ .stub(GroupsApi.prototype, "createGroup")
34
+ .callsFake(createGroupStub);
35
+ });
36
+
37
+ afterEach(() => {
38
+ sandbox.restore();
39
+ });
40
+
41
+ it("should create group successfully", async () => {
42
+ const mockResponse = {
43
+ data: { groupName: "test-group", organizationName: "test-org" },
44
+ };
45
+ createGroupStub.resolves(mockResponse);
46
+
47
+ await createGroup("http://test-server", "test-group", false);
48
+
49
+ expect(createGroupStub.calledOnce).to.be.true;
50
+ expect(createGroupStub.firstCall.args[0]).to.equal("test-org");
51
+ expect(createGroupStub.firstCall.args[1]).to.deep.include({
52
+ organizationName: "test-org",
53
+ groupName: "test-group",
54
+ description: "Group test-group",
55
+ });
56
+ expect(consoleLogStub.called).to.be.true;
57
+ expect(
58
+ loggerStub.info.calledWith(sinon.match("Group created successfully")),
59
+ ).to.be.true;
60
+ });
61
+
62
+ it("should create group with custom description", async () => {
63
+ const mockResponse = {
64
+ data: { groupName: "test-group" },
65
+ };
66
+ createGroupStub.resolves(mockResponse);
67
+
68
+ await createGroup(
69
+ "http://test-server",
70
+ "test-group",
71
+ false,
72
+ "Custom description",
73
+ );
74
+
75
+ expect(createGroupStub.firstCall.args[1]).to.deep.include({
76
+ description: "Custom description",
77
+ });
78
+ });
79
+
80
+ it("should handle missing config", async () => {
81
+ sandbox.restore();
82
+ sandbox = sinon.createSandbox();
83
+ createGroupStub = sandbox.stub();
84
+ loggerStub = sandbox.stub(logger);
85
+ consoleLogStub = sandbox.stub(console, "log");
86
+ processExitStub = sandbox.stub(process, "exit");
87
+
88
+ sandbox
89
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
90
+ .returns(false);
91
+ sandbox
92
+ .stub(GroupsApi.prototype, "createGroup")
93
+ .callsFake(createGroupStub);
94
+
95
+ await createGroup("http://test-server", "test-group", false);
96
+
97
+ expect(createGroupStub.called).to.be.false;
98
+ expect(
99
+ loggerStub.error.calledWith(
100
+ sinon.match("Please, make sure that you are logged in"),
101
+ ),
102
+ ).to.be.true;
103
+ });
104
+
105
+ it("should handle missing organization", async () => {
106
+ sandbox.restore();
107
+ sandbox = sinon.createSandbox();
108
+ createGroupStub = sandbox.stub();
109
+ loggerStub = sandbox.stub(logger);
110
+ consoleLogStub = sandbox.stub(console, "log");
111
+ processExitStub = sandbox.stub(process, "exit");
112
+
113
+ sandbox
114
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
115
+ .returns(true);
116
+ sandbox
117
+ .stub(ClientConfiguration.prototype, "getOrganization")
118
+ .returns(undefined);
119
+ sandbox
120
+ .stub(GroupsApi.prototype, "createGroup")
121
+ .callsFake(createGroupStub);
122
+
123
+ await createGroup("http://test-server", "test-group", false);
124
+
125
+ expect(createGroupStub.called).to.be.false;
126
+ expect(
127
+ loggerStub.error.calledWith(
128
+ sinon.match(
129
+ "No organization set. Please login or set an organization.",
130
+ ),
131
+ ),
132
+ ).to.be.true;
133
+ });
134
+
135
+ it("should handle API errors", async () => {
136
+ const mockError = new Error("API Error");
137
+ createGroupStub.rejects(mockError);
138
+
139
+ await createGroup("http://test-server", "test-group", true);
140
+
141
+ expect(createGroupStub.calledOnce).to.be.true;
142
+ expect(loggerStub.error.calledWith(sinon.match("Failed to create group")))
143
+ .to.be.true;
144
+ expect(processExitStub.calledWith(1)).to.be.true;
145
+ });
146
+ });