1 import { FormControl } from '@angular/forms';
3 import _ from 'lodash';
5 import { configureTestBed, Mocks } from '~/testing/unit-test-helper';
6 import { CrushNode } from '../models/crush-node';
7 import { CrushNodeSelectionClass } from './crush.node.selection.class';
9 describe('CrushNodeSelectionService', () => {
10 const nodes = Mocks.getCrushMap();
12 let service: CrushNodeSelectionClass;
19 // Object contains functions to get something
21 nodeByName: (name: string): CrushNode => nodes.find((node) => node.name === name),
22 nodesByNames: (names: string[]): CrushNode[] => names.map(get.nodeByName)
25 // Expects that are used frequently
27 formFieldValues: (root: CrushNode, failureDomain: string, device: string) => {
28 expect(controls.root.value).toEqual(root);
29 expect(controls.failure.value).toBe(failureDomain);
30 expect(controls.device.value).toBe(device);
34 expectedFailureDomain: string,
35 expectedDevice: string
37 const node = get.nodeByName(rootName);
38 controls.root.setValue(node);
39 assert.formFieldValues(node, expectedFailureDomain, expectedDevice);
42 failureDomains: { [failureDomain: string]: CrushNode[] },
43 expected: { [failureDomains: string]: string[] | CrushNode[] }
45 expect(Object.keys(failureDomains)).toEqual(Object.keys(expected));
46 Object.keys(failureDomains).forEach((key) => {
47 if (_.isString(expected[key][0])) {
48 expect(failureDomains[key]).toEqual(get.nodesByNames(expected[key] as string[]));
50 expect(failureDomains[key]).toEqual(expected[key]);
57 providers: [CrushNodeSelectionClass]
62 root: new FormControl(null),
63 failure: new FormControl(''),
64 device: new FormControl('')
66 // Normally this should be extended by the class using it
67 service = new CrushNodeSelectionClass();
68 // Therefore to get it working correctly use "this" instead of "service"
69 service.initCrushNodeSelection(nodes, controls.root, controls.failure, controls.device);
72 it('should be created', () => {
73 expect(service).toBeTruthy();
74 expect(nodes.length).toBe(12);
77 describe('lists', () => {
79 // The available buckets should not change
80 expect(service.buckets).toEqual(
81 get.nodesByNames(['default', 'hdd-rack', 'mix-host', 'ssd-host', 'ssd-rack'])
85 it('has the following lists after init', () => {
86 assert.failureDomainNodes(service.failureDomains, {
87 host: ['ssd-host', 'mix-host'],
88 osd: ['osd.1', 'osd.0', 'osd.2'],
89 rack: ['hdd-rack', 'ssd-rack'],
90 'osd-rack': ['osd2.0', 'osd2.1', 'osd3.0', 'osd3.1']
92 expect(service.devices).toEqual(['hdd', 'ssd']);
95 it('has the following lists after selection of ssd-host', () => {
96 controls.root.setValue(get.nodeByName('ssd-host'));
97 assert.failureDomainNodes(service.failureDomains, {
98 // Not host as it only exist once
99 osd: ['osd.1', 'osd.0', 'osd.2']
101 expect(service.devices).toEqual(['ssd']);
104 it('has the following lists after selection of mix-host', () => {
105 controls.root.setValue(get.nodeByName('mix-host'));
106 expect(service.devices).toEqual(['hdd', 'ssd']);
107 assert.failureDomainNodes(service.failureDomains, {
108 rack: ['hdd-rack', 'ssd-rack'],
109 'osd-rack': ['osd2.0', 'osd2.1', 'osd3.0', 'osd3.1']
114 describe('selection', () => {
115 it('selects the first root after init automatically', () => {
116 assert.formFieldValues(get.nodeByName('default'), 'osd-rack', '');
119 it('should select all values automatically by selecting "ssd-host" as root', () => {
120 assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
123 it('selects automatically the most common failure domain', () => {
124 // Select mix-host as mix-host has multiple failure domains (osd-rack and rack)
125 assert.valuesOnRootChange('mix-host', 'osd-rack', '');
128 it('should override automatic selections', () => {
129 assert.formFieldValues(get.nodeByName('default'), 'osd-rack', '');
130 assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
131 assert.valuesOnRootChange('mix-host', 'osd-rack', '');
134 it('should not override manual selections if possible', () => {
135 controls.failure.setValue('rack');
136 controls.failure.markAsDirty();
137 controls.device.setValue('ssd');
138 controls.device.markAsDirty();
139 assert.valuesOnRootChange('mix-host', 'rack', 'ssd');
142 it('should preselect device by domain selection', () => {
143 controls.failure.setValue('osd');
144 assert.formFieldValues(get.nodeByName('default'), 'osd', 'ssd');
148 describe('get available OSDs count', () => {
149 it('should have 4 available OSDs with the default selection', () => {
150 expect(service.deviceCount).toBe(4);
153 it('should reduce available OSDs to 2 if a device type is set', () => {
154 controls.device.setValue('ssd');
155 controls.device.markAsDirty();
156 expect(service.deviceCount).toBe(2);
159 it('should show 3 OSDs when selecting "ssd-host"', () => {
160 assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
161 expect(service.deviceCount).toBe(3);
165 describe('search tree', () => {
166 it('returns the following list after searching for mix-host', () => {
167 const subNodes = CrushNodeSelectionClass.search(nodes, 'mix-host');
168 expect(subNodes).toEqual(
181 it('returns the following list after searching for mix-host with SSDs', () => {
182 const subNodes = CrushNodeSelectionClass.search(nodes, 'mix-host~ssd');
183 expect(subNodes.map((n) => n.name)).toEqual(['mix-host', 'ssd-rack', 'osd3.0', 'osd3.1']);
186 it('returns an empty array if node can not be found', () => {
187 expect(CrushNodeSelectionClass.search(nodes, 'not-there')).toEqual([]);
190 it('returns the following list after searching for mix-host failure domains', () => {
191 const subNodes = CrushNodeSelectionClass.search(nodes, 'mix-host');
192 assert.failureDomainNodes(CrushNodeSelectionClass.getFailureDomains(subNodes), {
194 rack: ['hdd-rack', 'ssd-rack'],
195 'osd-rack': ['osd2.0', 'osd2.1', 'osd3.0', 'osd3.1']
199 it('returns the following list after searching for mix-host failure domains for a specific type', () => {
200 const subNodes = CrushNodeSelectionClass.search(nodes, 'mix-host~hdd');
201 const hddHost = _.cloneDeep(get.nodesByNames(['mix-host'])[0]);
202 hddHost.children = [-4];
203 assert.failureDomainNodes(CrushNodeSelectionClass.getFailureDomains(subNodes), {
206 'osd-rack': ['osd2.0', 'osd2.1']
208 const ssdHost = _.cloneDeep(get.nodesByNames(['mix-host'])[0]);
209 ssdHost.children = [-5];
210 assert.failureDomainNodes(
211 CrushNodeSelectionClass.searchFailureDomains(nodes, 'mix-host~ssd'),
215 'osd-rack': ['osd3.0', 'osd3.1']