pushwork 1.0.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/README.md +460 -0
- package/dist/browser/browser-sync-engine.d.ts +64 -0
- package/dist/browser/browser-sync-engine.d.ts.map +1 -0
- package/dist/browser/browser-sync-engine.js +303 -0
- package/dist/browser/browser-sync-engine.js.map +1 -0
- package/dist/browser/filesystem-adapter.d.ts +84 -0
- package/dist/browser/filesystem-adapter.d.ts.map +1 -0
- package/dist/browser/filesystem-adapter.js +413 -0
- package/dist/browser/filesystem-adapter.js.map +1 -0
- package/dist/browser/index.d.ts +36 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +90 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/types.d.ts +70 -0
- package/dist/browser/types.d.ts.map +1 -0
- package/dist/browser/types.js +6 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/cli/commands.d.ts +71 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +794 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +199 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/index.d.ts +71 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +314 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/change-detection.d.ts +78 -0
- package/dist/core/change-detection.d.ts.map +1 -0
- package/dist/core/change-detection.js +370 -0
- package/dist/core/change-detection.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +22 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/isomorphic-snapshot.d.ts +58 -0
- package/dist/core/isomorphic-snapshot.d.ts.map +1 -0
- package/dist/core/isomorphic-snapshot.js +204 -0
- package/dist/core/isomorphic-snapshot.js.map +1 -0
- package/dist/core/move-detection.d.ts +72 -0
- package/dist/core/move-detection.d.ts.map +1 -0
- package/dist/core/move-detection.js +200 -0
- package/dist/core/move-detection.js.map +1 -0
- package/dist/core/snapshot.d.ts +109 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/core/snapshot.js +263 -0
- package/dist/core/snapshot.js.map +1 -0
- package/dist/core/sync-engine.d.ts +110 -0
- package/dist/core/sync-engine.d.ts.map +1 -0
- package/dist/core/sync-engine.js +817 -0
- package/dist/core/sync-engine.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/platform/browser-filesystem.d.ts +26 -0
- package/dist/platform/browser-filesystem.d.ts.map +1 -0
- package/dist/platform/browser-filesystem.js +91 -0
- package/dist/platform/browser-filesystem.js.map +1 -0
- package/dist/platform/filesystem.d.ts +29 -0
- package/dist/platform/filesystem.d.ts.map +1 -0
- package/dist/platform/filesystem.js +65 -0
- package/dist/platform/filesystem.js.map +1 -0
- package/dist/platform/node-filesystem.d.ts +21 -0
- package/dist/platform/node-filesystem.d.ts.map +1 -0
- package/dist/platform/node-filesystem.js +93 -0
- package/dist/platform/node-filesystem.js.map +1 -0
- package/dist/types/config.d.ts +119 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/documents.d.ts +70 -0
- package/dist/types/documents.d.ts.map +1 -0
- package/dist/types/documents.js +23 -0
- package/dist/types/documents.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/snapshot.d.ts +81 -0
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +17 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/utils/content-similarity.d.ts +53 -0
- package/dist/utils/content-similarity.d.ts.map +1 -0
- package/dist/utils/content-similarity.js +155 -0
- package/dist/utils/content-similarity.js.map +1 -0
- package/dist/utils/content.d.ts +5 -0
- package/dist/utils/content.d.ts.map +1 -0
- package/dist/utils/content.js +30 -0
- package/dist/utils/content.js.map +1 -0
- package/dist/utils/fs-browser.d.ts +57 -0
- package/dist/utils/fs-browser.d.ts.map +1 -0
- package/dist/utils/fs-browser.js +311 -0
- package/dist/utils/fs-browser.js.map +1 -0
- package/dist/utils/fs-node.d.ts +53 -0
- package/dist/utils/fs-node.d.ts.map +1 -0
- package/dist/utils/fs-node.js +220 -0
- package/dist/utils/fs-node.js.map +1 -0
- package/dist/utils/fs.d.ts +62 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +293 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/isomorphic.d.ts +29 -0
- package/dist/utils/isomorphic.d.ts.map +1 -0
- package/dist/utils/isomorphic.js +139 -0
- package/dist/utils/isomorphic.js.map +1 -0
- package/dist/utils/mime-types.d.ts +13 -0
- package/dist/utils/mime-types.d.ts.map +1 -0
- package/dist/utils/mime-types.js +240 -0
- package/dist/utils/mime-types.js.map +1 -0
- package/dist/utils/network-sync.d.ts +12 -0
- package/dist/utils/network-sync.d.ts.map +1 -0
- package/dist/utils/network-sync.js +149 -0
- package/dist/utils/network-sync.js.map +1 -0
- package/dist/utils/pure.d.ts +25 -0
- package/dist/utils/pure.d.ts.map +1 -0
- package/dist/utils/pure.js +112 -0
- package/dist/utils/pure.js.map +1 -0
- package/dist/utils/repo-factory.d.ts +11 -0
- package/dist/utils/repo-factory.d.ts.map +1 -0
- package/dist/utils/repo-factory.js +77 -0
- package/dist/utils/repo-factory.js.map +1 -0
- package/package.json +83 -0
- package/src/cli/commands.ts +1053 -0
- package/src/cli/index.ts +2 -0
- package/src/cli.ts +287 -0
- package/src/config/index.ts +334 -0
- package/src/core/change-detection.ts +484 -0
- package/src/core/index.ts +5 -0
- package/src/core/move-detection.ts +269 -0
- package/src/core/snapshot.ts +285 -0
- package/src/core/sync-engine.ts +1167 -0
- package/src/index.ts +14 -0
- package/src/types/config.ts +130 -0
- package/src/types/documents.ts +72 -0
- package/src/types/index.ts +8 -0
- package/src/types/snapshot.ts +88 -0
- package/src/utils/content-similarity.ts +194 -0
- package/src/utils/content.ts +28 -0
- package/src/utils/fs.ts +289 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/mime-types.ts +236 -0
- package/src/utils/network-sync.ts +153 -0
- package/src/utils/repo-factory.ts +58 -0
- package/test/README-TESTING-GAPS.md +174 -0
- package/test/integration/README.md +328 -0
- package/test/integration/clone-test.sh +310 -0
- package/test/integration/conflict-resolution-test.sh +309 -0
- package/test/integration/deletion-behavior-test.sh +487 -0
- package/test/integration/deletion-sync-test-simple.sh +193 -0
- package/test/integration/deletion-sync-test.sh +297 -0
- package/test/integration/exclude-patterns.test.ts +152 -0
- package/test/integration/full-integration-test.sh +363 -0
- package/test/integration/sync-deletion.test.ts +339 -0
- package/test/integration/sync-flow.test.ts +309 -0
- package/test/run-tests.sh +225 -0
- package/test/unit/content-similarity.test.ts +236 -0
- package/test/unit/deletion-behavior.test.ts +260 -0
- package/test/unit/enhanced-mime-detection.test.ts +266 -0
- package/test/unit/snapshot.test.ts +431 -0
- package/test/unit/sync-timing.test.ts +178 -0
- package/test/unit/utils.test.ts +368 -0
- package/tools/browser-sync/README.md +116 -0
- package/tools/browser-sync/package.json +44 -0
- package/tools/browser-sync/patchwork.json +1 -0
- package/tools/browser-sync/pnpm-lock.yaml +4202 -0
- package/tools/browser-sync/src/components/BrowserSyncTool.tsx +599 -0
- package/tools/browser-sync/src/index.ts +20 -0
- package/tools/browser-sync/src/polyfills.ts +31 -0
- package/tools/browser-sync/src/styles.css +290 -0
- package/tools/browser-sync/src/types.ts +27 -0
- package/tools/browser-sync/vite.config.ts +25 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Deletion Sync Test: Bob deletes, Alice receives deletion
|
|
4
|
+
# Tests end-to-end deletion propagation through sync
|
|
5
|
+
|
|
6
|
+
set -e # Exit on any error
|
|
7
|
+
|
|
8
|
+
# Colors for output
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
BLUE='\033[0;34m'
|
|
12
|
+
YELLOW='\033[1;33m'
|
|
13
|
+
NC='\033[0m' # No Color
|
|
14
|
+
|
|
15
|
+
# Test configuration
|
|
16
|
+
TEST_DIR="/tmp/pushwork-deletion-test-$$"
|
|
17
|
+
BOB_DIR="$TEST_DIR/bob"
|
|
18
|
+
ALICE_DIR="$TEST_DIR/alice"
|
|
19
|
+
PUSHWORK_CMD="npm run start --silent --"
|
|
20
|
+
SYNC_SERVER="ws://localhost:3030"
|
|
21
|
+
STORAGE_ID="deletion-test-$(date +%s)"
|
|
22
|
+
TEST_FILE="shared-document.ts"
|
|
23
|
+
TEST_CONTENT="interface SharedInterface { id: number; name: string; }"
|
|
24
|
+
|
|
25
|
+
# Logging functions
|
|
26
|
+
log_info() {
|
|
27
|
+
echo -e "${BLUE}[INFO]${NC} $1"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
log_success() {
|
|
31
|
+
echo -e "${GREEN}[PASS]${NC} $1"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
log_error() {
|
|
35
|
+
echo -e "${RED}[FAIL]${NC} $1"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
log_test() {
|
|
39
|
+
echo -e "${YELLOW}[TEST]${NC} $1"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
log_warning() {
|
|
43
|
+
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Cleanup function
|
|
47
|
+
cleanup() {
|
|
48
|
+
if [ -d "$TEST_DIR" ]; then
|
|
49
|
+
log_info "Cleaning up test directories..."
|
|
50
|
+
rm -rf "$TEST_DIR"
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Error handler
|
|
55
|
+
handle_error() {
|
|
56
|
+
log_error "Test failed at line $1"
|
|
57
|
+
log_info "Bob's directory contents:"
|
|
58
|
+
if [ -d "$BOB_DIR" ]; then
|
|
59
|
+
ls -la "$BOB_DIR" || true
|
|
60
|
+
fi
|
|
61
|
+
log_info "Alice's directory contents:"
|
|
62
|
+
if [ -d "$ALICE_DIR" ]; then
|
|
63
|
+
ls -la "$ALICE_DIR" || true
|
|
64
|
+
fi
|
|
65
|
+
cleanup
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Set up error handling
|
|
70
|
+
trap 'handle_error $LINENO' ERR
|
|
71
|
+
trap cleanup EXIT
|
|
72
|
+
|
|
73
|
+
# Helper function to run pushwork commands
|
|
74
|
+
run_pushwork() {
|
|
75
|
+
local dir="$1"
|
|
76
|
+
local cmd="$2"
|
|
77
|
+
local user="$3"
|
|
78
|
+
|
|
79
|
+
log_test "${user}: Running 'pushwork $cmd'"
|
|
80
|
+
cd "$dir"
|
|
81
|
+
|
|
82
|
+
# Capture both stdout and stderr
|
|
83
|
+
if output=$(eval "$PUSHWORK_CMD $cmd" 2>&1); then
|
|
84
|
+
if [ -n "$output" ]; then
|
|
85
|
+
echo " Output: $output"
|
|
86
|
+
fi
|
|
87
|
+
return 0
|
|
88
|
+
else
|
|
89
|
+
log_error "${user}: Command failed: $output"
|
|
90
|
+
return 1
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Helper function to check if file exists
|
|
95
|
+
check_file_exists() {
|
|
96
|
+
local dir="$1"
|
|
97
|
+
local file="$2"
|
|
98
|
+
local user="$3"
|
|
99
|
+
local should_exist="$4"
|
|
100
|
+
|
|
101
|
+
if [ -f "$dir/$file" ]; then
|
|
102
|
+
if [ "$should_exist" = "true" ]; then
|
|
103
|
+
log_success "${user}: File '$file' exists (expected)"
|
|
104
|
+
return 0
|
|
105
|
+
else
|
|
106
|
+
log_error "${user}: File '$file' exists (should be deleted)"
|
|
107
|
+
return 1
|
|
108
|
+
fi
|
|
109
|
+
else
|
|
110
|
+
if [ "$should_exist" = "false" ]; then
|
|
111
|
+
log_success "${user}: File '$file' is deleted (expected)"
|
|
112
|
+
return 0
|
|
113
|
+
else
|
|
114
|
+
log_error "${user}: File '$file' is missing (should exist)"
|
|
115
|
+
return 1
|
|
116
|
+
fi
|
|
117
|
+
fi
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Helper function to show directory contents
|
|
121
|
+
show_directory_contents() {
|
|
122
|
+
local dir="$1"
|
|
123
|
+
local user="$2"
|
|
124
|
+
|
|
125
|
+
log_info "${user}'s directory contents:"
|
|
126
|
+
cd "$dir"
|
|
127
|
+
if [ "$(ls -A .)" ]; then
|
|
128
|
+
ls -la . | grep -v "^total" | tail -n +2 | while read line; do
|
|
129
|
+
echo " $line"
|
|
130
|
+
done
|
|
131
|
+
else
|
|
132
|
+
echo " (empty directory)"
|
|
133
|
+
fi
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Main test function
|
|
137
|
+
main() {
|
|
138
|
+
echo "======================================"
|
|
139
|
+
echo "Pushwork Deletion Sync Test"
|
|
140
|
+
echo "======================================"
|
|
141
|
+
echo "Testing: Bob deletes file → sync → Alice loses file"
|
|
142
|
+
echo ""
|
|
143
|
+
|
|
144
|
+
# Setup
|
|
145
|
+
log_info "Setting up test environment..."
|
|
146
|
+
mkdir -p "$BOB_DIR" "$ALICE_DIR"
|
|
147
|
+
|
|
148
|
+
log_info "Test configuration:"
|
|
149
|
+
echo " Bob's directory: $BOB_DIR"
|
|
150
|
+
echo " Alice's directory: $ALICE_DIR"
|
|
151
|
+
echo " Sync server: $SYNC_SERVER"
|
|
152
|
+
echo " Storage ID: $STORAGE_ID"
|
|
153
|
+
echo " Test file: $TEST_FILE"
|
|
154
|
+
echo ""
|
|
155
|
+
|
|
156
|
+
# Phase 1: Initialize both repositories
|
|
157
|
+
log_info "=== Phase 1: Initialize Repositories ==="
|
|
158
|
+
|
|
159
|
+
log_test "Initializing Bob's repository..."
|
|
160
|
+
run_pushwork "$BOB_DIR" "init . --sync-server '$SYNC_SERVER' --storage-id '$STORAGE_ID'" "Bob"
|
|
161
|
+
|
|
162
|
+
log_test "Initializing Alice's repository..."
|
|
163
|
+
run_pushwork "$ALICE_DIR" "clone --sync-server '$SYNC_SERVER' --storage-id '$STORAGE_ID' ." "Alice"
|
|
164
|
+
|
|
165
|
+
# Phase 2: Create initial shared file
|
|
166
|
+
log_info "=== Phase 2: Create Shared File ==="
|
|
167
|
+
|
|
168
|
+
log_test "Bob creates the shared file..."
|
|
169
|
+
echo "$TEST_CONTENT" > "$BOB_DIR/$TEST_FILE"
|
|
170
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "true"
|
|
171
|
+
|
|
172
|
+
log_test "Bob commits the file..."
|
|
173
|
+
run_pushwork "$BOB_DIR" "commit ." "Bob"
|
|
174
|
+
|
|
175
|
+
log_test "Bob syncs to share the file..."
|
|
176
|
+
run_pushwork "$BOB_DIR" "sync" "Bob"
|
|
177
|
+
|
|
178
|
+
log_test "Alice syncs to receive the file..."
|
|
179
|
+
run_pushwork "$ALICE_DIR" "sync" "Alice"
|
|
180
|
+
|
|
181
|
+
# Verify both have the file
|
|
182
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "true"
|
|
183
|
+
check_file_exists "$ALICE_DIR" "$TEST_FILE" "Alice" "true"
|
|
184
|
+
|
|
185
|
+
log_success "Phase 2: Both users have the shared file"
|
|
186
|
+
echo ""
|
|
187
|
+
|
|
188
|
+
# Phase 3: Bob deletes the file
|
|
189
|
+
log_info "=== Phase 3: Bob Deletes File ==="
|
|
190
|
+
|
|
191
|
+
show_directory_contents "$BOB_DIR" "Bob (before deletion)"
|
|
192
|
+
|
|
193
|
+
log_test "Bob deletes the shared file..."
|
|
194
|
+
rm "$BOB_DIR/$TEST_FILE"
|
|
195
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "false"
|
|
196
|
+
|
|
197
|
+
show_directory_contents "$BOB_DIR" "Bob (after deletion)"
|
|
198
|
+
|
|
199
|
+
log_test "Bob commits the deletion..."
|
|
200
|
+
run_pushwork "$BOB_DIR" "commit ." "Bob"
|
|
201
|
+
|
|
202
|
+
# Verify file is still gone on Bob's side
|
|
203
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "false"
|
|
204
|
+
|
|
205
|
+
log_success "Phase 3: Bob successfully deleted and committed"
|
|
206
|
+
echo ""
|
|
207
|
+
|
|
208
|
+
# Phase 4: Bob syncs the deletion
|
|
209
|
+
log_info "=== Phase 4: Bob Syncs Deletion ==="
|
|
210
|
+
|
|
211
|
+
log_test "Bob syncs to propagate the deletion..."
|
|
212
|
+
run_pushwork "$BOB_DIR" "sync" "Bob"
|
|
213
|
+
|
|
214
|
+
# Verify file is still gone on Bob's side after sync
|
|
215
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "false"
|
|
216
|
+
|
|
217
|
+
log_test "Bob checks status after sync..."
|
|
218
|
+
run_pushwork "$BOB_DIR" "status" "Bob"
|
|
219
|
+
|
|
220
|
+
log_success "Phase 4: Bob's deletion synced successfully"
|
|
221
|
+
echo ""
|
|
222
|
+
|
|
223
|
+
# Phase 5: Alice syncs to receive the deletion
|
|
224
|
+
log_info "=== Phase 5: Alice Syncs to Receive Deletion ==="
|
|
225
|
+
|
|
226
|
+
show_directory_contents "$ALICE_DIR" "Alice (before sync)"
|
|
227
|
+
|
|
228
|
+
# Alice should still have the file before syncing
|
|
229
|
+
check_file_exists "$ALICE_DIR" "$TEST_FILE" "Alice" "true"
|
|
230
|
+
|
|
231
|
+
log_test "Alice syncs to receive Bob's deletion..."
|
|
232
|
+
run_pushwork "$ALICE_DIR" "sync" "Alice"
|
|
233
|
+
|
|
234
|
+
show_directory_contents "$ALICE_DIR" "Alice (after sync)"
|
|
235
|
+
|
|
236
|
+
# Critical test: Alice should now have the file deleted
|
|
237
|
+
check_file_exists "$ALICE_DIR" "$TEST_FILE" "Alice" "false"
|
|
238
|
+
|
|
239
|
+
log_test "Alice checks status after sync..."
|
|
240
|
+
run_pushwork "$ALICE_DIR" "status" "Alice"
|
|
241
|
+
|
|
242
|
+
log_success "Phase 5: Alice received the deletion successfully"
|
|
243
|
+
echo ""
|
|
244
|
+
|
|
245
|
+
# Phase 6: Verification
|
|
246
|
+
log_info "=== Phase 6: Final Verification ==="
|
|
247
|
+
|
|
248
|
+
log_test "Final verification of both repositories..."
|
|
249
|
+
|
|
250
|
+
# Both should have no trace of the deleted file
|
|
251
|
+
check_file_exists "$BOB_DIR" "$TEST_FILE" "Bob" "false"
|
|
252
|
+
check_file_exists "$ALICE_DIR" "$TEST_FILE" "Alice" "false"
|
|
253
|
+
|
|
254
|
+
# Check for any unexpected files
|
|
255
|
+
show_directory_contents "$BOB_DIR" "Bob (final)"
|
|
256
|
+
show_directory_contents "$ALICE_DIR" "Alice (final)"
|
|
257
|
+
|
|
258
|
+
# Run final status checks
|
|
259
|
+
log_test "Bob's final status:"
|
|
260
|
+
run_pushwork "$BOB_DIR" "status" "Bob"
|
|
261
|
+
|
|
262
|
+
log_test "Alice's final status:"
|
|
263
|
+
run_pushwork "$ALICE_DIR" "status" "Alice"
|
|
264
|
+
|
|
265
|
+
log_success "Phase 6: All verifications passed"
|
|
266
|
+
echo ""
|
|
267
|
+
|
|
268
|
+
# Success!
|
|
269
|
+
echo "======================================"
|
|
270
|
+
echo "🎉 DELETION SYNC TEST PASSED! 🎉"
|
|
271
|
+
echo "======================================"
|
|
272
|
+
echo "✅ Bob deleted file successfully"
|
|
273
|
+
echo "✅ Bob's sync propagated deletion"
|
|
274
|
+
echo "✅ Alice received deletion correctly"
|
|
275
|
+
echo "✅ Both repositories in sync"
|
|
276
|
+
echo ""
|
|
277
|
+
echo "This test validates that file deletions"
|
|
278
|
+
echo "propagate correctly through the sync engine!"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# Check if we're in the right directory
|
|
282
|
+
if [ ! -f "package.json" ] || ! grep -q "pushwork" package.json; then
|
|
283
|
+
log_error "This script must be run from the pushwork project root directory"
|
|
284
|
+
exit 1
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Check if dependencies are available
|
|
288
|
+
if ! command -v npm &> /dev/null; then
|
|
289
|
+
log_error "npm is not installed or not in PATH"
|
|
290
|
+
exit 1
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Run the test
|
|
294
|
+
main
|
|
295
|
+
|
|
296
|
+
echo ""
|
|
297
|
+
echo "Test completed successfully! 🚀"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { ConfigManager } from "../../src/config";
|
|
5
|
+
import { DirectoryConfig } from "../../src/types";
|
|
6
|
+
import {
|
|
7
|
+
ensureDirectoryExists,
|
|
8
|
+
writeFileContent,
|
|
9
|
+
listDirectory,
|
|
10
|
+
} from "../../src/utils";
|
|
11
|
+
|
|
12
|
+
describe("Exclude Patterns", () => {
|
|
13
|
+
let tmpDir: string;
|
|
14
|
+
let syncToolDir: string;
|
|
15
|
+
let configManager: ConfigManager;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
tmpDir = await fs.mkdtemp(path.join(tmpdir(), "sync-test-"));
|
|
19
|
+
syncToolDir = path.join(tmpDir, ".pushwork");
|
|
20
|
+
await ensureDirectoryExists(syncToolDir);
|
|
21
|
+
await ensureDirectoryExists(path.join(syncToolDir, "automerge"));
|
|
22
|
+
|
|
23
|
+
configManager = new ConfigManager(tmpDir);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should exclude .pushwork directory from filesystem listing", async () => {
|
|
31
|
+
// Create files both inside and outside .pushwork directory
|
|
32
|
+
await writeFileContent(
|
|
33
|
+
path.join(tmpDir, "regular-file.txt"),
|
|
34
|
+
"regular content"
|
|
35
|
+
);
|
|
36
|
+
await writeFileContent(
|
|
37
|
+
path.join(tmpDir, "another-file.md"),
|
|
38
|
+
"markdown content"
|
|
39
|
+
);
|
|
40
|
+
await writeFileContent(
|
|
41
|
+
path.join(syncToolDir, "snapshot.json"),
|
|
42
|
+
'{"timestamp": 123}'
|
|
43
|
+
);
|
|
44
|
+
await writeFileContent(
|
|
45
|
+
path.join(syncToolDir, "config.json"),
|
|
46
|
+
'{"test": true}'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Create nested directory with file inside .pushwork
|
|
50
|
+
const nestedDir = path.join(syncToolDir, "nested");
|
|
51
|
+
await ensureDirectoryExists(nestedDir);
|
|
52
|
+
await writeFileContent(
|
|
53
|
+
path.join(nestedDir, "internal.log"),
|
|
54
|
+
"internal log data"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Test listDirectory with exclude patterns
|
|
58
|
+
const excludePatterns = [".pushwork"];
|
|
59
|
+
const entries = await listDirectory(tmpDir, true, excludePatterns);
|
|
60
|
+
|
|
61
|
+
// Verify that .pushwork files are excluded
|
|
62
|
+
const filePaths = entries.map((entry) => path.relative(tmpDir, entry.path));
|
|
63
|
+
|
|
64
|
+
expect(filePaths).toContain("regular-file.txt");
|
|
65
|
+
expect(filePaths).toContain("another-file.md");
|
|
66
|
+
expect(filePaths).not.toContain(".pushwork/snapshot.json");
|
|
67
|
+
expect(filePaths).not.toContain(".pushwork/config.json");
|
|
68
|
+
expect(filePaths).not.toContain(".pushwork/nested/internal.log");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should exclude files matching glob patterns", async () => {
|
|
72
|
+
// Create files that should and shouldn't be excluded
|
|
73
|
+
await writeFileContent(path.join(tmpDir, "include.txt"), "include me");
|
|
74
|
+
await writeFileContent(path.join(tmpDir, "exclude.tmp"), "exclude me");
|
|
75
|
+
await writeFileContent(path.join(tmpDir, "debug.log"), "exclude me too");
|
|
76
|
+
await writeFileContent(path.join(tmpDir, "readme.md"), "include me");
|
|
77
|
+
|
|
78
|
+
// Create node_modules directory with files
|
|
79
|
+
const nodeModulesDir = path.join(tmpDir, "node_modules");
|
|
80
|
+
await ensureDirectoryExists(nodeModulesDir);
|
|
81
|
+
await writeFileContent(
|
|
82
|
+
path.join(nodeModulesDir, "package.json"),
|
|
83
|
+
"exclude me"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Test listDirectory with various exclude patterns
|
|
87
|
+
const excludePatterns = ["*.tmp", "*.log", "node_modules", ".pushwork"];
|
|
88
|
+
const entries = await listDirectory(tmpDir, true, excludePatterns);
|
|
89
|
+
|
|
90
|
+
// Verify correct files are included/excluded
|
|
91
|
+
const filePaths = entries.map((entry) => path.relative(tmpDir, entry.path));
|
|
92
|
+
|
|
93
|
+
expect(filePaths).toContain("include.txt");
|
|
94
|
+
expect(filePaths).toContain("readme.md");
|
|
95
|
+
expect(filePaths).not.toContain("exclude.tmp");
|
|
96
|
+
expect(filePaths).not.toContain("debug.log");
|
|
97
|
+
expect(filePaths).not.toContain("node_modules/package.json");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should use merged configuration exclude patterns", async () => {
|
|
101
|
+
// Create global config
|
|
102
|
+
await configManager.createDefaultGlobal();
|
|
103
|
+
|
|
104
|
+
// Create local config with additional exclude patterns
|
|
105
|
+
const localConfig: DirectoryConfig = {
|
|
106
|
+
sync_server: "wss://test.server.com",
|
|
107
|
+
sync_enabled: true,
|
|
108
|
+
defaults: {
|
|
109
|
+
exclude_patterns: [".git", "*.tmp", ".pushwork", "*.env"],
|
|
110
|
+
large_file_threshold: "100MB",
|
|
111
|
+
},
|
|
112
|
+
diff: {
|
|
113
|
+
show_binary: false,
|
|
114
|
+
},
|
|
115
|
+
sync: {
|
|
116
|
+
move_detection_threshold: 0.8,
|
|
117
|
+
prompt_threshold: 0.5,
|
|
118
|
+
auto_sync: false,
|
|
119
|
+
parallel_operations: 4,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
await configManager.save(localConfig);
|
|
123
|
+
|
|
124
|
+
// Get merged config
|
|
125
|
+
const mergedConfig = await configManager.getMerged();
|
|
126
|
+
|
|
127
|
+
// Verify .pushwork is in the exclude patterns
|
|
128
|
+
expect(mergedConfig.defaults.exclude_patterns).toContain(".pushwork");
|
|
129
|
+
expect(mergedConfig.defaults.exclude_patterns).toContain("*.env");
|
|
130
|
+
expect(mergedConfig.defaults.exclude_patterns).toContain(".git");
|
|
131
|
+
|
|
132
|
+
// Create test files
|
|
133
|
+
await writeFileContent(path.join(tmpDir, "include.txt"), "include me");
|
|
134
|
+
await writeFileContent(path.join(tmpDir, "secret.env"), "exclude me");
|
|
135
|
+
await writeFileContent(
|
|
136
|
+
path.join(syncToolDir, "snapshot.json"),
|
|
137
|
+
"exclude me"
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Test with merged exclude patterns
|
|
141
|
+
const entries = await listDirectory(
|
|
142
|
+
tmpDir,
|
|
143
|
+
true,
|
|
144
|
+
mergedConfig.defaults.exclude_patterns
|
|
145
|
+
);
|
|
146
|
+
const filePaths = entries.map((entry) => path.relative(tmpDir, entry.path));
|
|
147
|
+
|
|
148
|
+
expect(filePaths).toContain("include.txt");
|
|
149
|
+
expect(filePaths).not.toContain("secret.env");
|
|
150
|
+
expect(filePaths).not.toContain(".pushwork/snapshot.json");
|
|
151
|
+
});
|
|
152
|
+
});
|