From 261a9d7c9037aff1d9b2c1a66f66d1a4fb554926 Mon Sep 17 00:00:00 2001 From: alinarublea Date: Wed, 17 Jan 2024 14:30:14 +0100 Subject: [PATCH] feat: data model for organizations --- .../src/models/organization.js | 2 +- .../test/unit/models/organization.test.js | 64 ++++++- .../unit/service/organization/index.test.js | 178 ++++++++++++++++++ .../test/unit/service/sites/index.test.js | 6 + 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 packages/spacecat-shared-data-access/test/unit/service/organization/index.test.js diff --git a/packages/spacecat-shared-data-access/src/models/organization.js b/packages/spacecat-shared-data-access/src/models/organization.js index 9bd7ff6f..97f980b4 100644 --- a/packages/spacecat-shared-data-access/src/models/organization.js +++ b/packages/spacecat-shared-data-access/src/models/organization.js @@ -51,7 +51,7 @@ const Organization = (data = {}) => { */ self.updateName = (name) => { if (!hasText(name)) { - throw new Error('Name must be provided'); + throw new Error('Org name must be provided'); } self.state.name = name; diff --git a/packages/spacecat-shared-data-access/test/unit/models/organization.test.js b/packages/spacecat-shared-data-access/test/unit/models/organization.test.js index 0f55360c..2ac46816 100644 --- a/packages/spacecat-shared-data-access/test/unit/models/organization.test.js +++ b/packages/spacecat-shared-data-access/test/unit/models/organization.test.js @@ -13,6 +13,7 @@ /* eslint-env mocha */ import { expect } from 'chai'; import { createOrganization } from '../../../src/models/organization.js'; +import { sleep } from '../util.js'; const validData = { id: '1111111', @@ -60,29 +61,90 @@ describe('Organization Model Tests', () => { expect(config.alerts).to.be.an('array'); expect(config.slack.workspace).to.equal('workspace'); expect(config.slack.channel).to.equal('channel'); + expect(config.alerts[0].mentions[0].slack[0]).to.equal('slackId'); }); }); describe('Organization Object Functionality', () => { + let organization; + beforeEach(() => { + organization = createOrganization(validData); }); it('updates name correctly', () => { + const name = 'newOrgName123'; + organization.updateName(name); + expect(organization.getName()).to.equal(name); }); it('updates imsOrgId correctly', () => { + const imsOrgId = 'newImsOrg123'; + organization.updateImsOrgId(imsOrgId); + expect(organization.getImsOrgId()).to.equal(imsOrgId); }); it('updates config correctly', () => { + const conf = { + slack: { + workspace: 'workspace', + channel: 'channel', + }, + alerts: [{ + type: '404', + byOrg: false, + mentions: [{ slack: ['slackId'] }], + }], + }; + organization.updateConfig(conf); + const updatedConf = organization.getConfig(); + expect(updatedConf.slack).to.be.an('object'); + expect(updatedConf.alerts).to.be.an('array'); + expect(updatedConf.slack.workspace).to.equal('workspace'); + expect(updatedConf.slack.channel).to.equal('channel'); + expect(updatedConf.alerts[0].mentions[0].slack[0]).to.equal('slackId'); + }); + + it('throws an error when updating with an empty name', () => { + expect(() => organization.updateName('')).to.throw('Org name must be provided'); }); - it('throws an error when updating with an invalid name', () => { + it('throws an error when updating with an empty imsOrgId', () => { + expect(() => organization.updateImsOrgId('')).to.throw('IMS Org ID must be provided'); + }); + + it('throws an error when updating with an invalid config', () => { + expect(() => organization.updateConfig('abcd')).to.throw('Config must be provided'); }); it('updates updatedAt when imsOrgId is updated', async () => { + const initialUpdatedAt = organization.getUpdatedAt(); + + await sleep(20); + + organization.updateName('Name123'); + + expect(organization.getUpdatedAt()).to.not.equal(initialUpdatedAt); }); it('updates updatedAt when name is updated', async () => { + const initialUpdatedAt = organization.getUpdatedAt(); + + await sleep(20); + + organization.updateImsOrgId('imsOrg123'); + + expect(organization.getUpdatedAt()).to.not.equal(initialUpdatedAt); + }); + + it('updates updatedAt when config is updated', async () => { + const initialUpdatedAt = organization.getUpdatedAt(); + + await sleep(20); + + organization.updateName('Name123'); + + expect(organization.getUpdatedAt()).to.not.equal(initialUpdatedAt); }); }); }); diff --git a/packages/spacecat-shared-data-access/test/unit/service/organization/index.test.js b/packages/spacecat-shared-data-access/test/unit/service/organization/index.test.js new file mode 100644 index 00000000..22557e4a --- /dev/null +++ b/packages/spacecat-shared-data-access/test/unit/service/organization/index.test.js @@ -0,0 +1,178 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/* eslint-env mocha */ + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import sinon from 'sinon'; + +import { organizationFunctions } from '../../../../src/service/organizations/index.js'; +import { createOrganization } from '../../../../src/models/organization.js'; + +chai.use(chaiAsPromised); + +const { expect } = chai; + +const TEST_DA_CONFIG = { + tableNameOrganizations: 'spacecat-services-organizations', + indexNameAllOrganizations: 'spacecat-services-all-organizations', + pkAllOrganizations: 'ALL_ORGANIZATIONS', +}; + +describe('Organization Access Pattern Tests', () => { + describe('Organization Functions Export Tests', () => { + const mockDynamoClient = {}; + const mockLog = {}; + + const exportedFunctions = organizationFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog); + + it('exports getOrganizations function', () => { + expect(exportedFunctions).to.have.property('getOrganizations'); + expect(exportedFunctions.getOrganizations).to.be.a('function'); + }); + + it('exports getOrganizationByID function', () => { + expect(exportedFunctions).to.have.property('getOrganizationByID'); + expect(exportedFunctions.getOrganizationByID).to.be.a('function'); + }); + }); + + describe('Organization Functions Tests', () => { + let mockDynamoClient; + let mockLog; + let exportedFunctions; + + beforeEach(() => { + mockDynamoClient = { + query: sinon.stub().returns(Promise.resolve([])), + getItem: sinon.stub().returns(Promise.resolve(null)), + }; + mockLog = { log: sinon.stub() }; + + exportedFunctions = organizationFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog); + }); + + it('calls getOrganizations and returns an array', async () => { + const result = await exportedFunctions.getOrganizations(); + expect(result).to.be.an('array'); + expect(mockDynamoClient.query.called).to.be.true; + }); + + it('calls getOrganizationByID and returns null', async () => { + const result = await exportedFunctions.getOrganizationByID(); + expect(result).to.be.null; + expect(mockDynamoClient.getItem.called).to.be.true; + }); + + it('calls getOrganizationByID and returns site', async () => { + const mockOrgData = { + id: 'organization1', + name: 'Organization1', + }; + + mockDynamoClient.getItem.onFirstCall().resolves(mockOrgData); + + const result = await exportedFunctions.getOrganizationByID(); + + expect(result).to.be.an('object'); + expect(result.getId()).to.equal(mockOrgData.id); + expect(result.getName()).to.equal(mockOrgData.name); + expect(mockDynamoClient.getItem.called).to.be.true; + }); + + describe('addOrganization Tests', () => { + beforeEach(() => { + mockDynamoClient = { + query: sinon.stub().returns(Promise.resolve([])), + putItem: sinon.stub().returns(Promise.resolve()), + }; + mockLog = { log: sinon.stub() }; + exportedFunctions = organizationFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog); + }); + + it('adds a new organization successfully', async () => { + const orgData = { name: 'Org1' }; + const result = await exportedFunctions.addOrganization(orgData); + expect(mockDynamoClient.putItem.calledOnce).to.be.true; + expect(result.getName()).to.equal(orgData.name); + expect(result.getId()).to.be.a('string'); + }); + }); + }); + + describe('updateOrganization Tests', () => { + let mockDynamoClient; + let mockLog; + let exportedFunctions; + + beforeEach(() => { + mockDynamoClient = { + getItem: sinon.stub().returns(Promise.resolve()), + putItem: sinon.stub().returns(Promise.resolve()), + }; + mockLog = { log: sinon.stub() }; + exportedFunctions = organizationFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog); + }); + + it('updates an existing organization successfully', async () => { + const orgData = { name: 'Org1' }; + mockDynamoClient.getItem.resolves(Promise.resolve(orgData)); + + const org = await exportedFunctions.getOrganizationByID('id1'); + // site.updateBaseURL('https://newsite.com'); + org.updateImsOrgId('newOrg123'); + + const result = await exportedFunctions.updateOrganization(org); + expect(mockDynamoClient.putItem.calledOnce).to.be.true; + expect(result.getName()).to.equal(org.getName()); + expect(result.getImsOrgId()).to.equal(org.getImsOrgId()); + }); + + it('throws an error if organization does not exist', async () => { + const org = createOrganization({ name: 'Org1' }); + await expect(exportedFunctions.updateOrganization(org)).to.be.rejectedWith('Organization not found'); + }); + }); + + describe('removeOrganization Tests', () => { + let mockDynamoClient; + let mockLog; + let exportedFunctions; + + beforeEach(() => { + mockDynamoClient = { + query: sinon.stub().returns(Promise.resolve([])), + removeItem: sinon.stub().returns(Promise.resolve()), + }; + mockLog = { + log: sinon.stub(), + error: sinon.stub(), + }; + exportedFunctions = organizationFunctions(mockDynamoClient, TEST_DA_CONFIG, mockLog); + }); + + it('removes the organization', async () => { + await exportedFunctions.removeOrganization('some-id'); + + expect(mockDynamoClient.removeItem.calledOnce).to.be.true; + }); + + it('logs an error and reject if the organization removal fails', async () => { + const errorMessage = 'Failed to delete org'; + mockDynamoClient.removeItem.rejects(new Error(errorMessage)); + + await expect(exportedFunctions.removeOrganization('some-id')).to.be.rejectedWith(errorMessage); + expect(mockLog.error.calledOnce).to.be.true; + }); + }); +}); diff --git a/packages/spacecat-shared-data-access/test/unit/service/sites/index.test.js b/packages/spacecat-shared-data-access/test/unit/service/sites/index.test.js index 4bbe2778..2274d622 100644 --- a/packages/spacecat-shared-data-access/test/unit/service/sites/index.test.js +++ b/packages/spacecat-shared-data-access/test/unit/service/sites/index.test.js @@ -29,6 +29,7 @@ const TEST_DA_CONFIG = { tableNameSites: 'test-sites', indexNameAllSites: 'test-index-all-sites', indexNameAllSitesByDeliveryType: 'test-index-all-sites-by-delivery-type', + indexNameAllSitesOrganizations: 'test-index-all-sites-organizations', indexNameAllLatestAuditScores: 'test-index-all-latest-audit-scores', pkAllSites: 'test-pk-all-sites', pkAllLatestAudits: 'test-pk-all-latest-audits', @@ -51,6 +52,11 @@ describe('Site Access Pattern Tests', () => { expect(exportedFunctions.getSitesByDeliveryType).to.be.a('function'); }); + it('exports getSitesByOrganizationId function', () => { + expect(exportedFunctions).to.have.property('getSitesByOrganizationId'); + expect(exportedFunctions.getSitesByOrganizationId).to.be.a('function'); + }); + it('exports getSitesToAudit function', () => { expect(exportedFunctions).to.have.property('getSitesToAudit'); expect(exportedFunctions.getSitesToAudit).to.be.a('function');