keycloak-api-manager 3.2.0 → 4.0.0

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 (45) hide show
  1. package/.env.example +27 -0
  2. package/Handlers/authenticationManagementHandler.js +1 -1
  3. package/Handlers/clientScopesHandler.js +5 -5
  4. package/Handlers/clientsHandler.js +241 -31
  5. package/Handlers/componentsHandler.js +2 -2
  6. package/Handlers/groupsHandler.js +18 -3
  7. package/Handlers/httpApiHelper.js +87 -0
  8. package/Handlers/identityProvidersHandler.js +3 -3
  9. package/Handlers/realmsHandler.js +26 -4
  10. package/Handlers/usersHandler.js +43 -10
  11. package/README.md +361 -26
  12. package/index.js +149 -29
  13. package/index.mjs +21 -0
  14. package/package.json +3 -2
  15. package/test/.mocharc.json +4 -0
  16. package/test/TESTING.md +327 -0
  17. package/test/config/CONFIGURATION.md +170 -0
  18. package/test/config/default.json +36 -0
  19. package/test/config/local.json.example +7 -0
  20. package/test/config/secrets.json.example +7 -0
  21. package/test/diagnostic-protocol-mappers.js +189 -0
  22. package/test/docker-keycloak/DEPLOYMENT_GUIDE.md +262 -0
  23. package/test/docker-keycloak/certs/.gitkeep +7 -0
  24. package/test/docker-keycloak/docker-compose-https.yml +50 -0
  25. package/test/docker-keycloak/docker-compose.yml +59 -0
  26. package/test/docker-keycloak/setup-keycloak.js +501 -0
  27. package/test/enableServerFeatures.js +315 -0
  28. package/test/helpers/config.js +218 -0
  29. package/test/helpers/docker-helpers.js +513 -0
  30. package/test/helpers/setup.js +186 -0
  31. package/test/package.json +18 -0
  32. package/test/setup.js +194 -0
  33. package/test/specs/authenticationManagement.test.js +224 -0
  34. package/test/specs/clientScopes.test.js +388 -0
  35. package/test/specs/clients.test.js +791 -0
  36. package/test/specs/components.test.js +151 -0
  37. package/test/specs/debugClientLibrary.test.js +88 -0
  38. package/test/specs/groups.test.js +362 -0
  39. package/test/specs/identityProviders.test.js +292 -0
  40. package/test/specs/realms.test.js +390 -0
  41. package/test/specs/roles.test.js +322 -0
  42. package/test/specs/users.test.js +445 -0
  43. package/test/testConfig.js +69 -0
  44. package/.idea/vcs.xml +0 -6
  45. package/.idea/workspace.xml +0 -94
@@ -0,0 +1,292 @@
1
+ const path = require('path');
2
+ const http = require('http');
3
+ const https = require('https');
4
+ const { expect } = require('chai');
5
+
6
+ process.env.NODE_ENV = process.env.NODE_ENV || 'test';
7
+ process.env.PROPERTIES_PATH = path.join(__dirname, '..', 'config');
8
+
9
+ const { conf } = require('propertiesmanager');
10
+ const keycloakManager = require('keycloak-api-manager');
11
+
12
+ function requestAdmin(baseUrl, token, apiPath, method = 'GET', body) {
13
+ const url = new URL(apiPath, baseUrl);
14
+ const transport = url.protocol === 'https:' ? https : http;
15
+ const payload = body ? JSON.stringify(body) : null;
16
+
17
+ const options = {
18
+ method,
19
+ headers: {
20
+ Accept: 'application/json',
21
+ Authorization: `Bearer ${token}`,
22
+ ...(payload ? { 'Content-Type': 'application/json' } : {}),
23
+ },
24
+ };
25
+
26
+ return new Promise((resolve, reject) => {
27
+ const req = transport.request(url, options, (res) => {
28
+ let data = '';
29
+ res.on('data', (chunk) => {
30
+ data += chunk;
31
+ });
32
+ res.on('end', () => {
33
+ let parsed = data;
34
+ try {
35
+ parsed = data ? JSON.parse(data) : null;
36
+ } catch (err) {
37
+ parsed = data;
38
+ }
39
+ resolve({ status: res.statusCode, body: parsed });
40
+ });
41
+ });
42
+
43
+ req.on('error', reject);
44
+ if (payload) {
45
+ req.write(payload);
46
+ }
47
+ req.end();
48
+ });
49
+ }
50
+
51
+ async function getAdminToken(baseUrl, username, password) {
52
+ const url = new URL('/realms/master/protocol/openid-connect/token', baseUrl);
53
+ const transport = url.protocol === 'https:' ? https : http;
54
+
55
+ const params = new URLSearchParams({
56
+ grant_type: 'password',
57
+ client_id: 'admin-cli',
58
+ username,
59
+ password,
60
+ });
61
+
62
+ const options = {
63
+ method: 'POST',
64
+ headers: {
65
+ 'Content-Type': 'application/x-www-form-urlencoded',
66
+ },
67
+ };
68
+
69
+ return new Promise((resolve, reject) => {
70
+ const req = transport.request(url, options, (res) => {
71
+ let data = '';
72
+ res.on('data', (chunk) => {
73
+ data += chunk;
74
+ });
75
+ res.on('end', () => {
76
+ if (res.statusCode === 200) {
77
+ const parsed = JSON.parse(data);
78
+ resolve(parsed.access_token);
79
+ } else {
80
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
81
+ }
82
+ });
83
+ });
84
+
85
+ req.on('error', reject);
86
+ req.write(params.toString());
87
+ req.end();
88
+ });
89
+ }
90
+
91
+ function shouldSkipFeature(err) {
92
+ if (!err || !err.message) {
93
+ return false;
94
+ }
95
+ const text = err.message.toLowerCase();
96
+ return (
97
+ text.includes('provider') && text.includes('not found') ||
98
+ text.includes('feature not enabled') ||
99
+ text.includes('not supported') ||
100
+ text.includes('http 404') ||
101
+ text.includes('unknown_error')
102
+ );
103
+ }
104
+
105
+ describe('IdentityProviders Handler', function () {
106
+ this.timeout(60000);
107
+
108
+ const keycloakConfig = (conf && conf.keycloak) || {};
109
+ const testRealm = `idp-realm-${Date.now()}`;
110
+ const idpAlias = `idp-oidc-${Date.now()}`;
111
+ const mapperName = `idp-mapper-${Date.now()}`;
112
+
113
+ let adminToken = null;
114
+ let idpCreated = false;
115
+ let mapperId = null;
116
+
117
+ before(async function () {
118
+ await keycloakManager.configure({
119
+ baseUrl: keycloakConfig.baseUrl,
120
+ realmName: keycloakConfig.realmName,
121
+ clientId: keycloakConfig.clientId,
122
+ clientSecret: keycloakConfig.clientSecret,
123
+ username: keycloakConfig.username,
124
+ password: keycloakConfig.password,
125
+ grantType: keycloakConfig.grantType,
126
+ tokenLifeSpan: keycloakConfig.tokenLifeSpan,
127
+ scope: keycloakConfig.scope,
128
+ });
129
+
130
+ adminToken = await getAdminToken(
131
+ keycloakConfig.baseUrl,
132
+ keycloakConfig.username,
133
+ keycloakConfig.password
134
+ );
135
+
136
+ await keycloakManager.realms.create({ realm: testRealm, enabled: true });
137
+ keycloakManager.setConfig({ realmName: testRealm });
138
+
139
+ try {
140
+ await keycloakManager.identityProviders.create({
141
+ alias: idpAlias,
142
+ providerId: 'oidc',
143
+ enabled: true,
144
+ trustEmail: false,
145
+ storeToken: false,
146
+ addReadTokenRoleOnCreate: false,
147
+ authenticateByDefault: false,
148
+ config: {
149
+ authorizationUrl: 'https://example.com/auth',
150
+ tokenUrl: 'https://example.com/token',
151
+ userInfoUrl: 'https://example.com/userinfo',
152
+ clientId: 'dummy-client-id',
153
+ clientSecret: 'dummy-client-secret',
154
+ defaultScope: 'openid profile email',
155
+ },
156
+ });
157
+ idpCreated = true;
158
+ } catch (err) {
159
+ if (shouldSkipFeature(err)) {
160
+ idpCreated = false;
161
+ } else {
162
+ throw err;
163
+ }
164
+ }
165
+ });
166
+
167
+ after(async function () {
168
+ try {
169
+ keycloakManager.setConfig({ realmName: testRealm });
170
+ } catch (err) {
171
+ // best effort
172
+ }
173
+
174
+ try {
175
+ if (mapperId && idpCreated) {
176
+ await keycloakManager.identityProviders.delMapper({ alias: idpAlias, id: mapperId });
177
+ }
178
+ } catch (err) {
179
+ // best effort
180
+ }
181
+
182
+ try {
183
+ if (idpCreated) {
184
+ await keycloakManager.identityProviders.del({ alias: idpAlias });
185
+ }
186
+ } catch (err) {
187
+ // best effort
188
+ }
189
+
190
+ try {
191
+ await keycloakManager.realms.del({ realm: testRealm });
192
+ } catch (err) {
193
+ // best effort
194
+ }
195
+
196
+ if (typeof keycloakManager.stop === 'function') {
197
+ keycloakManager.stop();
198
+ }
199
+ });
200
+
201
+ it('should list identity providers and find factory', async function () {
202
+ const providers = await keycloakManager.identityProviders.find();
203
+ expect(providers).to.be.an('array');
204
+
205
+ const factory = await keycloakManager.identityProviders.findFactory({ providerId: 'oidc' });
206
+ expect(factory).to.exist;
207
+ });
208
+
209
+ it('should create/findOne/update/delete identity provider when supported', async function () {
210
+ if (!idpCreated) {
211
+ this.skip();
212
+ return;
213
+ }
214
+
215
+ const one = await keycloakManager.identityProviders.findOne({ alias: idpAlias });
216
+ expect(one).to.be.an('object');
217
+ expect(one.alias).to.equal(idpAlias);
218
+
219
+ await keycloakManager.identityProviders.update(
220
+ { alias: idpAlias },
221
+ {
222
+ ...one,
223
+ displayName: 'Updated OIDC Provider',
224
+ }
225
+ );
226
+
227
+ const updated = await keycloakManager.identityProviders.findOne({ alias: idpAlias });
228
+ expect(updated.displayName).to.equal('Updated OIDC Provider');
229
+
230
+ const direct = await requestAdmin(
231
+ keycloakConfig.baseUrl,
232
+ adminToken,
233
+ `/admin/realms/${testRealm}/identity-provider/instances/${idpAlias}`
234
+ );
235
+ expect(direct.status).to.equal(200);
236
+ expect(direct.body.alias).to.equal(idpAlias);
237
+ });
238
+
239
+ it('should manage idp mappers when supported', async function () {
240
+ if (!idpCreated) {
241
+ this.skip();
242
+ return;
243
+ }
244
+
245
+ try {
246
+ const created = await keycloakManager.identityProviders.createMapper({
247
+ alias: idpAlias,
248
+ identityProviderMapper: {
249
+ name: mapperName,
250
+ identityProviderAlias: idpAlias,
251
+ identityProviderMapper: 'oidc-user-attribute-idp-mapper',
252
+ config: {
253
+ 'claim': 'email',
254
+ 'user.attribute': 'email',
255
+ 'syncMode': 'INHERIT',
256
+ },
257
+ },
258
+ });
259
+
260
+ mapperId = created.id;
261
+ expect(mapperId).to.exist;
262
+
263
+ const mappers = await keycloakManager.identityProviders.findMappers({ alias: idpAlias });
264
+ expect(mappers).to.be.an('array');
265
+
266
+ const one = await keycloakManager.identityProviders.findOneMapper({ alias: idpAlias, id: mapperId });
267
+ expect(one).to.be.an('object');
268
+ expect(one.name).to.equal(mapperName);
269
+
270
+ await keycloakManager.identityProviders.updateMapper(
271
+ { alias: idpAlias, id: mapperId },
272
+ {
273
+ ...one,
274
+ name: `${mapperName}-updated`,
275
+ }
276
+ );
277
+
278
+ const updated = await keycloakManager.identityProviders.findOneMapper({ alias: idpAlias, id: mapperId });
279
+ expect(updated.name).to.equal(`${mapperName}-updated`);
280
+
281
+ await keycloakManager.identityProviders.delMapper({ alias: idpAlias, id: mapperId });
282
+ mapperId = null;
283
+ } catch (err) {
284
+ if (shouldSkipFeature(err)) {
285
+ this.skip();
286
+ return;
287
+ }
288
+ throw err;
289
+ }
290
+ });
291
+
292
+ });
@@ -0,0 +1,390 @@
1
+ const path = require('path');
2
+ const http = require('http');
3
+ const https = require('https');
4
+ const { expect } = require('chai');
5
+
6
+ process.env.NODE_ENV = process.env.NODE_ENV || 'test';
7
+ process.env.PROPERTIES_PATH = path.join(__dirname, '..', 'config');
8
+
9
+ const { conf } = require('propertiesmanager');
10
+ const keycloakManager = require('keycloak-api-manager');
11
+
12
+ async function expectReject(promise, message) {
13
+ try {
14
+ await promise;
15
+ throw new Error(message || 'Expected promise to reject');
16
+ } catch (err) {
17
+ expect(err).to.be.instanceOf(Error);
18
+ }
19
+ }
20
+
21
+ function requestAdmin(baseUrl, token, apiPath, method = 'GET', body) {
22
+ const url = new URL(apiPath, baseUrl);
23
+ const transport = url.protocol === 'https:' ? https : http;
24
+ const payload = body ? JSON.stringify(body) : null;
25
+
26
+ const options = {
27
+ method,
28
+ headers: {
29
+ Accept: 'application/json',
30
+ Authorization: `Bearer ${token}`,
31
+ ...(payload ? { 'Content-Type': 'application/json' } : {}),
32
+ },
33
+ };
34
+
35
+ return new Promise((resolve, reject) => {
36
+ const req = transport.request(url, options, (res) => {
37
+ let data = '';
38
+ res.on('data', (chunk) => {
39
+ data += chunk;
40
+ });
41
+ res.on('end', () => {
42
+ let parsed = data;
43
+ try {
44
+ parsed = data ? JSON.parse(data) : null;
45
+ } catch (err) {
46
+ parsed = data;
47
+ }
48
+ resolve({ status: res.statusCode, body: parsed });
49
+ });
50
+ });
51
+
52
+ req.on('error', reject);
53
+ if (payload) {
54
+ req.write(payload);
55
+ }
56
+ req.end();
57
+ });
58
+ }
59
+
60
+ describe('Realms Handler', function () {
61
+ this.timeout(30000);
62
+
63
+ const keycloakConfig = (conf && conf.keycloak) || {};
64
+ const testRealm = `test-realm-${Date.now()}`;
65
+ const testGroupName = `test-group-${Date.now()}`;
66
+ const locale = 'en';
67
+ const localizationKey = `test-key-${Date.now()}`;
68
+ let initialAccessTokenId = null;
69
+ let groupId = null;
70
+ let adminBaseUrl = null;
71
+ let accessToken = null;
72
+ let tempRealmForDelete = null;
73
+
74
+ before(async function () {
75
+ await keycloakManager.configure({
76
+ baseUrl: keycloakConfig.baseUrl,
77
+ realmName: keycloakConfig.realmName,
78
+ clientId: keycloakConfig.clientId,
79
+ clientSecret: keycloakConfig.clientSecret,
80
+ username: keycloakConfig.username,
81
+ password: keycloakConfig.password,
82
+ grantType: keycloakConfig.grantType,
83
+ tokenLifeSpan: keycloakConfig.tokenLifeSpan,
84
+ scope: keycloakConfig.scope,
85
+ });
86
+
87
+ adminBaseUrl = keycloakConfig.baseUrl;
88
+ accessToken = keycloakManager.getToken().accessToken;
89
+
90
+ await keycloakManager.realms.create({
91
+ realm: testRealm,
92
+ enabled: true,
93
+ });
94
+
95
+ const group = await keycloakManager.groups.create({
96
+ realm: testRealm,
97
+ name: testGroupName,
98
+ });
99
+ groupId = group.id;
100
+ });
101
+
102
+ after(async function () {
103
+ try {
104
+ if (initialAccessTokenId) {
105
+ await keycloakManager.realms.delClientsInitialAccess({
106
+ realm: testRealm,
107
+ id: initialAccessTokenId,
108
+ });
109
+ }
110
+ } catch (err) {
111
+ // Best-effort cleanup.
112
+ }
113
+
114
+ try {
115
+ await keycloakManager.realms.del({ realm: testRealm });
116
+ } catch (err) {
117
+ // Best-effort cleanup.
118
+ }
119
+
120
+ try {
121
+ if (tempRealmForDelete) {
122
+ await keycloakManager.realms.del({ realm: tempRealmForDelete });
123
+ }
124
+ } catch (err) {
125
+ // Best-effort cleanup.
126
+ }
127
+
128
+ if (typeof keycloakManager.stop === 'function') {
129
+ keycloakManager.stop();
130
+ }
131
+ });
132
+
133
+ it('should find all realms', async function () {
134
+ const realms = await keycloakManager.realms.find();
135
+ expect(realms).to.be.an('array');
136
+ });
137
+
138
+ it('should create realm and verify via admin API', async function () {
139
+ const tempRealm = `create-realm-${Date.now()}`;
140
+ await keycloakManager.realms.create({
141
+ realm: tempRealm,
142
+ enabled: true,
143
+ });
144
+
145
+ const response = await requestAdmin(
146
+ adminBaseUrl,
147
+ accessToken,
148
+ `/admin/realms/${tempRealm}`
149
+ );
150
+ expect(response.status).to.equal(200);
151
+ expect(response.body.realm).to.equal(tempRealm);
152
+
153
+ tempRealmForDelete = tempRealm;
154
+ });
155
+
156
+ it('should find realm by name', async function () {
157
+ const realm = await keycloakManager.realms.findOne({ realm: testRealm });
158
+ expect(realm).to.be.an('object');
159
+ expect(realm.realm).to.equal(testRealm);
160
+ });
161
+
162
+ it('should update realm', async function () {
163
+ const displayName = `Updated-${Date.now()}`;
164
+ await keycloakManager.realms.update(
165
+ { realm: testRealm },
166
+ { displayName }
167
+ );
168
+
169
+ const updated = await keycloakManager.realms.findOne({ realm: testRealm });
170
+ expect(updated.displayName).to.equal(displayName);
171
+
172
+ const response = await requestAdmin(
173
+ adminBaseUrl,
174
+ accessToken,
175
+ `/admin/realms/${testRealm}`
176
+ );
177
+ expect(response.status).to.equal(200);
178
+ expect(response.body.displayName).to.equal(displayName);
179
+ });
180
+
181
+ it('should export realm configuration', async function () {
182
+ const exported = await keycloakManager.realms.export({
183
+ realm: testRealm,
184
+ exportClients: false,
185
+ exportGroupsAndRoles: false,
186
+ });
187
+ expect(exported).to.be.an('object');
188
+ });
189
+
190
+ it('should perform partial import', async function () {
191
+ const result = await keycloakManager.realms.partialImport({
192
+ realm: testRealm,
193
+ ifResourceExists: 'SKIP',
194
+ rep: {
195
+ realm: testRealm,
196
+ users: [],
197
+ roles: { realm: [] },
198
+ groups: [],
199
+ },
200
+ });
201
+ expect(result).to.be.an('object');
202
+ });
203
+
204
+ it('should get client registration policy providers', async function () {
205
+ const providers = await keycloakManager.realms.getClientRegistrationPolicyProviders({
206
+ realm: testRealm,
207
+ });
208
+ expect(providers).to.be.an('array');
209
+ });
210
+
211
+ it('should manage initial access tokens', async function () {
212
+ const created = await keycloakManager.realms.createClientsInitialAccess(
213
+ { realm: testRealm },
214
+ { count: 1, expiration: 3600 }
215
+ );
216
+
217
+ expect(created).to.be.an('object');
218
+ expect(created.id).to.exist;
219
+ initialAccessTokenId = created.id;
220
+
221
+ const tokens = await keycloakManager.realms.getClientsInitialAccess({
222
+ realm: testRealm,
223
+ });
224
+ expect(tokens).to.be.an('array');
225
+
226
+ await keycloakManager.realms.delClientsInitialAccess({
227
+ realm: testRealm,
228
+ id: initialAccessTokenId,
229
+ });
230
+ initialAccessTokenId = null;
231
+ });
232
+
233
+ it('should manage default groups and group paths', async function () {
234
+ await keycloakManager.realms.addDefaultGroup({
235
+ realm: testRealm,
236
+ id: groupId,
237
+ });
238
+
239
+ const defaults = await keycloakManager.realms.getDefaultGroups({
240
+ realm: testRealm,
241
+ });
242
+ expect(defaults).to.be.an('array');
243
+
244
+ const groupByPath = await keycloakManager.realms.getGroupByPath({
245
+ realm: testRealm,
246
+ path: `/${testGroupName}`,
247
+ });
248
+ expect(groupByPath).to.be.an('object');
249
+
250
+ await keycloakManager.realms.removeDefaultGroup({
251
+ realm: testRealm,
252
+ id: groupId,
253
+ });
254
+ });
255
+
256
+ it('should manage event configuration and events', async function () {
257
+ const config = await keycloakManager.realms.getConfigEvents({
258
+ realm: testRealm,
259
+ });
260
+ expect(config).to.be.an('object');
261
+
262
+ await keycloakManager.realms.updateConfigEvents(
263
+ { realm: testRealm },
264
+ {
265
+ eventsEnabled: true,
266
+ adminEventsEnabled: true,
267
+ adminEventsDetailsEnabled: true,
268
+ eventsListeners: ['jboss-logging'],
269
+ }
270
+ );
271
+
272
+ const events = await keycloakManager.realms.findEvents({
273
+ realm: testRealm,
274
+ max: 5,
275
+ });
276
+ expect(events).to.be.an('array');
277
+
278
+ const adminEvents = await keycloakManager.realms.findAdminEvents({
279
+ realm: testRealm,
280
+ max: 5,
281
+ });
282
+ expect(adminEvents).to.be.an('array');
283
+
284
+ await keycloakManager.realms.clearEvents({ realm: testRealm });
285
+ await keycloakManager.realms.clearAdminEvents({ realm: testRealm });
286
+ });
287
+
288
+ it('should read realm keys and session stats', async function () {
289
+ const keys = await keycloakManager.realms.getKeys({ realm: testRealm });
290
+ expect(keys).to.be.an('object');
291
+
292
+ const stats = await keycloakManager.realms.getClientSessionStats({
293
+ realm: testRealm,
294
+ });
295
+ expect(stats).to.be.an('array');
296
+ });
297
+
298
+ it('should push revocation and logout all', async function () {
299
+ await keycloakManager.realms.pushRevocation({ realm: testRealm });
300
+ await keycloakManager.realms.logoutAll({ realm: testRealm });
301
+ });
302
+
303
+ it('should manage localization texts', async function () {
304
+ await keycloakManager.realms.addLocalization(
305
+ {
306
+ realm: testRealm,
307
+ selectedLocale: locale,
308
+ key: localizationKey,
309
+ },
310
+ 'Test Value'
311
+ );
312
+
313
+ const texts = await keycloakManager.realms.getRealmLocalizationTexts({
314
+ realm: testRealm,
315
+ selectedLocale: locale,
316
+ });
317
+ expect(texts).to.be.an('object');
318
+ expect(texts[localizationKey]).to.equal('Test Value');
319
+
320
+ const locales = await keycloakManager.realms.getRealmSpecificLocales({
321
+ realm: testRealm,
322
+ selectedLocale: locale,
323
+ });
324
+ expect(locales).to.be.an('array');
325
+
326
+ await keycloakManager.realms.deleteRealmLocalizationTexts({
327
+ realm: testRealm,
328
+ selectedLocale: locale,
329
+ key: localizationKey,
330
+ });
331
+ });
332
+
333
+ it('should return errors for invalid LDAP/SMTP configs', async function () {
334
+ await expectReject(
335
+ keycloakManager.realms.testLDAPConnection(
336
+ { realm: testRealm },
337
+ {
338
+ action: 'testConnection',
339
+ connectionUrl: 'ldap://invalid-host:389',
340
+ bindDn: 'cn=admin,dc=example,dc=org',
341
+ bindCredential: 'invalid',
342
+ }
343
+ ),
344
+ 'LDAP connection should fail'
345
+ );
346
+
347
+ await expectReject(
348
+ keycloakManager.realms.ldapServerCapabilities(
349
+ { realm: testRealm },
350
+ {
351
+ action: 'testConnection',
352
+ connectionUrl: 'ldap://invalid-host:389',
353
+ bindDn: 'cn=admin,dc=example,dc=org',
354
+ bindCredential: 'invalid',
355
+ }
356
+ ),
357
+ 'LDAP capabilities should fail'
358
+ );
359
+
360
+ await expectReject(
361
+ keycloakManager.realms.testSMTPConnection(
362
+ { realm: testRealm },
363
+ {
364
+ from: 'noreply@example.com',
365
+ host: 'smtp.invalid',
366
+ port: 25,
367
+ auth: 'true',
368
+ user: 'invalid',
369
+ password: 'invalid',
370
+ }
371
+ ),
372
+ 'SMTP connection should fail'
373
+ );
374
+ });
375
+
376
+ it('should delete realm and verify via admin API', async function () {
377
+ if (!tempRealmForDelete) {
378
+ this.skip();
379
+ }
380
+
381
+ await keycloakManager.realms.del({ realm: tempRealmForDelete });
382
+ const response = await requestAdmin(
383
+ adminBaseUrl,
384
+ accessToken,
385
+ `/admin/realms/${tempRealmForDelete}`
386
+ );
387
+ expect(response.status).to.equal(404);
388
+ tempRealmForDelete = null;
389
+ });
390
+ });