id-codec.spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import { DECODED, ENCODED, MockIdStrategy } from '../../config/config.service.mock';
  2. import { IdCodec } from './id-codec';
  3. describe('IdCodecService', () => {
  4. let idCodec: IdCodec;
  5. beforeEach(async () => {
  6. idCodec = new IdCodec(new MockIdStrategy());
  7. });
  8. describe('encode()', () => {
  9. it('works with a string', () => {
  10. const input = 'id';
  11. const result = idCodec.encode(input);
  12. expect(result).toEqual(ENCODED);
  13. });
  14. it('works with a number', () => {
  15. const input = 123;
  16. const result = idCodec.encode(input);
  17. expect(result).toEqual(ENCODED);
  18. });
  19. it('works with a boolean', () => {
  20. const input = true;
  21. const result = idCodec.encode(input);
  22. expect(result).toEqual(true);
  23. });
  24. it('passes through null or undefined without throwing', () => {
  25. expect(idCodec.encode(null as any)).toBeNull();
  26. expect(idCodec.encode(undefined as any)).toBeUndefined();
  27. });
  28. it('returns a clone of the input object', () => {
  29. const input = { id: 'id', name: 'foo' };
  30. const result = idCodec.encode(input);
  31. expect(result).not.toBe(input);
  32. });
  33. it('returns a deep clone', () => {
  34. const obj1 = { 1: true };
  35. const obj2 = { 2: true };
  36. const arr = [obj1, obj2];
  37. const parent = { myArray: arr };
  38. const input = { foo: parent };
  39. const result = idCodec.encode(input);
  40. expect(result).not.toBe(input);
  41. expect(result.foo).not.toBe(parent);
  42. expect(result.foo.myArray).not.toBe(arr);
  43. expect(result.foo.myArray[0]).not.toBe(obj1);
  44. expect(result.foo.myArray[1]).not.toBe(obj2);
  45. });
  46. it('does not clone complex object instances', () => {
  47. // tslint:disable:no-floating-promises
  48. const promise = new Promise(() => {
  49. /**/
  50. });
  51. const date = new Date();
  52. const regex = new RegExp('');
  53. const input = {
  54. promise,
  55. date,
  56. regex,
  57. };
  58. const result = idCodec.encode(input);
  59. expect(result.promise).toBe(promise);
  60. expect(result.date).toBe(date);
  61. expect(result.regex).toBe(regex);
  62. // tslint:enable:no-floating-promises
  63. });
  64. it('works with simple entity', () => {
  65. const input = { id: 'id', name: 'foo' };
  66. const result = idCodec.encode(input);
  67. expect(result).toEqual({ id: ENCODED, name: 'foo' });
  68. });
  69. it('works with 2-level nested entities', () => {
  70. const input = {
  71. id: 'id',
  72. friend: { id: 'id' },
  73. };
  74. const result = idCodec.encode(input);
  75. expect(result).toEqual({
  76. id: ENCODED,
  77. friend: { id: ENCODED },
  78. });
  79. });
  80. it('works with 3-level nested entities', () => {
  81. const input = {
  82. id: 'id',
  83. friend: {
  84. dog: { id: 'id' },
  85. },
  86. };
  87. const result = idCodec.encode(input);
  88. expect(result).toEqual({
  89. id: ENCODED,
  90. friend: {
  91. dog: { id: ENCODED },
  92. },
  93. });
  94. });
  95. it('works with list of simple entities', () => {
  96. const input = [{ id: 'id', name: 'foo' }, { id: 'id', name: 'bar' }];
  97. const result = idCodec.encode(input);
  98. expect(result).toEqual([{ id: ENCODED, name: 'foo' }, { id: ENCODED, name: 'bar' }]);
  99. });
  100. it('does not throw with an empty list', () => {
  101. const input = [];
  102. const result = idCodec.encode(input);
  103. expect(() => idCodec.encode(input)).not.toThrow();
  104. });
  105. it('works with nested list of simple entities', () => {
  106. const input = {
  107. items: [{ id: 'id', name: 'foo' }, { id: 'id', name: 'bar' }],
  108. };
  109. const result = idCodec.encode(input);
  110. expect(result).toEqual({
  111. items: [{ id: ENCODED, name: 'foo' }, { id: ENCODED, name: 'bar' }],
  112. });
  113. });
  114. it('works with large and nested list', () => {
  115. const length = 100;
  116. const input = {
  117. items: Array.from({ length }).map(() => ({
  118. id: 'id',
  119. name: { bar: 'baz' },
  120. foo: 'yo',
  121. friends: [{ id: 'id', name: { first: 'boris', id: 'id' } }],
  122. })),
  123. };
  124. const result = idCodec.encode(input);
  125. expect(result.items.length).toBe(length);
  126. expect(result.items[0].id).toBe(ENCODED);
  127. expect(result.items[0].friends[0].id).toBe(ENCODED);
  128. expect(result.items[0].friends[0].name.id).toBe(ENCODED);
  129. });
  130. it('works with nested list of nested lists', () => {
  131. const input = {
  132. items: [
  133. {
  134. id: 'id',
  135. friends: [{ id: 'id' }, { id: 'id' }],
  136. },
  137. {
  138. id: 'id',
  139. friends: [{ id: 'id' }, { id: 'id' }],
  140. },
  141. ],
  142. };
  143. const result = idCodec.encode(input);
  144. expect(result).toEqual({
  145. items: [
  146. {
  147. id: ENCODED,
  148. friends: [{ id: ENCODED }, { id: ENCODED }],
  149. },
  150. {
  151. id: ENCODED,
  152. friends: [{ id: ENCODED }, { id: ENCODED }],
  153. },
  154. ],
  155. });
  156. });
  157. it('transformKeys can be customized', () => {
  158. const input = { id: 'id', name: 'foo' };
  159. const result = idCodec.encode(input, ['name']);
  160. expect(result).toEqual({ id: ENCODED, name: ENCODED });
  161. });
  162. });
  163. describe('decode()', () => {
  164. it('works with a string', () => {
  165. const input = 'id';
  166. const result = idCodec.decode(input);
  167. expect(result).toEqual(DECODED);
  168. });
  169. it('works with a number', () => {
  170. const input = 123;
  171. const result = idCodec.decode(input);
  172. expect(result).toEqual(DECODED);
  173. });
  174. it('works with simple entity', () => {
  175. const input = { id: 'id', name: 'foo' };
  176. const result = idCodec.decode(input);
  177. expect(result).toEqual({ id: DECODED, name: 'foo' });
  178. });
  179. it('works with 2-level nested entities', () => {
  180. const input = {
  181. id: 'id',
  182. friend: { id: 'id' },
  183. };
  184. const result = idCodec.decode(input);
  185. expect(result).toEqual({
  186. id: DECODED,
  187. friend: { id: DECODED },
  188. });
  189. });
  190. it('works with 3-level nested entities', () => {
  191. const input = {
  192. id: 'id',
  193. friend: {
  194. dog: { id: 'id' },
  195. },
  196. };
  197. const result = idCodec.decode(input);
  198. expect(result).toEqual({
  199. id: DECODED,
  200. friend: {
  201. dog: { id: DECODED },
  202. },
  203. });
  204. });
  205. it('works with list of simple entities', () => {
  206. const input = [{ id: 'id', name: 'foo' }, { id: 'id', name: 'bar' }];
  207. const result = idCodec.decode(input);
  208. expect(result).toEqual([{ id: DECODED, name: 'foo' }, { id: DECODED, name: 'bar' }]);
  209. });
  210. it('works with nested list of simple entities', () => {
  211. const input = {
  212. items: [{ id: 'id', name: 'foo' }, { id: 'id', name: 'bar' }],
  213. };
  214. const result = idCodec.decode(input);
  215. expect(result).toEqual({
  216. items: [{ id: DECODED, name: 'foo' }, { id: DECODED, name: 'bar' }],
  217. });
  218. });
  219. it('works with large and nested list', () => {
  220. const length = 100;
  221. const input = {
  222. items: Array.from({ length }).map(() => ({
  223. id: 'id',
  224. name: { bar: 'baz' },
  225. foo: 'yo',
  226. friends: [{ id: 'id', name: { first: 'boris', id: 'id' } }],
  227. })),
  228. };
  229. const result = idCodec.decode(input);
  230. expect(result.items.length).toBe(length);
  231. expect(result.items[0].id).toBe(DECODED);
  232. expect(result.items[0].friends[0].id).toBe(DECODED);
  233. expect(result.items[0].friends[0].name.id).toBe(DECODED);
  234. });
  235. it('works with nested list of nested lists', () => {
  236. const input = {
  237. items: [
  238. {
  239. id: 'id',
  240. friends: [{ id: 'id' }, { id: 'id' }],
  241. },
  242. {
  243. id: 'id',
  244. friends: [{ id: 'id' }, { id: 'id' }],
  245. },
  246. ],
  247. };
  248. const result = idCodec.decode(input);
  249. expect(result).toEqual({
  250. items: [
  251. {
  252. id: DECODED,
  253. friends: [{ id: DECODED }, { id: DECODED }],
  254. },
  255. {
  256. id: DECODED,
  257. friends: [{ id: DECODED }, { id: DECODED }],
  258. },
  259. ],
  260. });
  261. });
  262. it('transformKeys can be customized', () => {
  263. const input = { name: 'foo' };
  264. const result = idCodec.decode(input, ['name']);
  265. expect(result).toEqual({ name: DECODED });
  266. });
  267. it('id keys is still implicitly decoded when transformKeys are defined', () => {
  268. const input = { id: 'id', name: 'foo' };
  269. const result = idCodec.decode(input, ['name']);
  270. expect(result).toEqual({ id: DECODED, name: DECODED });
  271. });
  272. it('transformKeys works for nested matching keys', () => {
  273. const input = {
  274. input: {
  275. id: 'id',
  276. featuredAssetId: 'id',
  277. foo: 'bar',
  278. },
  279. };
  280. const result = idCodec.decode(input, ['featuredAssetId']);
  281. expect(result).toEqual({
  282. input: {
  283. id: DECODED,
  284. featuredAssetId: DECODED,
  285. foo: 'bar',
  286. },
  287. });
  288. });
  289. it('transformKeys works for nested matching key array', () => {
  290. const input = {
  291. input: {
  292. id: 'id',
  293. assetIds: ['id1', 'id2', 'id3'],
  294. foo: 'bar',
  295. },
  296. };
  297. const result = idCodec.decode(input, ['assetIds']);
  298. expect(result).toEqual({
  299. input: {
  300. id: DECODED,
  301. assetIds: [DECODED, DECODED, DECODED],
  302. foo: 'bar',
  303. },
  304. });
  305. });
  306. it('transformKeys works for multiple nested keys', () => {
  307. const input = {
  308. input: {
  309. id: 'id',
  310. featuredAssetId: 'id',
  311. assetIds: ['id1', 'id2', 'id3'],
  312. foo: 'bar',
  313. },
  314. };
  315. const result = idCodec.decode(input, ['featuredAssetId', 'assetIds']);
  316. expect(result).toEqual({
  317. input: {
  318. id: DECODED,
  319. featuredAssetId: DECODED,
  320. assetIds: [DECODED, DECODED, DECODED],
  321. foo: 'bar',
  322. },
  323. });
  324. });
  325. });
  326. });