pushwork 2.0.0-a.sub.1 → 2.0.0-preview.2
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/branches.d.ts +20 -0
- package/dist/branches.d.ts.map +1 -0
- package/dist/branches.js +111 -0
- package/dist/branches.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +245 -270
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +84 -0
- package/dist/config.js.map +1 -0
- package/dist/fs-tree.d.ts +6 -0
- package/dist/fs-tree.d.ts.map +1 -0
- package/dist/fs-tree.js +99 -0
- package/dist/fs-tree.js.map +1 -0
- package/dist/ignore.d.ts +6 -0
- package/dist/ignore.d.ts.map +1 -0
- package/dist/ignore.js +74 -0
- package/dist/ignore.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -4
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +14 -0
- package/dist/log.js.map +1 -0
- package/dist/pushwork.d.ts +129 -0
- package/dist/pushwork.d.ts.map +1 -0
- package/dist/pushwork.js +1062 -0
- package/dist/pushwork.js.map +1 -0
- package/dist/repo.d.ts +14 -0
- package/dist/repo.d.ts.map +1 -0
- package/dist/repo.js +60 -0
- package/dist/repo.js.map +1 -0
- package/dist/shapes/custom.d.ts +3 -0
- package/dist/shapes/custom.d.ts.map +1 -0
- package/dist/shapes/custom.js +57 -0
- package/dist/shapes/custom.js.map +1 -0
- package/dist/shapes/file.d.ts +20 -0
- package/dist/shapes/file.d.ts.map +1 -0
- package/dist/shapes/file.js +140 -0
- package/dist/shapes/file.js.map +1 -0
- package/dist/shapes/index.d.ts +10 -0
- package/dist/shapes/index.d.ts.map +1 -0
- package/dist/shapes/index.js +35 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/patchwork-folder.d.ts +3 -0
- package/dist/shapes/patchwork-folder.d.ts.map +1 -0
- package/dist/shapes/patchwork-folder.js +160 -0
- package/dist/shapes/patchwork-folder.js.map +1 -0
- package/dist/shapes/types.d.ts +38 -0
- package/dist/shapes/types.d.ts.map +1 -0
- package/dist/shapes/types.js +52 -0
- package/dist/shapes/types.js.map +1 -0
- package/dist/shapes/vfs.d.ts +3 -0
- package/dist/shapes/vfs.d.ts.map +1 -0
- package/dist/shapes/vfs.js +92 -0
- package/dist/shapes/vfs.js.map +1 -0
- package/dist/stash.d.ts +23 -0
- package/dist/stash.d.ts.map +1 -0
- package/dist/stash.js +118 -0
- package/dist/stash.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +93 -0
- package/dist/version.js.map +1 -0
- package/package.json +19 -48
- package/patches/@automerge__automerge-repo@2.6.0-subduction.15.patch +26 -0
- package/.prettierrc +0 -9
- package/ARCHITECTURE-ACCORDING-TO-CLAUDE.md +0 -248
- package/CLAUDE.md +0 -141
- package/README.md +0 -221
- package/babel.config.js +0 -5
- package/dist/cli/commands.d.ts +0 -71
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -794
- 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/commands.d.ts +0 -61
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js +0 -861
- package/dist/commands.js.map +0 -1
- package/dist/config/index.d.ts +0 -71
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -314
- package/dist/config/index.js.map +0 -1
- package/dist/core/change-detection.d.ts +0 -80
- package/dist/core/change-detection.d.ts.map +0 -1
- package/dist/core/change-detection.js +0 -523
- package/dist/core/change-detection.js.map +0 -1
- package/dist/core/config.d.ts +0 -81
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/config.js +0 -258
- package/dist/core/config.js.map +0 -1
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -6
- package/dist/core/index.js.map +0 -1
- package/dist/core/move-detection.d.ts +0 -34
- package/dist/core/move-detection.d.ts.map +0 -1
- package/dist/core/move-detection.js +0 -121
- package/dist/core/move-detection.js.map +0 -1
- package/dist/core/snapshot.d.ts +0 -105
- package/dist/core/snapshot.d.ts.map +0 -1
- package/dist/core/snapshot.js +0 -217
- package/dist/core/snapshot.js.map +0 -1
- package/dist/core/sync-engine.d.ts +0 -157
- package/dist/core/sync-engine.d.ts.map +0 -1
- package/dist/core/sync-engine.js +0 -1379
- package/dist/core/sync-engine.js.map +0 -1
- package/dist/types/config.d.ts +0 -99
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js +0 -5
- package/dist/types/config.js.map +0 -1
- package/dist/types/documents.d.ts +0 -88
- package/dist/types/documents.d.ts.map +0 -1
- package/dist/types/documents.js +0 -20
- package/dist/types/documents.js.map +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +0 -1
- package/dist/types/snapshot.d.ts +0 -64
- package/dist/types/snapshot.d.ts.map +0 -1
- package/dist/types/snapshot.js +0 -2
- package/dist/types/snapshot.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/content.d.ts +0 -10
- package/dist/utils/content.d.ts.map +0 -1
- package/dist/utils/content.js +0 -31
- package/dist/utils/content.js.map +0 -1
- package/dist/utils/directory.d.ts +0 -24
- package/dist/utils/directory.d.ts.map +0 -1
- package/dist/utils/directory.js +0 -52
- package/dist/utils/directory.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -74
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js +0 -248
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -5
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/mime-types.d.ts +0 -13
- package/dist/utils/mime-types.d.ts.map +0 -1
- package/dist/utils/mime-types.js +0 -209
- package/dist/utils/mime-types.js.map +0 -1
- package/dist/utils/network-sync.d.ts +0 -36
- package/dist/utils/network-sync.d.ts.map +0 -1
- package/dist/utils/network-sync.js +0 -250
- package/dist/utils/network-sync.js.map +0 -1
- package/dist/utils/node-polyfills.d.ts +0 -9
- package/dist/utils/node-polyfills.d.ts.map +0 -1
- package/dist/utils/node-polyfills.js +0 -9
- package/dist/utils/node-polyfills.js.map +0 -1
- package/dist/utils/output.d.ts +0 -129
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/output.js +0 -368
- package/dist/utils/output.js.map +0 -1
- package/dist/utils/repo-factory.d.ts +0 -13
- package/dist/utils/repo-factory.d.ts.map +0 -1
- package/dist/utils/repo-factory.js +0 -46
- package/dist/utils/repo-factory.js.map +0 -1
- package/dist/utils/string-similarity.d.ts +0 -14
- package/dist/utils/string-similarity.d.ts.map +0 -1
- package/dist/utils/string-similarity.js +0 -39
- package/dist/utils/string-similarity.js.map +0 -1
- package/dist/utils/text-diff.d.ts +0 -37
- package/dist/utils/text-diff.d.ts.map +0 -1
- package/dist/utils/text-diff.js +0 -93
- package/dist/utils/text-diff.js.map +0 -1
- package/dist/utils/trace.d.ts +0 -19
- package/dist/utils/trace.d.ts.map +0 -1
- package/dist/utils/trace.js +0 -63
- package/dist/utils/trace.js.map +0 -1
- package/src/cli.ts +0 -442
- package/src/commands.ts +0 -1134
- package/src/core/change-detection.ts +0 -712
- package/src/core/config.ts +0 -313
- package/src/core/index.ts +0 -5
- package/src/core/move-detection.ts +0 -169
- package/src/core/snapshot.ts +0 -275
- package/src/core/sync-engine.ts +0 -1795
- package/src/index.ts +0 -4
- package/src/types/config.ts +0 -111
- package/src/types/documents.ts +0 -91
- package/src/types/index.ts +0 -3
- package/src/types/snapshot.ts +0 -67
- package/src/utils/content.ts +0 -34
- package/src/utils/directory.ts +0 -73
- package/src/utils/fs.ts +0 -297
- package/src/utils/index.ts +0 -4
- package/src/utils/mime-types.ts +0 -244
- package/src/utils/network-sync.ts +0 -319
- package/src/utils/node-polyfills.ts +0 -8
- package/src/utils/output.ts +0 -450
- package/src/utils/repo-factory.ts +0 -73
- package/src/utils/string-similarity.ts +0 -54
- package/src/utils/text-diff.ts +0 -101
- package/src/utils/trace.ts +0 -70
- package/test/integration/README.md +0 -328
- package/test/integration/clone-test.sh +0 -310
- package/test/integration/conflict-resolution-test.sh +0 -309
- package/test/integration/debug-both-nested.sh +0 -74
- package/test/integration/debug-concurrent-nested.sh +0 -87
- package/test/integration/debug-nested.sh +0 -73
- package/test/integration/deletion-behavior-test.sh +0 -487
- package/test/integration/deletion-sync-test-simple.sh +0 -193
- package/test/integration/deletion-sync-test.sh +0 -297
- package/test/integration/exclude-patterns.test.ts +0 -144
- package/test/integration/full-integration-test.sh +0 -363
- package/test/integration/fuzzer.test.ts +0 -818
- package/test/integration/in-memory-sync.test.ts +0 -830
- package/test/integration/init-sync.test.ts +0 -89
- package/test/integration/manual-sync-test.sh +0 -84
- package/test/integration/sync-deletion.test.ts +0 -280
- package/test/integration/sync-flow.test.ts +0 -291
- package/test/jest.setup.ts +0 -34
- package/test/run-tests.sh +0 -225
- package/test/unit/deletion-behavior.test.ts +0 -249
- package/test/unit/enhanced-mime-detection.test.ts +0 -244
- package/test/unit/snapshot.test.ts +0 -404
- package/test/unit/sync-convergence.test.ts +0 -298
- package/test/unit/sync-timing.test.ts +0 -134
- package/test/unit/utils.test.ts +0 -366
- package/tsconfig.json +0 -23
|
@@ -1,89 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -x # Print commands as they execute
|
|
3
|
-
set -e # Exit on error
|
|
4
|
-
|
|
5
|
-
# Get absolute path to pushwork CLI
|
|
6
|
-
PUSHWORK_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
7
|
-
PUSHWORK_CLI="$PUSHWORK_ROOT/dist/cli.js"
|
|
8
|
-
|
|
9
|
-
echo "Pushwork CLI: $PUSHWORK_CLI"
|
|
10
|
-
|
|
11
|
-
# Create temp directory
|
|
12
|
-
TESTDIR=$(mktemp -d)
|
|
13
|
-
echo "Test directory: $TESTDIR"
|
|
14
|
-
|
|
15
|
-
REPO_A="$TESTDIR/repo-a"
|
|
16
|
-
REPO_B="$TESTDIR/repo-b"
|
|
17
|
-
|
|
18
|
-
mkdir -p "$REPO_A"
|
|
19
|
-
mkdir -p "$REPO_B"
|
|
20
|
-
|
|
21
|
-
# Step 1: Create initial file in repo A
|
|
22
|
-
echo "=== Step 1: Creating initial file in repo A ==="
|
|
23
|
-
echo "initial content" > "$REPO_A/test.txt"
|
|
24
|
-
cat "$REPO_A/test.txt"
|
|
25
|
-
|
|
26
|
-
# Step 2: Initialize repo A
|
|
27
|
-
echo "=== Step 2: Initializing repo A ==="
|
|
28
|
-
cd "$REPO_A"
|
|
29
|
-
node "$PUSHWORK_CLI" init .
|
|
30
|
-
sleep 1
|
|
31
|
-
|
|
32
|
-
# Step 3: Get root URL
|
|
33
|
-
echo "=== Step 3: Getting root URL from repo A ==="
|
|
34
|
-
ROOT_URL=$(node "$PUSHWORK_CLI" url)
|
|
35
|
-
echo "Root URL: $ROOT_URL"
|
|
36
|
-
|
|
37
|
-
# Step 4: Clone to repo B
|
|
38
|
-
echo "=== Step 4: Cloning to repo B ==="
|
|
39
|
-
cd "$TESTDIR"
|
|
40
|
-
node "$PUSHWORK_CLI" clone "$ROOT_URL" "$REPO_B"
|
|
41
|
-
sleep 1
|
|
42
|
-
|
|
43
|
-
# Step 5: Verify initial state
|
|
44
|
-
echo "=== Step 5: Verifying initial state ==="
|
|
45
|
-
echo "Content in A:"
|
|
46
|
-
cat "$REPO_A/test.txt"
|
|
47
|
-
echo "Content in B:"
|
|
48
|
-
cat "$REPO_B/test.txt"
|
|
49
|
-
|
|
50
|
-
# Step 6: Modify file in repo A
|
|
51
|
-
echo "=== Step 6: Modifying file in repo A ==="
|
|
52
|
-
echo "modified content" > "$REPO_A/test.txt"
|
|
53
|
-
echo "New content in A:"
|
|
54
|
-
cat "$REPO_A/test.txt"
|
|
55
|
-
|
|
56
|
-
# Step 7: Sync repo A (THIS IS WHERE IT MIGHT HANG)
|
|
57
|
-
echo "=== Step 7: Syncing repo A ==="
|
|
58
|
-
cd "$REPO_A"
|
|
59
|
-
echo "Running sync in A at $(date)..."
|
|
60
|
-
timeout 10 node "$PUSHWORK_CLI" sync || echo "SYNC A TIMED OUT!"
|
|
61
|
-
echo "Sync A completed at $(date)"
|
|
62
|
-
sleep 1
|
|
63
|
-
|
|
64
|
-
# Step 8: Sync repo B
|
|
65
|
-
echo "=== Step 8: Syncing repo B ==="
|
|
66
|
-
cd "$REPO_B"
|
|
67
|
-
echo "Running sync in B at $(date)..."
|
|
68
|
-
timeout 10 node "$PUSHWORK_CLI" sync || echo "SYNC B TIMED OUT!"
|
|
69
|
-
echo "Sync B completed at $(date)"
|
|
70
|
-
sleep 1
|
|
71
|
-
|
|
72
|
-
# Step 9: Verify final state
|
|
73
|
-
echo "=== Step 9: Verifying final state ==="
|
|
74
|
-
echo "Final content in A:"
|
|
75
|
-
cat "$REPO_A/test.txt"
|
|
76
|
-
echo "Final content in B:"
|
|
77
|
-
cat "$REPO_B/test.txt"
|
|
78
|
-
|
|
79
|
-
# Cleanup
|
|
80
|
-
echo "=== Cleanup ==="
|
|
81
|
-
echo "Test directory: $TESTDIR"
|
|
82
|
-
echo "To inspect manually: cd $TESTDIR"
|
|
83
|
-
# rm -rf "$TESTDIR"
|
|
84
|
-
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import {
|
|
5
|
-
readFileContent,
|
|
6
|
-
writeFileContent,
|
|
7
|
-
removePath,
|
|
8
|
-
pathExists,
|
|
9
|
-
} from "../../src/utils";
|
|
10
|
-
import { SnapshotManager } from "../../src/core/snapshot";
|
|
11
|
-
|
|
12
|
-
describe("Sync Engine Deletion Integration", () => {
|
|
13
|
-
let testDir: string;
|
|
14
|
-
let snapshotManager: SnapshotManager;
|
|
15
|
-
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
testDir = await fs.mkdtemp(path.join(tmpdir(), "sync-deletion-test-"));
|
|
18
|
-
snapshotManager = new SnapshotManager(testDir);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(async () => {
|
|
22
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe("Deletion Detection Logic", () => {
|
|
26
|
-
it("should properly detect local file deletions", async () => {
|
|
27
|
-
// Create initial state
|
|
28
|
-
const filePath = path.join(testDir, "will-be-deleted.ts");
|
|
29
|
-
const content = "interface ToDelete { id: number; }";
|
|
30
|
-
await writeFileContent(filePath, content);
|
|
31
|
-
|
|
32
|
-
// Create snapshot representing the "before" state
|
|
33
|
-
const snapshot = snapshotManager.createEmpty();
|
|
34
|
-
snapshotManager.updateFileEntry(snapshot, "will-be-deleted.ts", {
|
|
35
|
-
path: filePath,
|
|
36
|
-
url: "automerge:deletion-test" as any,
|
|
37
|
-
head: ["before-deletion"] as any,
|
|
38
|
-
extension: "ts",
|
|
39
|
-
mimeType: "text/typescript",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Verify initial state
|
|
43
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
44
|
-
expect(snapshot.files.has("will-be-deleted.ts")).toBe(true);
|
|
45
|
-
|
|
46
|
-
// Simulate user deleting the file
|
|
47
|
-
await removePath(filePath);
|
|
48
|
-
|
|
49
|
-
// File should be gone from filesystem but still in snapshot
|
|
50
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
51
|
-
expect(snapshot.files.has("will-be-deleted.ts")).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should handle multiple file deletions correctly", async () => {
|
|
55
|
-
const testFiles = [
|
|
56
|
-
{ name: "delete1.ts", content: "interface One { x: number; }" },
|
|
57
|
-
{ name: "delete2.js", content: "const two = 'value';" },
|
|
58
|
-
{ name: "delete3.json", content: '{"three": true}' },
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
const snapshot = snapshotManager.createEmpty();
|
|
62
|
-
|
|
63
|
-
// Create all files and add to snapshot
|
|
64
|
-
for (const file of testFiles) {
|
|
65
|
-
const filePath = path.join(testDir, file.name);
|
|
66
|
-
await writeFileContent(filePath, file.content);
|
|
67
|
-
|
|
68
|
-
snapshotManager.updateFileEntry(snapshot, file.name, {
|
|
69
|
-
path: filePath,
|
|
70
|
-
url: `automerge:${file.name}` as any,
|
|
71
|
-
head: [`head-${file.name}`] as any,
|
|
72
|
-
extension: path.extname(file.name).slice(1),
|
|
73
|
-
mimeType: file.name.endsWith(".ts")
|
|
74
|
-
? "text/typescript"
|
|
75
|
-
: "text/plain",
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
expect(snapshot.files.size).toBe(3);
|
|
80
|
-
|
|
81
|
-
// Delete all files
|
|
82
|
-
for (const file of testFiles) {
|
|
83
|
-
const filePath = path.join(testDir, file.name);
|
|
84
|
-
await removePath(filePath);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Verify all files are gone from filesystem
|
|
88
|
-
for (const file of testFiles) {
|
|
89
|
-
const filePath = path.join(testDir, file.name);
|
|
90
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Snapshot should still have entries (until sync processes them)
|
|
94
|
-
expect(snapshot.files.size).toBe(3);
|
|
95
|
-
|
|
96
|
-
// Simulate sync engine processing the deletions
|
|
97
|
-
for (const file of testFiles) {
|
|
98
|
-
snapshotManager.removeFileEntry(snapshot, file.name);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
expect(snapshot.files.size).toBe(0);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe("Deletion Timing and Race Conditions", () => {
|
|
106
|
-
it("should handle rapid create-modify-delete sequences", async () => {
|
|
107
|
-
const filePath = path.join(testDir, "rapid-changes.ts");
|
|
108
|
-
const snapshot = snapshotManager.createEmpty();
|
|
109
|
-
|
|
110
|
-
for (let i = 0; i < 3; i++) {
|
|
111
|
-
// Create
|
|
112
|
-
const content = `interface Cycle${i} { value: ${i}; }`;
|
|
113
|
-
await writeFileContent(filePath, content);
|
|
114
|
-
|
|
115
|
-
// Add to snapshot
|
|
116
|
-
snapshotManager.updateFileEntry(snapshot, "rapid-changes.ts", {
|
|
117
|
-
path: filePath,
|
|
118
|
-
url: `automerge:cycle-${i}` as any,
|
|
119
|
-
head: [`head-${i}`] as any,
|
|
120
|
-
extension: "ts",
|
|
121
|
-
mimeType: "text/typescript",
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Modify
|
|
125
|
-
const modifiedContent = content + `\n// Modified in cycle ${i}`;
|
|
126
|
-
await writeFileContent(filePath, modifiedContent);
|
|
127
|
-
|
|
128
|
-
// Delete
|
|
129
|
-
await removePath(filePath);
|
|
130
|
-
|
|
131
|
-
// Verify deletion
|
|
132
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
133
|
-
|
|
134
|
-
// Clean up snapshot
|
|
135
|
-
snapshotManager.removeFileEntry(snapshot, "rapid-changes.ts");
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("should handle deletion during content modification attempts", async () => {
|
|
140
|
-
const filePath = path.join(testDir, "modify-delete-race.ts");
|
|
141
|
-
const initialContent = "interface Race { test: boolean; }";
|
|
142
|
-
|
|
143
|
-
// Create initial file
|
|
144
|
-
await writeFileContent(filePath, initialContent);
|
|
145
|
-
|
|
146
|
-
// Start modification and deletion concurrently
|
|
147
|
-
const modifyPromise = writeFileContent(
|
|
148
|
-
filePath,
|
|
149
|
-
initialContent + "\n// Modified"
|
|
150
|
-
);
|
|
151
|
-
const deletePromise = (async () => {
|
|
152
|
-
// Small delay to let modification start
|
|
153
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
154
|
-
await removePath(filePath);
|
|
155
|
-
})();
|
|
156
|
-
|
|
157
|
-
// Wait for both operations to complete
|
|
158
|
-
await Promise.allSettled([modifyPromise, deletePromise]);
|
|
159
|
-
|
|
160
|
-
// File should be deleted regardless of modification timing
|
|
161
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe("Directory Structure Impact", () => {
|
|
166
|
-
it("should handle deletion of files in nested directories", async () => {
|
|
167
|
-
// Create nested structure
|
|
168
|
-
const nestedDir = path.join(testDir, "src", "components");
|
|
169
|
-
const filePath = path.join(nestedDir, "Button.tsx");
|
|
170
|
-
const content = "export const Button = () => <button>Click</button>;";
|
|
171
|
-
|
|
172
|
-
await fs.mkdir(nestedDir, { recursive: true });
|
|
173
|
-
await writeFileContent(filePath, content);
|
|
174
|
-
|
|
175
|
-
const snapshot = snapshotManager.createEmpty();
|
|
176
|
-
snapshotManager.updateFileEntry(snapshot, "src/components/Button.tsx", {
|
|
177
|
-
path: filePath,
|
|
178
|
-
url: "automerge:nested-button" as any,
|
|
179
|
-
head: ["nested-head"] as any,
|
|
180
|
-
extension: "tsx",
|
|
181
|
-
mimeType: "text/tsx",
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Delete just the file (not the directories)
|
|
185
|
-
await removePath(filePath);
|
|
186
|
-
|
|
187
|
-
// File should be gone, directories should remain
|
|
188
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
189
|
-
expect(await pathExists(nestedDir)).toBe(true);
|
|
190
|
-
expect(await pathExists(path.join(testDir, "src"))).toBe(true);
|
|
191
|
-
|
|
192
|
-
// Simulate snapshot cleanup
|
|
193
|
-
snapshotManager.removeFileEntry(snapshot, "src/components/Button.tsx");
|
|
194
|
-
expect(snapshot.files.size).toBe(0);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("should handle deletion of entire directory trees", async () => {
|
|
198
|
-
// Create multiple files in nested structure
|
|
199
|
-
const testStructure = [
|
|
200
|
-
"src/utils/helpers.ts",
|
|
201
|
-
"src/utils/constants.ts",
|
|
202
|
-
"src/components/Button.tsx",
|
|
203
|
-
"src/components/Input.tsx",
|
|
204
|
-
"src/types/index.ts",
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
const snapshot = snapshotManager.createEmpty();
|
|
208
|
-
|
|
209
|
-
for (const relativePath of testStructure) {
|
|
210
|
-
const fullPath = path.join(testDir, relativePath);
|
|
211
|
-
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
212
|
-
await writeFileContent(fullPath, `// Content for ${relativePath}`);
|
|
213
|
-
|
|
214
|
-
snapshotManager.updateFileEntry(snapshot, relativePath, {
|
|
215
|
-
path: fullPath,
|
|
216
|
-
url: `automerge:${relativePath.replace(/[\/\.]/g, "-")}` as any,
|
|
217
|
-
head: [`head-${relativePath}`] as any,
|
|
218
|
-
extension: path.extname(relativePath).slice(1),
|
|
219
|
-
mimeType: "text/typescript",
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
expect(snapshot.files.size).toBe(5);
|
|
224
|
-
|
|
225
|
-
// Delete entire src directory
|
|
226
|
-
await removePath(path.join(testDir, "src"));
|
|
227
|
-
|
|
228
|
-
// Verify all files and directories are gone
|
|
229
|
-
for (const relativePath of testStructure) {
|
|
230
|
-
const fullPath = path.join(testDir, relativePath);
|
|
231
|
-
expect(await pathExists(fullPath)).toBe(false);
|
|
232
|
-
}
|
|
233
|
-
expect(await pathExists(path.join(testDir, "src"))).toBe(false);
|
|
234
|
-
|
|
235
|
-
// Simulate snapshot cleanup for all files
|
|
236
|
-
for (const relativePath of testStructure) {
|
|
237
|
-
snapshotManager.removeFileEntry(snapshot, relativePath);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
expect(snapshot.files.size).toBe(0);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
describe("Error Recovery and Edge Cases", () => {
|
|
245
|
-
it("should handle deletion of non-existent files gracefully", async () => {
|
|
246
|
-
const nonExistentPath = path.join(testDir, "never-existed.ts");
|
|
247
|
-
|
|
248
|
-
// Attempt to delete non-existent file (should not throw)
|
|
249
|
-
await expect(removePath(nonExistentPath)).resolves.not.toThrow();
|
|
250
|
-
|
|
251
|
-
// Attempt to remove from snapshot (should not throw)
|
|
252
|
-
const snapshot = snapshotManager.createEmpty();
|
|
253
|
-
expect(() => {
|
|
254
|
-
snapshotManager.removeFileEntry(snapshot, "never-existed.ts");
|
|
255
|
-
}).not.toThrow();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("should provide debugging info for deletion failures", async () => {
|
|
259
|
-
const debugFilePath = path.join(testDir, "debug-deletion.ts");
|
|
260
|
-
const content = "interface Debug { info: string; }";
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
// Create file
|
|
264
|
-
await writeFileContent(debugFilePath, content);
|
|
265
|
-
|
|
266
|
-
// Verify file exists and is readable
|
|
267
|
-
const readBack = await readFileContent(debugFilePath);
|
|
268
|
-
expect(readBack).toBe(content);
|
|
269
|
-
|
|
270
|
-
// Delete file
|
|
271
|
-
await removePath(debugFilePath);
|
|
272
|
-
|
|
273
|
-
expect(await pathExists(debugFilePath)).toBe(false);
|
|
274
|
-
} catch (error) {
|
|
275
|
-
console.error(`❌ Deletion test failed:`, error);
|
|
276
|
-
throw error;
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
});
|