reducer.spec.ts 25 KB

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