@sumicom/quicksave 0.1.0 → 0.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.
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/connection/signaling.d.ts.map +1 -1
- package/dist/connection/signaling.js +5 -0
- package/dist/connection/signaling.js.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/package.json +22 -2
- package/dist/webrtc/connection.d.ts +0 -52
- package/dist/webrtc/connection.d.ts.map +0 -1
- package/dist/webrtc/connection.js +0 -255
- package/dist/webrtc/connection.js.map +0 -1
- package/dist/webrtc/signaling.d.ts +0 -41
- package/dist/webrtc/signaling.d.ts.map +0 -1
- package/dist/webrtc/signaling.js +0 -152
- package/dist/webrtc/signaling.js.map +0 -1
- package/src/ai/commitSummary.ts +0 -199
- package/src/config.ts +0 -97
- package/src/connection/connection.ts +0 -223
- package/src/connection/signaling.ts +0 -172
- package/src/git/operations.test.ts +0 -305
- package/src/git/operations.ts +0 -443
- package/src/handlers/messageHandler.test.ts +0 -294
- package/src/handlers/messageHandler.ts +0 -550
- package/src/index.ts +0 -151
- package/src/types/webrtc.d.ts +0 -38
- package/tsconfig.json +0 -13
- package/vitest.config.ts +0 -9
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { MessageHandler } from './messageHandler.js';
|
|
3
|
-
import { createMessage } from '@sumicom/quicksave-shared';
|
|
4
|
-
import { mkdir, writeFile, rm } from 'fs/promises';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
-
import { tmpdir } from 'os';
|
|
7
|
-
import { simpleGit } from 'simple-git';
|
|
8
|
-
|
|
9
|
-
describe('MessageHandler', () => {
|
|
10
|
-
let testRepoPath: string;
|
|
11
|
-
let handler: MessageHandler;
|
|
12
|
-
let defaultBranch: string;
|
|
13
|
-
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
// Create temporary test repo
|
|
16
|
-
testRepoPath = join(tmpdir(), `quicksave-handler-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
17
|
-
await mkdir(testRepoPath, { recursive: true });
|
|
18
|
-
|
|
19
|
-
// Initialize git repo
|
|
20
|
-
const git = simpleGit(testRepoPath);
|
|
21
|
-
await git.init();
|
|
22
|
-
await git.addConfig('user.email', 'test@test.com');
|
|
23
|
-
await git.addConfig('user.name', 'Test User');
|
|
24
|
-
|
|
25
|
-
// Create initial commit
|
|
26
|
-
await writeFile(join(testRepoPath, 'README.md'), '# Test Repo\n');
|
|
27
|
-
await git.add('README.md');
|
|
28
|
-
await git.commit('Initial commit');
|
|
29
|
-
|
|
30
|
-
// Get the default branch name (could be 'main' or 'master' depending on git config)
|
|
31
|
-
const status = await git.status();
|
|
32
|
-
defaultBranch = status.current || 'main';
|
|
33
|
-
|
|
34
|
-
handler = new MessageHandler([{ path: testRepoPath, name: 'test-repo' }]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
afterEach(async () => {
|
|
38
|
-
try {
|
|
39
|
-
await rm(testRepoPath, { recursive: true, force: true });
|
|
40
|
-
} catch {
|
|
41
|
-
// Ignore cleanup errors
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('handleMessage - ping/pong', () => {
|
|
46
|
-
it('should respond to ping with pong', async () => {
|
|
47
|
-
const message = createMessage('ping', { timestamp: Date.now() });
|
|
48
|
-
const response = await handler.handleMessage(message);
|
|
49
|
-
|
|
50
|
-
expect(response.type).toBe('pong');
|
|
51
|
-
expect(response.payload).toHaveProperty('timestamp');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('handleMessage - handshake', () => {
|
|
56
|
-
it('should respond to handshake with ack', async () => {
|
|
57
|
-
const message = createMessage('handshake', { publicKey: 'test-key' });
|
|
58
|
-
const response = await handler.handleMessage(message);
|
|
59
|
-
|
|
60
|
-
expect(response.type).toBe('handshake:ack');
|
|
61
|
-
expect(response.id).toBe(message.id);
|
|
62
|
-
expect((response.payload as any).success).toBe(true);
|
|
63
|
-
expect((response.payload as any).agentVersion).toBe('0.1.0');
|
|
64
|
-
expect((response.payload as any).repoPath).toBe(testRepoPath);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('handleMessage - git:status', () => {
|
|
69
|
-
it('should return git status', async () => {
|
|
70
|
-
const message = createMessage('git:status', {});
|
|
71
|
-
const response = await handler.handleMessage(message);
|
|
72
|
-
|
|
73
|
-
expect(response.type).toBe('git:status:response');
|
|
74
|
-
expect(response.id).toBe(message.id);
|
|
75
|
-
|
|
76
|
-
const payload = response.payload as any;
|
|
77
|
-
expect(payload.branch).toBe(defaultBranch);
|
|
78
|
-
expect(Array.isArray(payload.staged)).toBe(true);
|
|
79
|
-
expect(Array.isArray(payload.unstaged)).toBe(true);
|
|
80
|
-
expect(Array.isArray(payload.untracked)).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should detect changes in status', async () => {
|
|
84
|
-
await writeFile(join(testRepoPath, 'newfile.txt'), 'content');
|
|
85
|
-
|
|
86
|
-
const message = createMessage('git:status', {});
|
|
87
|
-
const response = await handler.handleMessage(message);
|
|
88
|
-
|
|
89
|
-
const payload = response.payload as any;
|
|
90
|
-
expect(payload.untracked).toContain('newfile.txt');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('handleMessage - git:diff', () => {
|
|
95
|
-
it('should return diff for modified file', async () => {
|
|
96
|
-
await writeFile(join(testRepoPath, 'README.md'), '# Modified\n');
|
|
97
|
-
|
|
98
|
-
const message = createMessage('git:diff', { path: 'README.md', staged: false });
|
|
99
|
-
const response = await handler.handleMessage(message);
|
|
100
|
-
|
|
101
|
-
expect(response.type).toBe('git:diff:response');
|
|
102
|
-
expect(response.id).toBe(message.id);
|
|
103
|
-
|
|
104
|
-
const payload = response.payload as any;
|
|
105
|
-
expect(payload.path).toBe('README.md');
|
|
106
|
-
expect(payload.hunks.length).toBeGreaterThan(0);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe('handleMessage - git:stage', () => {
|
|
111
|
-
it('should stage files successfully', async () => {
|
|
112
|
-
await writeFile(join(testRepoPath, 'newfile.txt'), 'content');
|
|
113
|
-
|
|
114
|
-
const message = createMessage('git:stage', { paths: ['newfile.txt'] });
|
|
115
|
-
const response = await handler.handleMessage(message);
|
|
116
|
-
|
|
117
|
-
expect(response.type).toBe('git:stage:response');
|
|
118
|
-
expect((response.payload as any).success).toBe(true);
|
|
119
|
-
|
|
120
|
-
// Verify file was staged
|
|
121
|
-
const statusMsg = createMessage('git:status', {});
|
|
122
|
-
const statusResp = await handler.handleMessage(statusMsg);
|
|
123
|
-
const status = statusResp.payload as any;
|
|
124
|
-
expect(status.staged.some((f: any) => f.path === 'newfile.txt')).toBe(true);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should return error for invalid path', async () => {
|
|
128
|
-
const message = createMessage('git:stage', { paths: ['nonexistent.txt'] });
|
|
129
|
-
const response = await handler.handleMessage(message);
|
|
130
|
-
|
|
131
|
-
expect(response.type).toBe('git:stage:response');
|
|
132
|
-
expect((response.payload as any).success).toBe(false);
|
|
133
|
-
expect((response.payload as any).error).toBeDefined();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe('handleMessage - git:unstage', () => {
|
|
138
|
-
it('should unstage files successfully', async () => {
|
|
139
|
-
await writeFile(join(testRepoPath, 'newfile.txt'), 'content');
|
|
140
|
-
await simpleGit(testRepoPath).add('newfile.txt');
|
|
141
|
-
|
|
142
|
-
const message = createMessage('git:unstage', { paths: ['newfile.txt'] });
|
|
143
|
-
const response = await handler.handleMessage(message);
|
|
144
|
-
|
|
145
|
-
expect(response.type).toBe('git:unstage:response');
|
|
146
|
-
expect((response.payload as any).success).toBe(true);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('handleMessage - git:commit', () => {
|
|
151
|
-
it('should create commit successfully', async () => {
|
|
152
|
-
await writeFile(join(testRepoPath, 'newfile.txt'), 'content');
|
|
153
|
-
await simpleGit(testRepoPath).add('newfile.txt');
|
|
154
|
-
|
|
155
|
-
const message = createMessage('git:commit', { message: 'Test commit' });
|
|
156
|
-
const response = await handler.handleMessage(message);
|
|
157
|
-
|
|
158
|
-
expect(response.type).toBe('git:commit:response');
|
|
159
|
-
expect((response.payload as any).success).toBe(true);
|
|
160
|
-
expect((response.payload as any).hash).toBeDefined();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should create commit with description', async () => {
|
|
164
|
-
await writeFile(join(testRepoPath, 'newfile.txt'), 'content');
|
|
165
|
-
await simpleGit(testRepoPath).add('newfile.txt');
|
|
166
|
-
|
|
167
|
-
const message = createMessage('git:commit', {
|
|
168
|
-
message: 'Title',
|
|
169
|
-
description: 'Extended description',
|
|
170
|
-
});
|
|
171
|
-
const response = await handler.handleMessage(message);
|
|
172
|
-
|
|
173
|
-
expect(response.type).toBe('git:commit:response');
|
|
174
|
-
expect((response.payload as any).success).toBe(true);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should handle empty commit gracefully', async () => {
|
|
178
|
-
const message = createMessage('git:commit', { message: 'Empty commit' });
|
|
179
|
-
const response = await handler.handleMessage(message);
|
|
180
|
-
|
|
181
|
-
// The behavior depends on git - it might succeed with empty commit or fail
|
|
182
|
-
// We just verify it returns a proper response
|
|
183
|
-
expect(response.type).toBe('git:commit:response');
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe('handleMessage - git:log', () => {
|
|
188
|
-
it('should return commit log', async () => {
|
|
189
|
-
const message = createMessage('git:log', { limit: 10 });
|
|
190
|
-
const response = await handler.handleMessage(message);
|
|
191
|
-
|
|
192
|
-
expect(response.type).toBe('git:log:response');
|
|
193
|
-
expect(response.id).toBe(message.id);
|
|
194
|
-
|
|
195
|
-
const payload = response.payload as any;
|
|
196
|
-
expect(Array.isArray(payload.commits)).toBe(true);
|
|
197
|
-
expect(payload.commits.length).toBeGreaterThan(0);
|
|
198
|
-
expect(payload.commits[0].message).toBe('Initial commit');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should use default limit when not specified', async () => {
|
|
202
|
-
const message = createMessage('git:log', {});
|
|
203
|
-
const response = await handler.handleMessage(message);
|
|
204
|
-
|
|
205
|
-
expect(response.type).toBe('git:log:response');
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe('handleMessage - git:branches', () => {
|
|
210
|
-
it('should return branches', async () => {
|
|
211
|
-
const message = createMessage('git:branches', {});
|
|
212
|
-
const response = await handler.handleMessage(message);
|
|
213
|
-
|
|
214
|
-
expect(response.type).toBe('git:branches:response');
|
|
215
|
-
|
|
216
|
-
const payload = response.payload as any;
|
|
217
|
-
expect(Array.isArray(payload.branches)).toBe(true);
|
|
218
|
-
expect(payload.current).toBe(defaultBranch);
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('handleMessage - git:checkout', () => {
|
|
223
|
-
it('should checkout existing branch', async () => {
|
|
224
|
-
await simpleGit(testRepoPath).checkoutLocalBranch('feature');
|
|
225
|
-
|
|
226
|
-
const message = createMessage('git:checkout', { branch: defaultBranch });
|
|
227
|
-
const response = await handler.handleMessage(message);
|
|
228
|
-
|
|
229
|
-
expect(response.type).toBe('git:checkout:response');
|
|
230
|
-
expect((response.payload as any).success).toBe(true);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should create and checkout new branch', async () => {
|
|
234
|
-
const message = createMessage('git:checkout', { branch: 'new-feature', create: true });
|
|
235
|
-
const response = await handler.handleMessage(message);
|
|
236
|
-
|
|
237
|
-
expect(response.type).toBe('git:checkout:response');
|
|
238
|
-
expect((response.payload as any).success).toBe(true);
|
|
239
|
-
|
|
240
|
-
// Verify branch was created
|
|
241
|
-
const branchMsg = createMessage('git:branches', {});
|
|
242
|
-
const branchResp = await handler.handleMessage(branchMsg);
|
|
243
|
-
expect((branchResp.payload as any).current).toBe('new-feature');
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should fail for non-existent branch', async () => {
|
|
247
|
-
const message = createMessage('git:checkout', { branch: 'nonexistent' });
|
|
248
|
-
const response = await handler.handleMessage(message);
|
|
249
|
-
|
|
250
|
-
expect(response.type).toBe('git:checkout:response');
|
|
251
|
-
expect((response.payload as any).success).toBe(false);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
describe('handleMessage - git:discard', () => {
|
|
256
|
-
it('should discard changes', async () => {
|
|
257
|
-
await writeFile(join(testRepoPath, 'README.md'), '# Modified\n');
|
|
258
|
-
|
|
259
|
-
const message = createMessage('git:discard', { paths: ['README.md'] });
|
|
260
|
-
const response = await handler.handleMessage(message);
|
|
261
|
-
|
|
262
|
-
expect(response.type).toBe('git:discard:response');
|
|
263
|
-
expect((response.payload as any).success).toBe(true);
|
|
264
|
-
|
|
265
|
-
// Verify changes were discarded
|
|
266
|
-
const statusMsg = createMessage('git:status', {});
|
|
267
|
-
const statusResp = await handler.handleMessage(statusMsg);
|
|
268
|
-
expect((statusResp.payload as any).unstaged).toHaveLength(0);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe('handleMessage - unknown type', () => {
|
|
273
|
-
it('should return error for unknown message type', async () => {
|
|
274
|
-
const message = createMessage('unknown:type' as any, {});
|
|
275
|
-
const response = await handler.handleMessage(message);
|
|
276
|
-
|
|
277
|
-
expect(response.type).toBe('error');
|
|
278
|
-
expect((response.payload as any).code).toBe('UNKNOWN_MESSAGE_TYPE');
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
describe('handleMessage - error handling', () => {
|
|
283
|
-
it('should preserve message ID in responses', async () => {
|
|
284
|
-
const message = createMessage('ping', {});
|
|
285
|
-
message.id = 'custom-id-123';
|
|
286
|
-
|
|
287
|
-
const response = await handler.handleMessage(message);
|
|
288
|
-
|
|
289
|
-
// pong doesn't preserve ID in current implementation
|
|
290
|
-
// but status and other git ops should
|
|
291
|
-
expect(response).toBeDefined();
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
});
|