trie.test.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. import { createTrie } from './trie';
  2. import { describe, it } from 'mocha';
  3. import { expect } from 'chai';
  4. describe('Trie', () => {
  5. it('should be possible to add items to a Trie.', () => {
  6. const trie = createTrie();
  7. trie.add('sukka');
  8. trie.add('ukka');
  9. trie.add('akku');
  10. expect(trie.size).to.equal(3);
  11. expect(trie.has('sukka')).to.equal(true);
  12. expect(trie.has('ukka')).to.equal(true);
  13. expect(trie.has('akku')).to.equal(true);
  14. expect(trie.has('noc')).to.equal(false);
  15. expect(trie.has('suk')).to.equal(false);
  16. expect(trie.has('sukkaw')).to.equal(false);
  17. });
  18. it('should be possible to add domains to a Trie (hostname).', () => {
  19. const trie = createTrie(null, true);
  20. trie.add('a.skk.moe');
  21. trie.add('skk.moe');
  22. trie.add('anotherskk.moe');
  23. expect(trie.size).to.equal(3);
  24. expect(trie.has('a.skk.moe')).to.equal(true);
  25. expect(trie.has('skk.moe')).to.equal(true);
  26. expect(trie.has('anotherskk.moe')).to.equal(true);
  27. expect(trie.has('example.com')).to.equal(false);
  28. expect(trie.has('skk.mo')).to.equal(false);
  29. expect(trie.has('another.skk.moe')).to.equal(false);
  30. });
  31. it('adding the same item several times should not increase size.', () => {
  32. const trie = createTrie();
  33. trie.add('rat');
  34. trie.add('erat');
  35. trie.add('rat');
  36. expect(trie.size).to.equal(2);
  37. expect(trie.has('rat')).to.equal(true);
  38. });
  39. it('adding the same item several times should not increase size (hostname).', () => {
  40. const trie = createTrie(null, true);
  41. trie.add('skk.moe');
  42. trie.add('blog.skk.moe');
  43. trie.add('skk.moe');
  44. expect(trie.size).to.equal(2);
  45. expect(trie.has('skk.moe')).to.equal(true);
  46. });
  47. it('should be possible to set the null sequence.', () => {
  48. let trie = createTrie();
  49. trie.add('');
  50. expect(trie.has('')).to.equal(true);
  51. trie = createTrie(null, true);
  52. trie.add('');
  53. expect(trie.has('')).to.equal(true);
  54. });
  55. it('should be possible to delete items.', () => {
  56. const trie = createTrie();
  57. trie.add('rat');
  58. trie.add('rate');
  59. trie.add('tar');
  60. expect(trie.delete('')).to.equal(false);
  61. expect(trie.delete('')).to.equal(false);
  62. expect(trie.delete('hello')).to.equal(false);
  63. expect(trie.delete('rat')).to.equal(true);
  64. expect(trie.has('rat')).to.equal(false);
  65. expect(trie.has('rate')).to.equal(true);
  66. expect(trie.size).to.equal(2);
  67. expect(trie.delete('rate')).to.equal(true);
  68. expect(trie.size).to.equal(1);
  69. expect(trie.delete('tar')).to.equal(true);
  70. expect(trie.size).to.equal(0);
  71. });
  72. it('should be possible to delete items (hostname).', () => {
  73. const trie = createTrie(null, true);
  74. trie.add('skk.moe');
  75. trie.add('example.com');
  76. trie.add('moe.sb');
  77. expect(trie.delete('')).to.equal(false);
  78. expect(trie.delete('')).to.equal(false);
  79. expect(trie.delete('example.org')).to.equal(false);
  80. expect(trie.delete('skk.moe')).to.equal(true);
  81. expect(trie.has('skk.moe')).to.equal(false);
  82. expect(trie.has('moe.sb')).to.equal(true);
  83. expect(trie.size).to.equal(2);
  84. expect(trie.delete('example.com')).to.equal(true);
  85. expect(trie.size).to.equal(1);
  86. expect(trie.delete('moe.sb')).to.equal(true);
  87. expect(trie.size).to.equal(0);
  88. });
  89. it('should be possible to check the existence of a sequence in the Trie.', () => {
  90. const trie = createTrie();
  91. trie.add('romanesque');
  92. expect(trie.has('romanesque')).to.equal(true);
  93. expect(trie.has('roman')).to.equal(false);
  94. expect(trie.has('esque')).to.equal(false);
  95. expect(trie.has('')).to.equal(false);
  96. });
  97. it('should be possible to check the existence of a sequence in the Trie (hostname).', () => {
  98. const trie = createTrie(null, true);
  99. trie.add('example.org.skk.moe');
  100. expect(trie.has('example.org.skk.moe')).to.equal(true);
  101. expect(trie.has('skk.moe')).to.equal(false);
  102. expect(trie.has('example.org')).to.equal(false);
  103. expect(trie.has('')).to.equal(false);
  104. });
  105. it('should be possible to retrieve items matching the given prefix.', () => {
  106. const trie = createTrie();
  107. trie.add('roman');
  108. trie.add('esqueroman');
  109. trie.add('sesqueroman');
  110. trie.add('greek');
  111. expect(trie.find('roman')).to.deep.equal(['roman', 'esqueroman', 'sesqueroman']);
  112. expect(trie.find('man')).to.deep.equal(['roman', 'esqueroman', 'sesqueroman']);
  113. expect(trie.find('esqueroman')).to.deep.equal(['esqueroman', 'sesqueroman']);
  114. expect(trie.find('eek')).to.deep.equal(['greek']);
  115. expect(trie.find('hello')).to.deep.equal([]);
  116. expect(trie.find('')).to.deep.equal(['greek', 'roman', 'esqueroman', 'sesqueroman']);
  117. });
  118. it('should be possible to retrieve items matching the given prefix (hostname).', () => {
  119. const trie = createTrie(null, true);
  120. trie.add('example.com');
  121. trie.add('blog.example.com');
  122. trie.add('cdn.example.com');
  123. trie.add('example.org');
  124. expect(trie.find('example.com')).to.deep.equal(['example.com', 'cdn.example.com', 'blog.example.com']);
  125. expect(trie.find('com')).to.deep.equal(['example.com', 'cdn.example.com', 'blog.example.com']);
  126. expect(trie.find('.example.com')).to.deep.equal(['cdn.example.com', 'blog.example.com']);
  127. expect(trie.find('org')).to.deep.equal(['example.org']);
  128. expect(trie.find('example.net')).to.deep.equal([]);
  129. expect(trie.find('')).to.deep.equal(['example.org', 'example.com', 'cdn.example.com', 'blog.example.com']);
  130. });
  131. it('should be possible to create a trie from an arbitrary iterable.', () => {
  132. let trie = createTrie(['roman', 'esqueroman']);
  133. expect(trie.size).to.equal(2);
  134. expect(trie.has('roman')).to.equal(true);
  135. trie = createTrie(new Set(['skk.moe', 'example.com']), true);
  136. expect(trie.size).to.equal(2);
  137. expect(trie.has('skk.moe')).to.equal(true);
  138. });
  139. });
  140. ([
  141. ['hostname mode off', false],
  142. ['hostname mode on', true]
  143. ] as const).forEach(([description, hostnameMode]) => {
  144. describe('surge domainset dedupe ' + description, () => {
  145. it('should not remove same entry', () => {
  146. const trie = createTrie(['.skk.moe', 'noc.one'], hostnameMode);
  147. expect(trie.find('.skk.moe')).to.deep.equal(['.skk.moe']);
  148. expect(trie.find('noc.one')).to.deep.equal(['noc.one']);
  149. });
  150. it('should match subdomain - 1', () => {
  151. const trie = createTrie(['www.noc.one', 'www.sukkaw.com', 'blog.skk.moe', 'image.cdn.skk.moe', 'cdn.sukkaw.net'], hostnameMode);
  152. expect(trie.find('.skk.moe')).to.deep.equal(['image.cdn.skk.moe', 'blog.skk.moe']);
  153. expect(trie.find('.sukkaw.com')).to.deep.equal(['www.sukkaw.com']);
  154. });
  155. it('should match subdomain - 2', () => {
  156. const trie = createTrie(['www.noc.one', 'www.sukkaw.com', '.skk.moe', 'blog.skk.moe', 'image.cdn.skk.moe', 'cdn.sukkaw.net'], hostnameMode);
  157. expect(trie.find('.skk.moe')).to.deep.equal(['.skk.moe', 'image.cdn.skk.moe', 'blog.skk.moe']);
  158. expect(trie.find('.sukkaw.com')).to.deep.equal(['www.sukkaw.com']);
  159. });
  160. it('should not remove non-subdomain', () => {
  161. const trie = createTrie(['skk.moe', 'sukkaskk.moe'], hostnameMode);
  162. expect(trie.find('.skk.moe')).to.deep.equal([]);
  163. });
  164. });
  165. });
  166. describe('smol tree', () => {
  167. it('should create simple tree - 1', () => {
  168. const trie = createTrie([
  169. '.skk.moe', 'blog.skk.moe', '.cdn.skk.moe', 'skk.moe',
  170. 'www.noc.one', 'cdn.noc.one',
  171. '.blog.sub.example.com', 'sub.example.com', 'cdn.sub.example.com', '.sub.example.com'
  172. ], true, true);
  173. expect(trie.dump()).to.deep.equal([
  174. '.sub.example.com',
  175. 'cdn.noc.one', 'www.noc.one',
  176. '.skk.moe'
  177. ]);
  178. });
  179. it('should create simple tree - 2', () => {
  180. const trie = createTrie([
  181. '.skk.moe', 'blog.skk.moe', '.cdn.skk.moe', 'skk.moe'
  182. ], true, true);
  183. expect(trie.dump()).to.deep.equal([
  184. '.skk.moe'
  185. ]);
  186. });
  187. it('should create simple tree - 2', () => {
  188. const trie = createTrie([
  189. '.blog.sub.example.com', 'cdn.sub.example.com', '.sub.example.com'
  190. ], true, true);
  191. expect(trie.dump()).to.deep.equal([
  192. '.sub.example.com'
  193. ]);
  194. trie.add('.sub.example.com');
  195. expect(trie.dump()).to.deep.equal([
  196. '.sub.example.com'
  197. ]);
  198. });
  199. it('should create simple tree - 3', () => {
  200. const trie = createTrie([
  201. 'commercial.shouji.360.cn',
  202. 'act.commercial.shouji.360.cn',
  203. 'cdn.creative.medialytics.com',
  204. 'px.cdn.creative.medialytics.com'
  205. ], true, true);
  206. expect(trie.dump()).to.deep.equal([
  207. 'cdn.creative.medialytics.com',
  208. 'px.cdn.creative.medialytics.com',
  209. 'commercial.shouji.360.cn',
  210. 'act.commercial.shouji.360.cn'
  211. ]);
  212. });
  213. it('should dedupe subdomain properly', () => {
  214. const trie = createTrie([
  215. 'skk.moe',
  216. 'anotherskk.moe',
  217. 'blog.anotherskk.moe',
  218. 'blog.skk.moe'
  219. ], true, true);
  220. expect(trie.dump()).to.deep.equal([
  221. 'anotherskk.moe',
  222. 'blog.anotherskk.moe',
  223. 'skk.moe',
  224. 'blog.skk.moe'
  225. ]);
  226. });
  227. it('should efficiently whitelist domains', () => {
  228. const trie = createTrie([
  229. 'skk.moe',
  230. 'anotherskk.moe',
  231. 'blog.anotherskk.moe',
  232. 'blog.skk.moe'
  233. ], true, true);
  234. expect(trie.dump()).to.deep.equal([
  235. 'anotherskk.moe',
  236. 'blog.anotherskk.moe',
  237. 'skk.moe',
  238. 'blog.skk.moe'
  239. ]);
  240. trie.whitelist('.skk.moe');
  241. expect(trie.dump()).to.deep.equal([
  242. 'anotherskk.moe',
  243. 'blog.anotherskk.moe'
  244. ]);
  245. trie.whitelist('anotherskk.moe');
  246. expect(trie.dump()).to.deep.equal([
  247. 'blog.anotherskk.moe'
  248. ]);
  249. trie.add('anotherskk.moe');
  250. trie.whitelist('.anotherskk.moe');
  251. expect(trie.dump()).to.deep.equal([]);
  252. });
  253. it('should whitelist trie correctly', () => {
  254. const trie = createTrie([
  255. '.t.co',
  256. 't.co',
  257. 'example.t.co',
  258. '.skk.moe',
  259. 'blog.cdn.example.com',
  260. 'cdn.example.com'
  261. ], true, true);
  262. expect(trie.dump()).to.deep.equal([
  263. 'cdn.example.com', 'blog.cdn.example.com',
  264. '.skk.moe',
  265. '.t.co'
  266. ]);
  267. trie.whitelist('.t.co');
  268. expect(trie.dump()).to.deep.equal([
  269. 'cdn.example.com', 'blog.cdn.example.com',
  270. '.skk.moe'
  271. ]);
  272. trie.whitelist('skk.moe');
  273. expect(trie.dump()).to.deep.equal(['cdn.example.com', 'blog.cdn.example.com']);
  274. trie.whitelist('cdn.example.com');
  275. expect(trie.dump()).to.deep.equal(['blog.cdn.example.com']);
  276. });
  277. });