-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstealthpatch_test.go
More file actions
496 lines (418 loc) · 17.1 KB
/
Copy pathstealthpatch_test.go
File metadata and controls
496 lines (418 loc) · 17.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
package elementary
import (
"os"
"path/filepath"
"testing"
)
/*
TestStealthPatchLifecycle is a test which verifies the stealth-patch build mechanism itself from
a genuine zero state: no Playwright driver, no browser binary, and no patched driver copy already
present anywhere. It redirects PLAYWRIGHT_DRIVER_PATH and PLAYWRIGHT_BROWSERS_PATH to fresh,
per-run temporary directories instead of touching the machine's real shared cache, so running this
does not disturb (or depend on) anything another tool might have installed at ~/.cache/ms-playwright*.
Because nothing is cached, every run performs the full download of the driver, the browser, and the
stealth patch from scratch. This intentionally does not exercise general browsing behavior (real-page
navigation, element interaction, etc.) — that is covered end-to-end, under stealth mode by default,
by elementary_test.go. This test verifies only what is unique to the patch mechanism: that Initialize
actually builds a patched copy, and that the original driver is left untouched.
Example:
Expected Inputs:
Network access to registry.npmjs.org.
Expected Outputs:
No errors are returned, and the original driver directory is left with a coreBundle.js that
differs from the patched copy's.
*/
func TestStealthPatchLifecycle(t *testing.T) {
driverDir := t.TempDir()
browsersDir := t.TempDir()
t.Setenv("PLAYWRIGHT_DRIVER_PATH", driverDir)
t.Setenv("PLAYWRIGHT_BROWSERS_PATH", browsersDir)
var browserAgent Instance
err := browserAgent.Initialize(
"stealth-test-context",
"stealth-test-page",
"chromium",
1280,
720,
false,
&BrowserOptions{StealthPatch: true},
)
if err != nil {
t.Fatalf("Failed to initialize with stealth patch enabled: %v", err)
}
defer func() {
if err := browserAgent.Close(); err != nil {
t.Errorf("Failed to cleanly close stealth-patched browser agent: %v", err)
}
if err := browserAgent.RestoreStealthPatch(); err != nil {
t.Errorf("Failed to remove the patched driver copy after stealth patch test: %v", err)
}
}()
// Verify the original driver install was never touched: the stock coreBundle.js under
// driverDir must still differ from the patched copy's, proving StealthPatch built a
// separate side-by-side copy instead of patching driverDir in place.
originalBundlePath := filepath.Join(driverDir, "package", "lib", "coreBundle.js")
patchedBundlePath := filepath.Join(driverDir+"-stealth", "package", "lib", "coreBundle.js")
originalBundle, err := os.ReadFile(originalBundlePath)
if err != nil {
t.Fatalf("Expected original coreBundle.js to exist at %s: %v", originalBundlePath, err)
}
patchedBundle, err := os.ReadFile(patchedBundlePath)
if err != nil {
t.Fatalf("Expected patched coreBundle.js to exist at %s: %v", patchedBundlePath, err)
}
if string(originalBundle) == string(patchedBundle) {
t.Error("Expected the original and patched coreBundle.js to differ, but they were identical")
}
}
/*
TestRestoreStealthPatchNoOp is a test which verifies that RestoreStealthPatch is a safe
no-op when no patch was ever applied, so callers can call it unconditionally during cleanup
without checking whether stealth mode was ever used.
Example:
Expected Inputs:
An initialized Instance that never enabled StealthPatch.
Expected Outputs:
RestoreStealthPatch returns nil.
*/
func TestRestoreStealthPatchNoOp(t *testing.T) {
var browserAgent Instance
err := browserAgent.Initialize(
"restore-noop-context",
"restore-noop-page",
"chromium",
1280,
720,
false,
nil,
)
if err != nil {
t.Fatalf("Failed to initialize: %v", err)
}
if err := browserAgent.Close(); err != nil {
t.Fatalf("Failed to close: %v", err)
}
if err := browserAgent.RestoreStealthPatch(); err != nil {
t.Errorf("Expected RestoreStealthPatch to be a safe no-op, got error: %v", err)
}
}
/*
TestRepairIsolatesPatchedAndUnpatchedDrivers is a test which verifies that Repair, when called
on a plain (non-stealth) Instance, never touches a stealth-patched sibling driver copy that
another Instance built against the same shared driver/browsers cache — and vice versa. This is
the isolation guarantee stealth-patched and plain Instances are documented to have with each
other (see stealthpatch.go), and Repair must preserve it rather than wiping the whole shared
cache indiscriminately.
Example:
Expected Inputs:
Network access to registry.npmjs.org.
Expected Outputs:
Repairing the plain Instance leaves the stealth-patched sibling's coreBundle.js byte-for-byte
unchanged, and repairing the stealth-patched Instance leaves the plain driver directory intact.
*/
func TestRepairIsolatesPatchedAndUnpatchedDrivers(t *testing.T) {
driverDir := t.TempDir()
browsersDir := t.TempDir()
t.Setenv("PLAYWRIGHT_DRIVER_PATH", driverDir)
t.Setenv("PLAYWRIGHT_BROWSERS_PATH", browsersDir)
var patchedAgent Instance
if err := patchedAgent.Initialize(
"repair-isolation-patched-context", "repair-isolation-patched-page",
"chromium", 1280, 720, false,
&BrowserOptions{StealthPatch: true},
); err != nil {
t.Fatalf("Failed to initialize stealth-patched instance: %v", err)
}
if err := patchedAgent.Close(); err != nil {
t.Fatalf("Failed to close stealth-patched instance: %v", err)
}
patchedBundlePath := filepath.Join(driverDir+"-stealth", "package", "lib", "coreBundle.js")
patchedBundleBefore, err := os.ReadFile(patchedBundlePath)
if err != nil {
t.Fatalf("Expected patched coreBundle.js to exist at %s: %v", patchedBundlePath, err)
}
var plainAgent Instance
if err := plainAgent.Initialize(
"repair-isolation-plain-context", "repair-isolation-plain-page",
"chromium", 1280, 720, false, nil,
); err != nil {
t.Fatalf("Failed to initialize plain instance: %v", err)
}
if err := plainAgent.Close(); err != nil {
t.Fatalf("Failed to close plain instance: %v", err)
}
if err := plainAgent.Repair(); err != nil {
t.Fatalf("Failed to repair plain instance: %v", err)
}
patchedBundleAfter, err := os.ReadFile(patchedBundlePath)
if err != nil {
t.Fatalf("Expected patched coreBundle.js to still exist at %s after repairing the plain instance: %v", patchedBundlePath, err)
}
if string(patchedBundleBefore) != string(patchedBundleAfter) {
t.Error("Expected repairing the plain instance to leave the stealth-patched sibling copy untouched, but its coreBundle.js changed")
}
originalBundlePath := filepath.Join(driverDir, "package", "lib", "coreBundle.js")
originalBundleBeforePatchedRepair, err := os.ReadFile(originalBundlePath)
if err != nil {
t.Fatalf("Expected repairing the plain instance to leave a usable driver at %s: %v", originalBundlePath, err)
}
if err := patchedAgent.Repair(); err != nil {
t.Fatalf("Failed to repair stealth-patched instance: %v", err)
}
originalBundleAfterPatchedRepair, err := os.ReadFile(originalBundlePath)
if err != nil {
t.Fatalf("Expected repairing the stealth-patched instance to leave the plain driver usable at %s: %v", originalBundlePath, err)
}
if string(originalBundleBeforePatchedRepair) != string(originalBundleAfterPatchedRepair) {
t.Error("Expected repairing the stealth-patched instance to leave the shared, unpatched driver directory untouched, but its coreBundle.js changed")
}
if err := patchedAgent.RestoreStealthPatch(); err != nil {
t.Errorf("Failed to remove the patched driver copy during cleanup: %v", err)
}
}
/*
TestResolveBaseDriverDirectoryHonorsOverride is a test which verifies that
resolveBaseDriverDirectory returns PLAYWRIGHT_DRIVER_PATH verbatim when it is set, matching
playwright-go's own resolution order.
Example:
dir, err := agent.resolveBaseDriverDirectory()
*/
func TestResolveBaseDriverDirectoryHonorsOverride(t *testing.T) {
var agent Instance
customDirectory := t.TempDir()
t.Setenv("PLAYWRIGHT_DRIVER_PATH", customDirectory)
got, err := agent.resolveBaseDriverDirectory()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != customDirectory {
t.Errorf("expected %q, got %q", customDirectory, got)
}
}
/*
TestResolveBaseDriverDirectoryDefaultsToCacheConvention is a test which verifies that
resolveBaseDriverDirectory falls back to the default cache-directory convention when
PLAYWRIGHT_DRIVER_PATH is unset.
Example:
dir, err := agent.resolveBaseDriverDirectory()
*/
func TestResolveBaseDriverDirectoryDefaultsToCacheConvention(t *testing.T) {
var agent Instance
t.Setenv("PLAYWRIGHT_DRIVER_PATH", "")
cacheDirectory, err := agent.getDefaultCacheDirectory()
if err != nil {
t.Fatalf("unexpected error resolving cache directory: %v", err)
}
version, err := resolvePlaywrightDriverVersion()
if err != nil {
t.Fatalf("unexpected error resolving playwright version: %v", err)
}
want := filepath.Join(cacheDirectory, "ms-playwright-go", version)
got, err := agent.resolveBaseDriverDirectory()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != want {
t.Errorf("expected %q, got %q", want, got)
}
}
/*
TestResolveBrowsersDirectoryHonorsOverride is a test which verifies that
resolveBrowsersDirectory returns PLAYWRIGHT_BROWSERS_PATH verbatim when it is set.
Example:
dir, err := agent.resolveBrowsersDirectory()
*/
func TestResolveBrowsersDirectoryHonorsOverride(t *testing.T) {
var agent Instance
customDirectory := t.TempDir()
t.Setenv("PLAYWRIGHT_BROWSERS_PATH", customDirectory)
got, err := agent.resolveBrowsersDirectory()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != customDirectory {
t.Errorf("expected %q, got %q", customDirectory, got)
}
}
/*
TestResolveBrowsersDirectoryDefaultsToCacheConvention is a test which verifies that
resolveBrowsersDirectory falls back to the default cache-directory convention when
PLAYWRIGHT_BROWSERS_PATH is unset.
Example:
dir, err := agent.resolveBrowsersDirectory()
*/
func TestResolveBrowsersDirectoryDefaultsToCacheConvention(t *testing.T) {
var agent Instance
t.Setenv("PLAYWRIGHT_BROWSERS_PATH", "")
cacheDirectory, err := agent.getDefaultCacheDirectory()
if err != nil {
t.Fatalf("unexpected error resolving cache directory: %v", err)
}
want := filepath.Join(cacheDirectory, "ms-playwright")
got, err := agent.resolveBrowsersDirectory()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != want {
t.Errorf("expected %q, got %q", want, got)
}
}
/*
TestStealthDriverUpToDateMissingDirectory is a test which verifies that stealthDriverUpToDate
reports false, without error, when the stealth driver directory does not exist yet.
Example:
upToDate, err := stealthDriverUpToDate(dir, "1.61.1")
*/
func TestStealthDriverUpToDateMissingDirectory(t *testing.T) {
upToDate, err := stealthDriverUpToDate(filepath.Join(t.TempDir(), "does-not-exist"), "1.61.1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if upToDate {
t.Error("expected a missing stealth driver directory to report not up to date")
}
}
/*
TestStealthDriverUpToDateMatchingVersion is a test which verifies that stealthDriverUpToDate
reports true when the version marker matches the requested Playwright driver version.
Example:
upToDate, err := stealthDriverUpToDate(dir, "1.61.1")
*/
func TestStealthDriverUpToDateMatchingVersion(t *testing.T) {
stealthDirectory := t.TempDir()
writeStealthVersionMarker(t, stealthDirectory, "1.61.1")
upToDate, err := stealthDriverUpToDate(stealthDirectory, "1.61.1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !upToDate {
t.Error("expected a matching version marker to report up to date")
}
}
/*
TestStealthDriverUpToDateStaleVersion is a test which verifies that stealthDriverUpToDate
reports false when the version marker does not match the requested Playwright driver version.
Example:
upToDate, err := stealthDriverUpToDate(dir, "1.61.1")
*/
func TestStealthDriverUpToDateStaleVersion(t *testing.T) {
stealthDirectory := t.TempDir()
writeStealthVersionMarker(t, stealthDirectory, "1.60.0")
upToDate, err := stealthDriverUpToDate(stealthDirectory, "1.61.1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if upToDate {
t.Error("expected a mismatched version marker to report not up to date")
}
}
/*
TestActivateStealthDriverDirectoryFreshRename is a test which verifies that
activateStealthDriverDirectory moves a freshly-built temp directory into place via a plain
rename when nothing already occupies the destination.
Example:
err := activateStealthDriverDirectory(tempDir, destination, "1.61.1")
*/
func TestActivateStealthDriverDirectoryFreshRename(t *testing.T) {
parentDirectory := t.TempDir()
tempDirectory := filepath.Join(parentDirectory, "building")
writeFile(t, filepath.Join(tempDirectory, "sentinel.txt"), "built")
destination := filepath.Join(parentDirectory, "1.61.1-stealth")
if err := activateStealthDriverDirectory(tempDirectory, destination, "1.61.1"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, err := os.Stat(tempDirectory); !os.IsNotExist(err) {
t.Error("expected the temp directory to be moved away by the rename")
}
data, err := os.ReadFile(filepath.Join(destination, "sentinel.txt"))
if err != nil || string(data) != "built" {
t.Errorf("expected activated directory to contain the built sentinel file, got %q, err=%v", data, err)
}
}
// Simulates a concurrent builder losing the race: the destination already holds a
// complete, correctly-versioned copy by the time this one tries to activate.
func TestActivateStealthDriverDirectoryKeepsExistingWinner(t *testing.T) {
parentDirectory := t.TempDir()
destination := filepath.Join(parentDirectory, "1.61.1-stealth")
writeStealthVersionMarker(t, destination, "1.61.1")
writeFile(t, filepath.Join(destination, "winner.txt"), "winner")
tempDirectory := filepath.Join(parentDirectory, "building")
writeFile(t, filepath.Join(tempDirectory, "loser.txt"), "loser")
if err := activateStealthDriverDirectory(tempDirectory, destination, "1.61.1"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, err := os.Stat(filepath.Join(destination, "winner.txt")); err != nil {
t.Error("expected the winning builder's content to be preserved")
}
if _, err := os.Stat(filepath.Join(destination, "loser.txt")); !os.IsNotExist(err) {
t.Error("expected this builder's redundant copy to be discarded rather than merged in")
}
}
// Simulates activating over a copy left behind by a previous Playwright driver version.
func TestActivateStealthDriverDirectoryReplacesStaleCopy(t *testing.T) {
parentDirectory := t.TempDir()
destination := filepath.Join(parentDirectory, "1.61.1-stealth")
writeStealthVersionMarker(t, destination, "1.60.0")
tempDirectory := filepath.Join(parentDirectory, "building")
writeFile(t, filepath.Join(tempDirectory, "fresh.txt"), "fresh")
if err := activateStealthDriverDirectory(tempDirectory, destination, "1.61.1"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, err := os.Stat(filepath.Join(destination, "fresh.txt")); err != nil {
t.Error("expected the stale copy to be replaced with the freshly built one")
}
}
/*
TestCopyDriverDirectoryPreservesModesAndSymlinks is a test which verifies that
copyDriverDirectory preserves file permissions (critical for the copied Node.js executable)
and recreates symlinks rather than following them.
Example:
err := copyDriverDirectory(source, destination)
*/
func TestCopyDriverDirectoryPreservesModesAndSymlinks(t *testing.T) {
source := t.TempDir()
destination := t.TempDir()
writeFileWithMode(t, filepath.Join(source, "node"), "#!/bin/sh\necho fake-node\n", 0o755)
writeFileWithMode(t, filepath.Join(source, "package", "lib", "coreBundle.js"), "console.log('bundle')", 0o644)
if err := os.Symlink("node", filepath.Join(source, "node-link")); err != nil {
t.Skipf("symlinks not supported on this filesystem: %v", err)
}
if err := copyDriverDirectory(source, destination); err != nil {
t.Fatalf("unexpected error: %v", err)
}
copiedExecutableInfo, err := os.Stat(filepath.Join(destination, "node"))
if err != nil {
t.Fatalf("expected copied executable to exist: %v", err)
}
if copiedExecutableInfo.Mode().Perm() != 0o755 {
t.Errorf("expected copied executable to keep mode 0755, got %v", copiedExecutableInfo.Mode().Perm())
}
copiedBundleData, err := os.ReadFile(filepath.Join(destination, "package", "lib", "coreBundle.js"))
if err != nil || string(copiedBundleData) != "console.log('bundle')" {
t.Errorf("expected copied file content to match, got %q, err=%v", copiedBundleData, err)
}
linkTarget, err := os.Readlink(filepath.Join(destination, "node-link"))
if err != nil {
t.Fatalf("expected copied symlink to exist: %v", err)
}
if linkTarget != "node" {
t.Errorf("expected copied symlink to point at %q, got %q", "node", linkTarget)
}
}
func writeStealthVersionMarker(t *testing.T, stealthDirectory string, version string) {
t.Helper()
writeFile(t, filepath.Join(stealthDirectory, "package", stealthDriverVersionMarkerName), version)
}
func writeFile(t *testing.T, path string, content string) {
t.Helper()
writeFileWithMode(t, path, content, 0o644)
}
func writeFileWithMode(t *testing.T, path string, content string, mode os.FileMode) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatalf("failed to create parent directory for %s: %v", path, err)
}
if err := os.WriteFile(path, []byte(content), mode); err != nil {
t.Fatalf("failed to write %s: %v", path, err)
}
}