@luzzle/core 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/README.md +41 -0
  2. package/dist/src/database/client.d.ts +4 -0
  3. package/dist/src/database/client.js +12 -0
  4. package/dist/src/database/client.js.map +1 -0
  5. package/dist/src/database/client.test.d.ts +1 -0
  6. package/dist/src/database/client.test.js +34 -0
  7. package/dist/src/database/client.test.js.map +1 -0
  8. package/dist/src/database/database.mock.d.ts +22 -0
  9. package/dist/src/database/database.mock.js +42 -0
  10. package/dist/src/database/database.mock.js.map +1 -0
  11. package/dist/src/database/migrations/2023-05-26T15:04:19.094Z.d.ts +3 -0
  12. package/dist/src/database/migrations/2023-05-26T15:04:19.094Z.js +49 -0
  13. package/dist/src/database/migrations/2023-05-26T15:04:19.094Z.js.map +1 -0
  14. package/dist/src/database/migrations/2023-10-30T23:24:40Z.d.ts +3 -0
  15. package/dist/src/database/migrations/2023-10-30T23:24:40Z.js +35 -0
  16. package/dist/src/database/migrations/2023-10-30T23:24:40Z.js.map +1 -0
  17. package/dist/src/database/migrations/2023-11-22T14:55:18Z.d.ts +3 -0
  18. package/dist/src/database/migrations/2023-11-22T14:55:18Z.js +16 -0
  19. package/dist/src/database/migrations/2023-11-22T14:55:18Z.js.map +1 -0
  20. package/dist/src/database/migrations/2023-11-27T04:32:07Z.d.ts +3 -0
  21. package/dist/src/database/migrations/2023-11-27T04:32:07Z.js +30 -0
  22. package/dist/src/database/migrations/2023-11-27T04:32:07Z.js.map +1 -0
  23. package/dist/src/database/migrations/2024-01-01T23:30:29Z.d.ts +3 -0
  24. package/dist/src/database/migrations/2024-01-01T23:30:29Z.js +11 -0
  25. package/dist/src/database/migrations/2024-01-01T23:30:29Z.js.map +1 -0
  26. package/dist/src/database/migrations/2024-01-06T15:52:43Z.d.ts +3 -0
  27. package/dist/src/database/migrations/2024-01-06T15:52:43Z.js +16 -0
  28. package/dist/src/database/migrations/2024-01-06T15:52:43Z.js.map +1 -0
  29. package/dist/src/database/migrations/2024-02-09T16:33:10Z.d.ts +3 -0
  30. package/dist/src/database/migrations/2024-02-09T16:33:10Z.js +37 -0
  31. package/dist/src/database/migrations/2024-02-09T16:33:10Z.js.map +1 -0
  32. package/dist/src/database/migrations/2024-02-13T05:56:43Z.d.ts +3 -0
  33. package/dist/src/database/migrations/2024-02-13T05:56:43Z.js +116 -0
  34. package/dist/src/database/migrations/2024-02-13T05:56:43Z.js.map +1 -0
  35. package/dist/src/database/migrations/2024-02-24T03:05:47Z-add-texts-table.d.ts +3 -0
  36. package/dist/src/database/migrations/2024-02-24T03:05:47Z-add-texts-table.js +42 -0
  37. package/dist/src/database/migrations/2024-02-24T03:05:47Z-add-texts-table.js.map +1 -0
  38. package/dist/src/database/migrations/2024-02-26T05:37:37Z-add-cache-table.d.ts +3 -0
  39. package/dist/src/database/migrations/2024-02-26T05:37:37Z-add-cache-table.js +20 -0
  40. package/dist/src/database/migrations/2024-02-26T05:37:37Z-add-cache-table.js.map +1 -0
  41. package/dist/src/database/migrations/2024-03-01T01:01:47Z-fix-trigger-bugs.d.ts +3 -0
  42. package/dist/src/database/migrations/2024-03-01T01:01:47Z-fix-trigger-bugs.js +43 -0
  43. package/dist/src/database/migrations/2024-03-01T01:01:47Z-fix-trigger-bugs.js.map +1 -0
  44. package/dist/src/database/migrations/2024-06-15T17:17:51Z-add-games-table.d.ts +3 -0
  45. package/dist/src/database/migrations/2024-06-15T17:17:51Z-add-games-table.js +54 -0
  46. package/dist/src/database/migrations/2024-06-15T17:17:51Z-add-games-table.js.map +1 -0
  47. package/dist/src/database/migrations/2024-06-24T20:16:48Z-remove-book-open-library-fields.d.ts +3 -0
  48. package/dist/src/database/migrations/2024-06-24T20:16:48Z-remove-book-open-library-fields.js +23 -0
  49. package/dist/src/database/migrations/2024-06-24T20:16:48Z-remove-book-open-library-fields.js.map +1 -0
  50. package/dist/src/database/migrations/2024-06-29T16:31:33Z-add-piece-manager-table.d.ts +3 -0
  51. package/dist/src/database/migrations/2024-06-29T16:31:33Z-add-piece-manager-table.js +17 -0
  52. package/dist/src/database/migrations/2024-06-29T16:31:33Z-add-piece-manager-table.js.map +1 -0
  53. package/dist/src/database/migrations/2024-07-14T06:59:07Z-remove-pieces.d.ts +3 -0
  54. package/dist/src/database/migrations/2024-07-14T06:59:07Z-remove-pieces.js +15 -0
  55. package/dist/src/database/migrations/2024-07-14T06:59:07Z-remove-pieces.js.map +1 -0
  56. package/dist/src/database/migrations/2024-11-06T15:27:05Z-remove-tags.d.ts +3 -0
  57. package/dist/src/database/migrations/2024-11-06T15:27:05Z-remove-tags.js +26 -0
  58. package/dist/src/database/migrations/2024-11-06T15:27:05Z-remove-tags.js.map +1 -0
  59. package/dist/src/database/migrations/2024-11-08T05:59:37Z-remove-pieces-items-unique-tables.d.ts +3 -0
  60. package/dist/src/database/migrations/2024-11-08T05:59:37Z-remove-pieces-items-unique-tables.js +24 -0
  61. package/dist/src/database/migrations/2024-11-08T05:59:37Z-remove-pieces-items-unique-tables.js.map +1 -0
  62. package/dist/src/database/migrations/2024-11-13T14:51:17Z-track-file-paths-on-piece.d.ts +3 -0
  63. package/dist/src/database/migrations/2024-11-13T14:51:17Z-track-file-paths-on-piece.js +54 -0
  64. package/dist/src/database/migrations/2024-11-13T14:51:17Z-track-file-paths-on-piece.js.map +1 -0
  65. package/dist/src/database/migrations/2024-12-15T06:35:24Z-track-assets-on-piece.d.ts +3 -0
  66. package/dist/src/database/migrations/2024-12-15T06:35:24Z-track-assets-on-piece.js +10 -0
  67. package/dist/src/database/migrations/2024-12-15T06:35:24Z-track-assets-on-piece.js.map +1 -0
  68. package/dist/src/database/migrations.d.ts +3 -0
  69. package/dist/src/database/migrations.js +16 -0
  70. package/dist/src/database/migrations.js.map +1 -0
  71. package/dist/src/database/migrations.test.d.ts +1 -0
  72. package/dist/src/database/migrations.test.js +37 -0
  73. package/dist/src/database/migrations.test.js.map +1 -0
  74. package/dist/src/database/tables/index.d.ts +21 -0
  75. package/dist/src/database/tables/index.js +7 -0
  76. package/dist/src/database/tables/index.js.map +1 -0
  77. package/dist/src/database/tables/pieces_cache.schema.d.ts +13 -0
  78. package/dist/src/database/tables/pieces_cache.schema.js +2 -0
  79. package/dist/src/database/tables/pieces_cache.schema.js.map +1 -0
  80. package/dist/src/database/tables/pieces_items.schema.d.ts +16 -0
  81. package/dist/src/database/tables/pieces_items.schema.js +2 -0
  82. package/dist/src/database/tables/pieces_items.schema.js.map +1 -0
  83. package/dist/src/database/tables/pieces_manager.schema.d.ts +13 -0
  84. package/dist/src/database/tables/pieces_manager.schema.js +2 -0
  85. package/dist/src/database/tables/pieces_manager.schema.js.map +1 -0
  86. package/dist/src/database/utils.d.ts +8 -0
  87. package/dist/src/database/utils.js +2 -0
  88. package/dist/src/database/utils.js.map +1 -0
  89. package/dist/src/database/utils.test.d.ts +1 -0
  90. package/dist/src/database/utils.test.js +9 -0
  91. package/dist/src/database/utils.test.js.map +1 -0
  92. package/dist/src/index.d.ts +11 -0
  93. package/dist/src/index.js +12 -0
  94. package/dist/src/index.js.map +1 -0
  95. package/dist/src/index.test.d.ts +1 -0
  96. package/dist/src/index.test.js +9 -0
  97. package/dist/src/index.test.js.map +1 -0
  98. package/dist/src/lib/ajv.d.ts +6 -0
  99. package/dist/src/lib/ajv.js +25 -0
  100. package/dist/src/lib/ajv.js.map +1 -0
  101. package/dist/src/lib/ajv.test.d.ts +1 -0
  102. package/dist/src/lib/ajv.test.js +43 -0
  103. package/dist/src/lib/ajv.test.js.map +1 -0
  104. package/dist/src/lib/frontmatter.d.ts +4 -0
  105. package/dist/src/lib/frontmatter.js +8 -0
  106. package/dist/src/lib/frontmatter.js.map +1 -0
  107. package/dist/src/lib/frontmatter.test.d.ts +1 -0
  108. package/dist/src/lib/frontmatter.test.js +37 -0
  109. package/dist/src/lib/frontmatter.test.js.map +1 -0
  110. package/dist/src/lib/markdown.d.ts +7 -0
  111. package/dist/src/lib/markdown.js +21 -0
  112. package/dist/src/lib/markdown.js.map +1 -0
  113. package/dist/src/lib/markdown.test.d.ts +1 -0
  114. package/dist/src/lib/markdown.test.js +34 -0
  115. package/dist/src/lib/markdown.test.js.map +1 -0
  116. package/dist/src/llm/google.d.ts +6 -0
  117. package/dist/src/llm/google.js +91 -0
  118. package/dist/src/llm/google.js.map +1 -0
  119. package/dist/src/llm/google.test.d.ts +1 -0
  120. package/dist/src/llm/google.test.js +253 -0
  121. package/dist/src/llm/google.test.js.map +1 -0
  122. package/dist/src/pieces/Piece.d.ts +66 -0
  123. package/dist/src/pieces/Piece.fixtures.d.ts +43 -0
  124. package/dist/src/pieces/Piece.fixtures.js +83 -0
  125. package/dist/src/pieces/Piece.fixtures.js.map +1 -0
  126. package/dist/src/pieces/Piece.js +256 -0
  127. package/dist/src/pieces/Piece.js.map +1 -0
  128. package/dist/src/pieces/Piece.test.d.ts +1 -0
  129. package/dist/src/pieces/Piece.test.js +839 -0
  130. package/dist/src/pieces/Piece.test.js.map +1 -0
  131. package/dist/src/pieces/Pieces.d.ts +55 -0
  132. package/dist/src/pieces/Pieces.js +144 -0
  133. package/dist/src/pieces/Pieces.js.map +1 -0
  134. package/dist/src/pieces/Pieces.test.d.ts +1 -0
  135. package/dist/src/pieces/Pieces.test.js +327 -0
  136. package/dist/src/pieces/Pieces.test.js.map +1 -0
  137. package/dist/src/pieces/assets.d.ts +5 -0
  138. package/dist/src/pieces/assets.js +6 -0
  139. package/dist/src/pieces/assets.js.map +1 -0
  140. package/dist/src/pieces/cache.d.ts +20 -0
  141. package/dist/src/pieces/cache.fixtures.d.ts +3 -0
  142. package/dist/src/pieces/cache.fixtures.js +12 -0
  143. package/dist/src/pieces/cache.fixtures.js.map +1 -0
  144. package/dist/src/pieces/cache.js +49 -0
  145. package/dist/src/pieces/cache.js.map +1 -0
  146. package/dist/src/pieces/cache.test.d.ts +1 -0
  147. package/dist/src/pieces/cache.test.js +98 -0
  148. package/dist/src/pieces/cache.test.js.map +1 -0
  149. package/dist/src/pieces/index.d.ts +8 -0
  150. package/dist/src/pieces/index.js +9 -0
  151. package/dist/src/pieces/index.js.map +1 -0
  152. package/dist/src/pieces/item.d.ts +9 -0
  153. package/dist/src/pieces/item.js +79 -0
  154. package/dist/src/pieces/item.js.map +1 -0
  155. package/dist/src/pieces/item.test.d.ts +1 -0
  156. package/dist/src/pieces/item.test.js +215 -0
  157. package/dist/src/pieces/item.test.js.map +1 -0
  158. package/dist/src/pieces/items.d.ts +39 -0
  159. package/dist/src/pieces/items.js +45 -0
  160. package/dist/src/pieces/items.js.map +1 -0
  161. package/dist/src/pieces/items.test.d.ts +1 -0
  162. package/dist/src/pieces/items.test.js +100 -0
  163. package/dist/src/pieces/items.test.js.map +1 -0
  164. package/dist/src/pieces/json.schema.d.ts +6 -0
  165. package/dist/src/pieces/json.schema.js +58 -0
  166. package/dist/src/pieces/json.schema.js.map +1 -0
  167. package/dist/src/pieces/json.schema.test.d.ts +1 -0
  168. package/dist/src/pieces/json.schema.test.js +27 -0
  169. package/dist/src/pieces/json.schema.test.js.map +1 -0
  170. package/dist/src/pieces/manager.d.ts +22 -0
  171. package/dist/src/pieces/manager.js +43 -0
  172. package/dist/src/pieces/manager.js.map +1 -0
  173. package/dist/src/pieces/manager.test.d.ts +1 -0
  174. package/dist/src/pieces/manager.test.js +72 -0
  175. package/dist/src/pieces/manager.test.js.map +1 -0
  176. package/dist/src/pieces/utils/frontmatter.d.ts +33 -0
  177. package/dist/src/pieces/utils/frontmatter.js +69 -0
  178. package/dist/src/pieces/utils/frontmatter.js.map +1 -0
  179. package/dist/src/pieces/utils/frontmatter.test.d.ts +1 -0
  180. package/dist/src/pieces/utils/frontmatter.test.js +185 -0
  181. package/dist/src/pieces/utils/frontmatter.test.js.map +1 -0
  182. package/dist/src/pieces/utils/markdown.d.ts +10 -0
  183. package/dist/src/pieces/utils/markdown.js +9 -0
  184. package/dist/src/pieces/utils/markdown.js.map +1 -0
  185. package/dist/src/pieces/utils/markdown.test.d.ts +1 -0
  186. package/dist/src/pieces/utils/markdown.test.js +38 -0
  187. package/dist/src/pieces/utils/markdown.test.js.map +1 -0
  188. package/dist/src/pieces/utils/piece.d.ts +13 -0
  189. package/dist/src/pieces/utils/piece.fixtures.d.ts +19 -0
  190. package/dist/src/pieces/utils/piece.fixtures.js +39 -0
  191. package/dist/src/pieces/utils/piece.fixtures.js.map +1 -0
  192. package/dist/src/pieces/utils/piece.js +142 -0
  193. package/dist/src/pieces/utils/piece.js.map +1 -0
  194. package/dist/src/pieces/utils/piece.test.d.ts +1 -0
  195. package/dist/src/pieces/utils/piece.test.js +333 -0
  196. package/dist/src/pieces/utils/piece.test.js.map +1 -0
  197. package/dist/src/pieces/utils.d.ts +10 -0
  198. package/dist/src/pieces/utils.js +101 -0
  199. package/dist/src/pieces/utils.js.map +1 -0
  200. package/dist/src/pieces/utils.test.d.ts +1 -0
  201. package/dist/src/pieces/utils.test.js +372 -0
  202. package/dist/src/pieces/utils.test.js.map +1 -0
  203. package/dist/src/storage/abstract.d.ts +19 -0
  204. package/dist/src/storage/abstract.js +4 -0
  205. package/dist/src/storage/abstract.js.map +1 -0
  206. package/dist/src/storage/fs.d.ts +22 -0
  207. package/dist/src/storage/fs.js +84 -0
  208. package/dist/src/storage/fs.js.map +1 -0
  209. package/dist/src/storage/fs.test.d.ts +1 -0
  210. package/dist/src/storage/fs.test.js +222 -0
  211. package/dist/src/storage/fs.test.js.map +1 -0
  212. package/dist/src/storage/index.d.ts +5 -0
  213. package/dist/src/storage/index.js +4 -0
  214. package/dist/src/storage/index.js.map +1 -0
  215. package/dist/src/storage/storage.mock.d.ts +2 -0
  216. package/dist/src/storage/storage.mock.js +18 -0
  217. package/dist/src/storage/storage.mock.js.map +1 -0
  218. package/dist/src/storage/types.d.ts +6 -0
  219. package/dist/src/storage/types.js +2 -0
  220. package/dist/src/storage/types.js.map +1 -0
  221. package/dist/src/storage/webdav.d.ts +25 -0
  222. package/dist/src/storage/webdav.js +65 -0
  223. package/dist/src/storage/webdav.js.map +1 -0
  224. package/dist/src/storage/webdav.test.d.ts +1 -0
  225. package/dist/src/storage/webdav.test.js +194 -0
  226. package/dist/src/storage/webdav.test.js.map +1 -0
  227. package/dist/tsconfig.tsbuildinfo +1 -0
  228. package/dist/vite.config.d.ts +2 -0
  229. package/dist/vite.config.js +36 -0
  230. package/dist/vite.config.js.map +1 -0
  231. package/package.json +60 -0
@@ -0,0 +1,839 @@
1
+ import { describe, expect, test, vi, afterEach } from 'vitest';
2
+ import { makeMarkdownSample, makePieceItemSelectable, makePieceMock, makeFrontmatterSample, makeSchema, makeStorage, } from './Piece.fixtures.js';
3
+ import { mockKysely } from '../database/database.mock.js';
4
+ import { addCache, removeCache, updateCache, getCache } from './cache.js';
5
+ import { getPieceFrontmatterSchemaFields, databaseValueToPieceFrontmatterValue, initializePieceFrontMatter, } from './utils/frontmatter.js';
6
+ import { makePieceMarkdown, makePieceMarkdownString } from './utils/markdown.js';
7
+ import { extractFullMarkdown } from '../lib/markdown.js';
8
+ import compile from '../lib/ajv.js';
9
+ import { makePieceItemInsertable, makePieceItemUpdatable, validatePieceItem, getValidatePieceItemErrors, } from './item.js';
10
+ import { selectItems, deleteItem, selectItem, insertItem, updateItem } from './items.js';
11
+ import { calculateHashFromFile, makePieceValue, makePieceAttachment } from './utils/piece.js';
12
+ import { makeCache } from './cache.fixtures.js';
13
+ import slugify from '@sindresorhus/slugify';
14
+ import { PassThrough } from 'stream';
15
+ import { cpus } from 'os';
16
+ vi.mock('./cache.js');
17
+ vi.mock('os');
18
+ vi.mock('@sindresorhus/slugify');
19
+ vi.mock('./utils/frontmatter.js');
20
+ vi.mock('../lib/ajv.js');
21
+ vi.mock('./utils/markdown.js');
22
+ vi.mock('../lib/markdown.js');
23
+ vi.mock('./item.js');
24
+ vi.mock('./items.js');
25
+ vi.mock('./utils/piece.js');
26
+ const mocks = {
27
+ makePieceMarkdown: vi.mocked(makePieceMarkdown),
28
+ toMarkdownString: vi.mocked(makePieceMarkdownString),
29
+ extract: vi.mocked(extractFullMarkdown),
30
+ addCache: vi.mocked(addCache),
31
+ removeCache: vi.mocked(removeCache),
32
+ updateCache: vi.mocked(updateCache),
33
+ getCache: vi.mocked(getCache),
34
+ compile: vi.mocked(compile),
35
+ getPieceSchemaFields: vi.mocked(getPieceFrontmatterSchemaFields),
36
+ databaseValueToFrontmatterValue: vi.mocked(databaseValueToPieceFrontmatterValue),
37
+ calculateHashFromFile: vi.mocked(calculateHashFromFile),
38
+ makeInsertable: vi.mocked(makePieceItemInsertable),
39
+ makeUpdatable: vi.mocked(makePieceItemUpdatable),
40
+ initializePieceFrontMatter: vi.mocked(initializePieceFrontMatter),
41
+ selectItems: vi.mocked(selectItems),
42
+ deleteItem: vi.mocked(deleteItem),
43
+ insertItem: vi.mocked(insertItem),
44
+ updateItem: vi.mocked(updateItem),
45
+ selectItem: vi.mocked(selectItem),
46
+ validatePieceItem: vi.mocked(validatePieceItem),
47
+ getValidatePieceItemErrors: vi.mocked(getValidatePieceItemErrors),
48
+ slugify: vi.mocked(slugify),
49
+ makePieceValue: vi.mocked(makePieceValue),
50
+ makePieceAttachment: vi.mocked(makePieceAttachment),
51
+ cpus: vi.mocked(cpus),
52
+ };
53
+ const spies = {};
54
+ describe('pieces/Piece.ts', () => {
55
+ afterEach(() => {
56
+ Object.values(mocks).forEach((mock) => {
57
+ mock.mockReset();
58
+ });
59
+ Object.keys(spies).forEach((key) => {
60
+ spies[key].mockRestore();
61
+ delete spies[key];
62
+ });
63
+ });
64
+ test('constructor throws', () => {
65
+ const schema = makeSchema('not-title');
66
+ const PieceType = makePieceMock();
67
+ const markdown = makeMarkdownSample();
68
+ const storage = makeStorage('root');
69
+ mocks.initializePieceFrontMatter.mockReturnValueOnce(markdown.frontmatter);
70
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
71
+ expect(() => new PieceType('books', storage, schema)).toThrow();
72
+ });
73
+ test('create', async () => {
74
+ const PieceType = makePieceMock();
75
+ const markdown = makeMarkdownSample();
76
+ const storage = makeStorage('root');
77
+ const title = markdown.frontmatter.title;
78
+ const file = markdown.filePath;
79
+ mocks.slugify.mockReturnValueOnce(title);
80
+ mocks.initializePieceFrontMatter.mockReturnValueOnce(markdown.frontmatter);
81
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
82
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(false);
83
+ const piece = new PieceType('books', storage);
84
+ const pieceMarkdown = await piece.create(file, title);
85
+ expect(pieceMarkdown).toEqual(markdown);
86
+ });
87
+ test('create throws on existing piece', async () => {
88
+ const PieceType = makePieceMock();
89
+ const markdown = makeMarkdownSample();
90
+ const storage = makeStorage('root');
91
+ const title = markdown.frontmatter.title;
92
+ const file = markdown.filePath;
93
+ mocks.slugify.mockReturnValueOnce(title);
94
+ mocks.initializePieceFrontMatter.mockReturnValueOnce(markdown.frontmatter);
95
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
96
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(true);
97
+ const piece = new PieceType('books', storage);
98
+ const creating = piece.create(file, title);
99
+ await expect(creating).rejects.toThrowError();
100
+ });
101
+ test('delete', async () => {
102
+ const path = 'path/to/slug.md';
103
+ const storage = makeStorage('root');
104
+ const PieceTest = makePieceMock();
105
+ const pieceTest = new PieceTest('books', storage);
106
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(true);
107
+ spies.delete = vi.spyOn(storage, 'delete').mockResolvedValueOnce();
108
+ await pieceTest.delete(path);
109
+ expect(spies.delete).toHaveBeenCalledOnce();
110
+ });
111
+ test('delete fails', async () => {
112
+ const path = 'path/to/slug.md';
113
+ const storage = makeStorage('root');
114
+ const PieceTest = makePieceMock();
115
+ const pieceTest = new PieceTest('books', storage);
116
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(false);
117
+ spies.delete = vi.spyOn(storage, 'delete').mockResolvedValueOnce();
118
+ const deleting = pieceTest.delete(path);
119
+ expect(deleting).rejects.toThrowError();
120
+ });
121
+ test('get schema', () => {
122
+ const PieceType = makePieceMock();
123
+ const type = 'table';
124
+ const schema = makeSchema(type);
125
+ const markdown = makeMarkdownSample();
126
+ const storage = makeStorage('root');
127
+ mocks.initializePieceFrontMatter.mockReturnValueOnce(markdown.frontmatter);
128
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
129
+ const piece = new PieceType(type, storage, schema);
130
+ expect(piece.schema).toEqual(schema);
131
+ });
132
+ test('isOutdated', async () => {
133
+ const filename = '/path/to/slug.books.md';
134
+ const db = mockKysely().db;
135
+ const PieceType = makePieceMock();
136
+ const cacheDate = new Date('11-11-2000').getTime();
137
+ const fileDate = new Date('11-11-2001');
138
+ const mockCache = makeCache({ date_updated: cacheDate });
139
+ const storage = makeStorage('root');
140
+ const pieceTest = new PieceType('books', storage);
141
+ mocks.getCache.mockResolvedValueOnce(mockCache);
142
+ spies.stat = vi
143
+ .spyOn(storage, 'stat')
144
+ .mockResolvedValueOnce({ last_modified: fileDate });
145
+ const isOutdated = await pieceTest.isOutdated(filename, db);
146
+ expect(isOutdated).toEqual(true);
147
+ });
148
+ test('isOutdated by date_added', async () => {
149
+ const filename = '/path/to/slug.books.md';
150
+ const db = mockKysely().db;
151
+ const PieceType = makePieceMock();
152
+ const cacheDate = new Date('11-11-2000').getTime();
153
+ const fileDate = new Date('11-11-2001');
154
+ const mockCache = makeCache({ date_added: cacheDate, date_updated: undefined });
155
+ const storage = makeStorage('root');
156
+ const pieceTest = new PieceType('books', storage);
157
+ mocks.getCache.mockResolvedValueOnce(mockCache);
158
+ spies.stat = vi
159
+ .spyOn(storage, 'stat')
160
+ .mockResolvedValueOnce({ last_modified: fileDate });
161
+ const isOutdated = await pieceTest.isOutdated(filename, db);
162
+ expect(isOutdated).toEqual(true);
163
+ });
164
+ test('isOutdated returns false', async () => {
165
+ const filename = '/path/to/slug.books.md';
166
+ const db = mockKysely().db;
167
+ const PieceType = makePieceMock();
168
+ const cacheDate = new Date('11-11-2000').getTime();
169
+ const fileDate = new Date('11-11-2000');
170
+ const mockCache = makeCache({ date_updated: cacheDate });
171
+ const storage = makeStorage('root');
172
+ const pieceTest = new PieceType('books', storage);
173
+ mocks.getCache.mockResolvedValueOnce(mockCache);
174
+ spies.stat = vi
175
+ .spyOn(storage, 'stat')
176
+ .mockResolvedValueOnce({ last_modified: fileDate });
177
+ const isOutdated = await pieceTest.isOutdated(filename, db);
178
+ expect(isOutdated).toEqual(false);
179
+ });
180
+ test('isOutdated throws', async () => {
181
+ const filename = '/path/to/slug.books.md';
182
+ const db = mockKysely().db;
183
+ const PieceType = makePieceMock();
184
+ const storage = makeStorage('root');
185
+ const pieceTest = new PieceType('books', storage);
186
+ spies.stat = vi.spyOn(storage, 'stat').mockRejectedValueOnce(new Error('oof'));
187
+ const isOutdating = pieceTest.isOutdated(filename, db);
188
+ await expect(isOutdating).rejects.toThrow();
189
+ });
190
+ test('validate', () => {
191
+ const PieceType = makePieceMock();
192
+ const markdown = makeMarkdownSample();
193
+ const piece = new PieceType();
194
+ mocks.validatePieceItem.mockReturnValueOnce(true);
195
+ const validate = piece.validate(markdown);
196
+ expect(validate).toEqual({ isValid: true });
197
+ });
198
+ test('validate isFalse', () => {
199
+ const PieceType = makePieceMock();
200
+ const markdown = makeMarkdownSample();
201
+ const piece = new PieceType();
202
+ const errors = ['error'];
203
+ mocks.validatePieceItem.mockReturnValueOnce(false);
204
+ mocks.getValidatePieceItemErrors.mockReturnValueOnce(errors);
205
+ const validate = piece.validate(markdown);
206
+ expect(validate).toEqual({ isValid: false, errors });
207
+ });
208
+ test('get', async () => {
209
+ const note = 'note';
210
+ const frontmatter = makeFrontmatterSample();
211
+ const path = 'path/to/slug.md';
212
+ const extracted = { markdown: note, frontmatter };
213
+ const markdown = makeMarkdownSample({ note, frontmatter });
214
+ const storage = makeStorage('root');
215
+ const PieceTest = makePieceMock();
216
+ const pieceTest = new PieceTest('books', storage);
217
+ mocks.extract.mockResolvedValueOnce(extracted);
218
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
219
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(true);
220
+ spies.readFile = vi.spyOn(storage, 'readFile').mockResolvedValueOnce(note);
221
+ const get = await pieceTest.get(path);
222
+ expect(mocks.extract).toHaveBeenCalledWith(note);
223
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledOnce();
224
+ expect(get).toEqual(markdown);
225
+ });
226
+ test('get throws', async () => {
227
+ const note = 'note';
228
+ const frontmatter = makeFrontmatterSample();
229
+ const path = '/path/to/slug.md';
230
+ const extracted = { markdown: note, frontmatter };
231
+ const markdown = makeMarkdownSample({ note, frontmatter });
232
+ const storage = makeStorage('root');
233
+ const PieceTest = makePieceMock();
234
+ const pieceTest = new PieceTest('books', storage);
235
+ mocks.extract.mockResolvedValueOnce(extracted);
236
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
237
+ spies.exists = vi.spyOn(storage, 'exists').mockResolvedValueOnce(false);
238
+ const getting = pieceTest.get(path);
239
+ await expect(getting).rejects.toThrowError();
240
+ });
241
+ test('write', async () => {
242
+ const sample = makeMarkdownSample();
243
+ const contents = JSON.stringify(sample.frontmatter);
244
+ const storage = makeStorage('root');
245
+ const PieceTest = makePieceMock();
246
+ const pieceTest = new PieceTest('books', storage);
247
+ mocks.validatePieceItem.mockReturnValueOnce(true);
248
+ mocks.toMarkdownString.mockReturnValueOnce(contents);
249
+ spies.write = vi.spyOn(storage, 'writeFile').mockResolvedValueOnce(undefined);
250
+ await pieceTest.write(sample);
251
+ expect(spies.write).toHaveBeenCalledWith(sample.filePath, contents);
252
+ });
253
+ test('write fails', async () => {
254
+ const sample = makeMarkdownSample();
255
+ const contents = JSON.stringify(sample.frontmatter);
256
+ const storage = makeStorage('root');
257
+ const PieceTest = makePieceMock();
258
+ const pieceTest = new PieceTest('books', storage);
259
+ mocks.validatePieceItem.mockReturnValueOnce(false);
260
+ mocks.getValidatePieceItemErrors.mockReturnValueOnce(['error']);
261
+ mocks.toMarkdownString.mockReturnValueOnce(contents);
262
+ const writing = pieceTest.write(sample);
263
+ await expect(writing).rejects.toThrowError();
264
+ });
265
+ test('prune', async () => {
266
+ const dbPieces = [
267
+ makePieceItemSelectable({ file_path: 'a' }),
268
+ makePieceItemSelectable({ file_path: 'b' }),
269
+ makePieceItemSelectable({ file_path: 'c' }),
270
+ ];
271
+ const db = mockKysely().db;
272
+ const storage = makeStorage('root');
273
+ const PieceTest = makePieceMock();
274
+ const pieceTest = new PieceTest('books', storage);
275
+ mocks.cpus.mockReturnValue([{}]);
276
+ mocks.selectItems.mockResolvedValueOnce(dbPieces);
277
+ mocks.deleteItem.mockResolvedValueOnce();
278
+ const gen = await pieceTest.prune(db, []);
279
+ const result = [];
280
+ for await (const pruned of gen) {
281
+ if (!pruned.error) {
282
+ result.push(pruned.file);
283
+ }
284
+ }
285
+ expect(mocks.selectItems).toHaveBeenCalledOnce();
286
+ expect(mocks.deleteItem).toHaveBeenCalledTimes(3);
287
+ expect(result).toEqual(['a', 'b', 'c']);
288
+ });
289
+ test('prune dryRun', async () => {
290
+ const dbPieces = [
291
+ makePieceItemSelectable({ file_path: 'a' }),
292
+ makePieceItemSelectable({ file_path: 'b' }),
293
+ makePieceItemSelectable({ file_path: 'c' }),
294
+ ];
295
+ const db = mockKysely().db;
296
+ const storage = makeStorage('root');
297
+ const PieceTest = makePieceMock();
298
+ const pieceTest = new PieceTest('books', storage);
299
+ mocks.cpus.mockReturnValue([{}]);
300
+ mocks.selectItems.mockResolvedValueOnce(dbPieces);
301
+ mocks.deleteItem.mockResolvedValueOnce();
302
+ const gen = await pieceTest.prune(db, [], { dryRun: true });
303
+ const result = [];
304
+ for await (const pruned of gen) {
305
+ if (!pruned.error) {
306
+ result.push(pruned.file);
307
+ }
308
+ }
309
+ expect(mocks.selectItems).toHaveBeenCalledOnce();
310
+ expect(mocks.deleteItem).not.toHaveBeenCalled();
311
+ expect(result).toEqual(['a', 'b', 'c']);
312
+ });
313
+ test('prune error', async () => {
314
+ const dbPieces = [makePieceItemSelectable({ file_path: 'a' })];
315
+ const db = mockKysely().db;
316
+ const storage = makeStorage('root');
317
+ const PieceTest = makePieceMock();
318
+ const pieceTest = new PieceTest('books', storage);
319
+ mocks.cpus.mockReturnValue([{}]);
320
+ mocks.selectItems.mockResolvedValueOnce(dbPieces);
321
+ mocks.deleteItem.mockRejectedValueOnce(new Error('oof'));
322
+ const gen = await pieceTest.prune(db, []);
323
+ const result = [];
324
+ for await (const pruned of gen) {
325
+ if (pruned.error) {
326
+ result.push(pruned.file);
327
+ }
328
+ }
329
+ expect(mocks.selectItems).toHaveBeenCalledOnce();
330
+ expect(mocks.deleteItem).toHaveBeenCalledOnce();
331
+ expect(result).toHaveLength(1);
332
+ });
333
+ test('syncMarkdownAdd', async () => {
334
+ const dbMocks = mockKysely();
335
+ const PieceTest = makePieceMock();
336
+ const markdown = makeMarkdownSample();
337
+ const hash = 'hash';
338
+ const pieceTest = new PieceTest();
339
+ mocks.makeInsertable.mockReturnValueOnce({});
340
+ mocks.insertItem.mockResolvedValueOnce({});
341
+ mocks.calculateHashFromFile.mockResolvedValueOnce(hash);
342
+ await pieceTest.syncMarkdownAdd(dbMocks.db, markdown);
343
+ expect(mocks.insertItem).toHaveBeenCalledOnce();
344
+ expect(mocks.addCache).toHaveBeenCalledWith(dbMocks.db, markdown.filePath, hash);
345
+ });
346
+ test('syncMarkdownAdd supports dryRun', async () => {
347
+ const dbMocks = mockKysely();
348
+ const PieceTest = makePieceMock();
349
+ const keywords = 'a,b'.split(',');
350
+ const markdown = makeMarkdownSample({ frontmatter: { keywords: keywords.join(',') } });
351
+ const pieceTest = new PieceTest();
352
+ await pieceTest.syncMarkdownAdd(dbMocks.db, markdown);
353
+ expect(dbMocks.queries.executeTakeFirst).not.toHaveBeenCalled();
354
+ });
355
+ test('syncMarkdown update', async () => {
356
+ const dbMocks = mockKysely();
357
+ const dbData = { id: 1, slug: 'slug' };
358
+ const PieceTest = makePieceMock();
359
+ const markdown = makeMarkdownSample();
360
+ const pieceTest = new PieceTest();
361
+ mocks.selectItem.mockResolvedValueOnce(dbData);
362
+ spies.syncUpdate = vi.spyOn(pieceTest, 'syncMarkdownUpdate').mockResolvedValueOnce();
363
+ await pieceTest.syncMarkdown(dbMocks.db, markdown);
364
+ expect(spies.syncUpdate).toHaveBeenCalledWith(dbMocks.db, markdown, dbData);
365
+ });
366
+ test('syncMarkdown add', async () => {
367
+ const dbMocks = mockKysely();
368
+ const PieceTest = makePieceMock();
369
+ const markdown = makeMarkdownSample();
370
+ const pieceTest = new PieceTest();
371
+ mocks.selectItem.mockResolvedValueOnce(undefined);
372
+ spies.syncAdd = vi.spyOn(pieceTest, 'syncMarkdownAdd').mockResolvedValueOnce();
373
+ await pieceTest.syncMarkdown(dbMocks.db, markdown);
374
+ expect(spies.syncAdd).toHaveBeenCalledWith(dbMocks.db, markdown);
375
+ });
376
+ test('syncMarkdownUpdate', async () => {
377
+ const dbMocks = mockKysely();
378
+ const PieceTest = makePieceMock();
379
+ const markdown = makeMarkdownSample();
380
+ const updated = { frontmatter_json: JSON.stringify(markdown.frontmatter), id: '1' };
381
+ const pieceData = { ...makePieceItemSelectable() };
382
+ const pieceTest = new PieceTest();
383
+ mocks.makeUpdatable.mockReturnValueOnce(updated);
384
+ mocks.updateItem.mockResolvedValueOnce();
385
+ await pieceTest.syncMarkdownUpdate(dbMocks.db, markdown, pieceData);
386
+ expect(mocks.updateItem).toHaveBeenCalledOnce();
387
+ });
388
+ test('syncMarkdownUpdate with keywords', async () => {
389
+ const dbMocks = mockKysely();
390
+ const PieceTest = makePieceMock();
391
+ const keywords = 'a,b'.split(',');
392
+ const markdown = makeMarkdownSample({ frontmatter: { keywords: keywords.join(',') } });
393
+ const pieceData = { ...makePieceItemSelectable() };
394
+ const updated = { frontmatter_json: JSON.stringify(markdown.frontmatter) };
395
+ const pieceTest = new PieceTest();
396
+ mocks.makeUpdatable.mockReturnValueOnce(updated);
397
+ mocks.updateItem.mockResolvedValueOnce();
398
+ await pieceTest.syncMarkdownUpdate(dbMocks.db, markdown, pieceData);
399
+ expect(mocks.updateItem).toHaveBeenCalledOnce();
400
+ });
401
+ test('syncMarkdownUpdate supports dryRun', async () => {
402
+ const PieceTest = makePieceMock();
403
+ const markdown = makeMarkdownSample();
404
+ const pieceData = makePieceItemSelectable();
405
+ const db = mockKysely().db;
406
+ const updated = { frontmatter_json: JSON.stringify(markdown.frontmatter) };
407
+ const pieceTest = new PieceTest();
408
+ mocks.makeUpdatable.mockReturnValueOnce(updated);
409
+ await pieceTest.syncMarkdownUpdate(db, markdown, pieceData);
410
+ });
411
+ test('toMarkdown', () => {
412
+ const pieceMarkdown = makeMarkdownSample();
413
+ const pieceSample = makePieceItemSelectable();
414
+ const PieceTest = makePieceMock();
415
+ mocks.makePieceMarkdown.mockReturnValueOnce(pieceMarkdown);
416
+ mocks.getPieceSchemaFields.mockReturnValueOnce([
417
+ { name: 'title', type: 'string', format: 'asset' },
418
+ ]);
419
+ mocks.databaseValueToFrontmatterValue.mockReturnValueOnce(pieceMarkdown.frontmatter.title);
420
+ const markdown = new PieceTest().toMarkdown(pieceSample);
421
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(pieceMarkdown.filePath, pieceMarkdown.piece, pieceMarkdown.note, pieceMarkdown.frontmatter);
422
+ expect(markdown).toEqual(pieceMarkdown);
423
+ });
424
+ test('toMarkdown with arrays', () => {
425
+ const pieceMarkdown = makeMarkdownSample();
426
+ const pieceSample = makePieceItemSelectable();
427
+ const PieceTest = makePieceMock();
428
+ const title = ['a', 'b'];
429
+ pieceSample.frontmatter_json = JSON.stringify({ title });
430
+ pieceMarkdown.frontmatter = JSON.parse(pieceSample.frontmatter_json);
431
+ mocks.makePieceMarkdown.mockReturnValueOnce(pieceMarkdown);
432
+ mocks.getPieceSchemaFields.mockReturnValueOnce([
433
+ { name: 'title', type: 'array', items: { type: 'string' } },
434
+ ]);
435
+ mocks.databaseValueToFrontmatterValue.mockReturnValueOnce(title);
436
+ const markdown = new PieceTest().toMarkdown(pieceSample);
437
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(pieceMarkdown.filePath, pieceMarkdown.piece, pieceMarkdown.note, pieceMarkdown.frontmatter);
438
+ expect(markdown).toEqual(pieceMarkdown);
439
+ });
440
+ test('sync', async () => {
441
+ const dbMocks = mockKysely();
442
+ const slugs = ['a', 'b', 'c'];
443
+ const markdown = makeMarkdownSample();
444
+ const storage = makeStorage('root');
445
+ const PieceTest = makePieceMock();
446
+ const pieceTest = new PieceTest('books', storage);
447
+ mocks.cpus.mockReturnValue([{}]);
448
+ mocks.calculateHashFromFile
449
+ .mockResolvedValueOnce('hash-old')
450
+ .mockResolvedValueOnce('hash')
451
+ .mockResolvedValueOnce('hash-new');
452
+ mocks.getCache.mockResolvedValue({
453
+ content_hash: 'hash',
454
+ id: '',
455
+ file_path: '',
456
+ date_added: 0,
457
+ date_updated: null,
458
+ });
459
+ spies.syncMarkdownAdd = vi.spyOn(pieceTest, 'syncMarkdownAdd').mockResolvedValue();
460
+ spies.syncMarkdownUpdate = vi.spyOn(pieceTest, 'syncMarkdownUpdate').mockResolvedValue();
461
+ spies.get = vi.spyOn(pieceTest, 'get').mockResolvedValue(markdown);
462
+ spies.selectItem = mocks.selectItem
463
+ .mockResolvedValueOnce({})
464
+ .mockResolvedValueOnce({})
465
+ .mockResolvedValueOnce(undefined);
466
+ const gen = await pieceTest.sync(dbMocks.db, slugs);
467
+ const added = [];
468
+ const skipped = [];
469
+ const updated = [];
470
+ for await (const file of gen) {
471
+ if (!file.error) {
472
+ if (file.action === 'added') {
473
+ added.push(file);
474
+ }
475
+ else if (file.action === 'skipped') {
476
+ skipped.push(file);
477
+ }
478
+ else if (file.action === 'updated') {
479
+ updated.push(file);
480
+ }
481
+ }
482
+ }
483
+ expect(spies.syncMarkdownAdd).toHaveBeenCalledOnce();
484
+ expect(spies.syncMarkdownUpdate).toHaveBeenCalledOnce();
485
+ expect(added).toHaveLength(1);
486
+ expect(skipped).toHaveLength(1);
487
+ expect(updated).toHaveLength(1);
488
+ });
489
+ test('sync dryRun', async () => {
490
+ const dbMocks = mockKysely();
491
+ const slugs = ['a', 'b', 'c'];
492
+ const markdown = makeMarkdownSample();
493
+ const storage = makeStorage('root');
494
+ const PieceTest = makePieceMock();
495
+ const pieceTest = new PieceTest('books', storage);
496
+ mocks.cpus.mockReturnValue([{}]);
497
+ mocks.calculateHashFromFile
498
+ .mockResolvedValueOnce('hash-old')
499
+ .mockResolvedValueOnce('hash')
500
+ .mockResolvedValueOnce('hash-new');
501
+ mocks.getCache.mockResolvedValue({
502
+ content_hash: 'hash',
503
+ id: '',
504
+ file_path: '',
505
+ date_added: 0,
506
+ date_updated: null,
507
+ });
508
+ spies.syncMarkdownAdd = vi.spyOn(pieceTest, 'syncMarkdownAdd').mockResolvedValue();
509
+ spies.syncMarkdownUpdate = vi.spyOn(pieceTest, 'syncMarkdownUpdate').mockResolvedValue();
510
+ spies.get = vi.spyOn(pieceTest, 'get').mockResolvedValue(markdown);
511
+ spies.selectItem = mocks.selectItem
512
+ .mockResolvedValueOnce({})
513
+ .mockResolvedValueOnce({})
514
+ .mockResolvedValueOnce(undefined);
515
+ const gen = await pieceTest.sync(dbMocks.db, slugs, { dryRun: true });
516
+ const added = [];
517
+ const skipped = [];
518
+ const updated = [];
519
+ for await (const file of gen) {
520
+ if (!file.error) {
521
+ if (file.action === 'added') {
522
+ added.push(file);
523
+ }
524
+ else if (file.action === 'skipped') {
525
+ skipped.push(file);
526
+ }
527
+ else if (file.action === 'updated') {
528
+ updated.push(file);
529
+ }
530
+ }
531
+ }
532
+ expect(spies.syncMarkdownAdd).not.toHaveBeenCalledOnce();
533
+ expect(spies.syncMarkdownUpdate).not.toHaveBeenCalledOnce();
534
+ expect(added).toHaveLength(1);
535
+ expect(skipped).toHaveLength(1);
536
+ expect(updated).toHaveLength(1);
537
+ });
538
+ test('sync force', async () => {
539
+ const dbMocks = mockKysely();
540
+ const slugs = ['a', 'b', 'c'];
541
+ const markdown = makeMarkdownSample();
542
+ const storage = makeStorage('root');
543
+ const PieceTest = makePieceMock();
544
+ const pieceTest = new PieceTest('books', storage);
545
+ mocks.cpus.mockReturnValue([{}]);
546
+ mocks.calculateHashFromFile
547
+ .mockResolvedValueOnce('hash-old')
548
+ .mockResolvedValueOnce('hash')
549
+ .mockResolvedValueOnce('hash-new');
550
+ mocks.getCache.mockResolvedValue({
551
+ content_hash: 'hash',
552
+ id: '',
553
+ file_path: '',
554
+ date_added: 0,
555
+ date_updated: null,
556
+ });
557
+ spies.syncMarkdownAdd = vi.spyOn(pieceTest, 'syncMarkdownAdd').mockResolvedValue();
558
+ spies.syncMarkdownUpdate = vi.spyOn(pieceTest, 'syncMarkdownUpdate').mockResolvedValue();
559
+ spies.get = vi.spyOn(pieceTest, 'get').mockResolvedValue(markdown);
560
+ spies.selectItem = mocks.selectItem
561
+ .mockResolvedValueOnce({})
562
+ .mockResolvedValueOnce({})
563
+ .mockResolvedValueOnce(undefined);
564
+ const gen = await pieceTest.sync(dbMocks.db, slugs, { force: true });
565
+ const added = [];
566
+ const updated = [];
567
+ for await (const file of gen) {
568
+ if (!file.error) {
569
+ if (file.action === 'added') {
570
+ added.push(file);
571
+ }
572
+ else if (file.action === 'updated') {
573
+ updated.push(file);
574
+ }
575
+ }
576
+ }
577
+ expect(spies.syncMarkdownAdd).toHaveBeenCalledOnce();
578
+ expect(spies.syncMarkdownUpdate).toHaveBeenCalledTimes(2);
579
+ expect(added).toHaveLength(1);
580
+ expect(updated).toHaveLength(2);
581
+ });
582
+ test('sync errors', async () => {
583
+ const dbMocks = mockKysely();
584
+ const slugs = ['a'];
585
+ const markdown = makeMarkdownSample();
586
+ const storage = makeStorage('root');
587
+ const PieceTest = makePieceMock();
588
+ const pieceTest = new PieceTest('books', storage);
589
+ mocks.cpus.mockReturnValue([{}]);
590
+ mocks.calculateHashFromFile
591
+ .mockResolvedValueOnce('hash-old');
592
+ mocks.getCache.mockResolvedValue({
593
+ content_hash: 'hash',
594
+ id: '',
595
+ file_path: '',
596
+ date_added: 0,
597
+ date_updated: null,
598
+ });
599
+ spies.syncMarkdownUpdate = vi.spyOn(pieceTest, 'syncMarkdownUpdate').mockRejectedValueOnce(new Error('oof'));
600
+ spies.get = vi.spyOn(pieceTest, 'get').mockResolvedValue(markdown);
601
+ spies.selectItem = mocks.selectItem
602
+ .mockResolvedValueOnce({});
603
+ const gen = await pieceTest.sync(dbMocks.db, slugs);
604
+ const errors = [];
605
+ for await (const file of gen) {
606
+ if (file.error) {
607
+ errors.push(file);
608
+ }
609
+ }
610
+ expect(spies.syncMarkdownUpdate).toHaveBeenCalledOnce();
611
+ expect(errors).toHaveLength(1);
612
+ });
613
+ test('get fields', async () => {
614
+ const PieceTest = makePieceMock();
615
+ const pieceTest = new PieceTest();
616
+ mocks.getPieceSchemaFields.mockReturnValueOnce(pieceTest.schema.properties);
617
+ const getFields = pieceTest.fields;
618
+ const getFields2 = pieceTest.fields;
619
+ expect(getFields).toEqual(getFields2);
620
+ });
621
+ test('setFields', async () => {
622
+ const markdown = makeMarkdownSample();
623
+ const field = 'title';
624
+ const value = 'new title';
625
+ const fields = [{ name: field, type: 'string' }];
626
+ const PieceTest = makePieceMock();
627
+ const pieceTest = new PieceTest();
628
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
629
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
630
+ mocks.makePieceValue.mockImplementation(async (_, value) => value);
631
+ await pieceTest.setFields(markdown, { [field]: value });
632
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, { ...markdown.frontmatter, [field]: value });
633
+ });
634
+ test('setField', async () => {
635
+ const markdown = makeMarkdownSample();
636
+ const field = 'title';
637
+ const value = 'new title';
638
+ const fields = [{ name: field, type: 'string' }];
639
+ const PieceTest = makePieceMock();
640
+ const pieceTest = new PieceTest();
641
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
642
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
643
+ mocks.makePieceValue.mockImplementation(async (_, value) => value);
644
+ await pieceTest.setField(markdown, field, value);
645
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, { ...markdown.frontmatter, [field]: value });
646
+ });
647
+ test('setField throws on bad field', async () => {
648
+ const markdown = makeMarkdownSample();
649
+ const field = 'title';
650
+ const value = 'new title';
651
+ const fields = [{ name: 'title2', type: 'string' }];
652
+ const PieceTest = makePieceMock();
653
+ const pieceTest = new PieceTest();
654
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
655
+ const updating = pieceTest.setField(markdown, field, value);
656
+ await expect(updating).rejects.toThrowError();
657
+ });
658
+ test('setField resets array types with scalar', async () => {
659
+ const tags = ['tag1', 'tag2'];
660
+ const markdown = makeMarkdownSample({ frontmatter: { tags } });
661
+ const field = 'tags';
662
+ const value = 'another-tag';
663
+ const fields = [
664
+ { name: field, type: 'array', items: { type: 'string' } },
665
+ ];
666
+ const PieceTest = makePieceMock();
667
+ const pieceTest = new PieceTest();
668
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
669
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
670
+ mocks.makePieceValue.mockImplementation(async (_, value) => value);
671
+ await pieceTest.setField(markdown, field, value);
672
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, { ...markdown.frontmatter, [field]: [value] });
673
+ });
674
+ test('setField resets array types with another array', async () => {
675
+ const tags = ['tag1', 'tag2'];
676
+ const markdown = makeMarkdownSample({ frontmatter: { tags } });
677
+ const field = 'tags';
678
+ const value = 'another-tag';
679
+ const fields = [
680
+ { name: field, type: 'array', items: { type: 'string' } },
681
+ ];
682
+ const PieceTest = makePieceMock();
683
+ const pieceTest = new PieceTest();
684
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
685
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
686
+ mocks.makePieceValue.mockImplementation(async (_, value) => value);
687
+ await pieceTest.setField(markdown, field, [...tags, value]);
688
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, { ...markdown.frontmatter, [field]: [...tags, value] });
689
+ });
690
+ test('setField with attachment', async () => {
691
+ const mockReadable = new PassThrough();
692
+ const markdown = makeMarkdownSample();
693
+ const field = 'cover';
694
+ const value = 'file';
695
+ const finalValue = 'path/to/file.jpg';
696
+ const fields = [
697
+ { name: field, type: 'string', format: 'asset' },
698
+ ];
699
+ const PieceTest = makePieceMock();
700
+ const storage = makeStorage('root');
701
+ const pieceTest = new PieceTest('books', storage);
702
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
703
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
704
+ mocks.makePieceValue.mockImplementationOnce(async () => mockReadable);
705
+ mocks.makePieceAttachment.mockResolvedValueOnce(finalValue);
706
+ await pieceTest.setField(markdown, field, value);
707
+ expect(mocks.makePieceAttachment).toHaveBeenCalledOnce();
708
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, {
709
+ ...markdown.frontmatter,
710
+ [field]: finalValue,
711
+ });
712
+ });
713
+ test('setField with bad attachment', async () => {
714
+ const mockReadable = new PassThrough();
715
+ const markdown = makeMarkdownSample();
716
+ const field = 'cover';
717
+ const value = 'file';
718
+ const fields = [
719
+ { name: field, type: 'string', format: 'asset' },
720
+ ];
721
+ const PieceTest = makePieceMock();
722
+ const storage = makeStorage('root');
723
+ const pieceTest = new PieceTest('books', storage);
724
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
725
+ mocks.makePieceValue.mockImplementationOnce(async () => mockReadable);
726
+ mocks.makePieceAttachment.mockRejectedValueOnce(new Error('oof'));
727
+ const newMarkdown = await pieceTest.setField(markdown, field, value);
728
+ expect(mocks.makePieceAttachment).toHaveBeenCalledOnce();
729
+ // Assert that the original markdown is returned and makePieceMarkdown is NOT called to create a new one.
730
+ expect(newMarkdown).toEqual(markdown);
731
+ expect(mocks.makePieceMarkdown).not.toHaveBeenCalled();
732
+ });
733
+ test('removeFields', async () => {
734
+ const markdown = makeMarkdownSample({ frontmatter: { title: 'title', subtitle: 'sub' } });
735
+ const field = 'subtitle';
736
+ const fields = [
737
+ { name: field, type: 'string', nullable: true },
738
+ ];
739
+ const PieceTest = makePieceMock();
740
+ const pieceTest = new PieceTest();
741
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
742
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
743
+ mocks.makePieceValue.mockImplementationOnce(async (_, value) => value);
744
+ await pieceTest.removeFields(markdown, [field]);
745
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, {
746
+ ...markdown.frontmatter,
747
+ [field]: undefined,
748
+ });
749
+ });
750
+ test('removeField', async () => {
751
+ const markdown = makeMarkdownSample({ frontmatter: { title: 'title', subtitle: 'sub' } });
752
+ const field = 'subtitle';
753
+ const fields = [
754
+ { name: field, type: 'string', nullable: true },
755
+ ];
756
+ const PieceTest = makePieceMock();
757
+ const pieceTest = new PieceTest();
758
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
759
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
760
+ mocks.makePieceValue.mockImplementationOnce(async (_, value) => value);
761
+ await pieceTest.removeField(markdown, field);
762
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, {
763
+ ...markdown.frontmatter,
764
+ [field]: undefined,
765
+ });
766
+ });
767
+ test('removeField throws on bad field', async () => {
768
+ const markdown = makeMarkdownSample();
769
+ const field = 'title2';
770
+ const fields = [{ name: 'title', type: 'string' }];
771
+ const PieceTest = makePieceMock();
772
+ const pieceTest = new PieceTest();
773
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
774
+ const updating = pieceTest.removeField(markdown, field);
775
+ await expect(updating).rejects.toThrowError();
776
+ });
777
+ test('removeField throws on required field', async () => {
778
+ const markdown = makeMarkdownSample();
779
+ const field = 'title';
780
+ const fields = [{ name: field, type: 'string' }];
781
+ const PieceTest = makePieceMock();
782
+ const pieceTest = new PieceTest();
783
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
784
+ const updating = pieceTest.removeField(markdown, field);
785
+ await expect(updating).rejects.toThrowError();
786
+ });
787
+ test('removeField with desired value', async () => {
788
+ const value = 'sub';
789
+ const markdown = makeMarkdownSample({ frontmatter: { title: 'title', subtitle: value } });
790
+ const field = 'subtitle';
791
+ const fields = [
792
+ { name: field, type: 'string', nullable: true },
793
+ ];
794
+ const PieceTest = makePieceMock();
795
+ const pieceTest = new PieceTest();
796
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
797
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
798
+ mocks.makePieceValue.mockImplementationOnce(async (_, value) => value);
799
+ await pieceTest.removeField(markdown, field, value);
800
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, {
801
+ ...markdown.frontmatter,
802
+ [field]: undefined,
803
+ });
804
+ });
805
+ test('removeField with non-existant value', async () => {
806
+ const value = 'sub';
807
+ const markdown = makeMarkdownSample({ frontmatter: { title: 'title', subtitle: value } });
808
+ const field = 'subtitle';
809
+ const fields = [
810
+ { name: field, type: 'string', nullable: true },
811
+ ];
812
+ const PieceTest = makePieceMock();
813
+ const pieceTest = new PieceTest();
814
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
815
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
816
+ mocks.makePieceValue.mockImplementationOnce(async (_, value) => value);
817
+ const result = await pieceTest.removeField(markdown, field, 'random');
818
+ expect(result).toEqual(markdown);
819
+ });
820
+ test('removeField one value from array field', async () => {
821
+ const values = ['one', 'two', 'three'];
822
+ const markdown = makeMarkdownSample({ frontmatter: { title: 'title', subtitle: values } });
823
+ const field = 'subtitle';
824
+ const fields = [
825
+ { name: field, type: 'array', nullable: true, items: { type: 'string' } },
826
+ ];
827
+ const PieceTest = makePieceMock();
828
+ const pieceTest = new PieceTest();
829
+ spies.pieceFields = vi.spyOn(pieceTest, 'fields', 'get').mockReturnValueOnce(fields);
830
+ mocks.makePieceMarkdown.mockReturnValueOnce(markdown);
831
+ mocks.makePieceValue.mockImplementationOnce(async (_, value) => value);
832
+ await pieceTest.removeField(markdown, field, 'two');
833
+ expect(mocks.makePieceMarkdown).toHaveBeenCalledWith(markdown.filePath, markdown.piece, markdown.note, {
834
+ ...markdown.frontmatter,
835
+ [field]: ['one', 'three'],
836
+ });
837
+ });
838
+ });
839
+ //# sourceMappingURL=Piece.test.js.map