widget_mixin_test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var testHelpers = require('test/helpers');
  20. describe('App.WidgetMixin', function () {
  21. var mixinClass = Em.Object.extend(App.WidgetMixin, {metrics: [], content: {}});
  22. var mixinObject;
  23. beforeEach(function () {
  24. mixinObject = mixinClass.create();
  25. });
  26. afterEach(function () {
  27. clearTimeout(mixinObject.get('timeoutId'));
  28. mixinObject.destroy();
  29. });
  30. describe('#loadMetrics()', function () {
  31. beforeEach(function () {
  32. this.mock = sinon.stub(mixinObject, 'getRequestData');
  33. sinon.stub(App.WidgetLoadAggregator, 'add');
  34. });
  35. afterEach(function () {
  36. this.mock.restore();
  37. App.WidgetLoadAggregator.add.restore();
  38. });
  39. it('has host_component_criteria', function () {
  40. this.mock.returns({'key1': {host_component_criteria: 'criteria'}});
  41. mixinObject.set('isLoaded', false);
  42. mixinObject.loadMetrics();
  43. expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
  44. });
  45. it('host_component_criteria is absent', function () {
  46. this.mock.returns({'key1': {}});
  47. mixinObject.set('isLoaded', false);
  48. mixinObject.loadMetrics();
  49. expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
  50. });
  51. });
  52. describe("#extractExpressions()", function () {
  53. var testCases = [
  54. {
  55. data: '',
  56. result: []
  57. },
  58. {
  59. data: 'text',
  60. result: []
  61. },
  62. {
  63. data: 'text${a}',
  64. result: ['a']
  65. },
  66. {
  67. data: 'text${a} - ${a.b}',
  68. result: ['a', 'a.b']
  69. },
  70. {
  71. data: '${o.a-(b+4)/cc*tt}',
  72. result: ['o.a-(b+4)/cc*tt']
  73. }
  74. ];
  75. testCases.forEach(function (test) {
  76. it('input: ' + test.data, function () {
  77. var input = {value: test.data};
  78. expect(mixinObject.extractExpressions(input)).to.eql(test.result);
  79. });
  80. });
  81. it('input is null', function () {
  82. var input = null;
  83. expect(mixinObject.extractExpressions(input)).to.be.empty;
  84. });
  85. });
  86. describe("#getRequestData()", function () {
  87. var data = [
  88. {
  89. "name": "regionserver.Server.percentFilesLocal",
  90. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  91. "service_name": "HBASE",
  92. "component_name": "HBASE_REGIONSERVER"
  93. },
  94. {
  95. "name": "regionserver.Server.percentFilesLocal2",
  96. "metric_path": "w2",
  97. "service_name": "HBASE",
  98. "component_name": "HBASE_REGIONSERVER"
  99. },
  100. {
  101. "name": "regionserver.Server.percentFilesLocal",
  102. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  103. "service_name": "HBASE",
  104. "component_name": "HBASE_REGIONSERVER",
  105. "host_component_criteria": 'c1'
  106. },
  107. {
  108. "name": "regionserver.Server.percentFilesLocal",
  109. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  110. "service_name": "HDFS",
  111. "component_name": "DATANODE",
  112. "host_component_criteria": 'c1'
  113. }
  114. ];
  115. beforeEach(function () {
  116. this.requestData = mixinClass.create().getRequestData(data);
  117. });
  118. it('HBASE_HBASE_REGIONSERVER', function () {
  119. var hbaseRegionServer = {
  120. "name": "regionserver.Server.percentFilesLocal",
  121. "service_name": "HBASE",
  122. "component_name": "HBASE_REGIONSERVER",
  123. "metric_paths": [
  124. {
  125. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  126. "metric_type": "POINT_IN_TIME",
  127. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  128. "context": {}
  129. },
  130. {
  131. "metric_path": "w2",
  132. "metric_type": "POINT_IN_TIME",
  133. "id": "w2_POINT_IN_TIME",
  134. "context": {}
  135. }
  136. ]
  137. };
  138. expect(JSON.stringify(this.requestData.HBASE_HBASE_REGIONSERVER)).to.equal(JSON.stringify(hbaseRegionServer));
  139. });
  140. it('HBASE_HBASE_REGIONSERVER_c1', function () {
  141. var hbaseRegionServerC1 = {
  142. "name": "regionserver.Server.percentFilesLocal",
  143. "service_name": "HBASE",
  144. "component_name": "HBASE_REGIONSERVER",
  145. "host_component_criteria": "c1",
  146. "metric_paths": [
  147. {
  148. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  149. "metric_type": "POINT_IN_TIME",
  150. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  151. "context": {}
  152. }
  153. ]
  154. };
  155. expect(JSON.stringify(this.requestData.HBASE_HBASE_REGIONSERVER_c1)).to.equal(JSON.stringify(hbaseRegionServerC1));
  156. });
  157. it('HDFS_DATANODE_c1', function () {
  158. var hdfsDataNodeC1 = {
  159. "name": "regionserver.Server.percentFilesLocal",
  160. "service_name": "HDFS",
  161. "component_name": "DATANODE",
  162. "host_component_criteria": "c1",
  163. "metric_paths": [
  164. {
  165. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  166. "metric_type": "POINT_IN_TIME",
  167. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  168. "context": {}
  169. }
  170. ]
  171. };
  172. expect(JSON.stringify(this.requestData.HDFS_DATANODE_c1)).to.equal(JSON.stringify(hdfsDataNodeC1));
  173. });
  174. });
  175. describe("#getServiceComponentMetrics()", function () {
  176. it("valid request is sent", function () {
  177. var request = {
  178. service_name: 'S1',
  179. component_name: 'C1',
  180. metric_paths: [
  181. {
  182. "metric_path": "w1",
  183. "metric_type": "POINT_IN_TIME",
  184. "id": "w1_POINT_IN_TIME",
  185. "context": {}
  186. },
  187. {
  188. "metric_path": "w2",
  189. "metric_type": "POINT_IN_TIME",
  190. "id": "w2_POINT_IN_TIME",
  191. "context": {}
  192. }
  193. ]
  194. };
  195. mixinObject.getServiceComponentMetrics(request);
  196. var args = testHelpers.findAjaxRequest('name', 'widgets.serviceComponent.metrics.get');
  197. expect(args[0]).exists;
  198. expect(args[0].sender).to.be.eql(mixinObject);
  199. expect(args[0].data).to.be.eql({
  200. serviceName: 'S1',
  201. componentName: 'C1',
  202. metricPaths: 'w1,w2'
  203. });
  204. });
  205. });
  206. describe("#getMetricsSuccessCallback()", function () {
  207. it("metric is mapped from provided path", function () {
  208. var data = {
  209. metrics: {
  210. "hbase": {
  211. "ipc": {
  212. "IPC": {
  213. "numOpenConnections": 11.5
  214. }
  215. }
  216. }
  217. }
  218. };
  219. mixinObject.set('content.metrics', [
  220. {
  221. metric_path: 'metrics/hbase/ipc/IPC/numOpenConnections'
  222. }
  223. ]);
  224. mixinObject.getMetricsSuccessCallback(data);
  225. expect(mixinObject.get('metrics').findProperty('metric_path', 'metrics/hbase/ipc/IPC/numOpenConnections').data).to.equal(11.5);
  226. });
  227. });
  228. describe("#getHostComponentMetrics()", function () {
  229. beforeEach(function () {
  230. sinon.stub(mixinObject, 'computeHostComponentCriteria').returns('criteria')
  231. });
  232. afterEach(function () {
  233. mixinObject.computeHostComponentCriteria.restore();
  234. });
  235. it("valid request is sent", function () {
  236. var request = {
  237. component_name: 'C1',
  238. metric_paths: [
  239. {
  240. "metric_path": "w1",
  241. "metric_type": "POINT_IN_TIME",
  242. "id": "w1_POINT_IN_TIME",
  243. "context": {}
  244. },
  245. {
  246. "metric_path": "w2",
  247. "metric_type": "POINT_IN_TIME",
  248. "id": "w2_POINT_IN_TIME",
  249. "context": {}
  250. }
  251. ],
  252. host_component_criteria: 'c1'
  253. };
  254. mixinObject.getHostComponentMetrics(request);
  255. var args = testHelpers.findAjaxRequest('name', 'widgets.hostComponent.metrics.get');
  256. expect(args[0]).exists;
  257. expect(args[0].sender).to.be.eql(mixinObject);
  258. expect(args[0].data).to.be.eql({
  259. componentName: 'C1',
  260. metricPaths: 'w1,w2',
  261. hostComponentCriteria: 'criteria'
  262. });
  263. });
  264. });
  265. describe("#calculateValues()", function () {
  266. beforeEach(function () {
  267. sinon.stub(mixinObject, 'extractExpressions');
  268. this.mock = sinon.stub(mixinObject, 'computeExpression');
  269. });
  270. afterEach(function () {
  271. mixinObject.extractExpressions.restore();
  272. this.mock.restore();
  273. });
  274. it("value compute correctly", function () {
  275. this.mock.returns({'${a}': 1});
  276. mixinObject.set('content.values', [{
  277. value: '${a}'
  278. }]);
  279. mixinObject.calculateValues();
  280. expect(mixinObject.get('content.values')[0].computedValue).to.equal('1');
  281. });
  282. it("value not available", function () {
  283. this.mock.returns({});
  284. mixinObject.set('content.values', [{
  285. value: '${a}'
  286. }]);
  287. mixinObject.calculateValues();
  288. expect(mixinObject.get('content.values')[0].computedValue).to.equal('<span class="grey">n/a</span>');
  289. });
  290. it("value is null", function () {
  291. this.mock.returns({'${a}': null});
  292. mixinObject.set('content.values', [{
  293. value: '${a}'
  294. }]);
  295. mixinObject.calculateValues();
  296. expect(mixinObject.get('content.values')[0].computedValue).to.equal('<span class="grey">n/a</span>');
  297. });
  298. });
  299. describe("#computeExpression()", function () {
  300. it("expression missing metrics", function () {
  301. var expressions = ['e.m1'];
  302. var metrics = [];
  303. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  304. "${e.m1}": ""
  305. });
  306. });
  307. it("Value is not correct mathematical expression", function () {
  308. var expressions = ['e.m1'];
  309. var metrics = [{
  310. name: 'e.m1',
  311. data: 'a+1'
  312. }];
  313. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  314. "${e.m1}": ""
  315. });
  316. });
  317. it("correct expression", function () {
  318. var expressions = ['e.m1+e.m1'];
  319. var metrics = [{
  320. name: 'e.m1',
  321. data: 1
  322. }];
  323. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  324. "${e.m1+e.m1}": "2"
  325. });
  326. });
  327. });
  328. describe("#cloneWidget()", function () {
  329. var popup;
  330. beforeEach(function () {
  331. sinon.spy(App, 'showConfirmationPopup');
  332. sinon.stub(mixinObject, 'postWidgetDefinition', Em.K);
  333. popup = mixinObject.cloneWidget();
  334. });
  335. afterEach(function () {
  336. App.showConfirmationPopup.restore();
  337. mixinObject.postWidgetDefinition.restore();
  338. });
  339. it("popup is shown", function () {
  340. expect(App.showConfirmationPopup.calledOnce).to.be.true;
  341. });
  342. it('postWidgetDefinition is called', function () {
  343. popup.onPrimary();
  344. expect(mixinObject.postWidgetDefinition.calledOnce).to.be.true;
  345. });
  346. });
  347. describe("#postWidgetDefinition()", function () {
  348. beforeEach(function () {
  349. sinon.stub(mixinObject, 'collectWidgetData').returns({});
  350. });
  351. afterEach(function () {
  352. mixinObject.collectWidgetData.restore();
  353. });
  354. it("valid request is sent", function () {
  355. mixinObject.postWidgetDefinition();
  356. var args = testHelpers.findAjaxRequest('name', 'widgets.wizard.add');
  357. expect(args[0]).exists;
  358. expect(args[0].sender).to.be.eql(mixinObject);
  359. expect(args[0].data).to.be.eql({
  360. data: {}
  361. });
  362. });
  363. });
  364. });
  365. describe('App.WidgetLoadAggregator', function () {
  366. var aggregator = App.WidgetLoadAggregator;
  367. describe("#add()", function () {
  368. beforeEach(function () {
  369. sinon.stub(window, 'setTimeout').returns('timeId');
  370. });
  371. afterEach(function () {
  372. window.setTimeout.restore();
  373. });
  374. it("timeout started", function () {
  375. aggregator.set('timeoutId', 'timeId');
  376. aggregator.get('requests').clear();
  377. aggregator.add({});
  378. expect(aggregator.get('requests')).to.not.be.empty;
  379. expect(window.setTimeout.called).to.be.false;
  380. });
  381. it("timeout started (2)", function () {
  382. aggregator.set('timeoutId', null);
  383. aggregator.get('requests').clear();
  384. aggregator.add({});
  385. expect(aggregator.get('requests')).to.not.be.empty;
  386. expect(window.setTimeout.calledOnce).to.be.true;
  387. expect(aggregator.get('timeoutId')).to.equal('timeId');
  388. });
  389. });
  390. describe("#groupRequests()", function () {
  391. var result;
  392. beforeEach(function () {
  393. var requests = [
  394. {
  395. startCallName: 'n1',
  396. data: {
  397. component_name: 'C1',
  398. metric_paths: ['m1']
  399. },
  400. context: Em.Object.create({
  401. content: {
  402. widgetType: 'GRAPH'
  403. }
  404. })
  405. },
  406. {
  407. startCallName: 'n1',
  408. data: {
  409. component_name: 'C1',
  410. metric_paths: ['m2']
  411. },
  412. context: Em.Object.create({
  413. content: {
  414. widgetType: 'NUMBER'
  415. }
  416. })
  417. },
  418. {
  419. startCallName: 'n2',
  420. data: {
  421. component_name: 'C1',
  422. metric_paths: ['m3']
  423. },
  424. context: Em.Object.create({
  425. content: {
  426. widgetType: 'TEMPLATE'
  427. }
  428. })
  429. },
  430. {
  431. startCallName: 'n1',
  432. data: {
  433. component_name: 'C2',
  434. metric_paths: ['m4']
  435. },
  436. context: Em.Object.create({
  437. content: {
  438. widgetType: 'GAUGE'
  439. }
  440. })
  441. }
  442. ];
  443. result = aggregator.groupRequests(requests);
  444. });
  445. it("result.n1_C1.subRequests.length", function () {
  446. expect(result.n1_C1.subRequests.length).to.equal(1);
  447. });
  448. it("result.n1_C1.data.metric_paths.length", function () {
  449. expect(result.n1_C1.data.metric_paths.length).to.equal(1);
  450. });
  451. it("result.n1_C1_graph.subRequests.length", function () {
  452. expect(result.n1_C1_graph.subRequests.length).to.equal(1);
  453. });
  454. it("result.n1_C1_graph.data.metric_paths.length", function () {
  455. expect(result.n1_C1_graph.data.metric_paths.length).to.equal(1);
  456. });
  457. it("result.n2_C1.subRequests.length", function () {
  458. expect(result.n2_C1.subRequests.length).to.equal(1);
  459. });
  460. it("result.n2_C1.data.metric_paths.length", function () {
  461. expect(result.n2_C1.data.metric_paths.length).to.equal(1);
  462. });
  463. it("result.n1_C2.subRequests.length", function () {
  464. expect(result.n1_C2.subRequests.length).to.equal(1);
  465. });
  466. it("result.n1_C2.data.metric_paths.length", function () {
  467. expect(result.n1_C2.data.metric_paths.length).to.equal(1);
  468. });
  469. });
  470. describe("#runRequests()", function () {
  471. var mock = Em.Object.create({
  472. f1: function () {
  473. return {
  474. done: Em.K,
  475. complete: Em.K
  476. }
  477. },
  478. state: 'inDOM'
  479. });
  480. beforeEach(function () {
  481. sinon.stub(aggregator, 'groupRequests', function (requests) {
  482. return requests;
  483. });
  484. sinon.spy(mock, 'f1');
  485. });
  486. afterEach(function () {
  487. aggregator.groupRequests.restore();
  488. mock.f1.restore();
  489. });
  490. it("view in DOM", function () {
  491. var requests = {
  492. 'r1': {
  493. data: {
  494. metric_paths: ['m1', 'm1', 'm2']
  495. },
  496. context: mock,
  497. startCallName: 'f1'
  498. }
  499. };
  500. aggregator.runRequests(requests);
  501. expect(mock.f1.calledWith(requests.r1.data)).to.be.true;
  502. });
  503. it("view destroyed", function () {
  504. var requests = {
  505. 'r1': {
  506. data: {
  507. metric_paths: ['m1', 'm1', 'm2']
  508. },
  509. context: mock,
  510. startCallName: 'f1'
  511. }
  512. };
  513. mock.set('state', 'destroyed');
  514. aggregator.runRequests(requests);
  515. expect(mock.f1.called).to.be.false;
  516. });
  517. });
  518. });