@noteplanco/noteplan-mcp 1.1.23 → 1.1.24
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.
- package/README.md +7 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/noteplan/attachments-paths.d.ts +13 -0
- package/dist/noteplan/attachments-paths.d.ts.map +1 -0
- package/dist/noteplan/attachments-paths.js +27 -0
- package/dist/noteplan/attachments-paths.js.map +1 -0
- package/dist/noteplan/embeddings.js +1 -1
- package/dist/noteplan/embeddings.js.map +1 -1
- package/dist/noteplan/file-reader.d.ts +37 -46
- package/dist/noteplan/file-reader.d.ts.map +1 -1
- package/dist/noteplan/file-reader.js +200 -202
- package/dist/noteplan/file-reader.js.map +1 -1
- package/dist/noteplan/file-reader.test.d.ts +2 -0
- package/dist/noteplan/file-reader.test.d.ts.map +1 -0
- package/dist/noteplan/file-reader.test.js +67 -0
- package/dist/noteplan/file-reader.test.js.map +1 -0
- package/dist/noteplan/file-writer.d.ts +35 -31
- package/dist/noteplan/file-writer.d.ts.map +1 -1
- package/dist/noteplan/file-writer.js +280 -164
- package/dist/noteplan/file-writer.js.map +1 -1
- package/dist/noteplan/file-writer.test.js +704 -191
- package/dist/noteplan/file-writer.test.js.map +1 -1
- package/dist/noteplan/filter-store.d.ts +5 -5
- package/dist/noteplan/filter-store.d.ts.map +1 -1
- package/dist/noteplan/filter-store.js +94 -79
- package/dist/noteplan/filter-store.js.map +1 -1
- package/dist/noteplan/ripgrep-search.d.ts +25 -2
- package/dist/noteplan/ripgrep-search.d.ts.map +1 -1
- package/dist/noteplan/ripgrep-search.js +75 -2
- package/dist/noteplan/ripgrep-search.js.map +1 -1
- package/dist/noteplan/space-row-utils.d.ts +20 -0
- package/dist/noteplan/space-row-utils.d.ts.map +1 -0
- package/dist/noteplan/space-row-utils.js +78 -0
- package/dist/noteplan/space-row-utils.js.map +1 -0
- package/dist/noteplan/space-row-utils.test.d.ts +2 -0
- package/dist/noteplan/space-row-utils.test.d.ts.map +1 -0
- package/dist/noteplan/space-row-utils.test.js +123 -0
- package/dist/noteplan/space-row-utils.test.js.map +1 -0
- package/dist/noteplan/sqlite-reader.d.ts +12 -27
- package/dist/noteplan/sqlite-reader.d.ts.map +1 -1
- package/dist/noteplan/sqlite-reader.js +315 -221
- package/dist/noteplan/sqlite-reader.js.map +1 -1
- package/dist/noteplan/sqlite-writer.d.ts +1 -1
- package/dist/noteplan/sqlite-writer.d.ts.map +1 -1
- package/dist/noteplan/sqlite-writer.js +2 -2
- package/dist/noteplan/sqlite-writer.js.map +1 -1
- package/dist/noteplan/unified-store.d.ts +41 -30
- package/dist/noteplan/unified-store.d.ts.map +1 -1
- package/dist/noteplan/unified-store.js +257 -159
- package/dist/noteplan/unified-store.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +142 -61
- package/dist/server.js.map +1 -1
- package/dist/tools/attachments.d.ts +9 -9
- package/dist/tools/attachments.d.ts.map +1 -1
- package/dist/tools/attachments.js +74 -83
- package/dist/tools/attachments.js.map +1 -1
- package/dist/tools/attachments.test.js +170 -129
- package/dist/tools/attachments.test.js.map +1 -1
- package/dist/tools/calendar.d.ts +16 -13
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +17 -16
- package/dist/tools/calendar.js.map +1 -1
- package/dist/tools/embeddings.d.ts +6 -6
- package/dist/tools/embeddings.d.ts.map +1 -1
- package/dist/tools/embeddings.js +6 -6
- package/dist/tools/embeddings.js.map +1 -1
- package/dist/tools/events.d.ts +7 -3
- package/dist/tools/events.d.ts.map +1 -1
- package/dist/tools/events.js +51 -16
- package/dist/tools/events.js.map +1 -1
- package/dist/tools/filters.d.ts +28 -33
- package/dist/tools/filters.d.ts.map +1 -1
- package/dist/tools/filters.js +42 -105
- package/dist/tools/filters.js.map +1 -1
- package/dist/tools/notes.d.ts +80 -218
- package/dist/tools/notes.d.ts.map +1 -1
- package/dist/tools/notes.js +180 -177
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/notes.test.js +242 -21
- package/dist/tools/notes.test.js.map +1 -1
- package/dist/tools/search.d.ts +4 -3
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +9 -5
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/search.test.d.ts +2 -0
- package/dist/tools/search.test.d.ts.map +1 -0
- package/dist/tools/search.test.js +37 -0
- package/dist/tools/search.test.js.map +1 -0
- package/dist/tools/spaces.d.ts +20 -20
- package/dist/tools/spaces.d.ts.map +1 -1
- package/dist/tools/spaces.js +28 -28
- package/dist/tools/spaces.js.map +1 -1
- package/dist/tools/tasks.d.ts +22 -22
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +22 -22
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/templates.d.ts +7 -7
- package/dist/tools/templates.d.ts.map +1 -1
- package/dist/tools/templates.js +4 -4
- package/dist/tools/templates.js.map +1 -1
- package/dist/tools/themes.js +1 -1
- package/dist/tools/themes.js.map +1 -1
- package/dist/transport/bridge-availability.d.ts +5 -0
- package/dist/transport/bridge-availability.d.ts.map +1 -0
- package/dist/transport/bridge-availability.js +92 -0
- package/dist/transport/bridge-availability.js.map +1 -0
- package/dist/transport/bridge-cascade.d.ts +18 -0
- package/dist/transport/bridge-cascade.d.ts.map +1 -0
- package/dist/transport/bridge-cascade.js +78 -0
- package/dist/transport/bridge-cascade.js.map +1 -0
- package/dist/transport/bridge-cascade.test.d.ts +2 -0
- package/dist/transport/bridge-cascade.test.d.ts.map +1 -0
- package/dist/transport/bridge-cascade.test.js +160 -0
- package/dist/transport/bridge-cascade.test.js.map +1 -0
- package/dist/transport/bridge-client.d.ts +197 -0
- package/dist/transport/bridge-client.d.ts.map +1 -0
- package/dist/transport/bridge-client.js +288 -0
- package/dist/transport/bridge-client.js.map +1 -0
- package/dist/transport/bridge-client.test.d.ts +2 -0
- package/dist/transport/bridge-client.test.d.ts.map +1 -0
- package/dist/transport/bridge-client.test.js +384 -0
- package/dist/transport/bridge-client.test.js.map +1 -0
- package/dist/transport/bridge-context.d.ts +10 -0
- package/dist/transport/bridge-context.d.ts.map +1 -0
- package/dist/transport/bridge-context.js +18 -0
- package/dist/transport/bridge-context.js.map +1 -0
- package/dist/transport/bridge-fs.d.ts +25 -0
- package/dist/transport/bridge-fs.d.ts.map +1 -0
- package/dist/transport/bridge-fs.js +129 -0
- package/dist/transport/bridge-fs.js.map +1 -0
- package/dist/utils/date-utils.d.ts +24 -0
- package/dist/utils/date-utils.d.ts.map +1 -1
- package/dist/utils/date-utils.js +55 -0
- package/dist/utils/date-utils.js.map +1 -1
- package/dist/utils/date-utils.test.d.ts +2 -0
- package/dist/utils/date-utils.test.d.ts.map +1 -0
- package/dist/utils/date-utils.test.js +109 -0
- package/dist/utils/date-utils.test.js.map +1 -0
- package/dist/utils/folder-access.d.ts +23 -0
- package/dist/utils/folder-access.d.ts.map +1 -0
- package/dist/utils/folder-access.js +131 -0
- package/dist/utils/folder-access.js.map +1 -0
- package/dist/utils/folder-access.test.d.ts +2 -0
- package/dist/utils/folder-access.test.d.ts.map +1 -0
- package/dist/utils/folder-access.test.js +182 -0
- package/dist/utils/folder-access.test.js.map +1 -0
- package/dist/utils/folder-matcher.d.ts.map +1 -1
- package/dist/utils/folder-matcher.js +16 -0
- package/dist/utils/folder-matcher.js.map +1 -1
- package/dist/utils/folder-matcher.test.js +42 -0
- package/dist/utils/folder-matcher.test.js.map +1 -1
- package/dist/utils/server-config.d.ts +10 -2
- package/dist/utils/server-config.d.ts.map +1 -1
- package/dist/utils/server-config.js +16 -2
- package/dist/utils/server-config.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +5 -1
- package/dist/utils/version.js.map +1 -1
- package/package.json +4 -3
- package/scripts/calendar-helper +0 -0
- package/scripts/reminders-helper +0 -0
|
@@ -1,17 +1,62 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
// ── Mocks ──
|
|
4
|
-
vi.mock('fs', () =>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
vi.mock('fs', () => {
|
|
5
|
+
const sync = {
|
|
6
|
+
existsSync: vi.fn(),
|
|
7
|
+
mkdirSync: vi.fn(),
|
|
8
|
+
writeFileSync: vi.fn(),
|
|
9
|
+
readFileSync: vi.fn(),
|
|
10
|
+
readdirSync: vi.fn(),
|
|
11
|
+
statSync: vi.fn(),
|
|
12
|
+
renameSync: vi.fn(),
|
|
13
|
+
copyFileSync: vi.fn(),
|
|
14
|
+
unlinkSync: vi.fn(),
|
|
15
|
+
rmdirSync: vi.fn(),
|
|
16
|
+
rmSync: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
// bridge-fs / attachments use fs.promises after the async cascade. Each
|
|
19
|
+
// promise variant forwards to the sync mock so existing assertions on
|
|
20
|
+
// fs.*Sync still pass.
|
|
21
|
+
const promises = {
|
|
22
|
+
mkdir: vi.fn(async (p, opts) => sync.mkdirSync(p, opts)),
|
|
23
|
+
writeFile: vi.fn(async (p, content, opts) => sync.writeFileSync(p, content, opts)),
|
|
24
|
+
readFile: vi.fn(async (p, opts) => {
|
|
25
|
+
if (!sync.existsSync(p)) {
|
|
26
|
+
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
|
27
|
+
}
|
|
28
|
+
return sync.readFileSync(p, opts);
|
|
29
|
+
}),
|
|
30
|
+
readdir: vi.fn(async (p, opts) => sync.readdirSync(p, opts)),
|
|
31
|
+
stat: vi.fn(async (p) => {
|
|
32
|
+
if (!sync.existsSync(p))
|
|
33
|
+
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
|
34
|
+
const explicit = sync.statSync(p) ?? {};
|
|
35
|
+
// bridge-fs.statPath calls stats.isDirectory(); ensure it exists even
|
|
36
|
+
// when a test only mocked size/mtime.
|
|
37
|
+
return {
|
|
38
|
+
isDirectory: typeof explicit.isDirectory === 'function' ? explicit.isDirectory : () => false,
|
|
39
|
+
isFile: typeof explicit.isFile === 'function' ? explicit.isFile : () => true,
|
|
40
|
+
size: explicit.size ?? 0,
|
|
41
|
+
mtime: explicit.mtime ?? new Date(0),
|
|
42
|
+
birthtime: explicit.birthtime ?? new Date(0),
|
|
43
|
+
};
|
|
44
|
+
}),
|
|
45
|
+
rename: vi.fn(async (a, b) => sync.renameSync(a, b)),
|
|
46
|
+
copyFile: vi.fn(async (a, b) => sync.copyFileSync(a, b)),
|
|
47
|
+
unlink: vi.fn(async (p) => sync.unlinkSync(p)),
|
|
48
|
+
rm: vi.fn(async (p) => {
|
|
49
|
+
if (sync.rmSync)
|
|
50
|
+
sync.rmSync(p, { recursive: true });
|
|
51
|
+
else
|
|
52
|
+
sync.unlinkSync(p);
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
return { ...sync, promises };
|
|
56
|
+
});
|
|
57
|
+
vi.mock('../transport/bridge-availability.js', () => ({
|
|
58
|
+
getBridgeClient: vi.fn(async () => null),
|
|
59
|
+
invalidateBridgeClient: vi.fn(),
|
|
15
60
|
}));
|
|
16
61
|
vi.mock('../noteplan/unified-store.js', () => ({
|
|
17
62
|
getNote: vi.fn(),
|
|
@@ -44,7 +89,7 @@ beforeEach(() => {
|
|
|
44
89
|
});
|
|
45
90
|
// ── Schema ──
|
|
46
91
|
describe('attachmentsSchema', () => {
|
|
47
|
-
it('accepts valid input with all fields', () => {
|
|
92
|
+
it('accepts valid input with all fields', async () => {
|
|
48
93
|
const result = attachmentsSchema.safeParse({
|
|
49
94
|
action: 'add',
|
|
50
95
|
id: '1',
|
|
@@ -53,7 +98,7 @@ describe('attachmentsSchema', () => {
|
|
|
53
98
|
});
|
|
54
99
|
expect(result.success).toBe(true);
|
|
55
100
|
});
|
|
56
|
-
it('rejects invalid action', () => {
|
|
101
|
+
it('rejects invalid action', async () => {
|
|
57
102
|
const result = attachmentsSchema.safeParse({ action: 'delete' });
|
|
58
103
|
expect(result.success).toBe(false);
|
|
59
104
|
});
|
|
@@ -68,49 +113,49 @@ describe('addAttachment', () => {
|
|
|
68
113
|
insertLink: false,
|
|
69
114
|
includeData: false,
|
|
70
115
|
};
|
|
71
|
-
it('returns error when data is missing', () => {
|
|
72
|
-
const result = addAttachment({ ...baseParams, data: undefined });
|
|
116
|
+
it('returns error when data is missing', async () => {
|
|
117
|
+
const result = await addAttachment({ ...baseParams, data: undefined });
|
|
73
118
|
expect(result.success).toBe(false);
|
|
74
119
|
expect(result.error).toMatch(/data.*required/i);
|
|
75
120
|
});
|
|
76
|
-
it('returns error when attachmentFilename is missing', () => {
|
|
77
|
-
const result = addAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
121
|
+
it('returns error when attachmentFilename is missing', async () => {
|
|
122
|
+
const result = await addAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
78
123
|
expect(result.success).toBe(false);
|
|
79
124
|
expect(result.error).toMatch(/attachmentFilename.*required/i);
|
|
80
125
|
});
|
|
81
|
-
it('returns error when note not found', () => {
|
|
82
|
-
vi.mocked(getNote).
|
|
83
|
-
const result = addAttachment(baseParams);
|
|
126
|
+
it('returns error when note not found', async () => {
|
|
127
|
+
vi.mocked(getNote).mockResolvedValue(null);
|
|
128
|
+
const result = await addAttachment(baseParams);
|
|
84
129
|
expect(result.success).toBe(false);
|
|
85
130
|
expect(result.error).toMatch(/not found/i);
|
|
86
131
|
});
|
|
87
|
-
it('returns error for space notes', () => {
|
|
88
|
-
vi.mocked(getNote).
|
|
89
|
-
const result = addAttachment(baseParams);
|
|
132
|
+
it('returns error for space notes', async () => {
|
|
133
|
+
vi.mocked(getNote).mockResolvedValue(mockNote({ source: 'space' }));
|
|
134
|
+
const result = await addAttachment(baseParams);
|
|
90
135
|
expect(result.success).toBe(false);
|
|
91
136
|
expect(result.error).toMatch(/space/i);
|
|
92
137
|
});
|
|
93
|
-
it('returns error when filename sanitizes to empty', () => {
|
|
94
|
-
vi.mocked(getNote).
|
|
95
|
-
const result = addAttachment({ ...baseParams, attachmentFilename: '()[]!' });
|
|
138
|
+
it('returns error when filename sanitizes to empty', async () => {
|
|
139
|
+
vi.mocked(getNote).mockResolvedValue(mockNote());
|
|
140
|
+
const result = await addAttachment({ ...baseParams, attachmentFilename: '()[]!' });
|
|
96
141
|
expect(result.success).toBe(false);
|
|
97
142
|
expect(result.error).toMatch(/invalid.*filename/i);
|
|
98
143
|
});
|
|
99
|
-
it('returns error for empty base64 (zero-length buffer)', () => {
|
|
100
|
-
vi.mocked(getNote).
|
|
144
|
+
it('returns error for empty base64 (zero-length buffer)', async () => {
|
|
145
|
+
vi.mocked(getNote).mockResolvedValue(mockNote());
|
|
101
146
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
102
147
|
// Use padding-only base64 that decodes to zero bytes
|
|
103
148
|
// Note: An empty string '' is falsy and caught by the !data check first,
|
|
104
149
|
// so we use a whitespace-only string that passes truthiness but decodes to empty
|
|
105
|
-
const result = addAttachment({ ...baseParams, data: ' ' });
|
|
150
|
+
const result = await addAttachment({ ...baseParams, data: ' ' });
|
|
106
151
|
expect(result.success).toBe(false);
|
|
107
152
|
expect(result.error).toMatch(/empty/i);
|
|
108
153
|
});
|
|
109
|
-
it('successfully writes attachment and returns correct markdownLink', () => {
|
|
154
|
+
it('successfully writes attachment and returns correct markdownLink', async () => {
|
|
110
155
|
const note = mockNote();
|
|
111
|
-
vi.mocked(getNote).
|
|
156
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
112
157
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
113
|
-
const result = addAttachment(baseParams);
|
|
158
|
+
const result = await addAttachment(baseParams);
|
|
114
159
|
expect(result.success).toBe(true);
|
|
115
160
|
expect(result).toHaveProperty('markdownLink');
|
|
116
161
|
expect(result.markdownLink).toBe('');
|
|
@@ -119,56 +164,52 @@ describe('addAttachment', () => {
|
|
|
119
164
|
expect(result.noteFilename).toBe('Notes/Test Note.md');
|
|
120
165
|
expect(fs.writeFileSync).toHaveBeenCalled();
|
|
121
166
|
});
|
|
122
|
-
it('
|
|
167
|
+
it('ensures the _attachments parent folder exists when writing', async () => {
|
|
123
168
|
const note = mockNote();
|
|
124
|
-
vi.mocked(getNote).
|
|
169
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
125
170
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
126
|
-
addAttachment(baseParams);
|
|
171
|
+
await addAttachment(baseParams);
|
|
172
|
+
// bridge-fs.writeFileBinary always mkdirs the parent (idempotent with
|
|
173
|
+
// `recursive: true`) before the write, so the explicit existsSync gate
|
|
174
|
+
// we used to have is no longer needed.
|
|
127
175
|
expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(NP_PATH, 'Notes', 'Test Note_attachments'), { recursive: true });
|
|
128
176
|
});
|
|
129
|
-
it('
|
|
177
|
+
it('generates correct image markdown link for png/jpg', async () => {
|
|
130
178
|
const note = mockNote();
|
|
131
|
-
vi.mocked(getNote).
|
|
132
|
-
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
133
|
-
addAttachment(baseParams);
|
|
134
|
-
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
it('generates correct image markdown link for png/jpg', () => {
|
|
137
|
-
const note = mockNote();
|
|
138
|
-
vi.mocked(getNote).mockReturnValue(note);
|
|
179
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
139
180
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
140
181
|
for (const ext of ['png', 'jpg', 'jpeg', 'gif', 'webp', 'heic']) {
|
|
141
|
-
const result = addAttachment({ ...baseParams, attachmentFilename: `photo.${ext}` });
|
|
182
|
+
const result = await addAttachment({ ...baseParams, attachmentFilename: `photo.${ext}` });
|
|
142
183
|
expect(result.markdownLink).toMatch(/^!\[image\]/);
|
|
143
184
|
expect(result.isImage).toBe(true);
|
|
144
185
|
}
|
|
145
186
|
});
|
|
146
|
-
it('generates correct file markdown link for pdf/txt', () => {
|
|
187
|
+
it('generates correct file markdown link for pdf/txt', async () => {
|
|
147
188
|
const note = mockNote();
|
|
148
|
-
vi.mocked(getNote).
|
|
189
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
149
190
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
150
191
|
for (const ext of ['pdf', 'txt', 'csv', 'mp3']) {
|
|
151
|
-
const result = addAttachment({ ...baseParams, attachmentFilename: `doc.${ext}` });
|
|
192
|
+
const result = await addAttachment({ ...baseParams, attachmentFilename: `doc.${ext}` });
|
|
152
193
|
expect(result.markdownLink).toMatch(/^!\[file\]/);
|
|
153
194
|
expect(result.isImage).toBe(false);
|
|
154
195
|
}
|
|
155
196
|
});
|
|
156
|
-
it('insertLink=false (default) does NOT modify note content', () => {
|
|
197
|
+
it('insertLink=false (default) does NOT modify note content', async () => {
|
|
157
198
|
const note = mockNote();
|
|
158
|
-
vi.mocked(getNote).
|
|
199
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
159
200
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
160
|
-
const result = addAttachment({ ...baseParams, insertLink: false });
|
|
201
|
+
const result = await addAttachment({ ...baseParams, insertLink: false });
|
|
161
202
|
expect(result.success).toBe(true);
|
|
162
203
|
expect(result.linkInserted).toBe(false);
|
|
163
204
|
// writeFileSync should be called once (for the attachment), not for the note
|
|
164
205
|
expect(vi.mocked(fs.writeFileSync).mock.calls.length).toBe(1);
|
|
165
206
|
});
|
|
166
|
-
it('insertLink=true appends link to note', () => {
|
|
207
|
+
it('insertLink=true appends link to note', async () => {
|
|
167
208
|
const note = mockNote({ content: '# Test\n' });
|
|
168
|
-
vi.mocked(getNote).
|
|
209
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
169
210
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
170
211
|
vi.mocked(fs.readFileSync).mockReturnValue('# Test\n');
|
|
171
|
-
const result = addAttachment({ ...baseParams, insertLink: true });
|
|
212
|
+
const result = await addAttachment({ ...baseParams, insertLink: true });
|
|
172
213
|
expect(result.success).toBe(true);
|
|
173
214
|
expect(result.linkInserted).toBe(true);
|
|
174
215
|
// writeFileSync should be called twice: once for attachment, once for note
|
|
@@ -177,11 +218,11 @@ describe('addAttachment', () => {
|
|
|
177
218
|
const writtenContent = noteWriteCall[1];
|
|
178
219
|
expect(writtenContent).toContain('');
|
|
179
220
|
});
|
|
180
|
-
it('cleans markdown-conflicting characters from filename', () => {
|
|
221
|
+
it('cleans markdown-conflicting characters from filename', async () => {
|
|
181
222
|
const note = mockNote();
|
|
182
|
-
vi.mocked(getNote).
|
|
223
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
183
224
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
184
|
-
const result = addAttachment({
|
|
225
|
+
const result = await addAttachment({
|
|
185
226
|
...baseParams,
|
|
186
227
|
attachmentFilename: 'photo (1) [copy]!.png',
|
|
187
228
|
});
|
|
@@ -190,11 +231,11 @@ describe('addAttachment', () => {
|
|
|
190
231
|
expect(result.markdownLink).toBe('');
|
|
191
232
|
expect(result.attachmentPath).toBe('Test Note_attachments/photo 1 copy.png');
|
|
192
233
|
});
|
|
193
|
-
it('percent-encodes special characters in the markdown link path', () => {
|
|
234
|
+
it('percent-encodes special characters in the markdown link path', async () => {
|
|
194
235
|
const note = mockNote({ filename: 'Notes/My (Special) Note.md' });
|
|
195
|
-
vi.mocked(getNote).
|
|
236
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
196
237
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
197
|
-
const result = addAttachment({ ...baseParams, attachmentFilename: 'file name.png' });
|
|
238
|
+
const result = await addAttachment({ ...baseParams, attachmentFilename: 'file name.png' });
|
|
198
239
|
expect(result.success).toBe(true);
|
|
199
240
|
// Note name has special chars that get encoded in the path
|
|
200
241
|
// "My (Special) Note_attachments/file name.png"
|
|
@@ -210,34 +251,34 @@ describe('listAttachments', () => {
|
|
|
210
251
|
insertLink: false,
|
|
211
252
|
includeData: false,
|
|
212
253
|
};
|
|
213
|
-
it('returns error when note not found', () => {
|
|
214
|
-
vi.mocked(getNote).
|
|
215
|
-
const result = listAttachments(baseParams);
|
|
254
|
+
it('returns error when note not found', async () => {
|
|
255
|
+
vi.mocked(getNote).mockResolvedValue(null);
|
|
256
|
+
const result = await listAttachments(baseParams);
|
|
216
257
|
expect(result.success).toBe(false);
|
|
217
258
|
expect(result.error).toMatch(/not found/i);
|
|
218
259
|
});
|
|
219
|
-
it('returns empty list when _attachments folder does not exist', () => {
|
|
220
|
-
vi.mocked(getNote).
|
|
260
|
+
it('returns empty list when _attachments folder does not exist', async () => {
|
|
261
|
+
vi.mocked(getNote).mockResolvedValue(mockNote());
|
|
221
262
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
222
|
-
const result = listAttachments(baseParams);
|
|
263
|
+
const result = await listAttachments(baseParams);
|
|
223
264
|
expect(result.success).toBe(true);
|
|
224
265
|
expect(result.count).toBe(0);
|
|
225
266
|
expect(result.attachments).toEqual([]);
|
|
226
267
|
});
|
|
227
|
-
it('lists attachments with correct metadata', () => {
|
|
268
|
+
it('lists attachments with correct metadata', async () => {
|
|
228
269
|
const note = mockNote();
|
|
229
|
-
vi.mocked(getNote).
|
|
270
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
230
271
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
231
272
|
const mockDate = new Date('2024-01-15T10:30:00Z');
|
|
232
273
|
vi.mocked(fs.readdirSync).mockReturnValue([
|
|
233
|
-
{ name: 'photo.png', isFile: () => true },
|
|
234
|
-
{ name: 'doc.pdf', isFile: () => true },
|
|
274
|
+
{ name: 'photo.png', isFile: () => true, isDirectory: () => false },
|
|
275
|
+
{ name: 'doc.pdf', isFile: () => true, isDirectory: () => false },
|
|
235
276
|
]);
|
|
236
277
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
237
278
|
size: 1024,
|
|
238
279
|
mtime: mockDate,
|
|
239
280
|
});
|
|
240
|
-
const result = listAttachments(baseParams);
|
|
281
|
+
const result = await listAttachments(baseParams);
|
|
241
282
|
expect(result.success).toBe(true);
|
|
242
283
|
expect(result.count).toBe(2);
|
|
243
284
|
expect(result.noteFilename).toBe('Notes/Test Note.md');
|
|
@@ -253,37 +294,37 @@ describe('listAttachments', () => {
|
|
|
253
294
|
expect(attachments[1].isImage).toBe(true);
|
|
254
295
|
expect(attachments[1].markdownLink).toBe('');
|
|
255
296
|
});
|
|
256
|
-
it('skips hidden files (starting with .)', () => {
|
|
297
|
+
it('skips hidden files (starting with .)', async () => {
|
|
257
298
|
const note = mockNote();
|
|
258
|
-
vi.mocked(getNote).
|
|
299
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
259
300
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
260
301
|
vi.mocked(fs.readdirSync).mockReturnValue([
|
|
261
|
-
{ name: '.DS_Store', isFile: () => true },
|
|
262
|
-
{ name: '.hidden', isFile: () => true },
|
|
263
|
-
{ name: 'visible.png', isFile: () => true },
|
|
302
|
+
{ name: '.DS_Store', isFile: () => true, isDirectory: () => false },
|
|
303
|
+
{ name: '.hidden', isFile: () => true, isDirectory: () => false },
|
|
304
|
+
{ name: 'visible.png', isFile: () => true, isDirectory: () => false },
|
|
264
305
|
]);
|
|
265
306
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
266
307
|
size: 100,
|
|
267
308
|
mtime: new Date(),
|
|
268
309
|
});
|
|
269
|
-
const result = listAttachments(baseParams);
|
|
310
|
+
const result = await listAttachments(baseParams);
|
|
270
311
|
expect(result.success).toBe(true);
|
|
271
312
|
expect(result.count).toBe(1);
|
|
272
313
|
expect(result.attachments[0].filename).toBe('visible.png');
|
|
273
314
|
});
|
|
274
|
-
it('skips directories', () => {
|
|
315
|
+
it('skips directories', async () => {
|
|
275
316
|
const note = mockNote();
|
|
276
|
-
vi.mocked(getNote).
|
|
317
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
277
318
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
278
319
|
vi.mocked(fs.readdirSync).mockReturnValue([
|
|
279
|
-
{ name: 'subfolder', isFile: () => false },
|
|
280
|
-
{ name: 'photo.png', isFile: () => true },
|
|
320
|
+
{ name: 'subfolder', isFile: () => false, isDirectory: () => true },
|
|
321
|
+
{ name: 'photo.png', isFile: () => true, isDirectory: () => false },
|
|
281
322
|
]);
|
|
282
323
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
283
324
|
size: 100,
|
|
284
325
|
mtime: new Date(),
|
|
285
326
|
});
|
|
286
|
-
const result = listAttachments(baseParams);
|
|
327
|
+
const result = await listAttachments(baseParams);
|
|
287
328
|
expect(result.success).toBe(true);
|
|
288
329
|
expect(result.count).toBe(1);
|
|
289
330
|
});
|
|
@@ -297,33 +338,33 @@ describe('getAttachment', () => {
|
|
|
297
338
|
insertLink: false,
|
|
298
339
|
includeData: false,
|
|
299
340
|
};
|
|
300
|
-
it('returns error when attachmentFilename is missing', () => {
|
|
301
|
-
const result = getAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
341
|
+
it('returns error when attachmentFilename is missing', async () => {
|
|
342
|
+
const result = await getAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
302
343
|
expect(result.success).toBe(false);
|
|
303
344
|
expect(result.error).toMatch(/attachmentFilename.*required/i);
|
|
304
345
|
});
|
|
305
|
-
it('returns error when note not found', () => {
|
|
306
|
-
vi.mocked(getNote).
|
|
307
|
-
const result = getAttachment(baseParams);
|
|
346
|
+
it('returns error when note not found', async () => {
|
|
347
|
+
vi.mocked(getNote).mockResolvedValue(null);
|
|
348
|
+
const result = await getAttachment(baseParams);
|
|
308
349
|
expect(result.success).toBe(false);
|
|
309
350
|
expect(result.error).toMatch(/not found/i);
|
|
310
351
|
});
|
|
311
|
-
it('returns error when attachment file does not exist', () => {
|
|
312
|
-
vi.mocked(getNote).
|
|
352
|
+
it('returns error when attachment file does not exist', async () => {
|
|
353
|
+
vi.mocked(getNote).mockResolvedValue(mockNote());
|
|
313
354
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
314
|
-
const result = getAttachment(baseParams);
|
|
355
|
+
const result = await getAttachment(baseParams);
|
|
315
356
|
expect(result.success).toBe(false);
|
|
316
357
|
expect(result.error).toMatch(/not found/i);
|
|
317
358
|
});
|
|
318
|
-
it('returns metadata without data when includeData is false', () => {
|
|
359
|
+
it('returns metadata without data when includeData is false', async () => {
|
|
319
360
|
const note = mockNote();
|
|
320
|
-
vi.mocked(getNote).
|
|
361
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
321
362
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
322
363
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
323
364
|
size: 2048,
|
|
324
365
|
mtime: new Date('2024-06-01T12:00:00Z'),
|
|
325
366
|
});
|
|
326
|
-
const result = getAttachment({ ...baseParams, includeData: false });
|
|
367
|
+
const result = await getAttachment({ ...baseParams, includeData: false });
|
|
327
368
|
expect(result.success).toBe(true);
|
|
328
369
|
expect(result.filename).toBe('photo.png');
|
|
329
370
|
expect(result.isImage).toBe(true);
|
|
@@ -332,9 +373,9 @@ describe('getAttachment', () => {
|
|
|
332
373
|
expect(result.markdownLink).toBe('');
|
|
333
374
|
expect(result).not.toHaveProperty('data');
|
|
334
375
|
});
|
|
335
|
-
it('returns base64 data when includeData is true', () => {
|
|
376
|
+
it('returns base64 data when includeData is true', async () => {
|
|
336
377
|
const note = mockNote();
|
|
337
|
-
vi.mocked(getNote).
|
|
378
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
338
379
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
339
380
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
340
381
|
size: 100,
|
|
@@ -342,13 +383,13 @@ describe('getAttachment', () => {
|
|
|
342
383
|
});
|
|
343
384
|
const fileBuffer = Buffer.from('file content');
|
|
344
385
|
vi.mocked(fs.readFileSync).mockReturnValue(fileBuffer);
|
|
345
|
-
const result = getAttachment({ ...baseParams, includeData: true });
|
|
386
|
+
const result = await getAttachment({ ...baseParams, includeData: true });
|
|
346
387
|
expect(result.success).toBe(true);
|
|
347
388
|
expect(result.data).toBe(fileBuffer.toString('base64'));
|
|
348
389
|
});
|
|
349
|
-
it('respects maxDataSize - returns dataTruncated=true for large images', () => {
|
|
390
|
+
it('respects maxDataSize - returns dataTruncated=true for large images', async () => {
|
|
350
391
|
const note = mockNote();
|
|
351
|
-
vi.mocked(getNote).
|
|
392
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
352
393
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
353
394
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
354
395
|
size: 5000,
|
|
@@ -357,7 +398,7 @@ describe('getAttachment', () => {
|
|
|
357
398
|
// Create a buffer larger than maxDataSize
|
|
358
399
|
const largeBuffer = Buffer.alloc(5000, 'x');
|
|
359
400
|
vi.mocked(fs.readFileSync).mockReturnValue(largeBuffer);
|
|
360
|
-
const result = getAttachment({
|
|
401
|
+
const result = await getAttachment({
|
|
361
402
|
...baseParams,
|
|
362
403
|
includeData: true,
|
|
363
404
|
maxDataSize: 1000,
|
|
@@ -368,9 +409,9 @@ describe('getAttachment', () => {
|
|
|
368
409
|
expect(result.originalSize).toBe(5000);
|
|
369
410
|
expect(result.hint).toBeDefined();
|
|
370
411
|
});
|
|
371
|
-
it('does NOT truncate non-image files even if over maxDataSize', () => {
|
|
412
|
+
it('does NOT truncate non-image files even if over maxDataSize', async () => {
|
|
372
413
|
const note = mockNote();
|
|
373
|
-
vi.mocked(getNote).
|
|
414
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
374
415
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
375
416
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
376
417
|
size: 5000,
|
|
@@ -378,7 +419,7 @@ describe('getAttachment', () => {
|
|
|
378
419
|
});
|
|
379
420
|
const largeBuffer = Buffer.alloc(5000, 'x');
|
|
380
421
|
vi.mocked(fs.readFileSync).mockReturnValue(largeBuffer);
|
|
381
|
-
const result = getAttachment({
|
|
422
|
+
const result = await getAttachment({
|
|
382
423
|
...baseParams,
|
|
383
424
|
attachmentFilename: 'doc.pdf',
|
|
384
425
|
includeData: true,
|
|
@@ -388,9 +429,9 @@ describe('getAttachment', () => {
|
|
|
388
429
|
expect(result.data).toBe(largeBuffer.toString('base64'));
|
|
389
430
|
expect(result.dataTruncated).toBeUndefined();
|
|
390
431
|
});
|
|
391
|
-
it('returns correct MIME type mapping', () => {
|
|
432
|
+
it('returns correct MIME type mapping', async () => {
|
|
392
433
|
const note = mockNote();
|
|
393
|
-
vi.mocked(getNote).
|
|
434
|
+
vi.mocked(getNote).mockResolvedValue(note);
|
|
394
435
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
395
436
|
vi.mocked(fs.statSync).mockReturnValue({
|
|
396
437
|
size: 100,
|
|
@@ -412,7 +453,7 @@ describe('getAttachment', () => {
|
|
|
412
453
|
['unknown.xyz', 'application/octet-stream'],
|
|
413
454
|
];
|
|
414
455
|
for (const [filename, expectedMime] of mimeTests) {
|
|
415
|
-
const result = getAttachment({
|
|
456
|
+
const result = await getAttachment({
|
|
416
457
|
...baseParams,
|
|
417
458
|
attachmentFilename: filename,
|
|
418
459
|
includeData: false,
|
|
@@ -443,57 +484,57 @@ describe('moveAttachment', () => {
|
|
|
443
484
|
insertLink: false,
|
|
444
485
|
includeData: false,
|
|
445
486
|
};
|
|
446
|
-
it('returns error when attachmentFilename is missing', () => {
|
|
447
|
-
const result = moveAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
487
|
+
it('returns error when attachmentFilename is missing', async () => {
|
|
488
|
+
const result = await moveAttachment({ ...baseParams, attachmentFilename: undefined });
|
|
448
489
|
expect(result.success).toBe(false);
|
|
449
490
|
expect(result.error).toMatch(/attachmentFilename.*required/i);
|
|
450
491
|
});
|
|
451
|
-
it('returns error when source note not found', () => {
|
|
452
|
-
vi.mocked(getNote).
|
|
453
|
-
const result = moveAttachment(baseParams);
|
|
492
|
+
it('returns error when source note not found', async () => {
|
|
493
|
+
vi.mocked(getNote).mockResolvedValue(null);
|
|
494
|
+
const result = await moveAttachment(baseParams);
|
|
454
495
|
expect(result.success).toBe(false);
|
|
455
496
|
expect(result.error).toMatch(/not found/i);
|
|
456
497
|
});
|
|
457
|
-
it('returns error when destination note not found', () => {
|
|
498
|
+
it('returns error when destination note not found', async () => {
|
|
458
499
|
// First call for source returns note, second call for destination returns null
|
|
459
500
|
vi.mocked(getNote)
|
|
460
|
-
.
|
|
461
|
-
.
|
|
462
|
-
const result = moveAttachment(baseParams);
|
|
501
|
+
.mockResolvedValueOnce(sourceNote)
|
|
502
|
+
.mockResolvedValueOnce(null);
|
|
503
|
+
const result = await moveAttachment(baseParams);
|
|
463
504
|
expect(result.success).toBe(false);
|
|
464
505
|
expect(result.error).toMatch(/not found/i);
|
|
465
506
|
});
|
|
466
|
-
it('returns error when attachment file does not exist at source', () => {
|
|
507
|
+
it('returns error when attachment file does not exist at source', async () => {
|
|
467
508
|
vi.mocked(getNote)
|
|
468
509
|
.mockReturnValueOnce(sourceNote)
|
|
469
510
|
.mockReturnValueOnce(destNote);
|
|
470
511
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
471
|
-
const result = moveAttachment(baseParams);
|
|
512
|
+
const result = await moveAttachment(baseParams);
|
|
472
513
|
expect(result.success).toBe(false);
|
|
473
514
|
expect(result.error).toMatch(/not found.*source/i);
|
|
474
515
|
});
|
|
475
|
-
it('successfully moves file and returns new markdownLink', () => {
|
|
516
|
+
it('successfully moves file and returns new markdownLink', async () => {
|
|
476
517
|
vi.mocked(getNote)
|
|
477
518
|
.mockReturnValueOnce(sourceNote)
|
|
478
519
|
.mockReturnValueOnce(destNote);
|
|
479
520
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
480
521
|
vi.mocked(fs.readFileSync).mockReturnValue('# Source\n\n');
|
|
481
522
|
vi.mocked(fs.readdirSync).mockReturnValue([]);
|
|
482
|
-
const result = moveAttachment(baseParams);
|
|
523
|
+
const result = await moveAttachment(baseParams);
|
|
483
524
|
expect(result.success).toBe(true);
|
|
484
525
|
expect(result.markdownLink).toBe('');
|
|
485
526
|
expect(result).toHaveProperty('movedFrom', 'Source Note_attachments/photo.png');
|
|
486
527
|
expect(result).toHaveProperty('movedTo', 'Dest Note_attachments/photo.png');
|
|
487
528
|
expect(fs.renameSync).toHaveBeenCalled();
|
|
488
529
|
});
|
|
489
|
-
it('removes old markdown link from source note content', () => {
|
|
530
|
+
it('removes old markdown link from source note content', async () => {
|
|
490
531
|
vi.mocked(getNote)
|
|
491
532
|
.mockReturnValueOnce(sourceNote)
|
|
492
533
|
.mockReturnValueOnce(destNote);
|
|
493
534
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
494
535
|
vi.mocked(fs.readFileSync).mockReturnValue('# Source\n\nMore text\n');
|
|
495
536
|
vi.mocked(fs.readdirSync).mockReturnValue([]);
|
|
496
|
-
const result = moveAttachment(baseParams);
|
|
537
|
+
const result = await moveAttachment(baseParams);
|
|
497
538
|
expect(result.success).toBe(true);
|
|
498
539
|
expect(result.oldLinkRemoved).toBe(true);
|
|
499
540
|
// Check that writeFileSync was called for the source note with link removed
|
|
@@ -503,7 +544,7 @@ describe('moveAttachment', () => {
|
|
|
503
544
|
expect(writtenContent).not.toContain('');
|
|
504
545
|
expect(writtenContent).toContain('More text');
|
|
505
546
|
});
|
|
506
|
-
it('creates destination folder if needed', () => {
|
|
547
|
+
it('creates destination folder if needed', async () => {
|
|
507
548
|
vi.mocked(getNote)
|
|
508
549
|
.mockReturnValueOnce(sourceNote)
|
|
509
550
|
.mockReturnValueOnce(destNote);
|
|
@@ -516,10 +557,10 @@ describe('moveAttachment', () => {
|
|
|
516
557
|
});
|
|
517
558
|
vi.mocked(fs.readFileSync).mockReturnValue('# Source\n');
|
|
518
559
|
vi.mocked(fs.readdirSync).mockReturnValue([]);
|
|
519
|
-
moveAttachment(baseParams);
|
|
560
|
+
await moveAttachment(baseParams);
|
|
520
561
|
expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(NP_PATH, 'Notes', 'Dest Note_attachments'), { recursive: true });
|
|
521
562
|
});
|
|
522
|
-
it('cleans up empty source folder after move', () => {
|
|
563
|
+
it('cleans up empty source folder after move', async () => {
|
|
523
564
|
vi.mocked(getNote)
|
|
524
565
|
.mockReturnValueOnce(sourceNote)
|
|
525
566
|
.mockReturnValueOnce(destNote);
|
|
@@ -527,10 +568,10 @@ describe('moveAttachment', () => {
|
|
|
527
568
|
vi.mocked(fs.readFileSync).mockReturnValue('# Source\n');
|
|
528
569
|
// Empty remaining files after move
|
|
529
570
|
vi.mocked(fs.readdirSync).mockReturnValue([]);
|
|
530
|
-
moveAttachment(baseParams);
|
|
531
|
-
expect(fs.
|
|
571
|
+
await moveAttachment(baseParams);
|
|
572
|
+
expect(fs.rmSync).toHaveBeenCalledWith(path.join(NP_PATH, 'Notes', 'Source Note_attachments'), { recursive: true });
|
|
532
573
|
});
|
|
533
|
-
it('does NOT remove source folder when other files remain', () => {
|
|
574
|
+
it('does NOT remove source folder when other files remain', async () => {
|
|
534
575
|
vi.mocked(getNote)
|
|
535
576
|
.mockReturnValueOnce(sourceNote)
|
|
536
577
|
.mockReturnValueOnce(destNote);
|
|
@@ -538,10 +579,10 @@ describe('moveAttachment', () => {
|
|
|
538
579
|
vi.mocked(fs.readFileSync).mockReturnValue('# Source\n');
|
|
539
580
|
// Other files remain
|
|
540
581
|
vi.mocked(fs.readdirSync).mockReturnValue(['other.png']);
|
|
541
|
-
moveAttachment(baseParams);
|
|
582
|
+
await moveAttachment(baseParams);
|
|
542
583
|
expect(fs.rmdirSync).not.toHaveBeenCalled();
|
|
543
584
|
});
|
|
544
|
-
it('falls back to copy+unlink when renameSync throws EXDEV', () => {
|
|
585
|
+
it('falls back to copy+unlink when renameSync throws EXDEV', async () => {
|
|
545
586
|
vi.mocked(getNote)
|
|
546
587
|
.mockReturnValueOnce(sourceNote)
|
|
547
588
|
.mockReturnValueOnce(destNote);
|
|
@@ -552,7 +593,7 @@ describe('moveAttachment', () => {
|
|
|
552
593
|
vi.mocked(fs.renameSync).mockImplementation(() => {
|
|
553
594
|
throw exdevError;
|
|
554
595
|
});
|
|
555
|
-
const result = moveAttachment(baseParams);
|
|
596
|
+
const result = await moveAttachment(baseParams);
|
|
556
597
|
expect(result.success).toBe(true);
|
|
557
598
|
expect(fs.copyFileSync).toHaveBeenCalled();
|
|
558
599
|
expect(fs.unlinkSync).toHaveBeenCalled();
|