experiments in a post-browser web
1#!/bin/bash
2# iOS Simulator E2E Sync Test Setup
3#
4# Usage: ./scripts/ios-sync-test.sh
5#
6# This script:
7# 1. Starts a local server with fresh temp data
8# 2. Creates a server profile (gets UUID for folder-based routing)
9# 3. Seeds test items on the server
10# 4. Finds the iOS simulator app container
11# 5. Pre-configures profiles.json with server URL, API key, server_profile_id
12# 6. Clears last_sync timestamps for a fresh pull
13# 7. Opens Xcode (build & run from there)
14#
15# The server keeps running until you press Ctrl+C.
16
17set -e
18
19SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
20PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
21SERVER_DIR="$PROJECT_DIR/backend/server"
22TAURI_DIR="$PROJECT_DIR/backend/tauri-mobile"
23XCODE_PROJECT="$TAURI_DIR/src-tauri/gen/apple/peek-save.xcodeproj"
24
25# --- Configuration ---
26
27PORT="${PORT:-3459}"
28API_KEY="ios-e2e-key-$(date +%s)"
29SERVER_TEMP_DIR="$(mktemp -d /tmp/e2e-peek-ios-XXXXXX)"
30
31LOCAL_IP=$(ipconfig getifaddr en0 2>/dev/null || echo "localhost")
32SERVER_URL="http://$LOCAL_IP:$PORT"
33
34echo "=========================================="
35echo " iOS E2E Sync Test Setup"
36echo "=========================================="
37echo ""
38echo " Server URL: $SERVER_URL"
39echo " API Key: $API_KEY"
40echo " Data dir: $SERVER_TEMP_DIR"
41echo ""
42
43# --- Cleanup trap ---
44
45SERVER_PID=""
46cleanup() {
47 echo ""
48 echo "Cleaning up..."
49 if [ -n "$SERVER_PID" ]; then
50 kill "$SERVER_PID" 2>/dev/null || true
51 wait "$SERVER_PID" 2>/dev/null || true
52 echo " Stopped server (PID $SERVER_PID)"
53 fi
54 rm -rf "$SERVER_TEMP_DIR"
55 echo " Removed $SERVER_TEMP_DIR"
56}
57trap cleanup EXIT
58
59# --- Step 1: Start server ---
60
61echo "Step 1: Starting server..."
62DATA_DIR="$SERVER_TEMP_DIR" PORT="$PORT" API_KEY="$API_KEY" node "$SERVER_DIR/index.js" &
63SERVER_PID=$!
64
65for i in {1..30}; do
66 if curl -sf "http://localhost:$PORT/" > /dev/null 2>&1; then
67 echo " Server ready on port $PORT (PID $SERVER_PID)"
68 break
69 fi
70 if [ "$i" -eq 30 ]; then
71 echo " ERROR: Server failed to start"
72 exit 1
73 fi
74 sleep 0.5
75done
76
77# --- Step 2: Create server profile ---
78
79echo ""
80echo "Step 2: Creating server profile..."
81PROFILE_RESP=$(curl -sf -X POST "http://localhost:$PORT/profiles" \
82 -H "Authorization: Bearer $API_KEY" \
83 -H "Content-Type: application/json" \
84 -d '{"name":"Default"}')
85
86SERVER_PROFILE_ID=$(echo "$PROFILE_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['profile']['id'])")
87echo " Server profile ID: $SERVER_PROFILE_ID"
88
89# --- Step 3: Seed test data ---
90
91echo ""
92echo "Step 3: Seeding test data..."
93
94PROFILE_PARAM="profile=$SERVER_PROFILE_ID"
95VER_HEADERS='-H "X-Peek-Datastore-Version: 1" -H "X-Peek-Protocol-Version: 1"'
96
97curl -sf -X POST "http://localhost:$PORT/items?$PROFILE_PARAM" \
98 -H "Authorization: Bearer $API_KEY" \
99 -H "Content-Type: application/json" \
100 -H "X-Peek-Datastore-Version: 1" \
101 -H "X-Peek-Protocol-Version: 1" \
102 -d '{"type":"url","content":"https://example.com/from-server","tags":["server","test"]}' > /dev/null
103
104curl -sf -X POST "http://localhost:$PORT/items?$PROFILE_PARAM" \
105 -H "Authorization: Bearer $API_KEY" \
106 -H "Content-Type: application/json" \
107 -H "X-Peek-Datastore-Version: 1" \
108 -H "X-Peek-Protocol-Version: 1" \
109 -d '{"type":"url","content":"https://github.com/anthropics/claude-code","tags":["github","ai"]}' > /dev/null
110
111curl -sf -X POST "http://localhost:$PORT/items?$PROFILE_PARAM" \
112 -H "Authorization: Bearer $API_KEY" \
113 -H "Content-Type: application/json" \
114 -H "X-Peek-Datastore-Version: 1" \
115 -H "X-Peek-Protocol-Version: 1" \
116 -d '{"type":"text","content":"Server test note for e2e sync","tags":["note","test"]}' > /dev/null
117
118curl -sf -X POST "http://localhost:$PORT/items?$PROFILE_PARAM" \
119 -H "Authorization: Bearer $API_KEY" \
120 -H "Content-Type: application/json" \
121 -H "X-Peek-Datastore-Version: 1" \
122 -H "X-Peek-Protocol-Version: 1" \
123 -d '{"type":"url","content":"https://news.ycombinator.com","tags":["news","tech"]}' > /dev/null
124
125echo " Seeded 4 items on server"
126
127# Verify
128ITEM_COUNT=$(curl -sf "http://localhost:$PORT/items?$PROFILE_PARAM" \
129 -H "Authorization: Bearer $API_KEY" \
130 -H "X-Peek-Datastore-Version: 1" \
131 -H "X-Peek-Protocol-Version: 1" | python3 -c "import sys,json; print(len(json.load(sys.stdin)['items']))")
132echo " Verified: $ITEM_COUNT items on server"
133
134# --- Step 4: Find iOS simulator app container ---
135
136echo ""
137echo "Step 4: Configuring iOS simulator..."
138
139APP_GROUP=$(xcrun simctl get_app_container booted com.dietrich.peek-mobile groups 2>/dev/null | grep "group.com.dietrich.peek-mobile" | awk '{print $2}')
140
141if [ -z "$APP_GROUP" ]; then
142 echo " WARNING: iOS app not installed in simulator."
143 echo " Build and run the app once from Xcode, then re-run this script."
144 echo ""
145 echo " Opening Xcode anyway so you can build..."
146 open "$XCODE_PROJECT"
147 echo ""
148 echo " Server is running. Press Ctrl+C to stop."
149 wait "$SERVER_PID"
150 exit 0
151fi
152
153PROFILES_JSON="$APP_GROUP/profiles.json"
154echo " App container: $APP_GROUP"
155
156# --- Step 5: Update profiles.json ---
157
158echo ""
159echo "Step 5: Updating profiles.json..."
160
161python3 << PYEOF
162import json, os
163
164path = "$PROFILES_JSON"
165
166# Read existing config or create minimal one
167config = {"profiles": [], "currentProfileId": ""}
168if os.path.exists(path):
169 try:
170 with open(path) as f:
171 config = json.load(f)
172 except Exception:
173 pass
174
175# Update every profile entry with our test server settings
176for p in config.get("profiles", []):
177 p["server_url"] = "$SERVER_URL"
178 p["api_key"] = "$API_KEY"
179 p["server_profile_id"] = "$SERVER_PROFILE_ID"
180
181# Update global sync settings
182config["sync"] = {
183 "server_url": "$SERVER_URL",
184 "api_key": "$API_KEY",
185 "auto_sync": False
186}
187
188with open(path, "w") as f:
189 json.dump(config, f, indent=2)
190
191n_profiles = len(config.get("profiles", []))
192current = config.get("currentProfileId", "?")
193print(f" Updated {n_profiles} profile(s)")
194print(f" Current profile: {current}")
195print(f" Server profile ID: $SERVER_PROFILE_ID")
196PYEOF
197
198# --- Step 6: Clear last_sync ---
199
200echo ""
201echo "Step 6: Clearing last_sync timestamps..."
202
203for dbfile in "$APP_GROUP"/peek-*.db; do
204 if [ -f "$dbfile" ]; then
205 sqlite3 "$dbfile" "DELETE FROM settings WHERE key = 'last_sync';" 2>/dev/null || true
206 echo " Cleared last_sync in $(basename "$dbfile")"
207 fi
208done
209
210# --- Step 7: Open Xcode ---
211
212echo ""
213echo "Step 7: Opening Xcode..."
214open "$XCODE_PROJECT"
215
216# --- Summary ---
217
218echo ""
219echo "=========================================="
220echo " Ready for E2E Sync Testing"
221echo "=========================================="
222echo ""
223echo " Server: $SERVER_URL"
224echo " API Key: $API_KEY"
225echo " Server Profile ID: $SERVER_PROFILE_ID"
226echo " Items on server: $ITEM_COUNT"
227echo ""
228echo " Test steps:"
229echo " 1. Build & run in Xcode (Debug, iPhone simulator)"
230echo " 2. Force-quit and relaunch app (to pick up profiles.json)"
231echo " 3. Go to Settings → verify server URL"
232echo " 4. Tap 'Sync All' → should pull $ITEM_COUNT items"
233echo " 5. Add a local item, tap Push → verify on server"
234echo ""
235echo " Verify server items:"
236echo " curl -s 'http://localhost:$PORT/items?profile=$SERVER_PROFILE_ID' \\"
237echo " -H 'Authorization: Bearer $API_KEY' | python3 -m json.tool"
238echo ""
239echo " Server is running. Press Ctrl+C to stop."
240echo "=========================================="
241echo ""
242
243wait "$SERVER_PID"