@ibgib/vcs-gib 0.0.47 → 0.0.49
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/dist/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/cli/cli-arg-parser.d.mts +2 -0
- package/dist/cli/cli-arg-parser.d.mts.map +1 -1
- package/dist/cli/cli-arg-parser.mjs +12 -1
- package/dist/cli/cli-arg-parser.mjs.map +1 -1
- package/dist/cli/cli-constants.d.mts +2 -0
- package/dist/cli/cli-constants.d.mts.map +1 -1
- package/dist/cli/cli-constants.mjs +14 -0
- package/dist/cli/cli-constants.mjs.map +1 -1
- package/dist/cli/commands/cli-commit-cmd.d.mts +2 -1
- package/dist/cli/commands/cli-commit-cmd.d.mts.map +1 -1
- package/dist/cli/commands/cli-commit-cmd.mjs +2 -1
- package/dist/cli/commands/cli-commit-cmd.mjs.map +1 -1
- package/dist/cli/commands/cli-status-cmd.d.mts.map +1 -1
- package/dist/cli/commands/cli-status-cmd.mjs +4 -1
- package/dist/cli/commands/cli-status-cmd.mjs.map +1 -1
- package/dist/cli/vcs-cli-main.d.mts.map +1 -1
- package/dist/cli/vcs-cli-main.mjs +1 -0
- package/dist/cli/vcs-cli-main.mjs.map +1 -1
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/cli/cli-arg-parser.mts +14 -0
- package/src/cli/cli-constants.mts +17 -0
- package/src/cli/commands/cli-commit-cmd.mts +3 -0
- package/src/cli/commands/cli-status-cmd.mts +4 -1
- package/src/cli/commands/delete-files.respec.mts +366 -0
- package/src/cli/vcs-cli-main.mts +1 -0
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module delete-files.respec
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for VCS file and folder deletion workflows:
|
|
5
|
+
* - vcs add <deleted-file>
|
|
6
|
+
* - vcs add -a (auto-staging missing files as deleted)
|
|
7
|
+
* - vcs status (rendering staged & unstaged deletions)
|
|
8
|
+
* - vcs commit (Merkle DAG negative-space pruning)
|
|
9
|
+
* - vcs commit --keep-empty-folders vs --rm-empty-folders
|
|
10
|
+
* - vcs restore <deleted-file> (recovering deleted file to worktree)
|
|
11
|
+
* - vcs reset <deleted-file> (unstaging deletion)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import fs from 'node:fs/promises';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
respecfully, ifWe, iReckon, ifWeMight,
|
|
19
|
+
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
20
|
+
|
|
21
|
+
import { executeInitCmd } from './cli-init-cmd.mjs';
|
|
22
|
+
import { executeAddCmd } from './cli-add-cmd.mjs';
|
|
23
|
+
import { executeCommitCmd } from './cli-commit-cmd.mjs';
|
|
24
|
+
import { executeStatusCmd } from './cli-status-cmd.mjs';
|
|
25
|
+
import { executeResetCmd } from './cli-reset-cmd.mjs';
|
|
26
|
+
import { executeRestoreCmd } from './cli-restore-cmd.mjs';
|
|
27
|
+
|
|
28
|
+
const lc = `[apps/vcs-gib/src/cli/commands/delete-files.respec.mts]`;
|
|
29
|
+
|
|
30
|
+
await respecfully(lc, `VCS Deletions Lifecycle Command Handlers`, async () => {
|
|
31
|
+
|
|
32
|
+
await ifWe(lc, `vcs add <deleted-file> stages deletion, and vcs commit prunes it from Merkle tree`, async () => {
|
|
33
|
+
const testDir = path.join(process.cwd(), '.test_delete_single');
|
|
34
|
+
const vcsFolderName = '.vcsgib_test_delete_single';
|
|
35
|
+
try {
|
|
36
|
+
await executeInitCmd({
|
|
37
|
+
targetDir: testDir,
|
|
38
|
+
vcsFolderName,
|
|
39
|
+
quiet: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await fs.writeFile(path.join(testDir, 'file1.txt'), 'Content 1', 'utf8');
|
|
43
|
+
|
|
44
|
+
await executeAddCmd({
|
|
45
|
+
paths: ['file1.txt'],
|
|
46
|
+
targetDir: testDir,
|
|
47
|
+
vcsFolderName,
|
|
48
|
+
quiet: true,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await executeCommitCmd({
|
|
52
|
+
message: 'Add file1.txt',
|
|
53
|
+
targetDir: testDir,
|
|
54
|
+
vcsFolderName,
|
|
55
|
+
quiet: true,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Delete file on disk
|
|
59
|
+
await fs.unlink(path.join(testDir, 'file1.txt'));
|
|
60
|
+
|
|
61
|
+
// Status shows unstaged deletion
|
|
62
|
+
let statusRes = await executeStatusCmd({
|
|
63
|
+
targetDir: testDir,
|
|
64
|
+
vcsFolderName,
|
|
65
|
+
quiet: true,
|
|
66
|
+
});
|
|
67
|
+
iReckon(lc, statusRes.deleted.map(x => x.path)).includes('file1.txt');
|
|
68
|
+
|
|
69
|
+
// Stage deletion via vcs add <path>
|
|
70
|
+
const addRes = await executeAddCmd({
|
|
71
|
+
paths: ['file1.txt'],
|
|
72
|
+
targetDir: testDir,
|
|
73
|
+
vcsFolderName,
|
|
74
|
+
quiet: true,
|
|
75
|
+
});
|
|
76
|
+
iReckon(lc, addRes.stagedPaths).includes('file1.txt');
|
|
77
|
+
|
|
78
|
+
// Status shows staged deletion
|
|
79
|
+
statusRes = await executeStatusCmd({
|
|
80
|
+
targetDir: testDir,
|
|
81
|
+
vcsFolderName,
|
|
82
|
+
quiet: true,
|
|
83
|
+
});
|
|
84
|
+
iReckon(lc, statusRes.staged.map(x => x.path)).includes('file1.txt');
|
|
85
|
+
const stagedItem = statusRes.staged.find(x => x.path === 'file1.txt');
|
|
86
|
+
iReckon(lc, stagedItem?.stagedType).willEqual('deleted');
|
|
87
|
+
|
|
88
|
+
// Commit deletion
|
|
89
|
+
const commitRes = await executeCommitCmd({
|
|
90
|
+
message: 'Delete file1.txt',
|
|
91
|
+
targetDir: testDir,
|
|
92
|
+
vcsFolderName,
|
|
93
|
+
quiet: true,
|
|
94
|
+
});
|
|
95
|
+
iReckon(lc, commitRes.committedPaths).includes('file1.txt');
|
|
96
|
+
|
|
97
|
+
// Status is clean after commit
|
|
98
|
+
statusRes = await executeStatusCmd({
|
|
99
|
+
targetDir: testDir,
|
|
100
|
+
vcsFolderName,
|
|
101
|
+
quiet: true,
|
|
102
|
+
});
|
|
103
|
+
iReckon(lc, statusRes.staged.length).willEqual(0);
|
|
104
|
+
iReckon(lc, statusRes.deleted.length).willEqual(0);
|
|
105
|
+
iReckon(lc, statusRes.modified.length).willEqual(0);
|
|
106
|
+
} finally {
|
|
107
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await ifWe(lc, `vcs add -a stages multiple deleted files and folder pruning occurs on commit`, async () => {
|
|
112
|
+
const testDir = path.join(process.cwd(), '.test_delete_all');
|
|
113
|
+
const vcsFolderName = '.vcsgib_test_delete_all';
|
|
114
|
+
try {
|
|
115
|
+
await executeInitCmd({
|
|
116
|
+
targetDir: testDir,
|
|
117
|
+
vcsFolderName,
|
|
118
|
+
quiet: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await fs.mkdir(path.join(testDir, 'src', 'utils'), { recursive: true });
|
|
122
|
+
await fs.writeFile(path.join(testDir, 'src', 'utils', 'math.ts'), 'export const pi = 3.14;', 'utf8');
|
|
123
|
+
await fs.writeFile(path.join(testDir, 'src', 'index.ts'), 'export * from "./utils/math.js";', 'utf8');
|
|
124
|
+
await fs.writeFile(path.join(testDir, 'readme.md'), '# Test Project', 'utf8');
|
|
125
|
+
|
|
126
|
+
await executeAddCmd({
|
|
127
|
+
all: true,
|
|
128
|
+
targetDir: testDir,
|
|
129
|
+
vcsFolderName,
|
|
130
|
+
quiet: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await executeCommitCmd({
|
|
134
|
+
message: 'Initial project files',
|
|
135
|
+
targetDir: testDir,
|
|
136
|
+
vcsFolderName,
|
|
137
|
+
quiet: true,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Delete math.ts
|
|
141
|
+
await fs.unlink(path.join(testDir, 'src', 'utils', 'math.ts'));
|
|
142
|
+
|
|
143
|
+
// vcs add -a auto-stages the deleted file
|
|
144
|
+
const addRes = await executeAddCmd({
|
|
145
|
+
all: true,
|
|
146
|
+
targetDir: testDir,
|
|
147
|
+
vcsFolderName,
|
|
148
|
+
quiet: true,
|
|
149
|
+
});
|
|
150
|
+
iReckon(lc, addRes.stagedPaths).includes('src/utils/math.ts');
|
|
151
|
+
|
|
152
|
+
// Status shows staged deletion
|
|
153
|
+
let statusRes = await executeStatusCmd({
|
|
154
|
+
targetDir: testDir,
|
|
155
|
+
vcsFolderName,
|
|
156
|
+
quiet: true,
|
|
157
|
+
});
|
|
158
|
+
iReckon(lc, statusRes.staged.map(x => x.path)).includes('src/utils/math.ts');
|
|
159
|
+
|
|
160
|
+
// Commit with default pruning (--rm-empty-folders)
|
|
161
|
+
const commitRes = await executeCommitCmd({
|
|
162
|
+
message: 'Remove math utility',
|
|
163
|
+
targetDir: testDir,
|
|
164
|
+
vcsFolderName,
|
|
165
|
+
quiet: true,
|
|
166
|
+
});
|
|
167
|
+
iReckon(lc, commitRes.committedPaths).includes('src/utils/math.ts');
|
|
168
|
+
|
|
169
|
+
statusRes = await executeStatusCmd({
|
|
170
|
+
targetDir: testDir,
|
|
171
|
+
vcsFolderName,
|
|
172
|
+
quiet: true,
|
|
173
|
+
});
|
|
174
|
+
iReckon(lc, statusRes.staged.length).willEqual(0);
|
|
175
|
+
iReckon(lc, statusRes.deleted.length).willEqual(0);
|
|
176
|
+
} finally {
|
|
177
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await ifWe(lc, `vcs commit with keepEmptyFolders retains empty folder node in tree`, async () => {
|
|
182
|
+
const testDir = path.join(process.cwd(), '.test_delete_keep_empty');
|
|
183
|
+
const vcsFolderName = '.vcsgib_test_delete_keep_empty';
|
|
184
|
+
try {
|
|
185
|
+
await executeInitCmd({
|
|
186
|
+
targetDir: testDir,
|
|
187
|
+
vcsFolderName,
|
|
188
|
+
quiet: true,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await fs.mkdir(path.join(testDir, 'emptyDir'), { recursive: true });
|
|
192
|
+
await fs.writeFile(path.join(testDir, 'emptyDir', 'temp.txt'), 'temporary', 'utf8');
|
|
193
|
+
|
|
194
|
+
await executeAddCmd({
|
|
195
|
+
all: true,
|
|
196
|
+
targetDir: testDir,
|
|
197
|
+
vcsFolderName,
|
|
198
|
+
quiet: true,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await executeCommitCmd({
|
|
202
|
+
message: 'Initial with temp.txt',
|
|
203
|
+
targetDir: testDir,
|
|
204
|
+
vcsFolderName,
|
|
205
|
+
quiet: true,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Delete temp.txt
|
|
209
|
+
await fs.unlink(path.join(testDir, 'emptyDir', 'temp.txt'));
|
|
210
|
+
|
|
211
|
+
await executeAddCmd({
|
|
212
|
+
paths: ['emptyDir/temp.txt'],
|
|
213
|
+
targetDir: testDir,
|
|
214
|
+
vcsFolderName,
|
|
215
|
+
quiet: true,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Commit with keepEmptyFolders: true
|
|
219
|
+
const commitRes = await executeCommitCmd({
|
|
220
|
+
message: 'Delete temp keeping empty dir',
|
|
221
|
+
targetDir: testDir,
|
|
222
|
+
vcsFolderName,
|
|
223
|
+
quiet: true,
|
|
224
|
+
keepEmptyFolders: true,
|
|
225
|
+
});
|
|
226
|
+
iReckon(lc, commitRes.committedPaths).includes('emptyDir/temp.txt');
|
|
227
|
+
|
|
228
|
+
const statusRes = await executeStatusCmd({
|
|
229
|
+
targetDir: testDir,
|
|
230
|
+
vcsFolderName,
|
|
231
|
+
quiet: true,
|
|
232
|
+
});
|
|
233
|
+
iReckon(lc, statusRes.staged.length).willEqual(0);
|
|
234
|
+
} finally {
|
|
235
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await ifWe(lc, `vcs restore <file> restores deleted file from committed snapshot to disk`, async () => {
|
|
240
|
+
const testDir = path.join(process.cwd(), '.test_restore_deleted');
|
|
241
|
+
const vcsFolderName = '.vcsgib_test_restore_deleted';
|
|
242
|
+
try {
|
|
243
|
+
await executeInitCmd({
|
|
244
|
+
targetDir: testDir,
|
|
245
|
+
vcsFolderName,
|
|
246
|
+
quiet: true,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await fs.mkdir(path.join(testDir, 'assets', 'css'), { recursive: true });
|
|
250
|
+
await fs.writeFile(path.join(testDir, 'assets', 'css', 'style.css'), 'body { margin: 0; }', 'utf8');
|
|
251
|
+
|
|
252
|
+
await executeAddCmd({
|
|
253
|
+
all: true,
|
|
254
|
+
targetDir: testDir,
|
|
255
|
+
vcsFolderName,
|
|
256
|
+
quiet: true,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await executeCommitCmd({
|
|
260
|
+
message: 'Add stylesheet',
|
|
261
|
+
targetDir: testDir,
|
|
262
|
+
vcsFolderName,
|
|
263
|
+
quiet: true,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Delete style.css from disk
|
|
267
|
+
await fs.unlink(path.join(testDir, 'assets', 'css', 'style.css'));
|
|
268
|
+
|
|
269
|
+
let statusRes = await executeStatusCmd({
|
|
270
|
+
targetDir: testDir,
|
|
271
|
+
vcsFolderName,
|
|
272
|
+
quiet: true,
|
|
273
|
+
});
|
|
274
|
+
iReckon(lc, statusRes.deleted.map(x => x.path)).includes('assets/css/style.css');
|
|
275
|
+
|
|
276
|
+
// Restore deleted file from committed snapshot
|
|
277
|
+
const restoreRes = await executeRestoreCmd({
|
|
278
|
+
paths: ['assets/css/style.css'],
|
|
279
|
+
targetDir: testDir,
|
|
280
|
+
vcsFolderName,
|
|
281
|
+
quiet: true,
|
|
282
|
+
});
|
|
283
|
+
iReckon(lc, restoreRes.restoredPaths).includes('assets/css/style.css');
|
|
284
|
+
|
|
285
|
+
// Verify file is back on disk at correct nested path with correct content
|
|
286
|
+
const restoredContent = await fs.readFile(path.join(testDir, 'assets', 'css', 'style.css'), 'utf8');
|
|
287
|
+
iReckon(lc, restoredContent).willEqual('body { margin: 0; }');
|
|
288
|
+
|
|
289
|
+
statusRes = await executeStatusCmd({
|
|
290
|
+
targetDir: testDir,
|
|
291
|
+
vcsFolderName,
|
|
292
|
+
quiet: true,
|
|
293
|
+
});
|
|
294
|
+
iReckon(lc, statusRes.deleted.length).willEqual(0);
|
|
295
|
+
} finally {
|
|
296
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await ifWe(lc, `vcs reset <file> unstages a staged deletion`, async () => {
|
|
301
|
+
const testDir = path.join(process.cwd(), '.test_reset_deletion');
|
|
302
|
+
const vcsFolderName = '.vcsgib_test_reset_deletion';
|
|
303
|
+
try {
|
|
304
|
+
await executeInitCmd({
|
|
305
|
+
targetDir: testDir,
|
|
306
|
+
vcsFolderName,
|
|
307
|
+
quiet: true,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
await fs.writeFile(path.join(testDir, 'important.txt'), 'Important data', 'utf8');
|
|
311
|
+
|
|
312
|
+
await executeAddCmd({
|
|
313
|
+
paths: ['important.txt'],
|
|
314
|
+
targetDir: testDir,
|
|
315
|
+
vcsFolderName,
|
|
316
|
+
quiet: true,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
await executeCommitCmd({
|
|
320
|
+
message: 'Add important file',
|
|
321
|
+
targetDir: testDir,
|
|
322
|
+
vcsFolderName,
|
|
323
|
+
quiet: true,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Delete from disk
|
|
327
|
+
await fs.unlink(path.join(testDir, 'important.txt'));
|
|
328
|
+
|
|
329
|
+
// Stage deletion
|
|
330
|
+
await executeAddCmd({
|
|
331
|
+
paths: ['important.txt'],
|
|
332
|
+
targetDir: testDir,
|
|
333
|
+
vcsFolderName,
|
|
334
|
+
quiet: true,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
let statusRes = await executeStatusCmd({
|
|
338
|
+
targetDir: testDir,
|
|
339
|
+
vcsFolderName,
|
|
340
|
+
quiet: true,
|
|
341
|
+
});
|
|
342
|
+
iReckon(lc, statusRes.staged.map(x => x.path)).includes('important.txt');
|
|
343
|
+
|
|
344
|
+
// Reset (unstage) deletion
|
|
345
|
+
const resetRes = await executeResetCmd({
|
|
346
|
+
paths: ['important.txt'],
|
|
347
|
+
targetDir: testDir,
|
|
348
|
+
vcsFolderName,
|
|
349
|
+
quiet: true,
|
|
350
|
+
});
|
|
351
|
+
iReckon(lc, resetRes.unstagedPaths).includes('important.txt');
|
|
352
|
+
|
|
353
|
+
statusRes = await executeStatusCmd({
|
|
354
|
+
targetDir: testDir,
|
|
355
|
+
vcsFolderName,
|
|
356
|
+
quiet: true,
|
|
357
|
+
});
|
|
358
|
+
// Should no longer be staged, but still unstaged deleted
|
|
359
|
+
iReckon(lc, statusRes.staged.length).willEqual(0);
|
|
360
|
+
iReckon(lc, statusRes.deleted.map(x => x.path)).includes('important.txt');
|
|
361
|
+
} finally {
|
|
362
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
});
|