reducer.spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. import {
  2. MessageSchemaSourceEnum,
  3. SortOrder,
  4. TopicColumnsToSort,
  5. ConfigSource,
  6. } from 'generated-sources';
  7. import reducer, {
  8. clearTopicsMessages,
  9. setTopicsSearch,
  10. setTopicsOrderBy,
  11. fetchTopicConsumerGroups,
  12. fetchTopicMessageSchema,
  13. recreateTopic,
  14. createTopic,
  15. deleteTopic,
  16. fetchTopicsList,
  17. fetchTopicDetails,
  18. fetchTopicConfig,
  19. updateTopic,
  20. updateTopicPartitionsCount,
  21. updateTopicReplicationFactor,
  22. deleteTopics,
  23. } from 'redux/reducers/topics/topicsSlice';
  24. import {
  25. createTopicPayload,
  26. createTopicResponsePayload,
  27. } from 'components/Topics/New/__test__/fixtures';
  28. import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures';
  29. import fetchMock from 'fetch-mock-jest';
  30. import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator';
  31. import { getTypeAndPayload } from 'lib/testHelpers';
  32. import {
  33. alertAdded,
  34. showSuccessAlert,
  35. } from 'redux/reducers/alerts/alertsSlice';
  36. const topic = {
  37. name: 'topic',
  38. };
  39. const messageSchema = {
  40. key: {
  41. name: 'key',
  42. source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
  43. schema: `{
  44. "$schema": "http://json-schema.org/draft-07/schema#",
  45. "$id": "http://example.com/myURI.schema.json",
  46. "title": "TestRecord",
  47. "type": "object",
  48. "additionalProperties": false,
  49. "properties": {
  50. "f1": {
  51. "type": "integer"
  52. },
  53. "f2": {
  54. "type": "string"
  55. },
  56. "schema": {
  57. "type": "string"
  58. }
  59. }
  60. }
  61. `,
  62. },
  63. value: {
  64. name: 'value',
  65. source: MessageSchemaSourceEnum.SCHEMA_REGISTRY,
  66. schema: `{
  67. "$schema": "http://json-schema.org/draft-07/schema#",
  68. "$id": "http://example.com/myURI1.schema.json",
  69. "title": "TestRecord",
  70. "type": "object",
  71. "additionalProperties": false,
  72. "properties": {
  73. "f1": {
  74. "type": "integer"
  75. },
  76. "f2": {
  77. "type": "string"
  78. },
  79. "schema": {
  80. "type": "string"
  81. }
  82. }
  83. }
  84. `,
  85. },
  86. };
  87. const config = [
  88. {
  89. name: 'compression.type',
  90. value: 'producer',
  91. defaultValue: 'producer',
  92. source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
  93. isSensitive: false,
  94. isReadOnly: false,
  95. synonyms: [
  96. {
  97. name: 'compression.type',
  98. value: 'producer',
  99. source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
  100. },
  101. {
  102. name: 'compression.type',
  103. value: 'producer',
  104. source: ConfigSource.DEFAULT_CONFIG,
  105. },
  106. ],
  107. },
  108. ];
  109. const details = {
  110. name: 'local',
  111. internal: false,
  112. partitionCount: 1,
  113. replicationFactor: 1,
  114. replicas: 1,
  115. inSyncReplicas: 1,
  116. segmentSize: 0,
  117. segmentCount: 0,
  118. cleanUpPolicy: 'DELETE',
  119. partitions: [
  120. {
  121. partition: 0,
  122. leader: 1,
  123. replicas: [{ broker: 1, leader: false, inSync: true }],
  124. offsetMax: 0,
  125. offsetMin: 0,
  126. },
  127. ],
  128. bytesInPerSec: 0.1,
  129. bytesOutPerSec: 0.1,
  130. };
  131. let state = {
  132. byName: {
  133. [topic.name]: topic,
  134. },
  135. allNames: [topic.name],
  136. messages: [],
  137. totalPages: 1,
  138. search: '',
  139. orderBy: null,
  140. sortOrder: SortOrder.ASC,
  141. consumerGroups: [],
  142. };
  143. const clusterName = 'local';
  144. describe('topics Slice', () => {
  145. describe('topics reducer', () => {
  146. describe('fetch topic details', () => {
  147. it('fetchTopicDetails/fulfilled', () => {
  148. expect(
  149. reducer(state, {
  150. type: fetchTopicDetails.fulfilled,
  151. payload: {
  152. clusterName,
  153. topicName: topic.name,
  154. topicDetails: details,
  155. },
  156. })
  157. ).toEqual({
  158. ...state,
  159. byName: {
  160. [topic.name]: {
  161. ...topic,
  162. ...details,
  163. },
  164. },
  165. allNames: [topic.name],
  166. });
  167. });
  168. });
  169. describe('fetch topics', () => {
  170. it('fetchTopicsList/fulfilled', () => {
  171. expect(
  172. reducer(state, {
  173. type: fetchTopicsList.fulfilled,
  174. payload: { clusterName, topicName: topic.name },
  175. })
  176. ).toEqual({
  177. ...state,
  178. byName: { topic },
  179. allNames: [topic.name],
  180. });
  181. });
  182. });
  183. describe('fetch topic config', () => {
  184. it('fetchTopicConfig/fulfilled', () => {
  185. expect(
  186. reducer(state, {
  187. type: fetchTopicConfig.fulfilled,
  188. payload: {
  189. clusterName,
  190. topicName: topic.name,
  191. topicConfig: config,
  192. },
  193. })
  194. ).toEqual({
  195. ...state,
  196. byName: {
  197. [topic.name]: {
  198. ...topic,
  199. config: config.map((conf) => ({ ...conf })),
  200. },
  201. },
  202. allNames: [topic.name],
  203. });
  204. });
  205. });
  206. describe('update topic', () => {
  207. it('updateTopic/fulfilled', () => {
  208. const updatedTopic = {
  209. name: 'topic',
  210. partitions: 1,
  211. };
  212. expect(
  213. reducer(state, {
  214. type: updateTopic.fulfilled,
  215. payload: {
  216. clusterName,
  217. topicName: topic.name,
  218. topic: updatedTopic,
  219. },
  220. })
  221. ).toEqual({
  222. ...state,
  223. byName: {
  224. [topic.name]: {
  225. ...updatedTopic,
  226. },
  227. },
  228. });
  229. });
  230. });
  231. describe('delete topic', () => {
  232. it('deleteTopic/fulfilled', () => {
  233. expect(
  234. reducer(state, {
  235. type: deleteTopic.fulfilled,
  236. payload: { clusterName, topicName: topic.name },
  237. })
  238. ).toEqual({
  239. ...state,
  240. byName: {},
  241. allNames: [],
  242. });
  243. });
  244. it('clearTopicsMessages/fulfilled', () => {
  245. expect(
  246. reducer(state, {
  247. type: clearTopicsMessages.fulfilled,
  248. payload: { clusterName, topicName: topic.name },
  249. })
  250. ).toEqual({
  251. ...state,
  252. messages: [],
  253. });
  254. });
  255. it('recreateTopic/fulfilled', () => {
  256. expect(
  257. reducer(state, {
  258. type: recreateTopic.fulfilled,
  259. payload: { topic, topicName: topic.name },
  260. })
  261. ).toEqual({
  262. ...state,
  263. byName: {
  264. [topic.name]: topic,
  265. },
  266. });
  267. });
  268. });
  269. describe('create topics', () => {
  270. it('createTopic/fulfilled', () => {
  271. expect(
  272. reducer(state, {
  273. type: createTopic.fulfilled,
  274. payload: { clusterName, data: createTopicPayload },
  275. })
  276. ).toEqual({
  277. ...state,
  278. });
  279. });
  280. });
  281. describe('search topics', () => {
  282. it('setTopicsSearch', () => {
  283. expect(
  284. reducer(state, {
  285. type: setTopicsSearch,
  286. payload: 'test',
  287. })
  288. ).toEqual({
  289. ...state,
  290. search: 'test',
  291. });
  292. });
  293. });
  294. describe('order topics', () => {
  295. it('setTopicsOrderBy', () => {
  296. expect(
  297. reducer(state, {
  298. type: setTopicsOrderBy,
  299. payload: TopicColumnsToSort.NAME,
  300. })
  301. ).toEqual({
  302. ...state,
  303. orderBy: TopicColumnsToSort.NAME,
  304. });
  305. });
  306. });
  307. describe('topic consumer groups', () => {
  308. it('fetchTopicConsumerGroups/fulfilled', () => {
  309. expect(
  310. reducer(state, {
  311. type: fetchTopicConsumerGroups.fulfilled,
  312. payload: {
  313. clusterName,
  314. topicName: topic.name,
  315. consumerGroups: consumerGroupPayload,
  316. },
  317. })
  318. ).toEqual({
  319. ...state,
  320. byName: {
  321. [topic.name]: {
  322. ...topic,
  323. ...consumerGroupPayload,
  324. },
  325. },
  326. });
  327. });
  328. });
  329. describe('message sending', () => {
  330. it('fetchTopicMessageSchema/fulfilled', () => {
  331. state = {
  332. byName: {
  333. [topic.name]: topic,
  334. },
  335. allNames: [topic.name],
  336. messages: [],
  337. totalPages: 1,
  338. search: '',
  339. orderBy: null,
  340. sortOrder: SortOrder.ASC,
  341. consumerGroups: [],
  342. };
  343. expect(
  344. reducer(state, {
  345. type: fetchTopicMessageSchema.fulfilled,
  346. payload: { topicName: topic.name, schema: messageSchema },
  347. }).byName
  348. ).toEqual({
  349. [topic.name]: { ...topic, messageSchema },
  350. });
  351. });
  352. });
  353. });
  354. describe('Thunks', () => {
  355. const store = mockStoreCreator;
  356. const topicName = topic.name;
  357. const RealDate = Date.now;
  358. beforeAll(() => {
  359. global.Date.now = jest.fn(() =>
  360. new Date('2019-04-07T10:20:30Z').getTime()
  361. );
  362. });
  363. afterAll(() => {
  364. global.Date.now = RealDate;
  365. });
  366. afterEach(() => {
  367. fetchMock.restore();
  368. store.clearActions();
  369. });
  370. describe('fetchTopicsList', () => {
  371. const topicResponse = {
  372. pageCount: 1,
  373. topics: [createTopicResponsePayload],
  374. };
  375. it('fetchTopicsList/fulfilled', async () => {
  376. fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, topicResponse);
  377. await store.dispatch(fetchTopicsList({ clusterName }));
  378. expect(getTypeAndPayload(store)).toEqual([
  379. { type: fetchTopicsList.pending.type },
  380. {
  381. type: fetchTopicsList.fulfilled.type,
  382. payload: { ...topicResponse },
  383. },
  384. ]);
  385. });
  386. it('fetchTopicsList/rejected', async () => {
  387. fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, 404);
  388. await store.dispatch(fetchTopicsList({ clusterName }));
  389. expect(getTypeAndPayload(store)).toEqual([
  390. { type: fetchTopicsList.pending.type },
  391. {
  392. type: fetchTopicsList.rejected.type,
  393. payload: {
  394. status: 404,
  395. statusText: 'Not Found',
  396. url: `/api/clusters/${clusterName}/topics`,
  397. message: undefined,
  398. },
  399. },
  400. ]);
  401. });
  402. });
  403. describe('fetchTopicDetails', () => {
  404. it('fetchTopicDetails/fulfilled', async () => {
  405. fetchMock.getOnce(
  406. `/api/clusters/${clusterName}/topics/${topicName}`,
  407. details
  408. );
  409. await store.dispatch(fetchTopicDetails({ clusterName, topicName }));
  410. expect(getTypeAndPayload(store)).toEqual([
  411. { type: fetchTopicDetails.pending.type },
  412. {
  413. type: fetchTopicDetails.fulfilled.type,
  414. payload: { topicDetails: { ...details }, topicName },
  415. },
  416. ]);
  417. });
  418. it('fetchTopicDetails/rejected', async () => {
  419. fetchMock.getOnce(
  420. `/api/clusters/${clusterName}/topics/${topicName}`,
  421. 404
  422. );
  423. await store.dispatch(fetchTopicDetails({ clusterName, topicName }));
  424. expect(getTypeAndPayload(store)).toEqual([
  425. { type: fetchTopicDetails.pending.type },
  426. {
  427. type: fetchTopicDetails.rejected.type,
  428. payload: {
  429. status: 404,
  430. statusText: 'Not Found',
  431. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  432. message: undefined,
  433. },
  434. },
  435. ]);
  436. });
  437. });
  438. describe('fetchTopicConfig', () => {
  439. it('fetchTopicConfig/fulfilled', async () => {
  440. fetchMock.getOnce(
  441. `/api/clusters/${clusterName}/topics/${topicName}/config`,
  442. config
  443. );
  444. await store.dispatch(fetchTopicConfig({ clusterName, topicName }));
  445. expect(getTypeAndPayload(store)).toEqual([
  446. { type: fetchTopicConfig.pending.type },
  447. {
  448. type: fetchTopicConfig.fulfilled.type,
  449. payload: {
  450. topicConfig: config,
  451. topicName,
  452. },
  453. },
  454. ]);
  455. });
  456. it('fetchTopicConfig/rejected', async () => {
  457. fetchMock.getOnce(
  458. `/api/clusters/${clusterName}/topics/${topicName}/config`,
  459. 404
  460. );
  461. await store.dispatch(fetchTopicConfig({ clusterName, topicName }));
  462. expect(getTypeAndPayload(store)).toEqual([
  463. { type: fetchTopicConfig.pending.type },
  464. {
  465. type: fetchTopicConfig.rejected.type,
  466. payload: {
  467. status: 404,
  468. statusText: 'Not Found',
  469. url: `/api/clusters/${clusterName}/topics/${topicName}/config`,
  470. message: undefined,
  471. },
  472. },
  473. ]);
  474. });
  475. });
  476. describe('deleteTopic', () => {
  477. it('deleteTopic/fulfilled', async () => {
  478. fetchMock.deleteOnce(
  479. `/api/clusters/${clusterName}/topics/${topicName}`,
  480. topicName
  481. );
  482. await store.dispatch(deleteTopic({ clusterName, topicName }));
  483. expect(getTypeAndPayload(store)).toEqual([
  484. { type: deleteTopic.pending.type },
  485. { type: showSuccessAlert.pending.type },
  486. {
  487. type: alertAdded.type,
  488. payload: {
  489. id: 'message-topic-local',
  490. title: '',
  491. type: 'success',
  492. createdAt: global.Date.now(),
  493. message: 'Topic successfully deleted!',
  494. },
  495. },
  496. { type: showSuccessAlert.fulfilled.type },
  497. {
  498. type: deleteTopic.fulfilled.type,
  499. payload: { topicName },
  500. },
  501. ]);
  502. });
  503. it('deleteTopic/rejected', async () => {
  504. fetchMock.deleteOnce(
  505. `/api/clusters/${clusterName}/topics/${topicName}`,
  506. 404
  507. );
  508. await store.dispatch(deleteTopic({ clusterName, topicName }));
  509. expect(getTypeAndPayload(store)).toEqual([
  510. { type: deleteTopic.pending.type },
  511. {
  512. type: deleteTopic.rejected.type,
  513. payload: {
  514. status: 404,
  515. statusText: 'Not Found',
  516. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  517. message: undefined,
  518. },
  519. },
  520. ]);
  521. });
  522. });
  523. describe('deleteTopics', () => {
  524. it('deleteTopics/fulfilled', async () => {
  525. fetchMock.delete(`/api/clusters/${clusterName}/topics/${topicName}`, [
  526. topicName,
  527. 'topic2',
  528. ]);
  529. await store.dispatch(
  530. deleteTopics({ clusterName, topicNames: [topicName, 'topic2'] })
  531. );
  532. expect(getTypeAndPayload(store)).toEqual([
  533. { type: deleteTopics.pending.type },
  534. { type: deleteTopic.pending.type },
  535. { type: deleteTopic.pending.type },
  536. { type: fetchTopicsList.pending.type },
  537. { type: deleteTopics.fulfilled.type },
  538. ]);
  539. });
  540. });
  541. describe('recreateTopic', () => {
  542. const recreateResponse = {
  543. cleanUpPolicy: 'DELETE',
  544. inSyncReplicas: 1,
  545. internal: false,
  546. name: topicName,
  547. partitionCount: 1,
  548. partitions: undefined,
  549. replicas: 1,
  550. replicationFactor: 1,
  551. segmentCount: 0,
  552. segmentSize: 0,
  553. underReplicatedPartitions: undefined,
  554. };
  555. it('recreateTopic/fulfilled', async () => {
  556. fetchMock.postOnce(
  557. `/api/clusters/${clusterName}/topics/${topicName}`,
  558. recreateResponse
  559. );
  560. await store.dispatch(recreateTopic({ clusterName, topicName }));
  561. expect(getTypeAndPayload(store)).toEqual([
  562. { type: recreateTopic.pending.type },
  563. { type: showSuccessAlert.pending.type },
  564. {
  565. type: alertAdded.type,
  566. payload: {
  567. id: 'message-topic-local',
  568. title: '',
  569. type: 'success',
  570. createdAt: global.Date.now(),
  571. message: 'Topic successfully recreated!',
  572. },
  573. },
  574. { type: showSuccessAlert.fulfilled.type },
  575. {
  576. type: recreateTopic.fulfilled.type,
  577. payload: { [topicName]: { ...recreateResponse } },
  578. },
  579. ]);
  580. });
  581. it('recreateTopic/rejected', async () => {
  582. fetchMock.postOnce(
  583. `/api/clusters/${clusterName}/topics/${topicName}`,
  584. 404
  585. );
  586. await store.dispatch(recreateTopic({ clusterName, topicName }));
  587. expect(getTypeAndPayload(store)).toEqual([
  588. { type: recreateTopic.pending.type },
  589. {
  590. type: recreateTopic.rejected.type,
  591. payload: {
  592. status: 404,
  593. statusText: 'Not Found',
  594. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  595. message: undefined,
  596. },
  597. },
  598. ]);
  599. });
  600. });
  601. describe('fetchTopicConsumerGroups', () => {
  602. const consumerGroups = [
  603. {
  604. groupId: 'groupId1',
  605. members: 0,
  606. topics: 1,
  607. simple: false,
  608. partitionAssignor: '',
  609. coordinator: {
  610. id: 1,
  611. port: undefined,
  612. host: 'host',
  613. },
  614. messagesBehind: undefined,
  615. state: undefined,
  616. },
  617. {
  618. groupId: 'groupId2',
  619. members: 0,
  620. topics: 1,
  621. simple: false,
  622. partitionAssignor: '',
  623. coordinator: {
  624. id: 1,
  625. port: undefined,
  626. host: 'host',
  627. },
  628. messagesBehind: undefined,
  629. state: undefined,
  630. },
  631. ];
  632. it('fetchTopicConsumerGroups/fulfilled', async () => {
  633. fetchMock.getOnce(
  634. `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  635. consumerGroups
  636. );
  637. await store.dispatch(
  638. fetchTopicConsumerGroups({ clusterName, topicName })
  639. );
  640. expect(getTypeAndPayload(store)).toEqual([
  641. { type: fetchTopicConsumerGroups.pending.type },
  642. {
  643. type: fetchTopicConsumerGroups.fulfilled.type,
  644. payload: { consumerGroups, topicName },
  645. },
  646. ]);
  647. });
  648. it('fetchTopicConsumerGroups/rejected', async () => {
  649. fetchMock.getOnce(
  650. `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  651. 404
  652. );
  653. await store.dispatch(
  654. fetchTopicConsumerGroups({ clusterName, topicName })
  655. );
  656. expect(getTypeAndPayload(store)).toEqual([
  657. { type: fetchTopicConsumerGroups.pending.type },
  658. {
  659. type: fetchTopicConsumerGroups.rejected.type,
  660. payload: {
  661. status: 404,
  662. statusText: 'Not Found',
  663. url: `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  664. message: undefined,
  665. },
  666. },
  667. ]);
  668. });
  669. });
  670. describe('updateTopicPartitionsCount', () => {
  671. it('updateTopicPartitionsCount/fulfilled', async () => {
  672. fetchMock.patchOnce(
  673. `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  674. { message: 'success' }
  675. );
  676. await store.dispatch(
  677. updateTopicPartitionsCount({
  678. clusterName,
  679. topicName,
  680. partitions: 1,
  681. })
  682. );
  683. expect(getTypeAndPayload(store)).toEqual([
  684. { type: updateTopicPartitionsCount.pending.type },
  685. { type: showSuccessAlert.pending.type },
  686. {
  687. type: alertAdded.type,
  688. payload: {
  689. id: 'message-topic-local-1',
  690. title: '',
  691. type: 'success',
  692. createdAt: global.Date.now(),
  693. message: 'Number of partitions successfully increased!',
  694. },
  695. },
  696. { type: fetchTopicDetails.pending.type },
  697. { type: showSuccessAlert.fulfilled.type },
  698. {
  699. type: updateTopicPartitionsCount.fulfilled.type,
  700. },
  701. ]);
  702. });
  703. it('updateTopicPartitionsCount/rejected', async () => {
  704. fetchMock.patchOnce(
  705. `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  706. 404
  707. );
  708. await store.dispatch(
  709. updateTopicPartitionsCount({
  710. clusterName,
  711. topicName,
  712. partitions: 1,
  713. })
  714. );
  715. expect(getTypeAndPayload(store)).toEqual([
  716. { type: updateTopicPartitionsCount.pending.type },
  717. {
  718. type: updateTopicPartitionsCount.rejected.type,
  719. payload: {
  720. status: 404,
  721. statusText: 'Not Found',
  722. url: `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  723. message: undefined,
  724. },
  725. },
  726. ]);
  727. });
  728. });
  729. describe('updateTopicReplicationFactor', () => {
  730. it('updateTopicReplicationFactor/fulfilled', async () => {
  731. fetchMock.patchOnce(
  732. `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  733. { message: 'success' }
  734. );
  735. await store.dispatch(
  736. updateTopicReplicationFactor({
  737. clusterName,
  738. topicName,
  739. replicationFactor: 1,
  740. })
  741. );
  742. expect(getTypeAndPayload(store)).toEqual([
  743. { type: updateTopicReplicationFactor.pending.type },
  744. {
  745. type: updateTopicReplicationFactor.fulfilled.type,
  746. },
  747. ]);
  748. });
  749. it('updateTopicReplicationFactor/rejected', async () => {
  750. fetchMock.patchOnce(
  751. `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  752. 404
  753. );
  754. await store.dispatch(
  755. updateTopicReplicationFactor({
  756. clusterName,
  757. topicName,
  758. replicationFactor: 1,
  759. })
  760. );
  761. expect(getTypeAndPayload(store)).toEqual([
  762. { type: updateTopicReplicationFactor.pending.type },
  763. {
  764. type: updateTopicReplicationFactor.rejected.type,
  765. payload: {
  766. status: 404,
  767. statusText: 'Not Found',
  768. url: `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  769. message: undefined,
  770. },
  771. },
  772. ]);
  773. });
  774. });
  775. describe('createTopic', () => {
  776. const newTopic = {
  777. name: 'newTopic',
  778. partitions: 0,
  779. replicationFactor: 0,
  780. minInSyncReplicas: 0,
  781. cleanupPolicy: 'DELETE',
  782. retentionMs: 1,
  783. retentionBytes: 1,
  784. maxMessageBytes: 1,
  785. customParams: [
  786. {
  787. name: '',
  788. value: '',
  789. },
  790. ],
  791. };
  792. it('createTopic/fulfilled', async () => {
  793. fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, {
  794. message: 'success',
  795. });
  796. await store.dispatch(
  797. createTopic({
  798. clusterName,
  799. data: newTopic,
  800. })
  801. );
  802. expect(getTypeAndPayload(store)).toEqual([
  803. { type: createTopic.pending.type },
  804. {
  805. type: createTopic.fulfilled.type,
  806. },
  807. ]);
  808. });
  809. it('createTopic/rejected', async () => {
  810. fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, 404);
  811. await store.dispatch(
  812. createTopic({
  813. clusterName,
  814. data: newTopic,
  815. })
  816. );
  817. expect(getTypeAndPayload(store)).toEqual([
  818. { type: createTopic.pending.type },
  819. {
  820. type: createTopic.rejected.type,
  821. payload: {
  822. status: 404,
  823. statusText: 'Not Found',
  824. url: `/api/clusters/${clusterName}/topics`,
  825. message: undefined,
  826. },
  827. },
  828. ]);
  829. });
  830. });
  831. describe('updateTopic', () => {
  832. const updateTopicResponse = {
  833. name: topicName,
  834. partitions: 0,
  835. replicationFactor: 0,
  836. minInSyncReplicas: 0,
  837. cleanupPolicy: 'DELETE',
  838. retentionMs: 0,
  839. retentionBytes: 0,
  840. maxMessageBytes: 0,
  841. customParams: {
  842. byIndex: {},
  843. allIndexes: [],
  844. },
  845. };
  846. it('updateTopic/fulfilled', async () => {
  847. fetchMock.patchOnce(
  848. `/api/clusters/${clusterName}/topics/${topicName}`,
  849. createTopicResponsePayload
  850. );
  851. await store.dispatch(
  852. updateTopic({
  853. clusterName,
  854. topicName,
  855. form: updateTopicResponse,
  856. })
  857. );
  858. expect(getTypeAndPayload(store)).toEqual([
  859. { type: updateTopic.pending.type },
  860. {
  861. type: updateTopic.fulfilled.type,
  862. payload: { [topicName]: { ...createTopicResponsePayload } },
  863. },
  864. ]);
  865. });
  866. it('updateTopic/rejected', async () => {
  867. fetchMock.patchOnce(
  868. `/api/clusters/${clusterName}/topics/${topicName}`,
  869. 404
  870. );
  871. await store.dispatch(
  872. updateTopic({
  873. clusterName,
  874. topicName,
  875. form: updateTopicResponse,
  876. })
  877. );
  878. expect(getTypeAndPayload(store)).toEqual([
  879. { type: updateTopic.pending.type },
  880. {
  881. type: updateTopic.rejected.type,
  882. payload: {
  883. status: 404,
  884. statusText: 'Not Found',
  885. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  886. message: undefined,
  887. },
  888. },
  889. ]);
  890. });
  891. });
  892. describe('clearTopicsMessages', () => {
  893. it('clearTopicsMessages/fulfilled', async () => {
  894. fetchMock.deleteOnce(
  895. `/api/clusters/${clusterName}/topics/${topicName}/messages`,
  896. [topicName, 'topic2']
  897. );
  898. await store.dispatch(
  899. clearTopicsMessages({
  900. clusterName,
  901. topicNames: [topicName, 'topic2'],
  902. })
  903. );
  904. expect(getTypeAndPayload(store)).toEqual([
  905. { type: clearTopicsMessages.pending.type },
  906. { type: clearTopicsMessages.fulfilled.type },
  907. ]);
  908. });
  909. });
  910. });
  911. });