| |
| /* |
| * Copyright 2017-present Open Networking Foundation |
| |
| * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import * as _ from 'lodash'; |
| import * as angular from 'angular'; |
| import 'angular-mocks'; |
| import {IXosGraphStore, XosGraphStore} from './graph.store'; |
| import {Subject} from 'rxjs/Subject'; |
| import {Graph} from 'graphlib'; |
| import {XosDebouncer} from '../../core/services/helpers/debounce.helper'; |
| import {IWSEvent} from '../../datasources/websocket/global'; |
| |
| interface ITestXosGraphStore extends IXosGraphStore { |
| |
| // state |
| serviceGraph: Graph; |
| |
| // private methods |
| getNodeId: any; |
| getModelType: any; |
| addNode: any; |
| addEdge: any; |
| nodesFromGraph: any; |
| |
| // observables |
| ServiceInstanceSubscription: any; |
| ServiceInstanceLinkSubscription: any; |
| } |
| |
| let service: ITestXosGraphStore; |
| |
| let scope: ng.IRootScopeService; |
| |
| const services = [ |
| { |
| id: 1, |
| class_names: 'Service,XOSBase', |
| name: 'Service 1' |
| }, |
| { |
| id: 2, |
| class_names: 'Service,XOSBase', |
| name: 'Service 2' |
| } |
| ]; |
| |
| const servicedependencies = [ |
| { |
| id: 1, |
| class_names: 'ServiceDependency,XOSBase', |
| provider_service_id: 2, |
| subscriber_service_id: 1 |
| } |
| ]; |
| |
| const serviceInstances = [ |
| { |
| id: 1, |
| class_names: 'ServiceInstance,XOSBase', |
| name: 'ServiceInstance 1', |
| owner_id: 1 |
| }, |
| { |
| id: 2, |
| class_names: 'ServiceInstance,XOSBase', |
| name: 'ServiceInstance 2', |
| owner_id: 2 |
| } |
| ]; |
| |
| const serviceInstanceLinks = [ |
| { |
| id: 1, |
| class_names: 'ServiceInstanceLink,XOSBase', |
| provider_service_instance_id: 1, |
| subscriber_service_instance_id: 2, |
| } |
| ]; |
| |
| const subject_services = new Subject(); |
| const subject_servicedependency = new Subject(); |
| const subject_serviceinstances = new Subject(); |
| const subject_serviceinstancelinks = new Subject(); |
| const subject_websocket = new Subject(); |
| |
| let MockModelStore = { |
| query: jasmine.createSpy('XosModelStore.query') |
| .and.callFake((model) => { |
| if (model === 'Service') { |
| return subject_services.asObservable(); |
| } |
| else if (model === 'ServiceDependency') { |
| return subject_servicedependency.asObservable(); |
| } |
| else if (model === 'ServiceInstance') { |
| return subject_serviceinstances.asObservable(); |
| } |
| else if (model === 'ServiceInstanceLink') { |
| return subject_serviceinstancelinks.asObservable(); |
| } |
| }) |
| }; |
| |
| let MockWebSocket = { |
| list: jasmine.createSpy('WebSocket.list') |
| .and.returnValue(subject_websocket) |
| }; |
| |
| |
| describe('The XosGraphStore service', () => { |
| |
| beforeEach(() => { |
| angular.module('XosGraphStore', []) |
| .service('XosGraphStore', XosGraphStore) |
| .value('XosModelStore', MockModelStore) |
| .value('WebSocket', MockWebSocket) |
| .service('XosDebouncer', XosDebouncer); |
| |
| angular.mock.module('XosGraphStore'); |
| }); |
| |
| beforeEach(angular.mock.inject((XosGraphStore: ITestXosGraphStore, |
| $rootScope: ng.IRootScopeService, |
| _$q_: ng.IQService) => { |
| |
| service = XosGraphStore; |
| scope = $rootScope; |
| |
| })); |
| |
| describe('when started', () => { |
| |
| let subscription; |
| |
| it('should load services and service-dependency and add nodes to the graph', (done) => { |
| let event = 0; |
| subscription = service.get().subscribe( |
| (graph: Graph) => { |
| if (event === 1) { |
| expect(graph.nodes().length).toBe(services.length); |
| expect(graph.nodes()).toEqual(['service~1', 'service~2']); |
| expect(graph.edges().length).toBe(servicedependencies.length); |
| expect(graph.edges()).toEqual([{v: 'service~1', w: 'service~2'}]); |
| done(); |
| } |
| else { |
| event = event + 1; |
| } |
| } |
| ); |
| subject_services.next(services); |
| subject_servicedependency.next(servicedependencies); |
| scope.$apply(); |
| }); |
| |
| afterEach(() => { |
| subscription.unsubscribe(); |
| }); |
| }); |
| |
| describe('when an observed model is removed', () => { |
| |
| let subscription; |
| |
| beforeEach(() => { |
| subject_services.next([]); |
| subject_servicedependency.next([]); |
| subject_services.next(services); |
| subject_servicedependency.next(servicedependencies); |
| service.addServiceInstances(); |
| subject_serviceinstances.next([serviceInstances[0]]); |
| }); |
| |
| it('should be removed from the graph', (done) => { |
| let event = 0; |
| subscription = service.get().subscribe( |
| (graph: Graph) => { |
| if (event === 1) { |
| expect(graph.nodes().length).toBe(0); |
| expect(graph.nodes()).toEqual([]); |
| expect(graph.edges().length).toBe(0); |
| expect(graph.edges()).toEqual([]); |
| done(); |
| } |
| else { |
| event = event + 1; |
| } |
| } |
| ); |
| |
| const removeService1: IWSEvent = { |
| model: 'Service', |
| deleted: true, |
| msg: { |
| changed_fields: [], |
| pk: 1, |
| object: { |
| id: 1, |
| class_names: 'Service, XOSBase' |
| } |
| } |
| }; |
| |
| const removeService2: IWSEvent = { |
| model: 'Service', |
| deleted: true, |
| msg: { |
| changed_fields: [], |
| pk: 2, |
| object: { |
| id: 2, |
| class_names: 'Service, XOSBase' |
| } |
| } |
| }; |
| |
| const removeServiceDependency1: IWSEvent = { |
| model: 'ServiceDependency', |
| deleted: true, |
| msg: { |
| changed_fields: [], |
| pk: 1, |
| object: { |
| id: 1, |
| class_names: 'ServiceDependency, XOSBase' |
| } |
| } |
| }; |
| |
| const removeServiceInstnace1: IWSEvent = { |
| model: 'VSGServiceInstance', |
| deleted: true, |
| msg: { |
| changed_fields: [], |
| pk: 1, |
| object: { |
| id: 1, |
| class_names: 'VSGServiceInstance,TenantWithContainer,ServiceInstance' |
| } |
| } |
| }; |
| |
| subject_websocket.next(removeService1); |
| subject_websocket.next(removeService2); |
| subject_websocket.next(removeServiceDependency1); |
| subject_websocket.next(removeServiceInstnace1); |
| scope.$apply(); |
| }); |
| |
| afterEach(() => { |
| subscription.unsubscribe(); |
| }); |
| }); |
| |
| describe(`the getModelType`, () => { |
| it('should return the node type', () => { |
| const res = service.getModelType(services[0]); |
| expect(res).toBe('service'); |
| }); |
| |
| it('should return the node type', () => { |
| const res = service.getModelType(serviceInstances[0]); |
| expect(res).toBe('serviceinstance'); |
| }); |
| }); |
| |
| describe('the getNodeId method', () => { |
| it('should return the id for a Service', () => { |
| const res = service.getNodeId(services[0]); |
| expect(res).toBe(`service~1`); |
| }); |
| |
| it('should return the id for a ServiceInstance', () => { |
| const res = service.getNodeId(serviceInstances[0]); |
| expect(res).toBe(`serviceinstance~1`); |
| }); |
| }); |
| |
| describe('the addNode method', () => { |
| |
| beforeEach(() => { |
| spyOn(service.serviceGraph, 'setNode'); |
| spyOn(service.serviceGraph, 'setEdge'); |
| }); |
| |
| it(`should a service to the graph`, () => { |
| service.addNode(services[0]); |
| expect(service.serviceGraph.setNode).toHaveBeenCalledWith('service~1', services[0]); |
| }); |
| |
| it('should add a service instance to the graph', () => { |
| service.addNode(serviceInstances[0]); |
| expect(service.serviceGraph.setNode).toHaveBeenCalledWith('serviceinstance~1', serviceInstances[0]); |
| }); |
| |
| it('should add an "ownership" edge to the graph', () => { |
| service.addNode(serviceInstances[0]); |
| expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('serviceinstance~1', 'service~1', {service: 1, service_instance: 1, type: 'ownership'}); |
| }); |
| }); |
| |
| describe('the addEdge method', () => { |
| |
| beforeEach(() => { |
| spyOn(service.serviceGraph, 'setEdge'); |
| }); |
| |
| it('should add a ServiceDependency to the graph', () => { |
| service.addEdge(servicedependencies[0]); |
| expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('service~1', 'service~2', servicedependencies[0]); |
| }); |
| |
| it('should add a ServiceInstanceLink to the graph', () => { |
| service.addEdge(serviceInstanceLinks[0]); |
| expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('serviceinstance~1', 'serviceinstance~2', serviceInstanceLinks[0]); |
| }); |
| }); |
| |
| describe('the nodesFromGraph and linksFromGraph methods', () => { |
| let graph: Graph; |
| |
| beforeEach(() => { |
| graph = new Graph(); |
| services.forEach(s => { |
| graph.setNode(`service~${s.id}`, s); |
| }); |
| |
| servicedependencies.forEach(sd => { |
| graph.setEdge('service~1', 'service~2', sd); |
| }); |
| }); |
| |
| it('should add id and type to the nodes', () => { |
| const nodes = service.nodesFromGraph(graph); |
| expect(nodes[0].id).toBe('service~1'); |
| expect(nodes[0].type).toBe('service'); |
| expect(nodes[0].data).toBeDefined(); |
| }); |
| |
| it('should add id and type to the links', () => { |
| const links = service.linksFromGraph(graph); |
| expect(links[0].id).toBe('service~1-service~2'); |
| expect(links[0].type).toBe('servicedependency'); |
| expect(links[0].data).toBeDefined(); |
| }); |
| |
| it('should handle ownership links', () => { |
| graph.setNode(`serviceinstance~1`, serviceInstances[0]); |
| graph.setEdge('service~1', 'serviceinstance~1', {type: 'ownership', service: 1, service_instance: 1}); |
| const links = service.linksFromGraph(graph); |
| expect(links[1].source).toBe(0); |
| expect(links[1].target).toBe(2); |
| }); |
| |
| it('should handle serviceinstancelink links', () => { |
| graph.setNode(`serviceinstance~1`, serviceInstances[0]); |
| graph.setNode(`serviceinstance~2`, serviceInstances[1]); |
| graph.setEdge('serviceinstance~1', 'serviceinstance~2', serviceInstanceLinks[0]); |
| const links = service.linksFromGraph(graph); |
| const targetLink = _.find(links, {id: `serviceinstance~1-serviceinstance~2`}); |
| expect(targetLink).toBeDefined(); |
| expect(targetLink.source).toBe(3); |
| expect(targetLink.target).toBe(2); |
| }); |
| }); |
| |
| describe('the addServiceInstance method', () => { |
| beforeEach(() => { |
| MockModelStore.query.calls.reset(); |
| }); |
| |
| it('should add them to the graph', () => { |
| service.addServiceInstances(); |
| expect(MockModelStore.query).toHaveBeenCalledWith(`ServiceInstance`, '/core/serviceinstances'); |
| expect(MockModelStore.query).toHaveBeenCalledWith(`ServiceInstanceLink`, '/core/serviceinstancelinks'); |
| expect(service.ServiceInstanceSubscription).toBeDefined(); |
| expect(service.ServiceInstanceLinkSubscription).toBeDefined(); |
| // TODO wait for the Observable to return and check the graph |
| }); |
| }); |
| |
| describe('the removeServiceInstance method', () => { |
| beforeEach(() => { |
| service.ServiceInstanceSubscription = { |
| unsubscribe: jasmine.createSpy('ServiceInstanceSubscription') |
| }; |
| service.ServiceInstanceLinkSubscription = { |
| unsubscribe: jasmine.createSpy('ServiceInstanceLinkSubscription') |
| }; |
| |
| service.serviceGraph = new Graph(); |
| |
| services.forEach(s => { |
| service.addNode(s); |
| }); |
| |
| serviceInstances.forEach(si => { |
| service.addNode(si); |
| }); |
| |
| serviceInstanceLinks.forEach(sil => { |
| service.addEdge(sil); |
| }); |
| }); |
| |
| it('should cancel subscriptions', () => { |
| service.removeServiceInstances(); |
| expect(service.ServiceInstanceSubscription.unsubscribe).toHaveBeenCalled(); |
| expect(service.ServiceInstanceLinkSubscription.unsubscribe).toHaveBeenCalled(); |
| }); |
| |
| it('should remove ServiceInstance and related nodes/edges from the graph', () => { |
| let filteredGraph = service.removeServiceInstances(); |
| expect(filteredGraph.nodes().length).toBe(2); |
| expect(filteredGraph.edges().length).toBe(0); |
| expect(service.serviceGraph.nodes().length).toBe(2); |
| expect(service.serviceGraph.edges().length).toBe(0); |
| }); |
| }); |
| }); |