pushwork 1.0.4 → 1.0.7
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 +87 -328
- package/dist/.pushwork/automerge/3P/Dm3ekE2pmjGnWvDaG3vSR7ww98/snapshot/aa2349c94955ea561f698720142f9d884a6872d9f82dc332d578c216beb0df0e +0 -0
- package/dist/.pushwork/automerge/st/orage-adapter-id +1 -0
- package/dist/.pushwork/config.json +15 -0
- package/dist/.pushwork/snapshot.json +7 -0
- package/dist/cli.js +231 -170
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +51 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +799 -0
- package/dist/commands.js.map +1 -0
- package/dist/core/change-detection.d.ts +6 -19
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +101 -80
- package/dist/core/change-detection.js.map +1 -1
- package/dist/{config/index.d.ts → core/config.d.ts} +13 -3
- package/dist/core/config.d.ts.map +1 -0
- package/dist/{config/index.js → core/config.js} +55 -73
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/move-detection.d.ts +12 -50
- package/dist/core/move-detection.d.ts.map +1 -1
- package/dist/core/move-detection.js +58 -139
- package/dist/core/move-detection.js.map +1 -1
- package/dist/core/snapshot.d.ts +0 -4
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/snapshot.js +2 -11
- package/dist/core/snapshot.js.map +1 -1
- package/dist/core/sync-engine.d.ts +5 -11
- package/dist/core/sync-engine.d.ts.map +1 -1
- package/dist/core/sync-engine.js +220 -362
- package/dist/core/sync-engine.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/types/config.d.ts +43 -67
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/documents.d.ts +15 -3
- package/dist/types/documents.d.ts.map +1 -1
- package/dist/types/documents.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/snapshot.d.ts +3 -21
- package/dist/types/snapshot.d.ts.map +1 -1
- package/dist/types/snapshot.js +0 -14
- package/dist/types/snapshot.js.map +1 -1
- package/dist/utils/content.d.ts.map +1 -1
- package/dist/utils/content.js +2 -6
- package/dist/utils/content.js.map +1 -1
- package/dist/utils/directory.d.ts +10 -0
- package/dist/utils/directory.d.ts.map +1 -0
- package/dist/utils/directory.js +37 -0
- package/dist/utils/directory.js.map +1 -0
- package/dist/utils/fs.d.ts +15 -2
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +63 -53
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/mime-types.d.ts.map +1 -1
- package/dist/utils/mime-types.js +11 -4
- package/dist/utils/mime-types.js.map +1 -1
- package/dist/utils/network-sync.d.ts +0 -6
- package/dist/utils/network-sync.d.ts.map +1 -1
- package/dist/utils/network-sync.js +55 -99
- package/dist/utils/network-sync.js.map +1 -1
- package/dist/utils/output.d.ts +129 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +375 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/repo-factory.d.ts +2 -6
- package/dist/utils/repo-factory.d.ts.map +1 -1
- package/dist/utils/repo-factory.js +8 -22
- package/dist/utils/repo-factory.js.map +1 -1
- package/dist/utils/string-similarity.d.ts +14 -0
- package/dist/utils/string-similarity.d.ts.map +1 -0
- package/dist/utils/string-similarity.js +43 -0
- package/dist/utils/string-similarity.js.map +1 -0
- package/dist/utils/trace.d.ts +19 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +68 -0
- package/dist/utils/trace.js.map +1 -0
- package/package.json +17 -12
- package/src/cli.ts +326 -252
- package/src/commands.ts +988 -0
- package/src/core/change-detection.ts +199 -162
- package/src/{config/index.ts → core/config.ts} +65 -82
- package/src/core/index.ts +1 -1
- package/src/core/move-detection.ts +74 -180
- package/src/core/snapshot.ts +2 -12
- package/src/core/sync-engine.ts +248 -499
- package/src/index.ts +0 -10
- package/src/types/config.ts +50 -72
- package/src/types/documents.ts +16 -3
- package/src/types/index.ts +0 -5
- package/src/types/snapshot.ts +1 -23
- package/src/utils/content.ts +2 -6
- package/src/utils/directory.ts +50 -0
- package/src/utils/fs.ts +67 -56
- package/src/utils/index.ts +1 -6
- package/src/utils/mime-types.ts +12 -4
- package/src/utils/network-sync.ts +79 -137
- package/src/utils/output.ts +450 -0
- package/src/utils/repo-factory.ts +13 -31
- package/src/utils/string-similarity.ts +54 -0
- package/src/utils/trace.ts +70 -0
- package/test/integration/exclude-patterns.test.ts +6 -15
- package/test/integration/fuzzer.test.ts +308 -391
- package/test/integration/init-sync.test.ts +89 -0
- package/test/integration/sync-deletion.test.ts +2 -61
- package/test/integration/sync-flow.test.ts +4 -24
- package/test/jest.setup.ts +34 -0
- package/test/unit/deletion-behavior.test.ts +3 -14
- package/test/unit/enhanced-mime-detection.test.ts +0 -22
- package/test/unit/snapshot.test.ts +2 -29
- package/test/unit/sync-convergence.test.ts +3 -198
- package/test/unit/sync-timing.test.ts +0 -44
- package/test/unit/utils.test.ts +0 -2
- package/tsconfig.json +3 -3
- package/dist/browser/browser-sync-engine.d.ts +0 -64
- package/dist/browser/browser-sync-engine.d.ts.map +0 -1
- package/dist/browser/browser-sync-engine.js +0 -303
- package/dist/browser/browser-sync-engine.js.map +0 -1
- package/dist/browser/filesystem-adapter.d.ts +0 -84
- package/dist/browser/filesystem-adapter.d.ts.map +0 -1
- package/dist/browser/filesystem-adapter.js +0 -413
- package/dist/browser/filesystem-adapter.js.map +0 -1
- package/dist/browser/index.d.ts +0 -36
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -90
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/types.d.ts +0 -70
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/browser/types.js +0 -6
- package/dist/browser/types.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -77
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -904
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -19
- package/dist/cli/index.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/core/isomorphic-snapshot.d.ts +0 -58
- package/dist/core/isomorphic-snapshot.d.ts.map +0 -1
- package/dist/core/isomorphic-snapshot.js +0 -204
- package/dist/core/isomorphic-snapshot.js.map +0 -1
- package/dist/platform/browser-filesystem.d.ts +0 -26
- package/dist/platform/browser-filesystem.d.ts.map +0 -1
- package/dist/platform/browser-filesystem.js +0 -91
- package/dist/platform/browser-filesystem.js.map +0 -1
- package/dist/platform/filesystem.d.ts +0 -29
- package/dist/platform/filesystem.d.ts.map +0 -1
- package/dist/platform/filesystem.js +0 -65
- package/dist/platform/filesystem.js.map +0 -1
- package/dist/platform/node-filesystem.d.ts +0 -21
- package/dist/platform/node-filesystem.d.ts.map +0 -1
- package/dist/platform/node-filesystem.js +0 -93
- package/dist/platform/node-filesystem.js.map +0 -1
- package/dist/utils/content-similarity.d.ts +0 -53
- package/dist/utils/content-similarity.d.ts.map +0 -1
- package/dist/utils/content-similarity.js +0 -155
- package/dist/utils/content-similarity.js.map +0 -1
- package/dist/utils/fs-browser.d.ts +0 -57
- package/dist/utils/fs-browser.d.ts.map +0 -1
- package/dist/utils/fs-browser.js +0 -311
- package/dist/utils/fs-browser.js.map +0 -1
- package/dist/utils/fs-node.d.ts +0 -53
- package/dist/utils/fs-node.d.ts.map +0 -1
- package/dist/utils/fs-node.js +0 -220
- package/dist/utils/fs-node.js.map +0 -1
- package/dist/utils/isomorphic.d.ts +0 -29
- package/dist/utils/isomorphic.d.ts.map +0 -1
- package/dist/utils/isomorphic.js +0 -139
- package/dist/utils/isomorphic.js.map +0 -1
- package/dist/utils/pure.d.ts +0 -25
- package/dist/utils/pure.d.ts.map +0 -1
- package/dist/utils/pure.js +0 -112
- package/dist/utils/pure.js.map +0 -1
- package/src/cli/commands.ts +0 -1207
- package/src/cli/index.ts +0 -2
- package/src/utils/content-similarity.ts +0 -194
- package/test/README-TESTING-GAPS.md +0 -174
- package/test/unit/content-similarity.test.ts +0 -236
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as tmp from "tmp";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { SnapshotManager } from "../../src/core";
|
|
6
|
+
|
|
7
|
+
describe("Init Command Integration", () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
let cleanup: () => void;
|
|
10
|
+
const pushworkCmd = `node "${path.join(__dirname, "../../dist/cli.js")}"`;
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
// Build the project before running tests
|
|
14
|
+
execSync("pnpm build", { cwd: path.join(__dirname, "../.."), stdio: "pipe" });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
|
19
|
+
tmpDir = tmpObj.name;
|
|
20
|
+
cleanup = tmpObj.removeCallback;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
cleanup();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("Initial Sync", () => {
|
|
28
|
+
it("should sync existing files during init", async () => {
|
|
29
|
+
// Create some files before initializing
|
|
30
|
+
await fs.writeFile(path.join(tmpDir, "file1.txt"), "Hello, World!");
|
|
31
|
+
await fs.writeFile(path.join(tmpDir, "file2.txt"), "Another file");
|
|
32
|
+
await fs.mkdir(path.join(tmpDir, "subdir"));
|
|
33
|
+
await fs.writeFile(
|
|
34
|
+
path.join(tmpDir, "subdir", "nested.txt"),
|
|
35
|
+
"Nested content"
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Run pushwork init
|
|
39
|
+
execSync(`${pushworkCmd} init "${tmpDir}"`, { stdio: "pipe" });
|
|
40
|
+
|
|
41
|
+
// Verify snapshot was created with file entries
|
|
42
|
+
const snapshotManager = new SnapshotManager(tmpDir);
|
|
43
|
+
const snapshot = await snapshotManager.load();
|
|
44
|
+
expect(snapshot).not.toBeNull();
|
|
45
|
+
expect(snapshot!.files.size).toBeGreaterThanOrEqual(3);
|
|
46
|
+
expect(snapshot!.files.has("file1.txt")).toBe(true);
|
|
47
|
+
expect(snapshot!.files.has("file2.txt")).toBe(true);
|
|
48
|
+
expect(snapshot!.files.has("subdir/nested.txt")).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should handle empty directory during init", async () => {
|
|
52
|
+
// Run pushwork init on empty directory
|
|
53
|
+
execSync(`${pushworkCmd} init "${tmpDir}"`, { stdio: "pipe" });
|
|
54
|
+
|
|
55
|
+
// Verify snapshot was created (even if empty)
|
|
56
|
+
const snapshotManager = new SnapshotManager(tmpDir);
|
|
57
|
+
const snapshot = await snapshotManager.load();
|
|
58
|
+
expect(snapshot).not.toBeNull();
|
|
59
|
+
expect(snapshot!.files.size).toBe(0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should respect exclude patterns during initial sync", async () => {
|
|
63
|
+
// Create files, including some that should be excluded by default
|
|
64
|
+
await fs.writeFile(path.join(tmpDir, "included.txt"), "Include me");
|
|
65
|
+
await fs.mkdir(path.join(tmpDir, "node_modules"));
|
|
66
|
+
await fs.writeFile(
|
|
67
|
+
path.join(tmpDir, "node_modules", "package.json"),
|
|
68
|
+
"{}"
|
|
69
|
+
);
|
|
70
|
+
await fs.mkdir(path.join(tmpDir, ".git"));
|
|
71
|
+
await fs.writeFile(
|
|
72
|
+
path.join(tmpDir, ".git", "config"),
|
|
73
|
+
"[core]"
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Run pushwork init
|
|
77
|
+
execSync(`${pushworkCmd} init "${tmpDir}"`, { stdio: "pipe" });
|
|
78
|
+
|
|
79
|
+
// Verify snapshot only contains included file
|
|
80
|
+
const snapshotManager = new SnapshotManager(tmpDir);
|
|
81
|
+
const snapshot = await snapshotManager.load();
|
|
82
|
+
expect(snapshot).not.toBeNull();
|
|
83
|
+
expect(snapshot!.files.has("included.txt")).toBe(true);
|
|
84
|
+
// node_modules and .git should be excluded by default
|
|
85
|
+
expect(snapshot!.files.has("node_modules/package.json")).toBe(false);
|
|
86
|
+
expect(snapshot!.files.has(".git/config")).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -8,8 +8,6 @@ import {
|
|
|
8
8
|
pathExists,
|
|
9
9
|
} from "../../src/utils";
|
|
10
10
|
import { SnapshotManager } from "../../src/core/snapshot";
|
|
11
|
-
import { ChangeDetector } from "../../src/core/change-detection";
|
|
12
|
-
import { ChangeType } from "../../src/types";
|
|
13
11
|
|
|
14
12
|
describe("Sync Engine Deletion Integration", () => {
|
|
15
13
|
let testDir: string;
|
|
@@ -26,8 +24,6 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
26
24
|
|
|
27
25
|
describe("Deletion Detection Logic", () => {
|
|
28
26
|
it("should properly detect local file deletions", async () => {
|
|
29
|
-
console.log("\n🧪 Testing Local File Deletion Detection");
|
|
30
|
-
|
|
31
27
|
// Create initial state
|
|
32
28
|
const filePath = path.join(testDir, "will-be-deleted.ts");
|
|
33
29
|
const content = "interface ToDelete { id: number; }";
|
|
@@ -43,27 +39,19 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
43
39
|
mimeType: "text/typescript",
|
|
44
40
|
});
|
|
45
41
|
|
|
46
|
-
console.log(`📄 Created file: ${filePath}`);
|
|
47
|
-
console.log(`📸 Snapshot has ${snapshot.files.size} files`);
|
|
48
|
-
|
|
49
42
|
// Verify initial state
|
|
50
43
|
expect(await pathExists(filePath)).toBe(true);
|
|
51
44
|
expect(snapshot.files.has("will-be-deleted.ts")).toBe(true);
|
|
52
45
|
|
|
53
46
|
// Simulate user deleting the file
|
|
54
47
|
await removePath(filePath);
|
|
55
|
-
console.log(`🗑️ File deleted from filesystem`);
|
|
56
48
|
|
|
57
49
|
// File should be gone from filesystem but still in snapshot
|
|
58
50
|
expect(await pathExists(filePath)).toBe(false);
|
|
59
51
|
expect(snapshot.files.has("will-be-deleted.ts")).toBe(true);
|
|
60
|
-
|
|
61
|
-
console.log(`✅ Deletion properly detected`);
|
|
62
52
|
});
|
|
63
53
|
|
|
64
54
|
it("should handle multiple file deletions correctly", async () => {
|
|
65
|
-
console.log("\n🧪 Testing Multiple File Deletions");
|
|
66
|
-
|
|
67
55
|
const testFiles = [
|
|
68
56
|
{ name: "delete1.ts", content: "interface One { x: number; }" },
|
|
69
57
|
{ name: "delete2.js", content: "const two = 'value';" },
|
|
@@ -86,8 +74,6 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
86
74
|
? "text/typescript"
|
|
87
75
|
: "text/plain",
|
|
88
76
|
});
|
|
89
|
-
|
|
90
|
-
console.log(`📄 Created: ${file.name}`);
|
|
91
77
|
}
|
|
92
78
|
|
|
93
79
|
expect(snapshot.files.size).toBe(3);
|
|
@@ -96,7 +82,6 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
96
82
|
for (const file of testFiles) {
|
|
97
83
|
const filePath = path.join(testDir, file.name);
|
|
98
84
|
await removePath(filePath);
|
|
99
|
-
console.log(`🗑️ Deleted: ${file.name}`);
|
|
100
85
|
}
|
|
101
86
|
|
|
102
87
|
// Verify all files are gone from filesystem
|
|
@@ -111,28 +96,21 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
111
96
|
// Simulate sync engine processing the deletions
|
|
112
97
|
for (const file of testFiles) {
|
|
113
98
|
snapshotManager.removeFileEntry(snapshot, file.name);
|
|
114
|
-
console.log(`📸 Removed from snapshot: ${file.name}`);
|
|
115
99
|
}
|
|
116
100
|
|
|
117
101
|
expect(snapshot.files.size).toBe(0);
|
|
118
|
-
console.log(`✅ Multiple deletions handled correctly`);
|
|
119
102
|
});
|
|
120
103
|
});
|
|
121
104
|
|
|
122
105
|
describe("Deletion Timing and Race Conditions", () => {
|
|
123
106
|
it("should handle rapid create-modify-delete sequences", async () => {
|
|
124
|
-
console.log("\n🧪 Testing Rapid Create-Modify-Delete Sequences");
|
|
125
|
-
|
|
126
107
|
const filePath = path.join(testDir, "rapid-changes.ts");
|
|
127
108
|
const snapshot = snapshotManager.createEmpty();
|
|
128
109
|
|
|
129
110
|
for (let i = 0; i < 3; i++) {
|
|
130
|
-
console.log(`\n--- Cycle ${i + 1} ---`);
|
|
131
|
-
|
|
132
111
|
// Create
|
|
133
112
|
const content = `interface Cycle${i} { value: ${i}; }`;
|
|
134
113
|
await writeFileContent(filePath, content);
|
|
135
|
-
console.log(`📄 Created with content: "${content}"`);
|
|
136
114
|
|
|
137
115
|
// Add to snapshot
|
|
138
116
|
snapshotManager.updateFileEntry(snapshot, "rapid-changes.ts", {
|
|
@@ -146,11 +124,9 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
146
124
|
// Modify
|
|
147
125
|
const modifiedContent = content + `\n// Modified in cycle ${i}`;
|
|
148
126
|
await writeFileContent(filePath, modifiedContent);
|
|
149
|
-
console.log(`📝 Modified content`);
|
|
150
127
|
|
|
151
128
|
// Delete
|
|
152
129
|
await removePath(filePath);
|
|
153
|
-
console.log(`🗑️ Deleted`);
|
|
154
130
|
|
|
155
131
|
// Verify deletion
|
|
156
132
|
expect(await pathExists(filePath)).toBe(false);
|
|
@@ -158,19 +134,14 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
158
134
|
// Clean up snapshot
|
|
159
135
|
snapshotManager.removeFileEntry(snapshot, "rapid-changes.ts");
|
|
160
136
|
}
|
|
161
|
-
|
|
162
|
-
console.log(`✅ Rapid sequences handled without errors`);
|
|
163
137
|
});
|
|
164
138
|
|
|
165
139
|
it("should handle deletion during content modification attempts", async () => {
|
|
166
|
-
console.log("\n🧪 Testing Deletion During Modification");
|
|
167
|
-
|
|
168
140
|
const filePath = path.join(testDir, "modify-delete-race.ts");
|
|
169
141
|
const initialContent = "interface Race { test: boolean; }";
|
|
170
142
|
|
|
171
143
|
// Create initial file
|
|
172
144
|
await writeFileContent(filePath, initialContent);
|
|
173
|
-
console.log(`📄 Created file with initial content`);
|
|
174
145
|
|
|
175
146
|
// Start modification and deletion concurrently
|
|
176
147
|
const modifyPromise = writeFileContent(
|
|
@@ -188,14 +159,11 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
188
159
|
|
|
189
160
|
// File should be deleted regardless of modification timing
|
|
190
161
|
expect(await pathExists(filePath)).toBe(false);
|
|
191
|
-
console.log(`✅ File properly deleted despite concurrent modification`);
|
|
192
162
|
});
|
|
193
163
|
});
|
|
194
164
|
|
|
195
165
|
describe("Directory Structure Impact", () => {
|
|
196
166
|
it("should handle deletion of files in nested directories", async () => {
|
|
197
|
-
console.log("\n🧪 Testing Nested Directory File Deletion");
|
|
198
|
-
|
|
199
167
|
// Create nested structure
|
|
200
168
|
const nestedDir = path.join(testDir, "src", "components");
|
|
201
169
|
const filePath = path.join(nestedDir, "Button.tsx");
|
|
@@ -213,12 +181,8 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
213
181
|
mimeType: "text/tsx",
|
|
214
182
|
});
|
|
215
183
|
|
|
216
|
-
console.log(`📁 Created nested structure: ${nestedDir}`);
|
|
217
|
-
console.log(`📄 Created file: src/components/Button.tsx`);
|
|
218
|
-
|
|
219
184
|
// Delete just the file (not the directories)
|
|
220
185
|
await removePath(filePath);
|
|
221
|
-
console.log(`🗑️ Deleted file, kept directory structure`);
|
|
222
186
|
|
|
223
187
|
// File should be gone, directories should remain
|
|
224
188
|
expect(await pathExists(filePath)).toBe(false);
|
|
@@ -228,13 +192,9 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
228
192
|
// Simulate snapshot cleanup
|
|
229
193
|
snapshotManager.removeFileEntry(snapshot, "src/components/Button.tsx");
|
|
230
194
|
expect(snapshot.files.size).toBe(0);
|
|
231
|
-
|
|
232
|
-
console.log(`✅ Nested file deletion handled correctly`);
|
|
233
195
|
});
|
|
234
196
|
|
|
235
197
|
it("should handle deletion of entire directory trees", async () => {
|
|
236
|
-
console.log("\n🧪 Testing Directory Tree Deletion");
|
|
237
|
-
|
|
238
198
|
// Create multiple files in nested structure
|
|
239
199
|
const testStructure = [
|
|
240
200
|
"src/utils/helpers.ts",
|
|
@@ -258,15 +218,12 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
258
218
|
extension: path.extname(relativePath).slice(1),
|
|
259
219
|
mimeType: "text/typescript",
|
|
260
220
|
});
|
|
261
|
-
|
|
262
|
-
console.log(`📄 Created: ${relativePath}`);
|
|
263
221
|
}
|
|
264
222
|
|
|
265
223
|
expect(snapshot.files.size).toBe(5);
|
|
266
224
|
|
|
267
225
|
// Delete entire src directory
|
|
268
226
|
await removePath(path.join(testDir, "src"));
|
|
269
|
-
console.log(`🗑️ Deleted entire src/ directory tree`);
|
|
270
227
|
|
|
271
228
|
// Verify all files and directories are gone
|
|
272
229
|
for (const relativePath of testStructure) {
|
|
@@ -278,58 +235,42 @@ describe("Sync Engine Deletion Integration", () => {
|
|
|
278
235
|
// Simulate snapshot cleanup for all files
|
|
279
236
|
for (const relativePath of testStructure) {
|
|
280
237
|
snapshotManager.removeFileEntry(snapshot, relativePath);
|
|
281
|
-
console.log(`📸 Removed from snapshot: ${relativePath}`);
|
|
282
238
|
}
|
|
283
239
|
|
|
284
240
|
expect(snapshot.files.size).toBe(0);
|
|
285
|
-
console.log(`✅ Directory tree deletion handled correctly`);
|
|
286
241
|
});
|
|
287
242
|
});
|
|
288
243
|
|
|
289
244
|
describe("Error Recovery and Edge Cases", () => {
|
|
290
245
|
it("should handle deletion of non-existent files gracefully", async () => {
|
|
291
|
-
console.log("\n🧪 Testing Non-Existent File Deletion");
|
|
292
|
-
|
|
293
246
|
const nonExistentPath = path.join(testDir, "never-existed.ts");
|
|
294
247
|
|
|
295
248
|
// Attempt to delete non-existent file (should not throw)
|
|
296
249
|
await expect(removePath(nonExistentPath)).resolves.not.toThrow();
|
|
297
|
-
console.log(`✅ Non-existent file deletion handled gracefully`);
|
|
298
250
|
|
|
299
251
|
// Attempt to remove from snapshot (should not throw)
|
|
300
252
|
const snapshot = snapshotManager.createEmpty();
|
|
301
253
|
expect(() => {
|
|
302
254
|
snapshotManager.removeFileEntry(snapshot, "never-existed.ts");
|
|
303
255
|
}).not.toThrow();
|
|
304
|
-
console.log(`✅ Non-existent snapshot entry removal handled gracefully`);
|
|
305
256
|
});
|
|
306
257
|
|
|
307
258
|
it("should provide debugging info for deletion failures", async () => {
|
|
308
|
-
console.log("\n🧪 Deletion Debugging Information");
|
|
309
|
-
|
|
310
259
|
const debugFilePath = path.join(testDir, "debug-deletion.ts");
|
|
311
260
|
const content = "interface Debug { info: string; }";
|
|
312
261
|
|
|
313
262
|
try {
|
|
314
263
|
// Create file
|
|
315
264
|
await writeFileContent(debugFilePath, content);
|
|
316
|
-
console.log(`📄 File created: ${debugFilePath}`);
|
|
317
|
-
console.log(`📏 File size: ${content.length} characters`);
|
|
318
265
|
|
|
319
266
|
// Verify file exists and is readable
|
|
320
267
|
const readBack = await readFileContent(debugFilePath);
|
|
321
|
-
|
|
322
|
-
console.log(`✅ Content matches: ${readBack === content}`);
|
|
268
|
+
expect(readBack).toBe(content);
|
|
323
269
|
|
|
324
270
|
// Delete file
|
|
325
|
-
const deleteStart = Date.now();
|
|
326
271
|
await removePath(debugFilePath);
|
|
327
|
-
const deleteTime = Date.now() - deleteStart;
|
|
328
272
|
|
|
329
|
-
|
|
330
|
-
console.log(
|
|
331
|
-
`🔍 File exists after deletion: ${await pathExists(debugFilePath)}`
|
|
332
|
-
);
|
|
273
|
+
expect(await pathExists(debugFilePath)).toBe(false);
|
|
333
274
|
} catch (error) {
|
|
334
275
|
console.error(`❌ Deletion test failed:`, error);
|
|
335
276
|
throw error;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as tmp from "tmp";
|
|
4
|
-
import { ConfigManager } from "../../src/
|
|
4
|
+
import { ConfigManager } from "../../src/core";
|
|
5
5
|
import { DirectoryConfig } from "../../src/types";
|
|
6
6
|
|
|
7
7
|
describe("Sync Flow Integration", () => {
|
|
@@ -26,18 +26,9 @@ describe("Sync Flow Integration", () => {
|
|
|
26
26
|
const testConfig: DirectoryConfig = {
|
|
27
27
|
sync_server: "wss://test.server.com",
|
|
28
28
|
sync_enabled: true,
|
|
29
|
-
|
|
30
|
-
exclude_patterns: [".git", "*.tmp"],
|
|
31
|
-
large_file_threshold: "1MB",
|
|
32
|
-
},
|
|
33
|
-
diff: {
|
|
34
|
-
show_binary: false,
|
|
35
|
-
},
|
|
29
|
+
exclude_patterns: [".git", "*.tmp"],
|
|
36
30
|
sync: {
|
|
37
31
|
move_detection_threshold: 0.8,
|
|
38
|
-
prompt_threshold: 0.5,
|
|
39
|
-
auto_sync: false,
|
|
40
|
-
parallel_operations: 2,
|
|
41
32
|
},
|
|
42
33
|
};
|
|
43
34
|
|
|
@@ -57,18 +48,9 @@ describe("Sync Flow Integration", () => {
|
|
|
57
48
|
const localConfig: DirectoryConfig = {
|
|
58
49
|
sync_server: "wss://local.server.com",
|
|
59
50
|
sync_enabled: true,
|
|
60
|
-
|
|
61
|
-
exclude_patterns: [".git", "*.tmp"],
|
|
62
|
-
large_file_threshold: "5MB",
|
|
63
|
-
},
|
|
64
|
-
diff: {
|
|
65
|
-
show_binary: true,
|
|
66
|
-
},
|
|
51
|
+
exclude_patterns: [".git", "*.tmp"],
|
|
67
52
|
sync: {
|
|
68
53
|
move_detection_threshold: 0.9,
|
|
69
|
-
prompt_threshold: 0.6,
|
|
70
|
-
auto_sync: true,
|
|
71
|
-
parallel_operations: 1,
|
|
72
54
|
},
|
|
73
55
|
};
|
|
74
56
|
|
|
@@ -77,9 +59,7 @@ describe("Sync Flow Integration", () => {
|
|
|
77
59
|
// Verify merged config
|
|
78
60
|
const mergedConfig = await configManager.getMerged();
|
|
79
61
|
expect(mergedConfig.sync_server).toBe("wss://local.server.com");
|
|
80
|
-
expect(mergedConfig.
|
|
81
|
-
expect(mergedConfig.defaults?.large_file_threshold).toBe("5MB");
|
|
82
|
-
expect(mergedConfig.diff?.show_binary).toBe(true);
|
|
62
|
+
expect(mergedConfig.exclude_patterns).toContain(".git");
|
|
83
63
|
expect(mergedConfig.sync?.move_detection_threshold).toBe(0.9);
|
|
84
64
|
});
|
|
85
65
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jest setup file to mock ESM modules that don't work well with Jest
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Mock chalk (ESM-only module)
|
|
6
|
+
jest.mock("chalk", () => ({
|
|
7
|
+
__esModule: true,
|
|
8
|
+
default: new Proxy(
|
|
9
|
+
{},
|
|
10
|
+
{
|
|
11
|
+
get: (target, prop) => {
|
|
12
|
+
if (prop === "default") return target;
|
|
13
|
+
// Return a function that returns the input string unchanged
|
|
14
|
+
return (str: string) => str;
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// Mock ora (ESM-only module)
|
|
21
|
+
jest.mock("ora", () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
default: jest.fn(() => ({
|
|
24
|
+
start: jest.fn().mockReturnThis(),
|
|
25
|
+
stop: jest.fn().mockReturnThis(),
|
|
26
|
+
succeed: jest.fn().mockReturnThis(),
|
|
27
|
+
fail: jest.fn().mockReturnThis(),
|
|
28
|
+
warn: jest.fn().mockReturnThis(),
|
|
29
|
+
info: jest.fn().mockReturnThis(),
|
|
30
|
+
clear: jest.fn().mockReturnThis(),
|
|
31
|
+
text: "",
|
|
32
|
+
})),
|
|
33
|
+
}));
|
|
34
|
+
|
|
@@ -9,18 +9,16 @@ import {
|
|
|
9
9
|
} from "../../src/utils";
|
|
10
10
|
import { SnapshotManager } from "../../src/core/snapshot";
|
|
11
11
|
import { ChangeDetector } from "../../src/core/change-detection";
|
|
12
|
-
import { ChangeType } from "../../src/types";
|
|
13
12
|
|
|
14
13
|
describe("File Deletion Behavior", () => {
|
|
15
14
|
let testDir: string;
|
|
16
15
|
let snapshotManager: SnapshotManager;
|
|
17
|
-
let changeDetector: ChangeDetector;
|
|
18
16
|
|
|
19
17
|
beforeEach(async () => {
|
|
20
18
|
testDir = await fs.mkdtemp(path.join(tmpdir(), "deletion-test-"));
|
|
21
19
|
snapshotManager = new SnapshotManager(testDir);
|
|
22
20
|
// Create a minimal change detector for testing (without Automerge repo)
|
|
23
|
-
|
|
21
|
+
new ChangeDetector(null as any, testDir, []);
|
|
24
22
|
});
|
|
25
23
|
|
|
26
24
|
afterEach(async () => {
|
|
@@ -230,31 +228,22 @@ describe("File Deletion Behavior", () => {
|
|
|
230
228
|
|
|
231
229
|
describe("Debug Information", () => {
|
|
232
230
|
it("should provide detailed info about deletion behavior", async () => {
|
|
233
|
-
console.log("\n=== Deletion Behavior Debug Info ===");
|
|
234
|
-
|
|
235
231
|
const filePath = path.join(testDir, "debug.txt");
|
|
236
232
|
const content = "Debug test content";
|
|
237
233
|
|
|
238
|
-
console.log(`Test directory: ${testDir}`);
|
|
239
|
-
console.log(`File path: ${filePath}`);
|
|
240
|
-
|
|
241
234
|
// Create file
|
|
242
235
|
await writeFileContent(filePath, content);
|
|
243
|
-
console.log(`✅ File created successfully`);
|
|
244
236
|
|
|
245
237
|
// Verify file content
|
|
246
238
|
const readBack = await readFileContent(filePath);
|
|
247
|
-
|
|
239
|
+
expect(readBack).toBe(content);
|
|
248
240
|
|
|
249
241
|
// Delete file
|
|
250
242
|
await removePath(filePath);
|
|
251
|
-
console.log(`✅ File deleted successfully`);
|
|
252
243
|
|
|
253
244
|
// Verify deletion
|
|
254
245
|
const exists = await pathExists(filePath);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
console.log("=== End Debug Info ===\n");
|
|
246
|
+
expect(exists).toBe(false);
|
|
258
247
|
});
|
|
259
248
|
});
|
|
260
249
|
});
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
isEnhancedTextFile,
|
|
7
7
|
shouldForceAsText,
|
|
8
8
|
getMimeType,
|
|
9
|
-
isTextFile,
|
|
10
9
|
} from "../../src/utils";
|
|
11
10
|
|
|
12
11
|
describe("Enhanced MIME Detection", () => {
|
|
@@ -27,19 +26,12 @@ describe("Enhanced MIME Detection", () => {
|
|
|
27
26
|
|
|
28
27
|
// Standard MIME detection (broken)
|
|
29
28
|
const standardMime = getMimeType(tsFile);
|
|
30
|
-
const standardIsText = await isTextFile(tsFile);
|
|
31
29
|
|
|
32
30
|
// Enhanced MIME detection (fixed)
|
|
33
31
|
const enhancedMime = getEnhancedMimeType(tsFile);
|
|
34
32
|
const enhancedIsText = await isEnhancedTextFile(tsFile);
|
|
35
33
|
const shouldForce = shouldForceAsText(tsFile);
|
|
36
34
|
|
|
37
|
-
console.log("TypeScript file (.ts) detection:");
|
|
38
|
-
console.log(` Standard MIME: ${standardMime}, Text: ${standardIsText}`);
|
|
39
|
-
console.log(
|
|
40
|
-
` Enhanced MIME: ${enhancedMime}, Text: ${enhancedIsText}, Force: ${shouldForce}`
|
|
41
|
-
);
|
|
42
|
-
|
|
43
35
|
// Verify the fix
|
|
44
36
|
expect(enhancedMime).toBe("text/typescript"); // Fixed!
|
|
45
37
|
expect(enhancedIsText).toBe(true); // Fixed!
|
|
@@ -57,10 +49,6 @@ describe("Enhanced MIME Detection", () => {
|
|
|
57
49
|
const enhancedMime = getEnhancedMimeType(tsxFile);
|
|
58
50
|
const enhancedIsText = await isEnhancedTextFile(tsxFile);
|
|
59
51
|
|
|
60
|
-
console.log("TSX file detection:");
|
|
61
|
-
console.log(` Standard MIME: ${standardMime}`);
|
|
62
|
-
console.log(` Enhanced MIME: ${enhancedMime}, Text: ${enhancedIsText}`);
|
|
63
|
-
|
|
64
52
|
expect(enhancedMime).toBe("text/tsx"); // Fixed!
|
|
65
53
|
expect(enhancedIsText).toBe(true); // Fixed!
|
|
66
54
|
expect(standardMime).toBe("application/octet-stream"); // Original problem
|
|
@@ -180,9 +168,6 @@ div { color: blue; }
|
|
|
180
168
|
{ name: "font.woff2", mime: "font/woff2", text: false },
|
|
181
169
|
];
|
|
182
170
|
|
|
183
|
-
console.log("\nComprehensive Developer File Detection:");
|
|
184
|
-
console.log("=====================================");
|
|
185
|
-
|
|
186
171
|
for (const file of developerFiles) {
|
|
187
172
|
const filePath = path.join(testDir, file.name);
|
|
188
173
|
|
|
@@ -195,13 +180,6 @@ div { color: blue; }
|
|
|
195
180
|
|
|
196
181
|
const enhancedMime = getEnhancedMimeType(filePath);
|
|
197
182
|
const enhancedIsText = await isEnhancedTextFile(filePath);
|
|
198
|
-
const forced = shouldForceAsText(filePath);
|
|
199
|
-
|
|
200
|
-
console.log(
|
|
201
|
-
`${file.name.padEnd(20)} | MIME: ${enhancedMime.padEnd(
|
|
202
|
-
25
|
|
203
|
-
)} | Text: ${enhancedIsText} | Forced: ${forced}`
|
|
204
|
-
);
|
|
205
183
|
|
|
206
184
|
expect(enhancedMime).toBe(file.mime);
|
|
207
185
|
expect(enhancedIsText).toBe(file.text);
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
1
|
import * as path from "path";
|
|
3
2
|
import * as tmp from "tmp";
|
|
4
3
|
import { SnapshotManager } from "../../src/core/snapshot";
|
|
5
|
-
import {
|
|
6
|
-
SyncSnapshot,
|
|
7
|
-
SnapshotFileEntry,
|
|
8
|
-
SnapshotDirectoryEntry,
|
|
9
|
-
} from "../../src/types";
|
|
4
|
+
import { SnapshotFileEntry, SnapshotDirectoryEntry } from "../../src/types";
|
|
10
5
|
import { UrlHeads } from "@automerge/automerge-repo";
|
|
11
6
|
|
|
12
7
|
describe("SnapshotManager", () => {
|
|
@@ -352,28 +347,6 @@ describe("SnapshotManager", () => {
|
|
|
352
347
|
});
|
|
353
348
|
});
|
|
354
349
|
|
|
355
|
-
describe("backup", () => {
|
|
356
|
-
it("should create backup of existing snapshot", async () => {
|
|
357
|
-
const snapshot = snapshotManager.createEmpty();
|
|
358
|
-
await snapshotManager.save(snapshot);
|
|
359
|
-
|
|
360
|
-
await snapshotManager.backup();
|
|
361
|
-
|
|
362
|
-
// Check that backup file exists
|
|
363
|
-
const syncToolDir = path.join(tmpDir, ".pushwork");
|
|
364
|
-
const files = await fs.readdir(syncToolDir);
|
|
365
|
-
const backupFiles = files.filter((f) =>
|
|
366
|
-
f.startsWith("snapshot.json.backup.")
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
expect(backupFiles.length).toBe(1);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it("should not fail when no snapshot exists", async () => {
|
|
373
|
-
await snapshotManager.backup(); // Should not throw
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
350
|
describe("clone", () => {
|
|
378
351
|
it("should create independent copy of snapshot", () => {
|
|
379
352
|
const originalSnapshot = snapshotManager.createEmpty();
|
|
@@ -419,7 +392,7 @@ describe("SnapshotManager", () => {
|
|
|
419
392
|
const originalTimestamp = snapshot.timestamp;
|
|
420
393
|
|
|
421
394
|
// Add small delay to ensure timestamp difference
|
|
422
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
395
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
423
396
|
|
|
424
397
|
snapshotManager.clear(snapshot);
|
|
425
398
|
|