Skip to content

Commit 75d3782

Browse files
Merge pull request #3603 from RedisInsight/be/feature/RI-5837-integration-tests-for-rdi
Added integration tests for rdi module
2 parents 27c85fd + c1b480f commit 75d3782

23 files changed

+1537
-8
lines changed

redisinsight/api/src/modules/rdi/client/api.rdi.client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export class ApiRdiClient extends RdiClient {
106106

107107
return await this.pollActionStatus(actionId);
108108
} catch (error) {
109-
throw wrapRdiPipelineError(error, error.response.data.message);
109+
throw wrapRdiPipelineError(error, error?.response?.data?.message);
110110
}
111111
}
112112

redisinsight/api/src/modules/rdi/rdi-pipeline.controller.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
ClassSerializerInterceptor, Controller, Get, Post, UseInterceptors, UsePipes, ValidationPipe,
44
Param,
55
} from '@nestjs/common';
6-
import { Rdi, RdiPipeline, RdiClientMetadata } from 'src/modules/rdi/models';
6+
import { RdiPipeline, RdiClientMetadata } from 'src/modules/rdi/models';
77
import { ApiTags } from '@nestjs/swagger';
88
import { ApiEndpoint } from 'src/decorators/api-endpoint.decorator';
99
import { RdiPipelineService } from 'src/modules/rdi/rdi-pipeline.service';
@@ -23,7 +23,7 @@ export class RdiPipelineController {
2323
@Get('/schema')
2424
@ApiEndpoint({
2525
description: 'Get pipeline schema',
26-
responses: [{ status: 200, type: Rdi }],
26+
responses: [{ status: 200, type: Object }],
2727
})
2828
async getSchema(
2929
@RequestRdiClientMetadata() rdiClientMetadata: RdiClientMetadata,
@@ -57,7 +57,7 @@ export class RdiPipelineController {
5757
@Post('/deploy')
5858
@ApiEndpoint({
5959
description: 'Deploy the pipeline',
60-
responses: [{ status: 200, type: RdiPipeline }],
60+
responses: [{ status: 200 }],
6161
})
6262
async deploy(
6363
@RequestRdiClientMetadata() rdiClientMetadata: RdiClientMetadata,
@@ -81,7 +81,7 @@ export class RdiPipelineController {
8181
@Get('/strategies')
8282
@ApiEndpoint({
8383
description: 'Get pipeline strategies and db types for template',
84-
responses: [{ status: 200, type: Rdi }],
84+
responses: [{ status: 200, type: Object }],
8585
})
8686
async getStrategies(
8787
@RequestRdiClientMetadata() rdiClientMetadata: RdiClientMetadata,

redisinsight/api/src/modules/rdi/rdi.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ describe('RdiService', () => {
7474
it('should throw an error if Rdi instance is not found', async () => {
7575
repository.get.mockResolvedValue(undefined);
7676

77-
await expect(service.get('123')).rejects.toThrowError('TBD not found');
77+
await expect(service.get('123')).rejects.toThrowError('RDI with id 123 was not found');
7878
});
7979
});
8080

redisinsight/api/src/modules/rdi/rdi.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { classToClass } from 'src/utils';
1212
import { RdiClientProvider } from 'src/modules/rdi/providers/rdi.client.provider';
1313
import { RdiClientFactory } from 'src/modules/rdi/providers/rdi.client.factory';
1414
import { SessionMetadata } from 'src/common/models';
15-
import { wrapRdiPipelineError } from 'src/modules/rdi/exceptions';
15+
import { RdiPipelineNotFoundException, wrapRdiPipelineError } from 'src/modules/rdi/exceptions';
1616
import { isUndefined, omitBy } from 'lodash';
1717
import { deepMerge } from 'src/common/utils';
1818
import { RdiAnalytics } from './rdi.analytics';
@@ -45,7 +45,7 @@ export class RdiService {
4545
const rdi = await this.repository.get(id);
4646

4747
if (!rdi) {
48-
throw new Error('TBD not found');
48+
throw new RdiPipelineNotFoundException(`RDI with id ${id} was not found`);
4949
}
5050

5151
return rdi;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
describe, expect, deps, getMainCheckFn,
3+
} from '../deps';
4+
5+
const {
6+
localDb, request, server, constants,
7+
} = deps;
8+
9+
const testRdiId = 'someTestId';
10+
const testRdiId2 = 'someTestId_2';
11+
12+
const endpoint = () => request(server).delete(`/${constants.API.RDI}/`);
13+
14+
const validInputData = {
15+
ids: [testRdiId, testRdiId2],
16+
};
17+
18+
const mainCheckFn = getMainCheckFn(endpoint);
19+
20+
describe('DELETE /rdi', () => {
21+
describe('Common', () => {
22+
[
23+
{
24+
name: 'Should throw error if ids are empty',
25+
data: { ids: [] },
26+
statusCode: 500,
27+
},
28+
{
29+
name: 'Should delete multiple rdis by ids',
30+
data: validInputData,
31+
statusCode: 200,
32+
before: async () => {
33+
await localDb.generateRdis({ id: testRdiId }, 1);
34+
await localDb.generateRdis({ id: testRdiId2 }, 1);
35+
const rdi1 = await localDb.getRdiById(testRdiId);
36+
const rdi2 = await localDb.getRdiById(testRdiId2);
37+
expect(rdi1.id).to.eql(testRdiId);
38+
expect(rdi2.id).to.eql(testRdiId2);
39+
},
40+
after: async () => {
41+
expect(await localDb.getRdiById(testRdiId)).to.eql(null);
42+
expect(await localDb.getRdiById(testRdiId2)).to.eql(null);
43+
},
44+
},
45+
{
46+
name: 'Should not throw error even if id does not exist',
47+
data: { ids: ['Not_existed'] },
48+
statusCode: 200,
49+
before: async () => {
50+
expect(await localDb.getRdiById('Not_existed')).to.eql(null);
51+
},
52+
},
53+
].forEach(mainCheckFn);
54+
});
55+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { RdiUrl } from 'src/modules/rdi/constants';
2+
import { sign } from 'jsonwebtoken';
3+
import {
4+
describe, expect, deps, getMainCheckFn,
5+
} from '../deps';
6+
import { nock } from '../../helpers/test';
7+
8+
const {
9+
localDb, request, server, constants,
10+
} = deps;
11+
12+
const testRdiId = 'someTEST';
13+
const notExistedRdiId = 'notExisted';
14+
const testRdiUrl = 'http://rdilocal.test';
15+
16+
const endpoint = (id) => request(server).get(`/${constants.API.RDI}/${id || testRdiId}/connect`);
17+
18+
const mockedAccessToken = sign({ exp: Math.trunc(Date.now() / 1000) + 3600 }, 'test');
19+
20+
const mainCheckFn = getMainCheckFn(endpoint);
21+
22+
describe('GET /rdi/:id/connect', () => {
23+
[
24+
{
25+
name: 'Should be success if rdi with :id is in db',
26+
statusCode: 200,
27+
before: async () => {
28+
await localDb.generateRdis({ id: testRdiId, url: testRdiUrl }, 1);
29+
nock(testRdiUrl).post(`/${RdiUrl.Login}`).query(true).reply(200, {
30+
access_token: mockedAccessToken,
31+
});
32+
},
33+
},
34+
{
35+
name: 'Should throw notFoundError if rdi with id in params does not exist',
36+
endpoint: () => endpoint(notExistedRdiId),
37+
statusCode: 404,
38+
before: async () => {
39+
expect(await localDb.getRdiById(notExistedRdiId)).to.eql(null);
40+
},
41+
},
42+
].forEach(mainCheckFn);
43+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { v4 as uuidv4 } from 'uuid';
2+
import {
3+
describe, expect, deps, getMainCheckFn,
4+
} from '../deps';
5+
import { Joi } from '../../helpers/test';
6+
7+
const {
8+
localDb, request, server, constants,
9+
} = deps;
10+
11+
const testRdiId = uuidv4();
12+
const notExistedRdiId = 'not-existed-rdi-id';
13+
14+
const endpoint = (rdiId) => request(server).get(`/${constants.API.RDI}/${rdiId || testRdiId}`);
15+
16+
const responseSchema = Joi.object().keys({
17+
id: Joi.string().required(),
18+
url: Joi.string().required(),
19+
name: Joi.string().max(500).required(),
20+
username: Joi.string().required(),
21+
password: Joi.string().required(),
22+
lastConnection: Joi.string().isoDate().required(),
23+
version: Joi.string().required(),
24+
}).required().strict(true);
25+
26+
const mainCheckFn = getMainCheckFn(endpoint);
27+
28+
describe('GET /rdi/:id', () => {
29+
[
30+
{
31+
name: 'Should return rdi data by id',
32+
responseSchema,
33+
statusCode: 200,
34+
checkFn: ({ body }) => {
35+
expect(body.id).to.eql(testRdiId);
36+
},
37+
before: async () => {
38+
await localDb.generateRdis({ id: testRdiId }, 1);
39+
},
40+
},
41+
{
42+
name: 'Should throw error if no rdi found in a db',
43+
statusCode: 404,
44+
endpoint: () => endpoint(notExistedRdiId),
45+
checkFn: ({ body }) => {
46+
expect(body).to.eql({
47+
message: `RDI with id ${notExistedRdiId} was not found`,
48+
statusCode: 404,
49+
error: 'RdiNotFound',
50+
errorCode: 11405,
51+
});
52+
},
53+
before: async () => {
54+
await (await localDb.getRepository(localDb.repositories.RDI)).clear();
55+
},
56+
},
57+
].forEach(mainCheckFn);
58+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
describe, expect, deps, getMainCheckFn,
3+
} from '../deps';
4+
import { Joi } from '../../helpers/test';
5+
6+
const {
7+
localDb, request, server, constants,
8+
} = deps;
9+
10+
const endpoint = () => request(server).get(`/${constants.API.RDI}`);
11+
12+
const responseSchema = Joi.array().items(Joi.object().keys({
13+
id: Joi.string().required(),
14+
url: Joi.string().required(),
15+
name: Joi.string().max(500).required(),
16+
username: Joi.string().required(),
17+
lastConnection: Joi.string().isoDate().required(),
18+
version: Joi.string().required(),
19+
})).required().strict(true);
20+
21+
const mainCheckFn = getMainCheckFn(endpoint);
22+
23+
describe('GET /rdi', () => {
24+
[
25+
{
26+
name: 'Should return empty array if no rdis yet',
27+
responseSchema,
28+
checkFn: ({ body }) => {
29+
expect(body).to.eql([]);
30+
},
31+
before: async () => {
32+
await (await localDb.getRepository(localDb.repositories.RDI)).clear();
33+
await request(server).get('/rdi');
34+
},
35+
},
36+
{
37+
name: 'Should get rdis list',
38+
responseSchema,
39+
before: async () => {
40+
await localDb.generateRdis({}, 2);
41+
await request(server).get('/rdi');
42+
},
43+
checkFn: ({ body }) => {
44+
expect(body.length).to.eql(2);
45+
expect(body[0].name).to.eql('Rdi');
46+
},
47+
},
48+
].forEach(mainCheckFn);
49+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { RdiUrl } from 'src/modules/rdi/constants';
2+
import { sign } from 'jsonwebtoken';
3+
import {
4+
describe, expect, deps, getMainCheckFn, generateInvalidDataTestCases,
5+
validateInvalidDataTestCase,
6+
} from '../deps';
7+
import { Joi, nock } from '../../helpers/test';
8+
9+
const {
10+
localDb, request, server, constants,
11+
} = deps;
12+
13+
const testRdiId = 'someTestId';
14+
const testRdiUrl = 'http://rdilocal.test';
15+
const testRdiName = 'Test Rdi Name';
16+
const testRdiBase = { id: testRdiId, name: testRdiName, url: testRdiUrl };
17+
18+
const endpoint = (id) => request(server).patch(`/${constants.API.RDI}/${id || testRdiId}`);
19+
20+
const dataSchema = Joi.object().keys({
21+
name: Joi.string().max(500).allow(null),
22+
username: Joi.string().allow(null),
23+
password: Joi.string().allow(null),
24+
}).messages({ 'any.required': '{#label} should not be empty' }).strict(true);
25+
26+
const validInputData = {
27+
name: 'Updated Rdi',
28+
username: 'rdiUsername Updated',
29+
password: constants.TEST_KEYTAR_PASSWORD,
30+
};
31+
32+
const responseSchema = Joi.object().keys({
33+
id: Joi.string().required(),
34+
url: Joi.string().required(),
35+
name: Joi.string().max(500).required(),
36+
username: Joi.string().required(),
37+
password: Joi.string().required(),
38+
lastConnection: Joi.string().isoDate().required(),
39+
version: Joi.string().required(),
40+
}).required().strict(true);
41+
42+
const mockedAccessToken = sign({ exp: Math.trunc(Date.now() / 1000) + 3600 }, 'test');
43+
44+
const mainCheckFn = getMainCheckFn(endpoint);
45+
46+
describe('PATCH /rdi/:id', () => {
47+
describe('Validation', () => {
48+
generateInvalidDataTestCases(dataSchema, validInputData).forEach(
49+
validateInvalidDataTestCase(endpoint, dataSchema),
50+
);
51+
});
52+
describe('Common', () => {
53+
[
54+
{
55+
name: 'Should update rdi name',
56+
responseSchema,
57+
data: { name: validInputData.name },
58+
statusCode: 200,
59+
checkFn: ({ body }) => {
60+
expect(body.name).to.eql(validInputData.name);
61+
expect(body.id).to.eql(testRdiId);
62+
},
63+
before: async () => {
64+
await localDb.generateRdis(testRdiBase, 1);
65+
nock(testRdiUrl).post(`/${RdiUrl.Login}`).query(true).reply(200, {
66+
access_token: mockedAccessToken,
67+
});
68+
},
69+
},
70+
{
71+
name: 'Should update rdi username',
72+
responseSchema,
73+
data: { username: validInputData.username },
74+
statusCode: 200,
75+
checkFn: ({ body }) => {
76+
expect(body.name).to.eql(testRdiName);
77+
expect(body.username).to.eql(validInputData.username);
78+
},
79+
before: async () => {
80+
await localDb.generateRdis(testRdiBase, 1);
81+
nock(testRdiUrl).post(`/${RdiUrl.Login}`).query(true).reply(200, {
82+
access_token: mockedAccessToken,
83+
});
84+
},
85+
},
86+
{
87+
name: 'Should throw error if rdiClient was not connected',
88+
statusCode: 401,
89+
data: validInputData,
90+
before: () => {
91+
nock(testRdiUrl).post(`/${RdiUrl.Login}`).query(true).reply(401, {
92+
message: 'Unauthorized',
93+
});
94+
},
95+
responseBody: {
96+
message: 'Authorization failed',
97+
statusCode: 401,
98+
error: 'RdiUnauthorized',
99+
errorCode: 11402,
100+
},
101+
},
102+
].forEach(mainCheckFn);
103+
});
104+
});

0 commit comments

Comments
 (0)