reducer.spec.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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. afterEach(() => {
  360. fetchMock.restore();
  361. store.clearActions();
  362. });
  363. describe('fetchTopicsList', () => {
  364. const topicResponse = {
  365. pageCount: 1,
  366. topics: [createTopicResponsePayload],
  367. };
  368. it('fetchTopicsList/fulfilled', async () => {
  369. fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, topicResponse);
  370. await store.dispatch(fetchTopicsList({ clusterName }));
  371. expect(getTypeAndPayload(store)).toEqual([
  372. { type: fetchTopicsList.pending.type },
  373. {
  374. type: fetchTopicsList.fulfilled.type,
  375. payload: { ...topicResponse },
  376. },
  377. ]);
  378. });
  379. it('fetchTopicsList/rejected', async () => {
  380. fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, 404);
  381. await store.dispatch(fetchTopicsList({ clusterName }));
  382. expect(getTypeAndPayload(store)).toEqual([
  383. { type: fetchTopicsList.pending.type },
  384. {
  385. type: fetchTopicsList.rejected.type,
  386. payload: {
  387. status: 404,
  388. statusText: 'Not Found',
  389. url: `/api/clusters/${clusterName}/topics`,
  390. message: undefined,
  391. },
  392. },
  393. ]);
  394. });
  395. });
  396. describe('fetchTopicDetails', () => {
  397. it('fetchTopicDetails/fulfilled', async () => {
  398. fetchMock.getOnce(
  399. `/api/clusters/${clusterName}/topics/${topicName}`,
  400. details
  401. );
  402. await store.dispatch(fetchTopicDetails({ clusterName, topicName }));
  403. expect(getTypeAndPayload(store)).toEqual([
  404. { type: fetchTopicDetails.pending.type },
  405. {
  406. type: fetchTopicDetails.fulfilled.type,
  407. payload: { topicDetails: { ...details }, topicName },
  408. },
  409. ]);
  410. });
  411. it('fetchTopicDetails/rejected', async () => {
  412. fetchMock.getOnce(
  413. `/api/clusters/${clusterName}/topics/${topicName}`,
  414. 404
  415. );
  416. await store.dispatch(fetchTopicDetails({ clusterName, topicName }));
  417. expect(getTypeAndPayload(store)).toEqual([
  418. { type: fetchTopicDetails.pending.type },
  419. {
  420. type: fetchTopicDetails.rejected.type,
  421. payload: {
  422. status: 404,
  423. statusText: 'Not Found',
  424. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  425. message: undefined,
  426. },
  427. },
  428. ]);
  429. });
  430. });
  431. describe('fetchTopicConfig', () => {
  432. it('fetchTopicConfig/fulfilled', async () => {
  433. fetchMock.getOnce(
  434. `/api/clusters/${clusterName}/topics/${topicName}/config`,
  435. config
  436. );
  437. await store.dispatch(fetchTopicConfig({ clusterName, topicName }));
  438. expect(getTypeAndPayload(store)).toEqual([
  439. { type: fetchTopicConfig.pending.type },
  440. {
  441. type: fetchTopicConfig.fulfilled.type,
  442. payload: {
  443. topicConfig: config,
  444. topicName,
  445. },
  446. },
  447. ]);
  448. });
  449. it('fetchTopicConfig/rejected', async () => {
  450. fetchMock.getOnce(
  451. `/api/clusters/${clusterName}/topics/${topicName}/config`,
  452. 404
  453. );
  454. await store.dispatch(fetchTopicConfig({ clusterName, topicName }));
  455. expect(getTypeAndPayload(store)).toEqual([
  456. { type: fetchTopicConfig.pending.type },
  457. {
  458. type: fetchTopicConfig.rejected.type,
  459. payload: {
  460. status: 404,
  461. statusText: 'Not Found',
  462. url: `/api/clusters/${clusterName}/topics/${topicName}/config`,
  463. message: undefined,
  464. },
  465. },
  466. ]);
  467. });
  468. });
  469. describe('deleteTopic', () => {
  470. it('deleteTopic/fulfilled', async () => {
  471. fetchMock.deleteOnce(
  472. `/api/clusters/${clusterName}/topics/${topicName}`,
  473. topicName
  474. );
  475. await store.dispatch(deleteTopic({ clusterName, topicName }));
  476. expect(getTypeAndPayload(store)).toEqual([
  477. { type: deleteTopic.pending.type },
  478. {
  479. type: deleteTopic.fulfilled.type,
  480. payload: { topicName },
  481. },
  482. ]);
  483. });
  484. it('deleteTopic/rejected', async () => {
  485. fetchMock.deleteOnce(
  486. `/api/clusters/${clusterName}/topics/${topicName}`,
  487. 404
  488. );
  489. await store.dispatch(deleteTopic({ clusterName, topicName }));
  490. expect(getTypeAndPayload(store)).toEqual([
  491. { type: deleteTopic.pending.type },
  492. {
  493. type: deleteTopic.rejected.type,
  494. payload: {
  495. status: 404,
  496. statusText: 'Not Found',
  497. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  498. message: undefined,
  499. },
  500. },
  501. ]);
  502. });
  503. });
  504. describe('deleteTopics', () => {
  505. it('deleteTopics/fulfilled', async () => {
  506. fetchMock.delete(`/api/clusters/${clusterName}/topics/${topicName}`, [
  507. topicName,
  508. 'topic2',
  509. ]);
  510. await store.dispatch(
  511. deleteTopics({ clusterName, topicNames: [topicName, 'topic2'] })
  512. );
  513. expect(getTypeAndPayload(store)).toEqual([
  514. { type: deleteTopics.pending.type },
  515. { type: deleteTopic.pending.type },
  516. { type: deleteTopic.pending.type },
  517. { type: deleteTopics.fulfilled.type },
  518. ]);
  519. });
  520. });
  521. describe('recreateTopic', () => {
  522. const recreateResponse = {
  523. cleanUpPolicy: 'DELETE',
  524. inSyncReplicas: 1,
  525. internal: false,
  526. name: topicName,
  527. partitionCount: 1,
  528. partitions: undefined,
  529. replicas: 1,
  530. replicationFactor: 1,
  531. segmentCount: 0,
  532. segmentSize: 0,
  533. underReplicatedPartitions: undefined,
  534. };
  535. it('recreateTopic/fulfilled', async () => {
  536. fetchMock.postOnce(
  537. `/api/clusters/${clusterName}/topics/${topicName}`,
  538. recreateResponse
  539. );
  540. await store.dispatch(recreateTopic({ clusterName, topicName }));
  541. expect(getTypeAndPayload(store)).toEqual([
  542. { type: recreateTopic.pending.type },
  543. {
  544. type: recreateTopic.fulfilled.type,
  545. payload: { [topicName]: { ...recreateResponse } },
  546. },
  547. ]);
  548. });
  549. it('recreateTopic/rejected', async () => {
  550. fetchMock.postOnce(
  551. `/api/clusters/${clusterName}/topics/${topicName}`,
  552. 404
  553. );
  554. await store.dispatch(recreateTopic({ clusterName, topicName }));
  555. expect(getTypeAndPayload(store)).toEqual([
  556. { type: recreateTopic.pending.type },
  557. {
  558. type: recreateTopic.rejected.type,
  559. payload: {
  560. status: 404,
  561. statusText: 'Not Found',
  562. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  563. message: undefined,
  564. },
  565. },
  566. ]);
  567. });
  568. });
  569. describe('fetchTopicConsumerGroups', () => {
  570. const consumerGroups = [
  571. {
  572. groupId: 'groupId1',
  573. members: 0,
  574. topics: 1,
  575. simple: false,
  576. partitionAssignor: '',
  577. coordinator: {
  578. id: 1,
  579. port: undefined,
  580. host: 'host',
  581. },
  582. messagesBehind: undefined,
  583. state: undefined,
  584. },
  585. {
  586. groupId: 'groupId2',
  587. members: 0,
  588. topics: 1,
  589. simple: false,
  590. partitionAssignor: '',
  591. coordinator: {
  592. id: 1,
  593. port: undefined,
  594. host: 'host',
  595. },
  596. messagesBehind: undefined,
  597. state: undefined,
  598. },
  599. ];
  600. it('fetchTopicConsumerGroups/fulfilled', async () => {
  601. fetchMock.getOnce(
  602. `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  603. consumerGroups
  604. );
  605. await store.dispatch(
  606. fetchTopicConsumerGroups({ clusterName, topicName })
  607. );
  608. expect(getTypeAndPayload(store)).toEqual([
  609. { type: fetchTopicConsumerGroups.pending.type },
  610. {
  611. type: fetchTopicConsumerGroups.fulfilled.type,
  612. payload: { consumerGroups, topicName },
  613. },
  614. ]);
  615. });
  616. it('fetchTopicConsumerGroups/rejected', async () => {
  617. fetchMock.getOnce(
  618. `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  619. 404
  620. );
  621. await store.dispatch(
  622. fetchTopicConsumerGroups({ clusterName, topicName })
  623. );
  624. expect(getTypeAndPayload(store)).toEqual([
  625. { type: fetchTopicConsumerGroups.pending.type },
  626. {
  627. type: fetchTopicConsumerGroups.rejected.type,
  628. payload: {
  629. status: 404,
  630. statusText: 'Not Found',
  631. url: `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`,
  632. message: undefined,
  633. },
  634. },
  635. ]);
  636. });
  637. });
  638. describe('updateTopicPartitionsCount', () => {
  639. const RealDate = Date.now;
  640. beforeAll(() => {
  641. global.Date.now = jest.fn(() =>
  642. new Date('2019-04-07T10:20:30Z').getTime()
  643. );
  644. });
  645. afterAll(() => {
  646. global.Date.now = RealDate;
  647. });
  648. it('updateTopicPartitionsCount/fulfilled', async () => {
  649. fetchMock.patchOnce(
  650. `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  651. { message: 'success' }
  652. );
  653. await store.dispatch(
  654. updateTopicPartitionsCount({
  655. clusterName,
  656. topicName,
  657. partitions: 1,
  658. })
  659. );
  660. expect(getTypeAndPayload(store)).toEqual([
  661. { type: updateTopicPartitionsCount.pending.type },
  662. { type: showSuccessAlert.pending.type },
  663. {
  664. type: alertAdded.type,
  665. payload: {
  666. id: 'message-topic-local-1',
  667. title: '',
  668. type: 'success',
  669. createdAt: global.Date.now(),
  670. message: 'Number of partitions successfully increased!',
  671. },
  672. },
  673. { type: fetchTopicDetails.pending.type },
  674. { type: showSuccessAlert.fulfilled.type },
  675. {
  676. type: updateTopicPartitionsCount.fulfilled.type,
  677. },
  678. ]);
  679. });
  680. it('updateTopicPartitionsCount/rejected', async () => {
  681. fetchMock.patchOnce(
  682. `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  683. 404
  684. );
  685. await store.dispatch(
  686. updateTopicPartitionsCount({
  687. clusterName,
  688. topicName,
  689. partitions: 1,
  690. })
  691. );
  692. expect(getTypeAndPayload(store)).toEqual([
  693. { type: updateTopicPartitionsCount.pending.type },
  694. {
  695. type: updateTopicPartitionsCount.rejected.type,
  696. payload: {
  697. status: 404,
  698. statusText: 'Not Found',
  699. url: `/api/clusters/${clusterName}/topics/${topicName}/partitions`,
  700. message: undefined,
  701. },
  702. },
  703. ]);
  704. });
  705. });
  706. describe('updateTopicReplicationFactor', () => {
  707. it('updateTopicReplicationFactor/fulfilled', async () => {
  708. fetchMock.patchOnce(
  709. `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  710. { message: 'success' }
  711. );
  712. await store.dispatch(
  713. updateTopicReplicationFactor({
  714. clusterName,
  715. topicName,
  716. replicationFactor: 1,
  717. })
  718. );
  719. expect(getTypeAndPayload(store)).toEqual([
  720. { type: updateTopicReplicationFactor.pending.type },
  721. {
  722. type: updateTopicReplicationFactor.fulfilled.type,
  723. },
  724. ]);
  725. });
  726. it('updateTopicReplicationFactor/rejected', async () => {
  727. fetchMock.patchOnce(
  728. `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  729. 404
  730. );
  731. await store.dispatch(
  732. updateTopicReplicationFactor({
  733. clusterName,
  734. topicName,
  735. replicationFactor: 1,
  736. })
  737. );
  738. expect(getTypeAndPayload(store)).toEqual([
  739. { type: updateTopicReplicationFactor.pending.type },
  740. {
  741. type: updateTopicReplicationFactor.rejected.type,
  742. payload: {
  743. status: 404,
  744. statusText: 'Not Found',
  745. url: `/api/clusters/${clusterName}/topics/${topicName}/replications`,
  746. message: undefined,
  747. },
  748. },
  749. ]);
  750. });
  751. });
  752. describe('createTopic', () => {
  753. const newTopic = {
  754. name: 'newTopic',
  755. partitions: 0,
  756. replicationFactor: 0,
  757. minInsyncReplicas: 0,
  758. cleanupPolicy: 'DELETE',
  759. retentionMs: 1,
  760. retentionBytes: 1,
  761. maxMessageBytes: 1,
  762. customParams: [
  763. {
  764. name: '',
  765. value: '',
  766. },
  767. ],
  768. };
  769. it('createTopic/fulfilled', async () => {
  770. fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, {
  771. message: 'success',
  772. });
  773. await store.dispatch(
  774. createTopic({
  775. clusterName,
  776. data: newTopic,
  777. })
  778. );
  779. expect(getTypeAndPayload(store)).toEqual([
  780. { type: createTopic.pending.type },
  781. {
  782. type: createTopic.fulfilled.type,
  783. },
  784. ]);
  785. });
  786. it('createTopic/rejected', async () => {
  787. fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, 404);
  788. await store.dispatch(
  789. createTopic({
  790. clusterName,
  791. data: newTopic,
  792. })
  793. );
  794. expect(getTypeAndPayload(store)).toEqual([
  795. { type: createTopic.pending.type },
  796. {
  797. type: createTopic.rejected.type,
  798. payload: {
  799. status: 404,
  800. statusText: 'Not Found',
  801. url: `/api/clusters/${clusterName}/topics`,
  802. message: undefined,
  803. },
  804. },
  805. ]);
  806. });
  807. });
  808. describe('updateTopic', () => {
  809. const updateTopicResponse = {
  810. name: topicName,
  811. partitions: 0,
  812. replicationFactor: 0,
  813. minInsyncReplicas: 0,
  814. cleanupPolicy: 'DELETE',
  815. retentionMs: 0,
  816. retentionBytes: 0,
  817. maxMessageBytes: 0,
  818. customParams: {
  819. byIndex: {},
  820. allIndexes: [],
  821. },
  822. };
  823. it('updateTopic/fulfilled', async () => {
  824. fetchMock.patchOnce(
  825. `/api/clusters/${clusterName}/topics/${topicName}`,
  826. createTopicResponsePayload
  827. );
  828. await store.dispatch(
  829. updateTopic({
  830. clusterName,
  831. topicName,
  832. form: updateTopicResponse,
  833. })
  834. );
  835. expect(getTypeAndPayload(store)).toEqual([
  836. { type: updateTopic.pending.type },
  837. {
  838. type: updateTopic.fulfilled.type,
  839. payload: { [topicName]: { ...createTopicResponsePayload } },
  840. },
  841. ]);
  842. });
  843. it('updateTopic/rejected', async () => {
  844. fetchMock.patchOnce(
  845. `/api/clusters/${clusterName}/topics/${topicName}`,
  846. 404
  847. );
  848. await store.dispatch(
  849. updateTopic({
  850. clusterName,
  851. topicName,
  852. form: updateTopicResponse,
  853. })
  854. );
  855. expect(getTypeAndPayload(store)).toEqual([
  856. { type: updateTopic.pending.type },
  857. {
  858. type: updateTopic.rejected.type,
  859. payload: {
  860. status: 404,
  861. statusText: 'Not Found',
  862. url: `/api/clusters/${clusterName}/topics/${topicName}`,
  863. message: undefined,
  864. },
  865. },
  866. ]);
  867. });
  868. });
  869. describe('clearTopicsMessages', () => {
  870. it('clearTopicsMessages/fulfilled', async () => {
  871. fetchMock.deleteOnce(
  872. `/api/clusters/${clusterName}/topics/${topicName}/messages`,
  873. [topicName, 'topic2']
  874. );
  875. await store.dispatch(
  876. clearTopicsMessages({
  877. clusterName,
  878. topicNames: [topicName, 'topic2'],
  879. })
  880. );
  881. expect(getTypeAndPayload(store)).toEqual([
  882. { type: clearTopicsMessages.pending.type },
  883. { type: clearTopicsMessages.fulfilled.type },
  884. ]);
  885. });
  886. });
  887. });
  888. });