Commit to switch
MODIFY
package.json
package.json
"mochatest:stripe": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/BillingStripeIntegrationTest.ts",
"mochatest:asset": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/AssetTest.ts",
"mochatest:car": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/CarTest.ts",
+ "mochatest:tag": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/TagTest.ts",
"mochatest:locking": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/LockingTest.ts",
"mochatest:firmwareUpdateStatus": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/FirmwareUpdateStatusTest.ts",
"mochatest:smartCharging": "cross-env TS_NODE_FILES=true mocha --no-parallel test/**/SmartChargingTest.ts",

ADD
test/api/TagTest.ts
+import User, { UserRole } from '../../src/types/User';
+import chai, { assert, expect } from 'chai';
+
+import CentralServerService from '../api/client/CentralServerService';
+import ChargingStationContext from './context/ChargingStationContext';
+import Constants from '../../src/utils/Constants';
+import ContextDefinition from './context/ContextDefinition';
+import ContextProvider from './context/ContextProvider';
+import Factory from '../factories/Factory';
+import { HTTPError } from '../../src/types/HTTPError';
+import { ServerRoute } from '../../src/types/Server';
+import Site from '../../src/types/Site';
+import SiteContext from './context/SiteContext';
+import { StatusCodes } from 'http-status-codes';
+import Tag from '../../src/types/Tag';
+import TenantContext from './context/TenantContext';
+import TestUtils from './TestUtils';
+import chaiSubset from 'chai-subset';
+import moment from 'moment';
+import responseHelper from '../helpers/responseHelper';
+
+chai.use(chaiSubset);
+chai.use(responseHelper);
+
+
+class TestData {
+ public tenantContext: TenantContext;
+ public centralUserContext: any;
+ public centralUserService: CentralServerService;
+ public userContext: any;
+ public userService: CentralServerService;
+ public siteContext: SiteContext;
+ public newUser: User;
+ public newTag: Tag;
+ public newTagUnassigned: Tag;
+ public createdUsers: any[] = [];
+ public createdTags: any[] = [];
+ public siteAreaContext: any;
+ public chargingStationContext: ChargingStationContext;
+ public tagsToImport: any;
+ public importedTags: Tag[];
+ public newSite: Site;
+ public createdSites: any[] = [];
+}
+
+const testData: TestData = new TestData();
+
+function login(userRole) {
+ testData.userContext = testData.tenantContext.getUserContext(userRole);
+ if (testData.userContext === testData.centralUserContext) {
+ // Reuse the central user service (to avoid double login)
+ testData.userService = testData.centralUserService;
+ } else {
+ testData.userService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain,
+ testData.userContext
+ );
+ }
+}
+
+describe('Tag', function() {
+ this.timeout(1000000); // Will automatically stop the unit test after that period of time
+
+ before(async () => {
+ chai.config.includeStack = true;
+ await ContextProvider.defaultInstance.prepareContexts();
+ });
+
+ afterEach(() => {
+ // Can be called after each UT to clean up created data
+ });
+
+ after(async () => {
+ // Final clean up at the end
+ await ContextProvider.defaultInstance.cleanUpCreatedContent();
+ });
+
+ describe('With component Organization (utorg)', () => {
+
+ before(async () => {
+ testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_ORGANIZATION);
+ testData.centralUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
+ testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_AUTO_USER_ASSIGNMENT);
+ testData.centralUserService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain,
+ testData.centralUserContext
+ );
+ testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
+ testData.chargingStationContext = testData.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
+ });
+
+ after(async () => {
+ // Delete any created user
+ for (const user of testData.createdUsers) {
+ await testData.centralUserService.deleteEntity(
+ testData.centralUserService.userApi,
+ user,
+ false
+ );
+ }
+ testData.createdUsers = [];
+ // Delete any created tag
+ for (const tag of testData.createdTags) {
+ await testData.centralUserService.userApi.deleteTag(tag.id);
+
+ }
+ testData.createdTags = [];
+ });
+
+ describe('Where admin user', () => {
+ before(async () => {
+ testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
+ assert(testData.userContext, 'User context cannot be null');
+ if (testData.userContext === testData.centralUserContext) {
+ // Reuse the central user service (to avoid double login)
+ testData.userService = testData.centralUserService;
+ } else {
+ testData.userService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain,
+ testData.userContext
+ );
+ }
+ assert(!!testData.userService, 'User service cannot be null');
+ // Create a site
+ testData.newSite = await testData.userService.createEntity(
+ testData.userService.siteApi,
+ Factory.site.build({ companyID: testData.tenantContext.getContext().companies[0].id, autoUserSiteAssignment: true }), true
+ );
+ testData.createdSites.push(testData.newSite);
+ });
+
+ describe('Using various basic APIs', () => {
+
+ it('Should be able to create a new user', async () => {
+ // Create
+ testData.newUser = await testData.userService.createEntity(
+ testData.userService.userApi,
+ Factory.user.build()
+ );
+ testData.newUser.issuer = true;
+ delete testData.newUser['password'];
+ testData.createdUsers.push(testData.newUser);
+ });
+
+ it('Should find the created user in the auto-assign site', async () => {
+ // Checks if the sites to which the new user is assigned contains the auto-assign site
+ await testData.userService.checkEntityInListWithParams(
+ testData.userService.siteApi,
+ testData.siteContext.getSite(),
+ { 'UserID': testData.newUser.id }
+ );
+ });
+
+ it('Should be able to create a tag for user', async () => {
+ testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
+ const response = await testData.userService.userApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ testData.createdTags.push(testData.newTag);
+ });
+
+ it('Should be able to create a tag without a user', async () => {
+ testData.newTagUnassigned = Factory.tag.build();
+ const response = await testData.userService.userApi.createTag(testData.newTagUnassigned);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ testData.createdTags.push(testData.newTagUnassigned);
+ });
+
+ it('Should be able to deactivate a badge', async () => {
+ testData.newTag.active = false;
+ const response = await testData.userService.userApi.updateTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.OK);
+ const tag = (await testData.userService.userApi.readTag(testData.newTag.id)).data;
+ expect(tag.active).to.equal(false);
+ });
+
+ it('Should not be able to start a transaction with a deactivated badge', async () => {
+ const connectorId = 1;
+ const tagId = testData.newTag.id;
+ const meterStart = 180;
+ const startDate = moment();
+ const response = await testData.chargingStationContext.startTransaction(
+ connectorId, tagId, meterStart, startDate.toDate());
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ expect(response).to.be.transactionStatus('Invalid');
+ });
+
+ it('Should be able to delete a badge that has not been used', async () => {
+ testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
+ let response = await testData.userService.userApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ response = await testData.userService.userApi.deleteTag(testData.newTag.id);
+ expect(response.status).to.equal(StatusCodes.OK);
+ response = (await testData.userService.userApi.readTag(testData.newTag.id));
+ expect(response.status).to.equal(HTTPError.OBJECT_DOES_NOT_EXIST_ERROR);
+ });
+
+ it('Should be able to export tag list', async () => {
+ const response = await testData.userService.userApi.exportTags({});
+ const tags = await testData.userService.userApi.readTags({});
+ const responseFileArray = TestUtils.convertExportFileToObjectArray(response.data);
+
+ expect(response.status).eq(StatusCodes.OK);
+ expect(response.data).not.null;
+ // Verify we have as many tags inserted as tags in the export
+ expect(responseFileArray.length).to.be.eql(tags.data.result.length);
+ });
+
+ // // TODO: Need to verify the real logic, not only if we can import (read create) tags
+ // // Something like this ?
+ // it('Should be able to import tag list', async () => {
+ // const response = await testData.tagService.insertTags(
+ // tenantid,
+ // user,
+ // action,
+ // tagsToBeImported,
+ // result);
+ // expect(response.status).to.equal(??);
+ // testData.importedTags.push(tag);
+ // });
+
+ it('Should get the user default car tag', async () => {
+ // Create a tag
+ testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
+ let response = await testData.userService.userApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ testData.createdTags.push(testData.newTag);
+ // Retrieve it
+ response = await testData.userService.userApi.getDefaultTagCar(testData.newUser.id);
+ expect(response.status).to.be.eq(StatusCodes.OK);
+ expect(response.data.tag.visualID).to.be.eq(testData.newTag.visualID);
+ expect(response.data.car).to.be.undefined;
+ expect(response.data.errorCodes).to.be.not.null;
+ });
+ });
+
+ });
+
+ describe('Where basic user', () => {
+ before(() => {
+ testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.BASIC_USER_NO_TAGS);
+ assert(testData.userContext, 'User context cannot be null');
+ if (testData.userContext === testData.centralUserContext) {
+ // Reuse the central user service (to avoid double login)
+ testData.userService = testData.centralUserService;
+ } else {
+ testData.userService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain,
+ testData.userContext
+ );
+ }
+ assert(!!testData.userService, 'User service cannot be null');
+ });
+
+ describe('Using various basic APIs', () => {
+
+ it('Should not be able to create a badge for user', async () => {
+ testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
+ const response = await testData.userService.tagApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.FORBIDDEN);
+ });
+
+ it('Should be able to assign a badge for himself', async () => {
+ testData.newTagUnassigned.userID = testData.userContext.id;
+ const response = await testData.userService.tagApi.assignTag(testData.newTagUnassigned);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ testData.createdTags.push(testData.newTagUnassigned);
+ });
+
+ it('Should be able to read his own badge', async () => {
+ const response = await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID);
+ expect(response.status).to.equal(StatusCodes.OK);
+ expect(response.data.visualID).to.equal(testData.newTagUnassigned.visualID);
+ });
+
+ it('Should be able to update his own badge', async () => {
+ testData.newTagUnassigned.description = 'My new description';
+ const response = await testData.userService.tagApi.updateTagByVisualID(testData.newTagUnassigned);
+ expect(response.status).to.equal(StatusCodes.OK);
+ const tag = (await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID)).data;
+ expect(tag.description).to.equal('My new description');
+ });
+
+ it('Should get the user default car tag', async () => {
+ // Retrieve it
+ const response = await testData.userService.userApi.getDefaultTagCar(testData.newTagUnassigned.userID);
+ expect(response.status).to.be.eq(StatusCodes.OK);
+ expect(response.data.tag.visualID).to.be.eq(testData.newTagUnassigned.visualID);
+ expect(response.data.car).to.be.undefined;
+ expect(response.data.errorCodes).to.be.not.null;
+ });
+
+ it('Should be able to unassign his own badge', async () => {
+ let response = await testData.userService.tagApi.unassignTag(testData.newTagUnassigned);
+ expect(response.status).to.equal(StatusCodes.OK);
+ response = await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID);
+ expect(response.status).to.equal(HTTPError.OBJECT_DOES_NOT_EXIST_ERROR);
+ });
+ });
+
+ });
+
+
+ // where site admin
+
+ describe('Where site admin user', () => {
+ before(() => {
+ testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.BASIC_USER_NO_TAGS);
+ assert(testData.userContext, 'User context cannot be null');
+ if (testData.userContext === testData.centralUserContext) {
+ // Reuse the central user service (to avoid double login)
+ testData.userService = testData.centralUserService;
+ } else {
+ testData.userService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain,
+ testData.userContext
+ );
+ }
+ assert(!!testData.userService, 'User service cannot be null');
+ });
+
+ describe('Using various basic APIs', () => {
+
+ it('Should not be able to create a badge for user not assigned to his site', async () => {
+ login(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
+ testData.userContext = await testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.BASIC_USER);
+ await testData.userService.siteApi.addUsersToSite(testData.newSite.id, [testData.userContext.id]);
+ await testData.userService.siteApi.assignSiteAdmin(testData.newSite.id, testData.userContext.id);
+ login(ContextDefinition.USER_CONTEXTS.BASIC_USER);
+ testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
+ const response = await testData.userService.tagApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.CREATED);
+ });
+
+ it('Should not be able to create a badge without a user', async () => {
+ testData.newTag = Factory.tag.build();
+ const response = await testData.userService.tagApi.createTag(testData.newTag);
+ expect(response.status).to.equal(StatusCodes.FORBIDDEN);
+ });
+
+ // it('Should be able to read his own badge', async () => {
+ // const response = await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID);
+ // expect(response.status).to.equal(StatusCodes.OK);
+ // expect(response.data.visualID).to.equal(testData.newTagUnassigned.visualID);
+ // });
+
+ // it('Should be able to update his own badge', async () => {
+ // testData.newTagUnassigned.description = 'My new description';
+ // const response = await testData.userService.tagApi.updateTagByVisualID(testData.newTagUnassigned);
+ // expect(response.status).to.equal(StatusCodes.OK);
+ // const tag = (await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID)).data;
+ // expect(tag.description).to.equal('My new description');
+ // });
+
+ // it('Should get the user default car tag', async () => {
+ // // Retrieve it
+ // const response = await testData.userService.userApi.getDefaultTagCar(testData.newTagUnassigned.userID);
+ // expect(response.status).to.be.eq(StatusCodes.OK);
+ // expect(response.data.tag.visualID).to.be.eq(testData.newTagUnassigned.visualID);
+ // expect(response.data.car).to.be.undefined;
+ // expect(response.data.errorCodes).to.be.not.null;
+ // });
+
+ // it('Should be able to unassign his own badge', async () => {
+ // let response = await testData.userService.tagApi.unassignTag(testData.newTagUnassigned);
+ // expect(response.status).to.equal(StatusCodes.OK);
+ // response = await testData.userService.tagApi.readTagByVisualID(testData.newTagUnassigned.visualID);
+ // expect(response.status).to.equal(HTTPError.OBJECT_DOES_NOT_EXIST_ERROR);
+ // });
+ });
+
+ });
+
+
+ });
+
+});

MODIFY
test/api/UserTest.ts
test/api/UserTest.ts
@@ -10,7 +10,6 @@ import Factory from '../factories/Factory';
import { HTTPError } from '../../src/types/HTTPError';
import { ServerRoute } from '../../src/types/Server';
import SiteContext from './context/SiteContext';
-import { StartTransactionErrorCode } from '../../src/types/Transaction';
import { StatusCodes } from 'http-status-codes';
import Tag from '../../src/types/Tag';
import TenantContext from './context/TenantContext';
@@ -184,53 +183,6 @@ describe('User', function() {
)).data;
});
- it('Should be able to create a tag for user', async () => {
- testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
- const response = await testData.userService.userApi.createTag(testData.newTag);
- expect(response.status).to.equal(StatusCodes.CREATED);
- testData.createdTags.push(testData.newTag);
- });
-
- it('Should be able to deactivate a badge', async () => {
- testData.newTag.active = false;
- const response = await testData.userService.userApi.updateTag(testData.newTag);
- expect(response.status).to.equal(StatusCodes.OK);
- const tag = (await testData.userService.userApi.readTag(testData.newTag.id)).data;
- expect(tag.active).to.equal(false);
- });
-
- it('Should not be able to start a transaction with a deactivated badge', async () => {
- const connectorId = 1;
- const tagId = testData.newTag.id;
- const meterStart = 180;
- const startDate = moment();
- const response = await testData.chargingStationContext.startTransaction(
- connectorId, tagId, meterStart, startDate.toDate());
- // eslint-disable-next-line @typescript-eslint/unbound-method
- expect(response).to.be.transactionStatus('Invalid');
- });
-
- it('Should be able to delete a badge that has not been used', async () => {
- testData.newTag = Factory.tag.build({ userID: testData.newUser.id });
- let response = await testData.userService.userApi.createTag(testData.newTag);
- expect(response.status).to.equal(StatusCodes.CREATED);
- response = await testData.userService.userApi.deleteTag(testData.newTag.id);
- expect(response.status).to.equal(StatusCodes.OK);
- response = (await testData.userService.userApi.readTag(testData.newTag.id));
- expect(response.status).to.equal(HTTPError.OBJECT_DOES_NOT_EXIST_ERROR);
- });
-
- it('Should be able to export tag list', async () => {
- const response = await testData.userService.userApi.exportTags({});
- const tags = await testData.userService.userApi.readTags({});
- const responseFileArray = TestUtils.convertExportFileToObjectArray(response.data);
-
- expect(response.status).eq(StatusCodes.OK);
- expect(response.data).not.null;
- // Verify we have as many tags inserted as tags in the export
- expect(responseFileArray.length).to.be.eql(tags.data.result.length);
- });
-
// // TODO: Need to verify the real logic, not only if we can import (read create) tags
// // Something like this ?
// it('Should be able to import tag list', async () => {

MODIFY
test/api/client/CentralServerService.ts
test/api/client/CentralServerService.ts
@@ -21,6 +21,7 @@ import SiteAreaApi from './SiteAreaApi';
import SmartChargingApi from './SmartChargingApi';
import StatisticsApi from './StatisticsApi';
import { StatusCodes } from 'http-status-codes';
+import TagApi from './TagApi';
import TenantApi from './TenantApi';
import { TenantComponents } from '../../../src/types/Tenant';
import TestConstants from './utils/TestConstants';
@@ -44,6 +45,7 @@ export default class CentralServerService {
public siteApi: SiteApi;
public siteAreaApi: SiteAreaApi;
public userApi: UserApi;
+ public tagApi: TagApi;
public chargingStationApi: ChargingStationApi;
public registrationApi: RegistrationTokenApi;
public transactionApi: TransactionApi;
@@ -94,6 +96,7 @@ export default class CentralServerService {
this.siteApi = new SiteApi(this.authenticatedApi);
this.siteAreaApi = new SiteAreaApi(this.authenticatedApi);
this.userApi = new UserApi(this.authenticatedApi);
+ this.tagApi = new TagApi(this.authenticatedApi);
this.chargingStationApi = new ChargingStationApi(this.authenticatedApi, this._baseApi);
this.transactionApi = new TransactionApi(this.authenticatedApi);
this.settingApi = new SettingApi(this.authenticatedApi);

ADD
test/api/client/TagApi.ts
+import AuthenticatedBaseApi from './utils/AuthenticatedBaseApi';
+import CrudApi from './utils/CrudApi';
+import { ServerRoute } from '../../../src/types/Server';
+import TestConstants from './utils/TestConstants';
+
+export default class TagApi extends CrudApi {
+ public constructor(authenticatedApi: AuthenticatedBaseApi) {
+ super(authenticatedApi);
+ }
+
+ public async readTags(params, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) {
+ return super.readAll(params, paging, ordering, this.buildRestEndpointUrl(ServerRoute.REST_TAGS));
+ }
+
+ public async readTag(id) {
+ return super.read({ ID: id }, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id }));
+ }
+
+ public async updateTag(data) {
+ return super.update(data, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id: data.id }));
+ }
+
+ public async assignTag(params?) {
+ return await super.update(params, '/client/api/TagAssign');
+ }
+
+ public async updateTagByVisualID(params?) {
+ return await super.update(params, '/client/api/TagUpdateByVisualID');
+ }
+
+ public async unassignTag(params?) {
+ return await super.update(params, '/client/api/TagUnassign');
+ }
+
+ public async readTagByVisualID(visualID) {
+ return super.read({ VisualID: visualID }, '/client/api/TagByVisualID');
+ }
+
+ public async createTag(data) {
+ return super.create(data, this.buildRestEndpointUrl(ServerRoute.REST_TAGS));
+ }
+
+ public async deleteTag(id) {
+ return super.delete(id, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id }));
+ }
+
+ public async exportTags(params) {
+ return await super.read(params, this.buildRestEndpointUrl(ServerRoute.REST_TAGS_EXPORT));
+ }
+
+ public async getDefaultTagCar(userID: string) {
+ const url = this.buildRestEndpointUrl(ServerRoute.REST_USER_DEFAULT_TAG_CAR, {});
+ return await super.read({ UserID: userID }, url);
+ }
+}

Commit to switch