@jordancoin/notioncli 1.2.1 โ†’ 1.3.0

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.
@@ -1,309 +0,0 @@
1
- #!/usr/bin/env node
2
- // Live integration test: Relations, Rollups & Blocks CRUD
3
- // Creates temp databases, links them, tests CLI output, cleans up.
4
-
5
- const { Client } = require('@notionhq/client');
6
- const { execSync } = require('child_process');
7
- const path = require('path');
8
-
9
- const CLI = path.join(__dirname, '..', 'bin', 'notion.js');
10
- const run = (cmd) => execSync(`node ${CLI} ${cmd}`, { encoding: 'utf-8' }).trim();
11
- const runJson = (cmd) => JSON.parse(execSync(`node ${CLI} --json ${cmd}`, { encoding: 'utf-8' }));
12
-
13
- const notion = new Client({ auth: process.env.NOTION_API_KEY || require('../lib/helpers').loadConfig(
14
- require('../lib/helpers').getConfigPaths().CONFIG_PATH
15
- ).apiKey });
16
-
17
- let parentPageId = null;
18
- let projectsDbId = null;
19
- let projectsDsId = null;
20
- let tasksDbId = null;
21
- let tasksDsId = null;
22
- let testPageIds = [];
23
- let createdDbIds = [];
24
-
25
- async function setup() {
26
- console.log('\n๐Ÿ”ง Setting up test databases...\n');
27
-
28
- // Create a dedicated test page as parent (needs to be under a page the integration can access)
29
- // First, find an existing page the integration has access to
30
- const search = await notion.search({ filter: { value: 'page', property: 'object' }, page_size: 20 });
31
-
32
- // Look for a top-level page (parent is workspace), or any page that's not inside a database
33
- let rootPageId = null;
34
- for (const p of search.results) {
35
- if (p.parent?.type === 'workspace' || p.parent?.type === 'page_id') {
36
- rootPageId = p.id;
37
- break;
38
- }
39
- }
40
-
41
- if (!rootPageId) {
42
- // No standalone page found โ€” create the DBs using the workspace parent directly
43
- // by creating a page in the integration's space
44
- console.log('No standalone page found. Will use first available page.');
45
- rootPageId = search.results[0]?.id;
46
- if (!rootPageId) throw new Error('No pages accessible by integration');
47
- }
48
-
49
- // Create a test container page
50
- const containerPage = await notion.pages.create({
51
- parent: { type: 'page_id', page_id: rootPageId },
52
- properties: {
53
- title: { title: [{ text: { content: 'CLI Test Container (auto-delete)' } }] },
54
- },
55
- });
56
- parentPageId = containerPage.id;
57
- testPageIds.push(parentPageId);
58
- console.log(`๐Ÿ“„ Created test container page: ${parentPageId.slice(0, 8)}โ€ฆ`);
59
-
60
- // 1. Create "CLI Test Projects" database
61
- console.log('\nCreating Projects DB...');
62
- const projectsDb = await notion.databases.create({
63
- parent: { type: 'page_id', page_id: parentPageId },
64
- title: [{ text: { content: 'CLI Test Projects' } }],
65
- properties: {
66
- 'Name': { title: {} },
67
- 'Status': { select: { options: [
68
- { name: 'Active', color: 'green' },
69
- { name: 'Done', color: 'gray' },
70
- ]}},
71
- 'Priority': { number: {} },
72
- },
73
- });
74
- // databases.create() returns: .id = data_source_id, .database_id = database_id for page creation
75
- projectsDsId = projectsDb.id;
76
- projectsDbId = projectsDb.database_id || projectsDb.id;
77
- createdDbIds.push(projectsDb.id);
78
- console.log(`โœ… Projects DB (db: ${projectsDbId.slice(0, 8)}โ€ฆ, ds: ${projectsDsId.slice(0, 8)}โ€ฆ)`);
79
-
80
- // 2. Create "CLI Test Tasks" database with relation to Projects
81
- console.log('Creating Tasks DB with relation...');
82
- const tasksDb = await notion.databases.create({
83
- parent: { type: 'page_id', page_id: parentPageId },
84
- title: [{ text: { content: 'CLI Test Tasks' } }],
85
- properties: {
86
- 'Name': { title: {} },
87
- 'Done': { checkbox: {} },
88
- 'Project': { relation: { database_id: projectsDbId } },
89
- },
90
- });
91
- tasksDsId = tasksDb.id;
92
- tasksDbId = tasksDb.database_id || tasksDb.id;
93
- createdDbIds.push(tasksDb.id);
94
- console.log(`โœ… Tasks DB (db: ${tasksDbId.slice(0, 8)}โ€ฆ, ds: ${tasksDsId.slice(0, 8)}โ€ฆ)`);
95
-
96
- // 3. Add project pages
97
- console.log('\nAdding test data...');
98
- const proj1 = await notion.pages.create({
99
- parent: { type: 'database_id', database_id: projectsDbId },
100
- properties: {
101
- 'Name': { title: [{ text: { content: 'Build CLI' } }] },
102
- 'Status': { select: { name: 'Active' } },
103
- 'Priority': { number: 1 },
104
- },
105
- });
106
- testPageIds.push(proj1.id);
107
- console.log(` ๐Ÿ“Œ Project: "Build CLI"`);
108
-
109
- const proj2 = await notion.pages.create({
110
- parent: { type: 'database_id', database_id: projectsDbId },
111
- properties: {
112
- 'Name': { title: [{ text: { content: 'Write Docs' } }] },
113
- 'Status': { select: { name: 'Done' } },
114
- 'Priority': { number: 2 },
115
- },
116
- });
117
- testPageIds.push(proj2.id);
118
- console.log(` ๐Ÿ“Œ Project: "Write Docs"`);
119
-
120
- // 4. Add task pages linked to projects
121
- const task1 = await notion.pages.create({
122
- parent: { type: 'database_id', database_id: tasksDbId },
123
- properties: {
124
- 'Name': { title: [{ text: { content: 'Implement relations' } }] },
125
- 'Done': { checkbox: true },
126
- 'Project': { relation: [{ id: proj1.id }] },
127
- },
128
- });
129
- testPageIds.push(task1.id);
130
- console.log(` ๐Ÿ“‹ Task: "Implement relations" โ†’ Build CLI`);
131
-
132
- const task2 = await notion.pages.create({
133
- parent: { type: 'database_id', database_id: tasksDbId },
134
- properties: {
135
- 'Name': { title: [{ text: { content: 'Add tests' } }] },
136
- 'Done': { checkbox: false },
137
- 'Project': { relation: [{ id: proj1.id }] },
138
- },
139
- });
140
- testPageIds.push(task2.id);
141
- console.log(` ๐Ÿ“‹ Task: "Add tests" โ†’ Build CLI`);
142
-
143
- const task3 = await notion.pages.create({
144
- parent: { type: 'database_id', database_id: tasksDbId },
145
- properties: {
146
- 'Name': { title: [{ text: { content: 'Write README' } }] },
147
- 'Done': { checkbox: true },
148
- 'Project': { relation: [{ id: proj2.id }] },
149
- },
150
- });
151
- testPageIds.push(task3.id);
152
- console.log(` ๐Ÿ“‹ Task: "Write README" โ†’ Write Docs`);
153
-
154
- // 5. Register CLI aliases
155
- run(`alias add test-projects ${projectsDsId}`);
156
- run(`alias add test-tasks ${tasksDsId}`);
157
- console.log(`\nโœ… Aliases registered: test-projects, test-tasks\n`);
158
- }
159
-
160
- async function runTests() {
161
- let passed = 0;
162
- let failed = 0;
163
-
164
- function check(name, condition, detail) {
165
- if (condition) {
166
- console.log(` โœ… ${name}`);
167
- passed++;
168
- } else {
169
- console.log(` โŒ ${name}${detail ? ' โ€” ' + detail : ''}`);
170
- failed++;
171
- }
172
- }
173
-
174
- console.log('๐Ÿงช Running live tests...\n');
175
-
176
- // --- Test 1: Query tasks โ€” relation column formatting ---
177
- console.log('--- 1. query (relation display) ---');
178
- try {
179
- const out = run('query test-tasks');
180
- check('query shows tasks', out.includes('Implement relations'));
181
- check('relation shows โ†’ format', out.includes('โ†’'));
182
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
183
- } catch (e) { check('query test-tasks', false, e.message); }
184
-
185
- // --- Test 2: Get task โ€” resolve relation to project title ---
186
- console.log('--- 2. get (relation resolution) ---');
187
- try {
188
- const out = run('get test-tasks --filter "Name=Implement relations"');
189
- check('get resolves relation to title', out.includes('Build CLI'));
190
- check('get shows URL', out.includes('notion.so'));
191
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
192
- } catch (e) { check('get with relation', false, e.message); }
193
-
194
- // --- Test 3: Relations command โ€” graph explorer ---
195
- console.log('--- 3. relations (graph explorer) ---');
196
- try {
197
- const out = run('relations test-tasks --filter "Name=Implement relations"');
198
- check('relations shows linked pages', out.includes('linked') || out.includes('Build CLI'));
199
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
200
- } catch (e) { check('relations command', false, e.message); }
201
-
202
- // --- Test 4: Reverse relation (project โ†’ tasks) ---
203
- console.log('--- 4. reverse relation ---');
204
- try {
205
- const out = run('relations test-projects --filter "Name=Build CLI"');
206
- const hasLinks = out.includes('Implement') || out.includes('Add tests') || out.includes('linked');
207
- check('project shows reverse relations', hasLinks);
208
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
209
- } catch (e) { check('reverse relation', false, e.message); }
210
-
211
- // --- Test 5: Blocks --ids ---
212
- console.log('--- 5. blocks --ids ---');
213
- try {
214
- run('append test-tasks "Test block for live test" --filter "Name=Implement relations"');
215
- const out = run('blocks test-tasks --filter "Name=Implement relations" --ids');
216
- check('blocks --ids shows ID prefix', out.includes('['));
217
- check('blocks shows appended text', out.includes('Test block'));
218
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
219
- } catch (e) { check('blocks --ids', false, e.message); }
220
-
221
- // --- Test 6: Block edit ---
222
- console.log('--- 6. block-edit ---');
223
- try {
224
- const json = runJson('blocks test-tasks --filter "Name=Implement relations"');
225
- const blockId = json.results[json.results.length - 1].id;
226
- const out = run(`block-edit ${blockId} "EDITED by CLI test"`);
227
- check('block-edit succeeds', out.includes('โœ…'));
228
- const verify = run('blocks test-tasks --filter "Name=Implement relations"');
229
- check('edited content visible', verify.includes('EDITED'));
230
- console.log(`\n${verify.split('\n').map(l => ' ' + l).join('\n')}\n`);
231
- } catch (e) { check('block-edit', false, e.message); }
232
-
233
- // --- Test 7: Block delete ---
234
- console.log('--- 7. block-delete ---');
235
- try {
236
- const json = runJson('blocks test-tasks --filter "Name=Implement relations"');
237
- const blockId = json.results[json.results.length - 1].id;
238
- const out = run(`block-delete ${blockId}`);
239
- check('block-delete succeeds', out.includes('๐Ÿ—‘'));
240
- const verify = run('blocks test-tasks --filter "Name=Implement relations"');
241
- check('deleted content gone', !verify.includes('EDITED'));
242
- } catch (e) { check('block-delete', false, e.message); }
243
-
244
- // --- Test 8: JSON output preserves relation data ---
245
- console.log('--- 8. json output ---');
246
- try {
247
- const json = runJson('get test-tasks --filter "Name=Implement relations"');
248
- check('json has properties', !!json.properties);
249
- check('json has relation property', !!json.properties.Project);
250
- check('relation type correct', json.properties.Project.type === 'relation');
251
- check('relation has linked IDs', json.properties.Project.relation.length > 0);
252
- } catch (e) { check('json output', false, e.message); }
253
-
254
- // --- Test 9: Query projects with rollup-like display ---
255
- console.log('--- 9. query projects ---');
256
- try {
257
- const out = run('query test-projects');
258
- check('query shows projects', out.includes('Build CLI'));
259
- check('query shows both projects', out.includes('Write Docs'));
260
- console.log(`\n${out.split('\n').map(l => ' ' + l).join('\n')}\n`);
261
- } catch (e) { check('query projects', false, e.message); }
262
-
263
- console.log(`\n${'โ•'.repeat(50)}`);
264
- console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed}`);
265
- console.log('โ•'.repeat(50));
266
-
267
- return failed;
268
- }
269
-
270
- async function cleanup() {
271
- console.log('\n๐Ÿงน Cleaning up...');
272
-
273
- // Archive pages first (before their parent DBs get deleted)
274
- for (const id of testPageIds.filter(id => !createdDbIds.includes(id))) {
275
- try { await notion.pages.update({ page_id: id, archived: true }); } catch {}
276
- }
277
-
278
- // Then delete DBs and container page
279
- for (const id of createdDbIds) {
280
- try { await notion.blocks.delete({ block_id: id }); } catch {}
281
- }
282
-
283
- // Delete container page last
284
- if (parentPageId) {
285
- try { await notion.blocks.delete({ block_id: parentPageId }); } catch {}
286
- }
287
-
288
- try { run('alias remove test-projects'); } catch {}
289
- try { run('alias remove test-tasks'); } catch {}
290
-
291
- console.log('โœ… Cleaned up\n');
292
- }
293
-
294
- async function main() {
295
- try {
296
- await setup();
297
- const failures = await runTests();
298
- await cleanup();
299
- process.exit(failures > 0 ? 1 : 0);
300
- } catch (err) {
301
- console.error('\n๐Ÿ’ฅ Test crashed:', err.message);
302
- if (err.body) console.error('API body:', JSON.stringify(err.body).slice(0, 500));
303
- if (err.stack) console.error(err.stack);
304
- await cleanup().catch(() => {});
305
- process.exit(1);
306
- }
307
- }
308
-
309
- main();