@zoebuildsai/trace 1.5.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.
Files changed (130) hide show
  1. package/.gitignore +115 -0
  2. package/.trace/progress.json +22 -0
  3. package/README.md +466 -0
  4. package/RELEASE-NOTES-1.5.0.md +410 -0
  5. package/STATUS.md +245 -0
  6. package/dist/auto-commit.d.ts +66 -0
  7. package/dist/auto-commit.d.ts.map +1 -0
  8. package/dist/auto-commit.js +180 -0
  9. package/dist/auto-commit.js.map +1 -0
  10. package/dist/cli.d.ts +7 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +246 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands.d.ts +46 -0
  15. package/dist/commands.d.ts.map +1 -0
  16. package/dist/commands.js +256 -0
  17. package/dist/commands.js.map +1 -0
  18. package/dist/diff.d.ts +23 -0
  19. package/dist/diff.d.ts.map +1 -0
  20. package/dist/diff.js +106 -0
  21. package/dist/diff.js.map +1 -0
  22. package/dist/github.d.ts.map +1 -0
  23. package/dist/github.js.map +1 -0
  24. package/dist/index-cache.d.ts +35 -0
  25. package/dist/index-cache.d.ts.map +1 -0
  26. package/dist/index-cache.js +114 -0
  27. package/dist/index-cache.js.map +1 -0
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +25 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/storage.d.ts +45 -0
  33. package/dist/storage.d.ts.map +1 -0
  34. package/dist/storage.js +151 -0
  35. package/dist/storage.js.map +1 -0
  36. package/dist/sync.d.ts +60 -0
  37. package/dist/sync.js +184 -0
  38. package/dist/tags.d.ts +85 -0
  39. package/dist/tags.d.ts.map +1 -0
  40. package/dist/tags.js +219 -0
  41. package/dist/tags.js.map +1 -0
  42. package/dist/types.d.ts +102 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +6 -0
  45. package/dist/types.js.map +1 -0
  46. package/docs/.nojekyll +0 -0
  47. package/docs/README.md +73 -0
  48. package/docs/_config.yml +2 -0
  49. package/docs/index.html +960 -0
  50. package/docs-website/package.json +20 -0
  51. package/jest.config.js +21 -0
  52. package/package.json +50 -0
  53. package/scripts/init.ts +290 -0
  54. package/src/agent-audit.ts +270 -0
  55. package/src/agent-checkout.ts +227 -0
  56. package/src/agent-coordination.ts +318 -0
  57. package/src/async-queue.ts +203 -0
  58. package/src/auto-branching.ts +279 -0
  59. package/src/auto-commit.ts +166 -0
  60. package/src/cherry-pick.ts +252 -0
  61. package/src/chunked-upload.ts +224 -0
  62. package/src/cli-v2.ts +335 -0
  63. package/src/cli.ts +318 -0
  64. package/src/cliff-detection.ts +232 -0
  65. package/src/commands.ts +267 -0
  66. package/src/commit-hash-system.ts +351 -0
  67. package/src/compression.ts +176 -0
  68. package/src/conflict-resolution-ui.ts +277 -0
  69. package/src/conflict-visualization.ts +238 -0
  70. package/src/diff-formatter.ts +184 -0
  71. package/src/diff.ts +124 -0
  72. package/src/distributed-coordination.ts +273 -0
  73. package/src/git-interop.ts +316 -0
  74. package/src/index-cache.ts +88 -0
  75. package/src/index.ts +38 -0
  76. package/src/merge-engine.ts +143 -0
  77. package/src/message-search.ts +370 -0
  78. package/src/performance-monitoring.ts +236 -0
  79. package/src/rebase.ts +327 -0
  80. package/src/rollback.ts +215 -0
  81. package/src/semantic-grouping.ts +245 -0
  82. package/src/stage-area.ts +324 -0
  83. package/src/stash.ts +278 -0
  84. package/src/storage.ts +131 -0
  85. package/src/sync.ts +205 -0
  86. package/src/tags.ts +244 -0
  87. package/src/types.ts +119 -0
  88. package/src/webhooks.ts +119 -0
  89. package/src/workspace-isolation.ts +298 -0
  90. package/tests/auto-commit.test.ts +308 -0
  91. package/tests/checkout.test.ts +136 -0
  92. package/tests/commit.test.ts +118 -0
  93. package/tests/diff.test.ts +191 -0
  94. package/tests/github.test.ts +94 -0
  95. package/tests/integration.test.ts +267 -0
  96. package/tests/log.test.ts +125 -0
  97. package/tests/phase2-integration.test.ts +370 -0
  98. package/tests/storage.test.ts +167 -0
  99. package/tests/tags.test.ts +477 -0
  100. package/tests/types.test.ts +75 -0
  101. package/tests/v1.1/agent-audit.test.ts +472 -0
  102. package/tests/v1.1/agent-coordination.test.ts +308 -0
  103. package/tests/v1.1/async-queue.test.ts +253 -0
  104. package/tests/v1.1/comprehensive.test.ts +521 -0
  105. package/tests/v1.1/diff-formatter.test.ts +238 -0
  106. package/tests/v1.1/integration.test.ts +389 -0
  107. package/tests/v1.1/onboarding.test.ts +365 -0
  108. package/tests/v1.1/rollback.test.ts +370 -0
  109. package/tests/v1.1/semantic-grouping.test.ts +230 -0
  110. package/tests/v1.2/chunked-upload.test.ts +301 -0
  111. package/tests/v1.2/cliff-detection.test.ts +272 -0
  112. package/tests/v1.2/commit-hash-system.test.ts +288 -0
  113. package/tests/v1.2/compression.test.ts +220 -0
  114. package/tests/v1.2/conflict-visualization.test.ts +263 -0
  115. package/tests/v1.2/distributed.test.ts +261 -0
  116. package/tests/v1.2/performance-monitoring.test.ts +328 -0
  117. package/tests/v1.3/auto-branching.test.ts +270 -0
  118. package/tests/v1.3/message-search.test.ts +264 -0
  119. package/tests/v1.3/stage-area.test.ts +330 -0
  120. package/tests/v1.3/stash-rebase-cherry-pick.test.ts +361 -0
  121. package/tests/v1.4/cli.test.ts +171 -0
  122. package/tests/v1.4/conflict-resolution-advanced.test.ts +429 -0
  123. package/tests/v1.4/conflict-resolution-ui.test.ts +286 -0
  124. package/tests/v1.4/workspace-isolation-advanced.test.ts +382 -0
  125. package/tests/v1.4/workspace-isolation.test.ts +268 -0
  126. package/tests/v1.5/agent-coordination.real.test.ts +401 -0
  127. package/tests/v1.5/cli-v2.test.ts +354 -0
  128. package/tests/v1.5/git-interop.real.test.ts +358 -0
  129. package/tests/v1.5/integration-testing.real.test.ts +440 -0
  130. package/tsconfig.json +26 -0
@@ -0,0 +1,477 @@
1
+ /**
2
+ * Tags Feature Tests
3
+ *
4
+ * Comprehensive test suite for tagging functionality:
5
+ * - Create, list, delete tags
6
+ * - Checkout to tagged commits
7
+ * - Tag metadata (created_at, description, creator)
8
+ * - Tag conflicts and edge cases
9
+ * - Performance requirements (<50ms)
10
+ */
11
+
12
+ import { TagManager } from '../src/tags';
13
+ import { TraceCommands } from '../src/commands';
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import * as os from 'os';
17
+
18
+ describe('Tags Feature', () => {
19
+ let testDir: string;
20
+ let homeDir: string;
21
+ let memoryDir: string;
22
+ let commands: TraceCommands;
23
+ let tagManager: TagManager;
24
+
25
+ beforeEach(() => {
26
+ testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-git-'));
27
+ homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'home-'));
28
+ process.env.HOME = homeDir;
29
+ memoryDir = path.join(homeDir, '.openclaw/memory');
30
+ fs.mkdirSync(memoryDir, { recursive: true });
31
+
32
+ commands = new TraceCommands(testDir);
33
+
34
+ // New tags manager with fresh directory
35
+ tagManager = new TagManager(commands, testDir);
36
+
37
+ // Create initial commit
38
+ fs.writeFileSync(path.join(memoryDir, 'initial.md'), 'initial');
39
+ commands.commit('initial');
40
+ });
41
+
42
+ afterEach(() => {
43
+ if (fs.existsSync(testDir)) {
44
+ fs.rmSync(testDir, { recursive: true, force: true });
45
+ }
46
+ if (fs.existsSync(homeDir)) {
47
+ fs.rmSync(homeDir, { recursive: true, force: true });
48
+ }
49
+ });
50
+
51
+ describe('Tag creation', () => {
52
+ it('should create a simple tag', () => {
53
+ const hash = commands.status().clean ? '1234567890abcdef' : undefined;
54
+ const tag = tagManager.create('v1.0', { description: 'Version 1.0' });
55
+
56
+ expect(tag).toBeDefined();
57
+ expect(tag.name).toBe('v1.0');
58
+ expect(tag.created_at).toBeDefined();
59
+ });
60
+
61
+ it('should create tag with metadata', () => {
62
+ const tag = tagManager.create('checkpoint-1', {
63
+ description: 'Training checkpoint',
64
+ creator: 'agent',
65
+ });
66
+
67
+ expect(tag.metadata?.description).toBe('Training checkpoint');
68
+ expect(tag.metadata?.creator).toBe('agent');
69
+ });
70
+
71
+ it('should record created_at timestamp', () => {
72
+ const beforeCreate = Date.now();
73
+ const tag = tagManager.create('test-tag');
74
+ const afterCreate = Date.now();
75
+
76
+ expect(tag.created_at).toBeGreaterThanOrEqual(beforeCreate);
77
+ expect(tag.created_at).toBeLessThanOrEqual(afterCreate);
78
+ });
79
+
80
+ it('should handle unicode tag names', () => {
81
+ const tag = tagManager.create('标签-v1.0', {
82
+ description: 'Unicode tag',
83
+ });
84
+
85
+ expect(tag.name).toBe('标签-v1.0');
86
+ expect(tagManager.get('标签-v1.0')).toBeDefined();
87
+ });
88
+
89
+ it('should handle special characters in tag names', () => {
90
+ const validNames = ['release/1.0', 'alpha-2', 'test_1'];
91
+ validNames.forEach(name => {
92
+ const tag = tagManager.create(name);
93
+ expect(tag.name).toBe(name);
94
+ });
95
+ });
96
+
97
+ it('should reject invalid tag names', () => {
98
+ expect(() => {
99
+ tagManager.create(''); // empty
100
+ }).toThrow();
101
+
102
+ expect(() => {
103
+ tagManager.create('tag with spaces');
104
+ }).toThrow();
105
+ });
106
+ });
107
+
108
+ describe('Tag conflicts', () => {
109
+ it('should prevent duplicate tag names', () => {
110
+ tagManager.create('v1.0');
111
+
112
+ expect(() => {
113
+ tagManager.create('v1.0');
114
+ }).toThrow();
115
+ });
116
+
117
+ it('should allow updating existing tag with force flag', () => {
118
+ tagManager.create('v1.0');
119
+
120
+ // Create new commit
121
+ fs.writeFileSync(path.join(memoryDir, 'file2.md'), 'content');
122
+ commands.commit('second');
123
+
124
+ const updated = tagManager.create('v1.0', { force: true });
125
+ expect(updated).toBeDefined();
126
+ });
127
+
128
+ it('should handle tag deletion and recreation', () => {
129
+ tagManager.create('v1.0');
130
+ tagManager.delete('v1.0');
131
+
132
+ const recreated = tagManager.create('v1.0');
133
+ expect(recreated).toBeDefined();
134
+ });
135
+ });
136
+
137
+ describe('Tag listing', () => {
138
+ it('should list all tags', () => {
139
+ tagManager.create('v1.0');
140
+ tagManager.create('v1.1');
141
+ tagManager.create('v2.0');
142
+
143
+ const tags = tagManager.list();
144
+ expect(tags.length).toBe(3);
145
+ expect(tags.map(t => t.name)).toContain('v1.0');
146
+ });
147
+
148
+ it('should list tags with full metadata', () => {
149
+ tagManager.create('test', { description: 'Test tag' });
150
+
151
+ const tags = tagManager.list();
152
+ const testTag = tags.find(t => t.name === 'test');
153
+
154
+ expect(testTag?.metadata?.description).toBe('Test tag');
155
+ expect(testTag?.created_at).toBeDefined();
156
+ });
157
+
158
+ it('should return empty list when no tags', () => {
159
+ const tags = tagManager.list();
160
+ expect(tags).toEqual([]);
161
+ });
162
+
163
+ it('should list tags sorted by creation time', (done) => {
164
+ tagManager.create('first');
165
+
166
+ // Small delay to ensure different timestamp
167
+ setTimeout(() => {
168
+ tagManager.create('second');
169
+
170
+ const tags = tagManager.list({ sortBy: 'created_at' });
171
+ expect(tags[0].name).toBe('first');
172
+ expect(tags[1].name).toBe('second');
173
+ done();
174
+ }, 20);
175
+ });
176
+
177
+ it('should filter tags by pattern', () => {
178
+ tagManager.create('v1.0');
179
+ tagManager.create('v1.1');
180
+ tagManager.create('release-2.0');
181
+
182
+ const vTags = tagManager.list({ pattern: 'v*' });
183
+ expect(vTags.length).toBe(2);
184
+ expect(vTags.every(t => t.name.startsWith('v'))).toBe(true);
185
+ });
186
+ });
187
+
188
+ describe('Tag deletion', () => {
189
+ it('should delete a tag', () => {
190
+ tagManager.create('v1.0');
191
+ tagManager.delete('v1.0');
192
+
193
+ expect(tagManager.get('v1.0')).toBeUndefined();
194
+ });
195
+
196
+ it('should throw on deleting non-existent tag', () => {
197
+ expect(() => {
198
+ tagManager.delete('nonexistent');
199
+ }).toThrow();
200
+ });
201
+
202
+ it('should allow deleting tag while on tagged commit', () => {
203
+ tagManager.create('v1.0');
204
+ const tags = tagManager.list();
205
+ expect(tags.length).toBe(1);
206
+
207
+ tagManager.delete('v1.0');
208
+ expect(tagManager.list().length).toBe(0);
209
+ });
210
+
211
+ it('should handle rapid tag creation/deletion', () => {
212
+ for (let i = 0; i < 100; i++) {
213
+ const name = `tag-${i}`;
214
+ tagManager.create(name);
215
+ }
216
+
217
+ for (let i = 0; i < 100; i++) {
218
+ tagManager.delete(`tag-${i}`);
219
+ }
220
+
221
+ expect(tagManager.list().length).toBe(0);
222
+ });
223
+ });
224
+
225
+ describe('Checkout to tag', () => {
226
+ it('should checkout to tagged commit', () => {
227
+ // Create first state
228
+ fs.writeFileSync(path.join(memoryDir, 'v1.md'), 'v1 content');
229
+ const hash1 = commands.commit('version 1');
230
+
231
+ tagManager.create('v1.0');
232
+
233
+ // Create second state
234
+ fs.writeFileSync(path.join(memoryDir, 'v2.md'), 'v2 content');
235
+ commands.commit('version 2');
236
+
237
+ // Create third state
238
+ fs.writeFileSync(path.join(memoryDir, 'v3.md'), 'v3 content');
239
+ commands.commit('version 3');
240
+
241
+ // Checkout to v1.0
242
+ const restored = tagManager.checkout('v1.0');
243
+ expect(restored).toBe(true);
244
+
245
+ // Verify we're pointing at the right commit
246
+ const currentCommit = commands.getCurrentCommit();
247
+ expect(currentCommit?.hash).toBe(hash1);
248
+
249
+ const log = commands.log(1);
250
+ expect(log[0].message).toBe('version 1');
251
+ });
252
+
253
+ it('should restore to tagged commit state', () => {
254
+ const file1 = path.join(memoryDir, 'doc.md');
255
+ fs.writeFileSync(file1, 'documentation');
256
+ const hash1 = commands.commit('added docs');
257
+ tagManager.create('with-docs');
258
+
259
+ // Remove the file
260
+ fs.unlinkSync(file1);
261
+ commands.commit('removed docs');
262
+
263
+ // Restore
264
+ tagManager.checkout('with-docs');
265
+
266
+ // Verify we're at the right commit
267
+ const currentCommit = commands.getCurrentCommit();
268
+ expect(currentCommit?.hash).toBe(hash1);
269
+
270
+ const log = commands.log(1);
271
+ expect(log[0].message).toBe('added docs');
272
+ });
273
+
274
+ it('should throw on checkout to non-existent tag', () => {
275
+ expect(() => {
276
+ tagManager.checkout('nonexistent');
277
+ }).toThrow();
278
+ });
279
+
280
+ it('should handle checkout with uncommitted changes', () => {
281
+ tagManager.create('v1');
282
+
283
+ // Make uncommitted change
284
+ fs.writeFileSync(path.join(memoryDir, 'uncommitted.md'), 'new');
285
+
286
+ // Should handle gracefully
287
+ expect(() => {
288
+ tagManager.checkout('v1', { force: true });
289
+ }).not.toThrow();
290
+ });
291
+ });
292
+
293
+ describe('Tag storage & persistence', () => {
294
+ it('should persist tags to disk', () => {
295
+ tagManager.create('persistent-tag', { description: 'Should persist' });
296
+
297
+ // Create new manager instance
298
+ const newManager = new TagManager(commands, testDir);
299
+ const tags = newManager.list();
300
+
301
+ expect(tags.find(t => t.name === 'persistent-tag')).toBeDefined();
302
+ });
303
+
304
+ it('should store tags in refs/tags/ directory', () => {
305
+ tagManager.create('test-tag');
306
+
307
+ const tagsDir = path.join(testDir, 'refs', 'tags');
308
+ expect(fs.existsSync(tagsDir)).toBe(true);
309
+
310
+ // The tag name is stored as-is (no special chars in 'test-tag')
311
+ const files = fs.readdirSync(tagsDir);
312
+ expect(files.length).toBeGreaterThan(0);
313
+ expect(files[0]).toBe('test-tag');
314
+ });
315
+
316
+ it('should handle tag file corruption gracefully', () => {
317
+ tagManager.create('good-tag');
318
+
319
+ // Corrupt a tag file
320
+ const tagsDir = path.join(testDir, 'refs', 'tags');
321
+ fs.mkdirSync(tagsDir, { recursive: true });
322
+ const badFile = path.join(tagsDir, 'bad-tag');
323
+ fs.writeFileSync(badFile, 'invalid json');
324
+
325
+ // Should not crash
326
+ const newManager = new TagManager(commands, testDir);
327
+ const tags = newManager.list();
328
+
329
+ expect(tags.find(t => t.name === 'good-tag')).toBeDefined();
330
+ });
331
+ });
332
+
333
+ describe('Tag indexing & lookup', () => {
334
+ it('should build fast tag index', () => {
335
+ for (let i = 0; i < 1000; i++) {
336
+ tagManager.create(`tag-${i}`);
337
+ }
338
+
339
+ const startTime = Date.now();
340
+ const tag = tagManager.get('tag-500');
341
+ const duration = Date.now() - startTime;
342
+
343
+ expect(duration).toBeLessThan(50);
344
+ expect(tag?.name).toBe('tag-500');
345
+ });
346
+
347
+ it('should handle 1000+ tags efficiently', () => {
348
+ for (let i = 0; i < 1000; i++) {
349
+ tagManager.create(`tag-${i}`);
350
+ }
351
+
352
+ const tags = tagManager.list();
353
+ expect(tags.length).toBe(1000);
354
+ });
355
+
356
+ it('should rebuild index on demand', () => {
357
+ tagManager.create('v1');
358
+ tagManager.create('v2');
359
+
360
+ tagManager.rebuildIndex();
361
+
362
+ const tags = tagManager.list();
363
+ expect(tags.length).toBe(2);
364
+ });
365
+ });
366
+
367
+ describe('Performance requirements', () => {
368
+ it('should create tag in <50ms', () => {
369
+ const startTime = Date.now();
370
+ tagManager.create('perf-test');
371
+ const duration = Date.now() - startTime;
372
+
373
+ expect(duration).toBeLessThan(50);
374
+ });
375
+
376
+ it('should list tags in <50ms even with 100 tags', () => {
377
+ for (let i = 0; i < 100; i++) {
378
+ tagManager.create(`tag-${i}`);
379
+ }
380
+
381
+ const startTime = Date.now();
382
+ tagManager.list();
383
+ const duration = Date.now() - startTime;
384
+
385
+ expect(duration).toBeLessThan(50);
386
+ });
387
+
388
+ it('should lookup tag in <20ms', () => {
389
+ for (let i = 0; i < 100; i++) {
390
+ tagManager.create(`tag-${i}`);
391
+ }
392
+
393
+ const startTime = Date.now();
394
+ tagManager.get('tag-50');
395
+ const duration = Date.now() - startTime;
396
+
397
+ expect(duration).toBeLessThan(20);
398
+ });
399
+
400
+ it('should delete tag in <50ms', () => {
401
+ tagManager.create('delete-test');
402
+
403
+ const startTime = Date.now();
404
+ tagManager.delete('delete-test');
405
+ const duration = Date.now() - startTime;
406
+
407
+ expect(duration).toBeLessThan(50);
408
+ });
409
+ });
410
+
411
+ describe('Edge cases & robustness', () => {
412
+ it('should handle very long tag names (255+ chars)', () => {
413
+ const longName = 'tag-' + 'x'.repeat(250);
414
+ const tag = tagManager.create(longName);
415
+ expect(tag.name).toBe(longName);
416
+ });
417
+
418
+ it('should handle empty metadata', () => {
419
+ const tag = tagManager.create('empty');
420
+ expect(tag.metadata).toBeUndefined();
421
+ });
422
+
423
+ it('should handle large metadata objects', () => {
424
+ const largeMetadata = {
425
+ description: 'x'.repeat(10000),
426
+ data: Array(1000).fill('item'),
427
+ };
428
+ const tag = tagManager.create('large', largeMetadata);
429
+ expect(tag.metadata?.description.length).toBeGreaterThan(1000);
430
+ });
431
+
432
+ it('should recover from partial write', () => {
433
+ tagManager.create('good-tag');
434
+ tagManager.create('also-good');
435
+
436
+ // Simulate partial write by corrupting a tag file
437
+ const tagsDir = path.join(testDir, 'refs', 'tags');
438
+ const files = fs.readdirSync(tagsDir);
439
+ if (files.length > 0) {
440
+ fs.writeFileSync(path.join(tagsDir, files[0]), '{"incomplete');
441
+ }
442
+
443
+ // New manager should load good tags and skip corrupt ones
444
+ const newManager = new TagManager(commands, testDir);
445
+ const tags = newManager.list();
446
+
447
+ // Should have at least recovered the uncorrupted tag
448
+ expect(tags.length).toBeGreaterThanOrEqual(1);
449
+ expect(tags.some(t => t.name === 'also-good' || t.name === 'good-tag')).toBe(true);
450
+ });
451
+ });
452
+
453
+ describe('Tag metadata tracking', () => {
454
+ it('should track commit hash with tag', () => {
455
+ const tag = tagManager.create('v1.0');
456
+ expect(tag.commit_hash).toBeDefined();
457
+ expect(tag.commit_hash.length).toBeGreaterThan(0);
458
+ });
459
+
460
+ it('should track creator in metadata', () => {
461
+ const tag = tagManager.create('creator-test', { creator: 'agent' });
462
+ expect(tag.metadata?.creator).toBe('agent');
463
+ });
464
+
465
+ it('should allow custom metadata fields', () => {
466
+ const tag = tagManager.create('custom', {
467
+ milestone: 'M1',
468
+ tested: true,
469
+ version: '1.0.0',
470
+ });
471
+
472
+ expect(tag.metadata?.milestone).toBe('M1');
473
+ expect(tag.metadata?.tested).toBe(true);
474
+ expect(tag.metadata?.version).toBe('1.0.0');
475
+ });
476
+ });
477
+ });
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Type tests to ensure compile-time correctness
3
+ */
4
+
5
+ import {
6
+ CommitObject,
7
+ TreeObject,
8
+ FileEntry,
9
+ DiffResult,
10
+ StatusResult,
11
+ CommitOptions,
12
+ } from '../src/types';
13
+
14
+ describe('Types', () => {
15
+ it('should create valid CommitObject', () => {
16
+ const tree: TreeObject = {
17
+ files: new Map(),
18
+ hash: 'abc123',
19
+ };
20
+
21
+ const commit: CommitObject = {
22
+ hash: 'abc123',
23
+ message: 'test commit',
24
+ timestamp: Date.now(),
25
+ author: 'test',
26
+ parent: null,
27
+ tree,
28
+ };
29
+
30
+ expect(commit.hash).toBeDefined();
31
+ expect(commit.message).toBeDefined();
32
+ expect(commit.timestamp).toBeGreaterThan(0);
33
+ });
34
+
35
+ it('should create valid FileEntry', () => {
36
+ const entry: FileEntry = {
37
+ path: 'test.txt',
38
+ hash: 'xyz789',
39
+ size: 100,
40
+ mode: 'file',
41
+ lastModified: Date.now(),
42
+ };
43
+
44
+ expect(entry.path).toBe('test.txt');
45
+ expect(entry.mode).toBe('file');
46
+ });
47
+
48
+ it('should create valid DiffResult', () => {
49
+ const diff: DiffResult = {
50
+ added: new Map(),
51
+ modified: new Map(),
52
+ deleted: new Map(),
53
+ stats: {
54
+ filesChanged: 0,
55
+ linesAdded: 0,
56
+ linesRemoved: 0,
57
+ totalChanges: 0,
58
+ },
59
+ };
60
+
61
+ expect(diff.added).toBeInstanceOf(Map);
62
+ expect(diff.stats.filesChanged).toBe(0);
63
+ });
64
+
65
+ it('should create valid CommitOptions', () => {
66
+ const opts: CommitOptions = {
67
+ message: 'test',
68
+ author: 'test-author',
69
+ metadata: { version: '1.0' },
70
+ };
71
+
72
+ expect(opts.message).toBeDefined();
73
+ expect(opts.metadata).toHaveProperty('version');
74
+ });
75
+ });