jj workspaces over the network
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add QA.md with comprehensive test plan

+420
+420
QA.md
··· 1 + # Tandem QA Test Plan 2 + 3 + ## Prerequisites 4 + 5 + 1. Rust toolchain installed 6 + 2. `jj` (Jujutsu) installed 7 + 3. `sqlite3` CLI available 8 + 4. `curl` and `jq` for API testing 9 + 5. Two terminal windows minimum 10 + 11 + ## Build 12 + 13 + ```bash 14 + cd /home/lau/code/laulauland/tandem 15 + cargo build --release 16 + ``` 17 + 18 + **Expected**: Build completes with warnings only, no errors. Binaries at: 19 + - `target/release/tandem-server` 20 + - `target/release/jjf` 21 + 22 + --- 23 + 24 + ## Test 1: Server Startup 25 + 26 + ### Steps 27 + ```bash 28 + # Terminal 1 29 + DATABASE_URL=sqlite:tandem.db DATA_DIR=./data ./target/release/tandem-server 30 + ``` 31 + 32 + ### Expected Output 33 + ``` 34 + Server running on http://localhost:3000 35 + ``` 36 + 37 + ### Verify 38 + ```bash 39 + curl http://localhost:3000/health 40 + ``` 41 + 42 + ### Expected Response 43 + ```json 44 + {"status":"ok"} 45 + ``` 46 + 47 + --- 48 + 49 + ## Test 2: User Creation and Authentication 50 + 51 + ### Steps 52 + ```bash 53 + # Create database tables (auto-created on first run, but verify) 54 + sqlite3 tandem.db ".tables" 55 + ``` 56 + 57 + ### Expected Output 58 + ``` 59 + auth_tokens repo_access repos users 60 + ``` 61 + 62 + ### Create Test User 63 + ```bash 64 + sqlite3 tandem.db "INSERT INTO users (id, email, name, password_hash) 65 + VALUES ('user-alice', 'alice@example.com', 'Alice', 'password123');" 66 + ``` 67 + 68 + ### Login 69 + ```bash 70 + curl -s -X POST http://localhost:3000/api/auth/login \ 71 + -H "Content-Type: application/json" \ 72 + -d '{"email":"alice@example.com","password":"password123"}' 73 + ``` 74 + 75 + ### Expected Response 76 + ```json 77 + { 78 + "token": "<64-char-hex-string>", 79 + "expires_at": "<RFC3339-timestamp>" 80 + } 81 + ``` 82 + 83 + ### Save Token 84 + ```bash 85 + export TOKEN="<paste-token-here>" 86 + ``` 87 + 88 + ### Verify Token 89 + ```bash 90 + curl -s http://localhost:3000/api/auth/me \ 91 + -H "Authorization: Bearer $TOKEN" 92 + ``` 93 + 94 + ### Expected Response 95 + ```json 96 + { 97 + "id": "user-alice", 98 + "email": "alice@example.com", 99 + "name": "Alice" 100 + } 101 + ``` 102 + 103 + --- 104 + 105 + ## Test 3: Repository Creation 106 + 107 + ### Steps 108 + ```bash 109 + curl -s -X POST http://localhost:3000/api/repos \ 110 + -H "Authorization: Bearer $TOKEN" \ 111 + -H "Content-Type: application/json" \ 112 + -d '{"name":"my-project","org":"acme"}' 113 + ``` 114 + 115 + ### Expected Response 116 + ```json 117 + { 118 + "id": "<uuid>", 119 + "name": "my-project", 120 + "org": "acme", 121 + "created_at": "<RFC3339-timestamp>" 122 + } 123 + ``` 124 + 125 + ### Save Repo ID 126 + ```bash 127 + export REPO_ID="<paste-repo-id-here>" 128 + ``` 129 + 130 + ### Verify Repo Created 131 + ```bash 132 + curl -s http://localhost:3000/api/repos \ 133 + -H "Authorization: Bearer $TOKEN" 134 + ``` 135 + 136 + ### Expected Response 137 + Array containing the created repo (user has admin access as creator). 138 + 139 + --- 140 + 141 + ## Test 4: Access Control 142 + 143 + ### Create Second User Without Access 144 + ```bash 145 + sqlite3 tandem.db "INSERT INTO users (id, email, name, password_hash) 146 + VALUES ('user-bob', 'bob@example.com', 'Bob', 'password456');" 147 + 148 + # Login as Bob 149 + BOB_TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/login \ 150 + -H "Content-Type: application/json" \ 151 + -d '{"email":"bob@example.com","password":"password456"}' | jq -r .token) 152 + ``` 153 + 154 + ### Try to Access Repo as Bob 155 + ```bash 156 + curl -s http://localhost:3000/api/repos/$REPO_ID \ 157 + -H "Authorization: Bearer $BOB_TOKEN" 158 + ``` 159 + 160 + ### Expected Response 161 + HTTP 403 Forbidden (Bob has no access to Alice's repo) 162 + 163 + ### Grant Bob Read Access 164 + ```bash 165 + sqlite3 tandem.db "INSERT INTO repo_access (repo_id, user_id, role) 166 + VALUES ('$REPO_ID', 'user-bob', 'read');" 167 + ``` 168 + 169 + ### Retry Access 170 + ```bash 171 + curl -s http://localhost:3000/api/repos/$REPO_ID \ 172 + -H "Authorization: Bearer $BOB_TOKEN" 173 + ``` 174 + 175 + ### Expected Response 176 + ```json 177 + { 178 + "id": "<repo-id>", 179 + "name": "my-project", 180 + "org": "acme", 181 + "created_at": "<timestamp>" 182 + } 183 + ``` 184 + 185 + --- 186 + 187 + ## Test 5: jjf CLI - Link Repository 188 + 189 + ### Setup Local jj Repo 190 + ```bash 191 + # Terminal 2 192 + mkdir /tmp/test-project && cd /tmp/test-project 193 + jj init 194 + echo "Hello World" > README.md 195 + jj new -m "Initial commit" 196 + ``` 197 + 198 + ### Link to Forge 199 + ```bash 200 + /home/lau/code/laulauland/tandem/target/release/jjf link \ 201 + http://localhost:3000/acme/my-project \ 202 + --token $TOKEN 203 + ``` 204 + 205 + ### Expected Output 206 + ``` 207 + ✓ Linked to forge: http://localhost:3000/acme/my-project 208 + Run 'jjf daemon start' to begin syncing 209 + ``` 210 + 211 + ### Verify Config Created 212 + ```bash 213 + cat .jj/forge.toml 214 + ``` 215 + 216 + ### Expected Content 217 + ```toml 218 + [forge] 219 + url = "http://localhost:3000/acme/my-project" 220 + ``` 221 + 222 + --- 223 + 224 + ## Test 6: jjf CLI - Status 225 + 226 + ### Steps 227 + ```bash 228 + /home/lau/code/laulauland/tandem/target/release/jjf status 229 + ``` 230 + 231 + ### Expected Output 232 + ``` 233 + Repository: /tmp/test-project 234 + Forge: http://localhost:3000/acme/my-project 235 + Status: Not syncing (daemon not running) 236 + ``` 237 + 238 + --- 239 + 240 + ## Test 7: jjf Daemon - WebSocket Sync 241 + 242 + ### Start Daemon 243 + ```bash 244 + # Terminal 2 (in /tmp/test-project) 245 + /home/lau/code/laulauland/tandem/target/release/jjf daemon start 246 + ``` 247 + 248 + ### Expected Output 249 + ``` 250 + Starting daemon for /tmp/test-project 251 + Connecting to forge: ws://localhost:3000/sync/my-project 252 + ``` 253 + 254 + ### Verify in Server Logs (Terminal 1) 255 + ``` 256 + Client connected to sync for repo my-project 257 + ``` 258 + 259 + ### Stop Daemon 260 + Press `Ctrl+C` 261 + 262 + ### Expected Output 263 + ``` 264 + Daemon shutting down 265 + ``` 266 + 267 + --- 268 + 269 + ## Test 8: jjf Clone 270 + 271 + ### Steps 272 + ```bash 273 + # Terminal 2 274 + cd /tmp 275 + /home/lau/code/laulauland/tandem/target/release/jjf clone \ 276 + http://localhost:3000/acme/my-project \ 277 + --token $TOKEN 278 + ``` 279 + 280 + ### Expected Output 281 + ``` 282 + Cloning into '/tmp/my-project'... 283 + Syncing initial state... 284 + ✓ Received X changes, Y bookmarks 285 + ✓ Linked to forge: http://localhost:3000/acme/my-project 286 + Run 'jjf daemon start' to begin syncing 287 + ✓ Cloned repository to /tmp/my-project 288 + ``` 289 + 290 + ### Verify Clone 291 + ```bash 292 + cd /tmp/my-project 293 + ls -la .jj/ 294 + cat .jj/forge.toml 295 + ``` 296 + 297 + --- 298 + 299 + ## Test 9: WebSocket Broadcast (Multi-Client Sync) 300 + 301 + ### Setup 302 + Start two daemon instances connected to the same repo. 303 + 304 + ### Terminal 2 305 + ```bash 306 + cd /tmp/test-project 307 + /home/lau/code/laulauland/tandem/target/release/jjf daemon start 308 + ``` 309 + 310 + ### Terminal 3 311 + ```bash 312 + cd /tmp/my-project # The cloned repo 313 + /home/lau/code/laulauland/tandem/target/release/jjf daemon start 314 + ``` 315 + 316 + ### Expected Server Logs 317 + ``` 318 + Client connected to sync for repo my-project 319 + Client connected to sync for repo my-project 320 + ``` 321 + 322 + ### Test Sync 323 + Make a change in one repo and verify it appears in the other. 324 + 325 + **Note**: Full bidirectional sync requires the daemon to push local jj changes, which needs additional integration with jj-lib's operation log watching. 326 + 327 + --- 328 + 329 + ## Test 10: REST API - Changes and Bookmarks 330 + 331 + ### List Changes 332 + ```bash 333 + curl -s http://localhost:3000/api/repos/$REPO_ID/changes \ 334 + -H "Authorization: Bearer $TOKEN" 335 + ``` 336 + 337 + ### Expected Response 338 + ```json 339 + [] 340 + ``` 341 + (Empty until changes are synced from a jj repo) 342 + 343 + ### List Bookmarks 344 + ```bash 345 + curl -s http://localhost:3000/api/repos/$REPO_ID/bookmarks \ 346 + -H "Authorization: Bearer $TOKEN" 347 + ``` 348 + 349 + ### Expected Response 350 + ```json 351 + [] 352 + ``` 353 + 354 + --- 355 + 356 + ## Test 11: Content Endpoint 357 + 358 + ### Store Test Content (via server) 359 + This requires content to be synced first. Manual test: 360 + 361 + ```bash 362 + curl -s http://localhost:3000/api/repos/$REPO_ID/content/abc123 \ 363 + -H "Authorization: Bearer $TOKEN" 364 + ``` 365 + 366 + ### Expected Response 367 + HTTP 404 (content not found - expected for non-existent hash) 368 + 369 + --- 370 + 371 + ## Test 12: Events WebSocket 372 + 373 + ### Connect to Events 374 + ```bash 375 + # Requires websocat or similar 376 + websocat ws://localhost:3000/events/$REPO_ID 377 + ``` 378 + 379 + ### Expected Initial Message 380 + ```json 381 + {"type":"connected","repo_id":"<repo-id>"} 382 + ``` 383 + 384 + --- 385 + 386 + ## Test 13: jj-lib Integration 387 + 388 + ### Verify jj Repository Reading 389 + ```bash 390 + cd /tmp/test-project 391 + /home/lau/code/laulauland/tandem/target/release/jjf list 392 + ``` 393 + 394 + ### Expected Output 395 + List of changes from the jj repository (may be empty for new repos). 396 + 397 + --- 398 + 399 + ## Known Limitations 400 + 401 + 1. **Tree Hash Placeholder**: Tree hashes use first 20 bytes of change_id (jj-lib async limitation) 402 + 2. **Password Storage**: Plaintext comparison (use bcrypt in production) 403 + 3. **Token in CLI**: Currently passed as flag (should use keychain) 404 + 4. **Daemon Status**: `jjf daemon status` not fully implemented 405 + 5. **Presence Warnings**: Require daemon IPC (currently stub) 406 + 6. **Log Interception**: `jjf alias` log presence injection is stub 407 + 408 + --- 409 + 410 + ## Cleanup 411 + 412 + ```bash 413 + # Stop server (Ctrl+C in Terminal 1) 414 + 415 + # Remove test data 416 + rm -rf /tmp/test-project /tmp/my-project 417 + rm tandem.db data/ 418 + 419 + # Or keep for further testing 420 + ```