widget_mixin_test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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. describe('App.WidgetMixin', function () {
  20. var mixinClass = Em.Object.extend(App.WidgetMixin, {metrics: [], content: {}});
  21. describe('#loadMetrics()', function () {
  22. var mixinObject = mixinClass.create();
  23. beforeEach(function () {
  24. this.mock = sinon.stub(mixinObject, 'getRequestData');
  25. sinon.stub(App.WidgetLoadAggregator, 'add');
  26. });
  27. afterEach(function () {
  28. this.mock.restore();
  29. App.WidgetLoadAggregator.add.restore();
  30. });
  31. it('has host_component_criteria', function () {
  32. this.mock.returns({'key1': {host_component_criteria: 'criteria'}});
  33. mixinObject.set('isLoaded', false);
  34. mixinObject.loadMetrics();
  35. expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
  36. });
  37. it('host_component_criteria is absent', function () {
  38. this.mock.returns({'key1': {}});
  39. mixinObject.set('isLoaded', false);
  40. mixinObject.loadMetrics();
  41. expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
  42. });
  43. });
  44. describe("#extractExpressions()", function () {
  45. var mixinObject = mixinClass.create();
  46. var testCases = [
  47. {
  48. data: '',
  49. result: []
  50. },
  51. {
  52. data: 'text',
  53. result: []
  54. },
  55. {
  56. data: 'text${a}',
  57. result: ['a']
  58. },
  59. {
  60. data: 'text${a} - ${a.b}',
  61. result: ['a', 'a.b']
  62. },
  63. {
  64. data: '${o.a-(b+4)/cc*tt}',
  65. result: ['o.a-(b+4)/cc*tt']
  66. }
  67. ];
  68. testCases.forEach(function (test) {
  69. it('input: ' + test.data, function () {
  70. var input = {value: test.data};
  71. expect(mixinObject.extractExpressions(input)).to.eql(test.result);
  72. });
  73. });
  74. it('input is null', function () {
  75. var input = null;
  76. expect(mixinObject.extractExpressions(input)).to.be.empty;
  77. });
  78. });
  79. describe("#getRequestData()", function () {
  80. var mixinObject = mixinClass.create();
  81. it("", function () {
  82. var data = [
  83. {
  84. "name": "regionserver.Server.percentFilesLocal",
  85. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  86. "service_name": "HBASE",
  87. "component_name": "HBASE_REGIONSERVER"
  88. },
  89. {
  90. "name": "regionserver.Server.percentFilesLocal2",
  91. "metric_path": "w2",
  92. "service_name": "HBASE",
  93. "component_name": "HBASE_REGIONSERVER"
  94. },
  95. {
  96. "name": "regionserver.Server.percentFilesLocal",
  97. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  98. "service_name": "HBASE",
  99. "component_name": "HBASE_REGIONSERVER",
  100. "host_component_criteria": 'c1'
  101. },
  102. {
  103. "name": "regionserver.Server.percentFilesLocal",
  104. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  105. "service_name": "HDFS",
  106. "component_name": "DATANODE",
  107. "host_component_criteria": 'c1'
  108. }
  109. ];
  110. expect(JSON.stringify(mixinObject.getRequestData(data))).to.eql(JSON.stringify({
  111. "HBASE_HBASE_REGIONSERVER": {
  112. "name": "regionserver.Server.percentFilesLocal",
  113. "service_name": "HBASE",
  114. "component_name": "HBASE_REGIONSERVER",
  115. "metric_paths": [
  116. {
  117. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  118. "metric_type": "POINT_IN_TIME",
  119. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  120. "context": {}
  121. },
  122. {
  123. "metric_path": "w2",
  124. "metric_type": "POINT_IN_TIME",
  125. "id": "w2_POINT_IN_TIME",
  126. "context": {}
  127. }
  128. ]
  129. },
  130. "HBASE_HBASE_REGIONSERVER_c1": {
  131. "name": "regionserver.Server.percentFilesLocal",
  132. "service_name": "HBASE",
  133. "component_name": "HBASE_REGIONSERVER",
  134. "host_component_criteria": "c1",
  135. "metric_paths": [
  136. {
  137. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  138. "metric_type": "POINT_IN_TIME",
  139. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  140. "context": {}
  141. }
  142. ]
  143. },
  144. "HDFS_DATANODE_c1": {
  145. "name": "regionserver.Server.percentFilesLocal",
  146. "service_name": "HDFS",
  147. "component_name": "DATANODE",
  148. "host_component_criteria": "c1",
  149. "metric_paths": [
  150. {
  151. "metric_path": "metrics/hbase/regionserver/percentFilesLocal",
  152. "metric_type": "POINT_IN_TIME",
  153. "id": "metrics/hbase/regionserver/percentFilesLocal_POINT_IN_TIME",
  154. "context": {}
  155. }
  156. ]
  157. }
  158. }));
  159. });
  160. });
  161. describe("#getServiceComponentMetrics()", function () {
  162. var mixinObject = mixinClass.create();
  163. before(function () {
  164. sinon.stub(App.ajax, 'send');
  165. });
  166. after(function () {
  167. App.ajax.send.restore();
  168. });
  169. it("", function () {
  170. var request = {
  171. service_name: 'S1',
  172. component_name: 'C1',
  173. metric_paths: [
  174. {
  175. "metric_path": "w1",
  176. "metric_type": "POINT_IN_TIME",
  177. "id": "w1_POINT_IN_TIME",
  178. "context": {}
  179. },
  180. {
  181. "metric_path": "w2",
  182. "metric_type": "POINT_IN_TIME",
  183. "id": "w2_POINT_IN_TIME",
  184. "context": {}
  185. }
  186. ]
  187. };
  188. mixinObject.getServiceComponentMetrics(request);
  189. expect(App.ajax.send.getCall(0).args[0]).to.eql({
  190. name: 'widgets.serviceComponent.metrics.get',
  191. sender: mixinObject,
  192. data: {
  193. serviceName: 'S1',
  194. componentName: 'C1',
  195. metricPaths: 'w1,w2'
  196. }
  197. })
  198. });
  199. });
  200. describe("#getMetricsSuccessCallback()", function () {
  201. var mixinObject = mixinClass.create();
  202. it("", function () {
  203. var data = {
  204. metrics: {
  205. "hbase": {
  206. "ipc": {
  207. "IPC": {
  208. "numOpenConnections": 11.5
  209. }
  210. }
  211. }
  212. }
  213. };
  214. mixinObject.set('content.metrics', [
  215. {
  216. metric_path: 'metrics/hbase/ipc/IPC/numOpenConnections'
  217. }
  218. ]);
  219. mixinObject.getMetricsSuccessCallback(data);
  220. expect(mixinObject.get('metrics').findProperty('metric_path', 'metrics/hbase/ipc/IPC/numOpenConnections').data).to.equal(11.5);
  221. });
  222. });
  223. describe("#getHostComponentMetrics()", function () {
  224. var mixinObject = mixinClass.create();
  225. before(function () {
  226. sinon.stub(App.ajax, 'send');
  227. sinon.stub(mixinObject, 'computeHostComponentCriteria').returns('criteria')
  228. });
  229. after(function () {
  230. App.ajax.send.restore();
  231. mixinObject.computeHostComponentCriteria.restore();
  232. });
  233. it("", function () {
  234. var request = {
  235. component_name: 'C1',
  236. metric_paths: [
  237. {
  238. "metric_path": "w1",
  239. "metric_type": "POINT_IN_TIME",
  240. "id": "w1_POINT_IN_TIME",
  241. "context": {}
  242. },
  243. {
  244. "metric_path": "w2",
  245. "metric_type": "POINT_IN_TIME",
  246. "id": "w2_POINT_IN_TIME",
  247. "context": {}
  248. }
  249. ],
  250. host_component_criteria: 'c1'
  251. };
  252. mixinObject.getHostComponentMetrics(request);
  253. expect(App.ajax.send.getCall(0).args[0]).to.eql({
  254. name: 'widgets.hostComponent.metrics.get',
  255. sender: mixinObject,
  256. data: {
  257. componentName: 'C1',
  258. metricPaths: 'w1,w2',
  259. hostComponentCriteria: 'criteria'
  260. }
  261. })
  262. });
  263. });
  264. describe("#calculateValues()", function () {
  265. var mixinObject = mixinClass.create();
  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. var mixinObject = mixinClass.create();
  301. it("expression missing metrics", function () {
  302. var expressions = ['e.m1'];
  303. var metrics = [];
  304. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  305. "${e.m1}": ""
  306. });
  307. });
  308. it("Value is not correct mathematical expression", function () {
  309. var expressions = ['e.m1'];
  310. var metrics = [{
  311. name: 'e.m1',
  312. data: 'a+1'
  313. }];
  314. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  315. "${e.m1}": ""
  316. });
  317. });
  318. it("correct expression", function () {
  319. var expressions = ['e.m1+e.m1'];
  320. var metrics = [{
  321. name: 'e.m1',
  322. data: 1
  323. }];
  324. expect(mixinObject.computeExpression(expressions, metrics)).to.eql({
  325. "${e.m1+e.m1}": "2"
  326. });
  327. });
  328. });
  329. describe("#cloneWidget()", function () {
  330. var mixinObject = mixinClass.create();
  331. before(function () {
  332. sinon.spy(App, 'showConfirmationPopup');
  333. sinon.stub(mixinObject, 'postWidgetDefinition', Em.K);
  334. });
  335. after(function () {
  336. App.showConfirmationPopup.restore();
  337. mixinObject.postWidgetDefinition.restore();
  338. });
  339. it("", function () {
  340. var popup = mixinObject.cloneWidget();
  341. expect(App.showConfirmationPopup.calledOnce).to.be.true;
  342. popup.onPrimary();
  343. expect(mixinObject.postWidgetDefinition.calledOnce).to.be.true;
  344. });
  345. });
  346. describe("#postWidgetDefinition()", function () {
  347. var mixinObject = mixinClass.create();
  348. before(function () {
  349. sinon.spy(App.ajax, 'send');
  350. sinon.stub(mixinObject, 'collectWidgetData').returns({});
  351. });
  352. after(function () {
  353. App.ajax.send.restore();
  354. mixinObject.collectWidgetData.restore();
  355. });
  356. it("", function () {
  357. mixinObject.postWidgetDefinition();
  358. expect(App.ajax.send.getCall(0).args[0]).to.eql({
  359. name: 'widgets.wizard.add',
  360. sender: mixinObject,
  361. data: {
  362. data: {}
  363. },
  364. success: 'postWidgetDefinitionSuccessCallback'
  365. });
  366. });
  367. });
  368. });
  369. describe('App.WidgetLoadAggregator', function () {
  370. var aggregator = App.WidgetLoadAggregator;
  371. describe("#add()", function () {
  372. beforeEach(function () {
  373. sinon.stub(window, 'setTimeout').returns('timeId');
  374. });
  375. afterEach(function () {
  376. window.setTimeout.restore();
  377. });
  378. it("timeout started", function () {
  379. aggregator.set('timeoutId', 'timeId');
  380. aggregator.get('requests').clear();
  381. aggregator.add({});
  382. expect(aggregator.get('requests')).to.not.be.empty;
  383. expect(window.setTimeout.called).to.be.false;
  384. });
  385. it("timeout started", function () {
  386. aggregator.set('timeoutId', null);
  387. aggregator.get('requests').clear();
  388. aggregator.add({});
  389. expect(aggregator.get('requests')).to.not.be.empty;
  390. expect(window.setTimeout.calledOnce).to.be.true;
  391. expect(aggregator.get('timeoutId')).to.equal('timeId');
  392. });
  393. });
  394. describe("#groupRequests()", function () {
  395. it("", function () {
  396. var requests = [
  397. {
  398. startCallName: 'n1',
  399. data: {
  400. component_name: 'C1',
  401. metric_paths: ['m1']
  402. },
  403. context: Em.Object.create({
  404. content: {
  405. widgetType: 'GRAPH'
  406. }
  407. })
  408. },
  409. {
  410. startCallName: 'n1',
  411. data: {
  412. component_name: 'C1',
  413. metric_paths: ['m2']
  414. },
  415. context: Em.Object.create({
  416. content: {
  417. widgetType: 'NUMBER'
  418. }
  419. })
  420. },
  421. {
  422. startCallName: 'n2',
  423. data: {
  424. component_name: 'C1',
  425. metric_paths: ['m3']
  426. },
  427. context: Em.Object.create({
  428. content: {
  429. widgetType: 'TEMPLATE'
  430. }
  431. })
  432. },
  433. {
  434. startCallName: 'n1',
  435. data: {
  436. component_name: 'C2',
  437. metric_paths: ['m4']
  438. },
  439. context: Em.Object.create({
  440. content: {
  441. widgetType: 'GAUGE'
  442. }
  443. })
  444. }
  445. ];
  446. var result = aggregator.groupRequests(requests);
  447. expect(result['n1_C1'].subRequests.length).to.equal(1);
  448. expect(result['n1_C1'].data.metric_paths.length).to.equal(1);
  449. expect(result['n1_C1_graph'].subRequests.length).to.equal(1);
  450. expect(result['n1_C1_graph'].data.metric_paths.length).to.equal(1);
  451. expect(result['n2_C1'].subRequests.length).to.equal(1);
  452. expect(result['n2_C1'].data.metric_paths.length).to.equal(1);
  453. expect(result['n1_C2'].subRequests.length).to.equal(1);
  454. expect(result['n1_C2'].data.metric_paths.length).to.equal(1);
  455. });
  456. });
  457. describe("#runRequests()", function () {
  458. var mock = Em.Object.create({
  459. f1: function () {
  460. return {
  461. done: Em.K,
  462. complete: Em.K
  463. }
  464. },
  465. state: 'inDOM'
  466. });
  467. beforeEach(function () {
  468. sinon.stub(aggregator, 'groupRequests', function (requests) {
  469. return requests;
  470. });
  471. sinon.spy(mock, 'f1');
  472. });
  473. afterEach(function () {
  474. aggregator.groupRequests.restore();
  475. mock.f1.restore();
  476. });
  477. it("view in DOM", function () {
  478. var requests = {
  479. 'r1': {
  480. data: {
  481. metric_paths: ['m1', 'm1', 'm2']
  482. },
  483. context: mock,
  484. startCallName: 'f1'
  485. }
  486. };
  487. aggregator.runRequests(requests);
  488. expect(mock.f1.calledWith(requests['r1'].data)).to.be.true;
  489. });
  490. it("view destroyed", function () {
  491. var requests = {
  492. 'r1': {
  493. data: {
  494. metric_paths: ['m1', 'm1', 'm2']
  495. },
  496. context: mock,
  497. startCallName: 'f1'
  498. }
  499. };
  500. mock.set('state', 'destroyed');
  501. aggregator.runRequests(requests);
  502. expect(mock.f1.called).to.be.false;
  503. });
  504. });
  505. });