cabloy 5.1.215 → 5.1.217
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/.cabloy-version +1 -1
- package/.github/workflows/playwright-e2e.yml +163 -13
- package/.github/workflows/vona-test-sqlite3.yml +77 -7
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +4 -2
- package/package.json +1 -1
- package/repo-docs/frontend/scripts.md +2 -0
- package/repo-docs/reference/repo-scripts.md +27 -1
- package/repo-e2e/scripts/e2e.test.ts +1 -1
- package/repo-e2e/scripts/managedTest.test.ts +115 -3
- package/repo-e2e/scripts/managedTest.ts +69 -20
- package/repo-e2e/scripts/managedTestImports.test.ts +55 -5
- package/repo-e2e/scripts/runE2e.test.ts +109 -0
- package/repo-e2e/scripts/runE2e.ts +58 -39
- package/repo-e2e/scripts/runE2eArgs.test.ts +16 -1
- package/repo-e2e/scripts/runE2eArgs.ts +5 -0
- package/repo-e2e/specs/a-commerce.spec.ts +10 -2
- package/repo-e2e/specs/home-user-account.spec.ts +4 -0
- package/repo-e2e/specs/markdown.spec.ts +14 -3
- package/scripts/generateOpenapiIsolated.test.ts +5 -1
- package/scripts/runIsolatedTest.test.ts +403 -36
- package/scripts/runIsolatedTest.ts +139 -12
- package/scripts/testBrowserGuardianWindows.test.ts +327 -0
- package/scripts/testClaimCleanup.test.ts +25 -9
- package/scripts/testClaimCleanup.ts +14 -45
- package/scripts/testClaimProcesses.test.ts +81 -2
- package/scripts/testClaimProcesses.ts +46 -10
- package/scripts/testProcessGuardianWindows.test.ts +602 -0
- package/scripts/testProcessGuardianWindows.ts +155 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.build.ts +4 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +4 -4
- package/vona/packages-cli/cli-set-api/test/binBuildEntries.test.ts +159 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts +298 -17
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.ts +205 -97
- package/vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts +247 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.ts +224 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaimFilesystemWindows.cs +229 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaimFilesystemWindows.ps1 +20 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaimFilesystemWindows.ts +200 -0
- package/vona/packages-vona/vona-core/src/lib/core/testProcessWindowsNative.cs +336 -0
- package/vona/packages-vona/vona-core/src/lib/core/testProcessWindowsNative.ps1 +20 -0
- package/vona/packages-vona/vona-core/src/lib/core/testProcessWindowsNative.ts +531 -0
- package/vona/packages-vona/vona-core/src/lib/framework/bootstrap.test.ts +61 -0
- package/vona/packages-vona/vona-core/src/lib/framework/index.ts +1 -0
- package/vona/packages-vona/vona-core/src/lib/framework/processWorker.test.ts +59 -0
- package/vona/packages-vona/vona-core/src/lib/framework/processWorker.ts +8 -5
- package/vona/packages-vona/vona-core/src/lib/framework/useApp.test.ts +84 -0
- package/vona/packages-vona/vona-core/src/lib/framework/useApp.ts +2 -2
- package/vona/packages-vona/vona-core/src/lib/utils/util.ts +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +22 -22
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-executor/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-executor/src/bean/bean.executor.ts +2 -1
- package/vona/src/suite-vendor/a-vona/modules/a-logger/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/bean/middlewareSystem.httpLog.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts +16 -0
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.ts +6 -3
- package/vona/src/suite-vendor/a-vona/modules/a-logger/test/middlewareSystem.httpLog.test.ts +80 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.health.ts +73 -2
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/test/middlewareSystem.health.test.ts +309 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/transactionAsyncLocalStorage_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-static/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-static/src/bean/middlewareSystem.static.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-static/test/middlewareSystem.static.test.ts +101 -0
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +2 -2
- package/zova/packages-utils/zova-vite/package.json +1 -1
- package/zova/packages-utils/zova-vite/src/configUtils.ts +1 -1
- package/zova/packages-utils/zova-vite/test/configUtils.test.ts +55 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +8 -4
- package/zova/src/suite/a-commerce/modules/commerce-trade/test/lib/cartModel.test.ts +71 -0
- /package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/{<%=argv.detailDtoBaseName%>.tsx_ → {{detailDtoBaseName}}.tsx_} +0 -0
- /package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/{<%=argv.detailDtoMutateName%>.tsx_ → {{detailDtoMutateName}}.tsx_} +0 -0
- /package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/{<%=argv.detailDtoResItemName%>.tsx_ → {{detailDtoResItemName}}.tsx_} +0 -0
- /package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/{<%=argv.detailDtoViewName%>.tsx_ → {{detailDtoViewName}}.tsx_} +0 -0
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.217
|
|
@@ -5,15 +5,24 @@ on:
|
|
|
5
5
|
- main
|
|
6
6
|
jobs:
|
|
7
7
|
basic-e2e:
|
|
8
|
-
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
9
13
|
env:
|
|
10
14
|
DATABASE_DEFAULT_CLIENT: sqlite3
|
|
11
15
|
PNPM_CONFIG_MINIMUM_RELEASE_AGE: 0
|
|
12
16
|
services:
|
|
13
17
|
redis:
|
|
14
|
-
image: redis:latest
|
|
18
|
+
image: ${{ matrix.os == 'ubuntu-latest' && 'redis:latest' || '' }}
|
|
15
19
|
ports:
|
|
16
20
|
- 6379:6379
|
|
21
|
+
options: >-
|
|
22
|
+
--health-cmd "redis-cli ping"
|
|
23
|
+
--health-interval 5s
|
|
24
|
+
--health-timeout 3s
|
|
25
|
+
--health-retries 12
|
|
17
26
|
steps:
|
|
18
27
|
- uses: actions/checkout@v6
|
|
19
28
|
- uses: actions/setup-node@v6
|
|
@@ -22,32 +31,106 @@ jobs:
|
|
|
22
31
|
- uses: pnpm/action-setup@v5
|
|
23
32
|
with:
|
|
24
33
|
version: 11.5.2
|
|
34
|
+
- name: start Redis on macOS
|
|
35
|
+
if: runner.os == 'macOS'
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
brew install redis
|
|
39
|
+
redis_bin="$(brew --prefix redis)/bin"
|
|
40
|
+
redis_log="$RUNNER_TEMP/redis.log"
|
|
41
|
+
if ! "$redis_bin/redis-server" --bind 127.0.0.1 --port 6379 \
|
|
42
|
+
--daemonize yes --save '' --appendonly no --logfile "$redis_log"; then
|
|
43
|
+
if [[ -f "$redis_log" ]]; then
|
|
44
|
+
cat "$redis_log"
|
|
45
|
+
fi
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
for attempt in {1..30}; do
|
|
49
|
+
if response="$("$redis_bin/redis-cli" -h 127.0.0.1 -p 6379 ping 2>&1)" &&
|
|
50
|
+
[[ "$response" == PONG ]]; then
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
sleep 1
|
|
54
|
+
done
|
|
55
|
+
printf '::error::Redis readiness failed: %s\n' "$response"
|
|
56
|
+
if [[ -f "$redis_log" ]]; then
|
|
57
|
+
cat "$redis_log"
|
|
58
|
+
fi
|
|
59
|
+
exit 1
|
|
60
|
+
- name: start Redis on Windows
|
|
61
|
+
if: runner.os == 'Windows'
|
|
62
|
+
shell: pwsh
|
|
63
|
+
run: |
|
|
64
|
+
$ErrorActionPreference = 'Stop'
|
|
65
|
+
# Pinned third-party development build; retain its bundled Cygwin runtime.
|
|
66
|
+
$archive = Join-Path $env:RUNNER_TEMP 'redis-8.2.10.zip'
|
|
67
|
+
$directory = Join-Path $env:RUNNER_TEMP 'redis-8.2.10'
|
|
68
|
+
Invoke-WebRequest 'https://github.com/redis-windows/redis-windows/releases/download/8.2.10/Redis-8.2.10-Windows-x64-cygwin.zip' -OutFile $archive
|
|
69
|
+
$expected = '92d9bdde87ed52bf859a677e5197d8ac855ba78d9940d457881723ae960df429'
|
|
70
|
+
if ((Get-FileHash $archive -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected) {
|
|
71
|
+
throw 'Redis archive checksum mismatch'
|
|
72
|
+
}
|
|
73
|
+
Expand-Archive $archive -DestinationPath $directory
|
|
74
|
+
$servers = @(Get-ChildItem $directory -Recurse -Filter redis-server.exe)
|
|
75
|
+
if ($servers.Count -ne 1) { throw 'Expected one Redis server executable' }
|
|
76
|
+
$redisDirectory = $servers[0].DirectoryName
|
|
77
|
+
$cli = Join-Path $redisDirectory 'redis-cli.exe'
|
|
78
|
+
$config = Join-Path $redisDirectory 'ci.conf'
|
|
79
|
+
@('bind 127.0.0.1', 'port 6379', 'protected-mode yes', 'save ""', 'appendonly no', 'daemonize no', 'logfile redis.log') |
|
|
80
|
+
Set-Content $config -Encoding utf8NoBOM
|
|
81
|
+
$server = Start-Process $servers[0].FullName -ArgumentList 'ci.conf' -WorkingDirectory $redisDirectory -PassThru
|
|
82
|
+
for ($attempt = 0; $attempt -lt 30; $attempt++) {
|
|
83
|
+
$server.Refresh()
|
|
84
|
+
if ($server.HasExited) { break }
|
|
85
|
+
$response = & $cli -h 127.0.0.1 -p 6379 ping 2>&1
|
|
86
|
+
if ($LASTEXITCODE -eq 0 -and "$response".Trim() -eq 'PONG') { exit 0 }
|
|
87
|
+
Start-Sleep -Seconds 1
|
|
88
|
+
}
|
|
89
|
+
$log = Join-Path $redisDirectory 'redis.log'
|
|
90
|
+
if (Test-Path $log) { Get-Content $log }
|
|
91
|
+
throw 'Redis did not become ready'
|
|
25
92
|
- name: init
|
|
26
93
|
run: npm run init
|
|
27
|
-
- name: install Chromium
|
|
94
|
+
- name: install Chromium with Linux dependencies
|
|
95
|
+
if: runner.os == 'Linux'
|
|
28
96
|
run: npx playwright install --with-deps chromium
|
|
97
|
+
- name: install Chromium on Windows and macOS
|
|
98
|
+
if: runner.os != 'Linux'
|
|
99
|
+
run: npx playwright install chromium
|
|
100
|
+
- name: run focused managed E2E regression gate
|
|
101
|
+
run: node --test zova/packages-utils/zova-vite/test/configUtils.test.ts scripts/testClaimProcesses.test.ts scripts/runIsolatedTest.test.ts scripts/generateOpenapiIsolated.test.ts scripts/testClaimCleanup.test.ts scripts/testProcessGuardianWindows.test.ts scripts/testBrowserGuardianWindows.test.ts vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts vona/packages-vona/vona-core/src/lib/framework/processWorker.test.ts vona/src/suite-vendor/a-vona/modules/a-static/test/middlewareSystem.static.test.ts vona/src/suite-vendor/a-vona/modules/a-metrics/test/middlewareSystem.health.test.ts vona/src/suite-vendor/a-vona/modules/a-logger/test/middlewareSystem.httpLog.test.ts vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts repo-e2e/scripts/managedTest.test.ts repo-e2e/scripts/managedTestImports.test.ts repo-e2e/scripts/e2e.test.ts repo-e2e/scripts/runE2eArgs.test.ts repo-e2e/scripts/runE2e.test.ts
|
|
29
102
|
- name: run Basic E2E baseline
|
|
30
103
|
run: npm run test:e2e -- cabloy-basic home-user-account
|
|
31
104
|
- name: upload Playwright artifacts
|
|
32
105
|
if: failure()
|
|
33
106
|
uses: actions/upload-artifact@v7
|
|
34
107
|
with:
|
|
35
|
-
name: playwright-artifacts-basic
|
|
108
|
+
name: playwright-artifacts-basic-${{ matrix.os }}
|
|
36
109
|
path: |
|
|
37
|
-
playwright-report/
|
|
38
|
-
test-results/
|
|
110
|
+
vona/.app-test-*/playwright-report/
|
|
111
|
+
vona/.app-test-*/test-results/
|
|
112
|
+
include-hidden-files: true
|
|
39
113
|
if-no-files-found: ignore
|
|
40
114
|
|
|
41
115
|
commerce-e2e:
|
|
42
|
-
|
|
116
|
+
strategy:
|
|
117
|
+
fail-fast: false
|
|
118
|
+
matrix:
|
|
119
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
120
|
+
runs-on: ${{ matrix.os }}
|
|
43
121
|
env:
|
|
44
122
|
DATABASE_DEFAULT_CLIENT: sqlite3
|
|
45
123
|
PNPM_CONFIG_MINIMUM_RELEASE_AGE: 0
|
|
46
124
|
services:
|
|
47
125
|
redis:
|
|
48
|
-
image: redis:latest
|
|
126
|
+
image: ${{ matrix.os == 'ubuntu-latest' && 'redis:latest' || '' }}
|
|
49
127
|
ports:
|
|
50
128
|
- 6379:6379
|
|
129
|
+
options: >-
|
|
130
|
+
--health-cmd "redis-cli ping"
|
|
131
|
+
--health-interval 5s
|
|
132
|
+
--health-timeout 3s
|
|
133
|
+
--health-retries 12
|
|
51
134
|
steps:
|
|
52
135
|
- uses: actions/checkout@v6
|
|
53
136
|
- uses: actions/setup-node@v6
|
|
@@ -56,22 +139,89 @@ jobs:
|
|
|
56
139
|
- uses: pnpm/action-setup@v5
|
|
57
140
|
with:
|
|
58
141
|
version: 11.5.2
|
|
142
|
+
- name: start Redis on macOS
|
|
143
|
+
if: runner.os == 'macOS'
|
|
144
|
+
shell: bash
|
|
145
|
+
run: |
|
|
146
|
+
brew install redis
|
|
147
|
+
redis_bin="$(brew --prefix redis)/bin"
|
|
148
|
+
redis_log="$RUNNER_TEMP/redis.log"
|
|
149
|
+
if ! "$redis_bin/redis-server" --bind 127.0.0.1 --port 6379 \
|
|
150
|
+
--daemonize yes --save '' --appendonly no --logfile "$redis_log"; then
|
|
151
|
+
if [[ -f "$redis_log" ]]; then
|
|
152
|
+
cat "$redis_log"
|
|
153
|
+
fi
|
|
154
|
+
exit 1
|
|
155
|
+
fi
|
|
156
|
+
for attempt in {1..30}; do
|
|
157
|
+
if response="$("$redis_bin/redis-cli" -h 127.0.0.1 -p 6379 ping 2>&1)" &&
|
|
158
|
+
[[ "$response" == PONG ]]; then
|
|
159
|
+
exit 0
|
|
160
|
+
fi
|
|
161
|
+
sleep 1
|
|
162
|
+
done
|
|
163
|
+
printf '::error::Redis readiness failed: %s\n' "$response"
|
|
164
|
+
if [[ -f "$redis_log" ]]; then
|
|
165
|
+
cat "$redis_log"
|
|
166
|
+
fi
|
|
167
|
+
exit 1
|
|
168
|
+
- name: start Redis on Windows
|
|
169
|
+
if: runner.os == 'Windows'
|
|
170
|
+
shell: pwsh
|
|
171
|
+
run: |
|
|
172
|
+
$ErrorActionPreference = 'Stop'
|
|
173
|
+
# Pinned third-party development build; retain its bundled Cygwin runtime.
|
|
174
|
+
$archive = Join-Path $env:RUNNER_TEMP 'redis-8.2.10.zip'
|
|
175
|
+
$directory = Join-Path $env:RUNNER_TEMP 'redis-8.2.10'
|
|
176
|
+
Invoke-WebRequest 'https://github.com/redis-windows/redis-windows/releases/download/8.2.10/Redis-8.2.10-Windows-x64-cygwin.zip' -OutFile $archive
|
|
177
|
+
$expected = '92d9bdde87ed52bf859a677e5197d8ac855ba78d9940d457881723ae960df429'
|
|
178
|
+
if ((Get-FileHash $archive -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected) {
|
|
179
|
+
throw 'Redis archive checksum mismatch'
|
|
180
|
+
}
|
|
181
|
+
Expand-Archive $archive -DestinationPath $directory
|
|
182
|
+
$servers = @(Get-ChildItem $directory -Recurse -Filter redis-server.exe)
|
|
183
|
+
if ($servers.Count -ne 1) { throw 'Expected one Redis server executable' }
|
|
184
|
+
$redisDirectory = $servers[0].DirectoryName
|
|
185
|
+
$cli = Join-Path $redisDirectory 'redis-cli.exe'
|
|
186
|
+
$config = Join-Path $redisDirectory 'ci.conf'
|
|
187
|
+
@('bind 127.0.0.1', 'port 6379', 'protected-mode yes', 'save ""', 'appendonly no', 'daemonize no', 'logfile redis.log') |
|
|
188
|
+
Set-Content $config -Encoding utf8NoBOM
|
|
189
|
+
$server = Start-Process $servers[0].FullName -ArgumentList 'ci.conf' -WorkingDirectory $redisDirectory -PassThru
|
|
190
|
+
for ($attempt = 0; $attempt -lt 30; $attempt++) {
|
|
191
|
+
$server.Refresh()
|
|
192
|
+
if ($server.HasExited) { break }
|
|
193
|
+
$response = & $cli -h 127.0.0.1 -p 6379 ping 2>&1
|
|
194
|
+
if ($LASTEXITCODE -eq 0 -and "$response".Trim() -eq 'PONG') { exit 0 }
|
|
195
|
+
Start-Sleep -Seconds 1
|
|
196
|
+
}
|
|
197
|
+
$log = Join-Path $redisDirectory 'redis.log'
|
|
198
|
+
if (Test-Path $log) { Get-Content $log }
|
|
199
|
+
throw 'Redis did not become ready'
|
|
59
200
|
- name: init
|
|
60
201
|
run: npm run init
|
|
61
|
-
- name: install Chromium
|
|
202
|
+
- name: install Chromium with Linux dependencies
|
|
203
|
+
if: runner.os == 'Linux'
|
|
62
204
|
run: npx playwright install --with-deps chromium
|
|
205
|
+
- name: install Chromium on Windows and macOS
|
|
206
|
+
if: runner.os != 'Linux'
|
|
207
|
+
run: npx playwright install chromium
|
|
208
|
+
- name: run focused managed E2E regression gate
|
|
209
|
+
run: node --test zova/packages-utils/zova-vite/test/configUtils.test.ts scripts/testClaimProcesses.test.ts scripts/runIsolatedTest.test.ts scripts/generateOpenapiIsolated.test.ts scripts/testClaimCleanup.test.ts scripts/testProcessGuardianWindows.test.ts scripts/testBrowserGuardianWindows.test.ts vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts vona/packages-vona/vona-core/src/lib/framework/processWorker.test.ts vona/src/suite-vendor/a-vona/modules/a-static/test/middlewareSystem.static.test.ts vona/src/suite-vendor/a-vona/modules/a-metrics/test/middlewareSystem.health.test.ts vona/src/suite-vendor/a-vona/modules/a-logger/test/middlewareSystem.httpLog.test.ts vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts repo-e2e/scripts/managedTest.test.ts repo-e2e/scripts/managedTestImports.test.ts repo-e2e/scripts/e2e.test.ts repo-e2e/scripts/runE2eArgs.test.ts repo-e2e/scripts/runE2e.test.ts
|
|
210
|
+
- name: run Commerce cart model regression
|
|
211
|
+
run: node --test zova/src/suite/a-commerce/modules/commerce-trade/test/lib/cartModel.test.ts
|
|
63
212
|
- name: build Commerce Zova artifacts
|
|
64
213
|
run: npm run build:zova:commerce
|
|
65
214
|
- name: synchronize Vona dependencies
|
|
66
215
|
run: npm run deps:vona
|
|
67
216
|
- name: run Commerce E2E baseline
|
|
68
|
-
run: npm run test:e2e a-commerce markdown
|
|
217
|
+
run: npm run test:e2e -- a-commerce markdown
|
|
69
218
|
- name: upload Playwright artifacts
|
|
70
219
|
if: failure()
|
|
71
220
|
uses: actions/upload-artifact@v7
|
|
72
221
|
with:
|
|
73
|
-
name: playwright-artifacts-commerce
|
|
222
|
+
name: playwright-artifacts-commerce-${{ matrix.os }}
|
|
74
223
|
path: |
|
|
75
|
-
playwright-report/
|
|
76
|
-
test-results/
|
|
224
|
+
vona/.app-test-*/playwright-report/
|
|
225
|
+
vona/.app-test-*/test-results/
|
|
226
|
+
include-hidden-files: true
|
|
77
227
|
if-no-files-found: ignore
|
|
@@ -8,15 +8,22 @@ jobs:
|
|
|
8
8
|
strategy:
|
|
9
9
|
fail-fast: false
|
|
10
10
|
matrix:
|
|
11
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
11
12
|
node-version: [24]
|
|
12
|
-
runs-on:
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
13
14
|
env:
|
|
14
15
|
PNPM_CONFIG_MINIMUM_RELEASE_AGE: 0
|
|
16
|
+
DATABASE_DEFAULT_CLIENT: sqlite3
|
|
15
17
|
services:
|
|
16
18
|
redis:
|
|
17
|
-
image: redis:latest
|
|
19
|
+
image: ${{ matrix.os == 'ubuntu-latest' && 'redis:latest' || '' }}
|
|
18
20
|
ports:
|
|
19
21
|
- 6379:6379
|
|
22
|
+
options: >-
|
|
23
|
+
--health-cmd "redis-cli ping"
|
|
24
|
+
--health-interval 5s
|
|
25
|
+
--health-timeout 3s
|
|
26
|
+
--health-retries 12
|
|
20
27
|
steps:
|
|
21
28
|
- uses: actions/checkout@v6
|
|
22
29
|
- uses: actions/setup-node@v6
|
|
@@ -25,19 +32,82 @@ jobs:
|
|
|
25
32
|
- uses: pnpm/action-setup@v5
|
|
26
33
|
with:
|
|
27
34
|
version: 11.5.2
|
|
35
|
+
- name: start Redis on macOS
|
|
36
|
+
if: runner.os == 'macOS'
|
|
37
|
+
shell: bash
|
|
38
|
+
run: |
|
|
39
|
+
brew install redis
|
|
40
|
+
redis_bin="$(brew --prefix redis)/bin"
|
|
41
|
+
redis_log="$RUNNER_TEMP/redis.log"
|
|
42
|
+
if ! "$redis_bin/redis-server" --bind 127.0.0.1 --port 6379 \
|
|
43
|
+
--daemonize yes --save '' --appendonly no --logfile "$redis_log"; then
|
|
44
|
+
if [[ -f "$redis_log" ]]; then
|
|
45
|
+
cat "$redis_log"
|
|
46
|
+
fi
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
for attempt in {1..30}; do
|
|
50
|
+
if response="$("$redis_bin/redis-cli" -h 127.0.0.1 -p 6379 ping 2>&1)" &&
|
|
51
|
+
[[ "$response" == PONG ]]; then
|
|
52
|
+
exit 0
|
|
53
|
+
fi
|
|
54
|
+
sleep 1
|
|
55
|
+
done
|
|
56
|
+
printf '::error::Redis readiness failed: %s\n' "$response"
|
|
57
|
+
if [[ -f "$redis_log" ]]; then
|
|
58
|
+
cat "$redis_log"
|
|
59
|
+
fi
|
|
60
|
+
exit 1
|
|
61
|
+
- name: start Redis on Windows
|
|
62
|
+
if: runner.os == 'Windows'
|
|
63
|
+
shell: pwsh
|
|
64
|
+
run: |
|
|
65
|
+
$ErrorActionPreference = 'Stop'
|
|
66
|
+
# Pinned third-party development build; retain its bundled Cygwin runtime.
|
|
67
|
+
$archive = Join-Path $env:RUNNER_TEMP 'redis-8.2.10.zip'
|
|
68
|
+
$directory = Join-Path $env:RUNNER_TEMP 'redis-8.2.10'
|
|
69
|
+
Invoke-WebRequest 'https://github.com/redis-windows/redis-windows/releases/download/8.2.10/Redis-8.2.10-Windows-x64-cygwin.zip' -OutFile $archive
|
|
70
|
+
$expected = '92d9bdde87ed52bf859a677e5197d8ac855ba78d9940d457881723ae960df429'
|
|
71
|
+
if ((Get-FileHash $archive -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected) {
|
|
72
|
+
throw 'Redis archive checksum mismatch'
|
|
73
|
+
}
|
|
74
|
+
Expand-Archive $archive -DestinationPath $directory
|
|
75
|
+
$servers = @(Get-ChildItem $directory -Recurse -Filter redis-server.exe)
|
|
76
|
+
if ($servers.Count -ne 1) { throw 'Expected one Redis server executable' }
|
|
77
|
+
$redisDirectory = $servers[0].DirectoryName
|
|
78
|
+
$cli = Join-Path $redisDirectory 'redis-cli.exe'
|
|
79
|
+
$config = Join-Path $redisDirectory 'ci.conf'
|
|
80
|
+
@('bind 127.0.0.1', 'port 6379', 'protected-mode yes', 'save ""', 'appendonly no', 'daemonize no', 'logfile redis.log') |
|
|
81
|
+
Set-Content $config -Encoding utf8NoBOM
|
|
82
|
+
$server = Start-Process $servers[0].FullName -ArgumentList 'ci.conf' -WorkingDirectory $redisDirectory -PassThru
|
|
83
|
+
for ($attempt = 0; $attempt -lt 30; $attempt++) {
|
|
84
|
+
$server.Refresh()
|
|
85
|
+
if ($server.HasExited) { break }
|
|
86
|
+
$response = & $cli -h 127.0.0.1 -p 6379 ping 2>&1
|
|
87
|
+
if ($LASTEXITCODE -eq 0 -and "$response".Trim() -eq 'PONG') { exit 0 }
|
|
88
|
+
Start-Sleep -Seconds 1
|
|
89
|
+
}
|
|
90
|
+
$log = Join-Path $redisDirectory 'redis.log'
|
|
91
|
+
if (Test-Path $log) { Get-Content $log }
|
|
92
|
+
throw 'Redis did not become ready'
|
|
28
93
|
- name: init
|
|
29
94
|
run: npm run init
|
|
95
|
+
- name: run isolated test infrastructure gate
|
|
96
|
+
run: node --test scripts/testClaimProcesses.test.ts scripts/runIsolatedTest.test.ts scripts/testClaimCleanup.test.ts scripts/testProcessGuardianWindows.test.ts vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts vona/packages-vona/vona-core/src/lib/core/testClaimFilesystem.test.ts vona/packages-vona/vona-core/src/lib/framework/bootstrap.test.ts vona/packages-vona/vona-core/src/lib/framework/useApp.test.ts vona/packages-cli/cli-set-api/test/binBuildEntries.test.ts
|
|
30
97
|
- name: run A-Pay focused gate
|
|
31
|
-
run:
|
|
98
|
+
run: npm run test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/paypalProvider.test.ts test-pay/test/paypalLifecycle.test.ts test-pay/test/stripeProvider.test.ts test-pay/test/stripeLifecycle.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts
|
|
32
99
|
working-directory: vona
|
|
33
100
|
- name: run Commerce payment, shipment, and refund lifecycle gate
|
|
34
|
-
run:
|
|
101
|
+
run: npm run test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts commerce-trade/test/refundLifecycle.test.ts
|
|
35
102
|
working-directory: vona
|
|
36
|
-
- run:
|
|
103
|
+
- run: npm run test
|
|
37
104
|
working-directory: vona
|
|
38
|
-
- run:
|
|
105
|
+
- run: npm run build -- --flavor=ci
|
|
39
106
|
working-directory: vona
|
|
107
|
+
env:
|
|
108
|
+
# The full backend bundle exceeds the macOS runner's default 2 GiB V8 heap.
|
|
109
|
+
NODE_OPTIONS: --max-old-space-size=4096
|
|
40
110
|
- run: pnpm -C ./dist/ci install
|
|
41
111
|
working-directory: vona
|
|
42
|
-
- run:
|
|
112
|
+
- run: npm run play -- --mode=prod --flavor=ci
|
|
43
113
|
working-directory: vona
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.217
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add end-to-end testing on Windows.
|
|
8
|
+
|
|
9
|
+
### Improvements
|
|
10
|
+
|
|
11
|
+
- Improve Windows support and end-to-end test coverage.
|
|
12
|
+
- Update the SQLite test workflow.
|
|
13
|
+
- Update claim tests.
|
|
14
|
+
|
|
15
|
+
## 5.1.216
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- Apply feature updates.
|
|
20
|
+
|
|
21
|
+
### Improvements
|
|
22
|
+
|
|
23
|
+
- Update the agent governance configuration.
|
|
24
|
+
|
|
3
25
|
## 5.1.215
|
|
4
26
|
|
|
5
27
|
### Features
|
package/CONTRIBUTING.md
CHANGED
|
@@ -21,7 +21,7 @@ npm run init
|
|
|
21
21
|
|
|
22
22
|
`npm run init` is more than a package install: it installs multiple workspaces, prepares generated local configuration, initializes and synchronizes Vona and Zova, and generates required frontend and backend artifacts. Run it intentionally rather than as a retry for an unrelated narrow change.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
CI workflows run after pushes to `main`; do not assume that every pull request is automatically validated. The SQLite unit-test and Chromium Basic/Commerce E2E workflows configure Ubuntu, Windows, and macOS with Node.js 24 and pnpm 11.5.2. Matrix configuration alone is not proof that the platform gates or all six Basic/Commerce E2E combinations passed. For maintained edition and onboarding guidance, see [Cabloy Editions](https://cabloy.com/editions/overview#cabloy-basic).
|
|
25
25
|
|
|
26
26
|
## Develop and validate
|
|
27
27
|
|
|
@@ -44,7 +44,9 @@ Start with the narrowest meaningful check, then broaden validation when a change
|
|
|
44
44
|
| Synchronize Zova dependencies into Vona | `npm run deps:vona` |
|
|
45
45
|
| Build the public documentation site | `npm run docs:build` |
|
|
46
46
|
|
|
47
|
-
`npm run test:e2e` starts a fresh runner-managed local Vona worker in test mode and runs Playwright against Vona integrated SSR. Select flat spec basenames directly after the script, for example `npm run test:e2e a-commerce`; place Playwright filters after npm's `--` delimiter, for example `npm run test:e2e a-commerce -- --tag @smoke`. Browser commands consume existing SSR/REST artifacts and do not rebuild them.
|
|
47
|
+
`npm run test:e2e` starts a fresh runner-managed local Vona worker in test mode and runs Playwright against Vona integrated SSR. Select flat spec basenames directly after the script, for example `npm run test:e2e a-commerce`; place Playwright filters after npm's `--` delimiter, for example `npm run test:e2e a-commerce -- --tag @smoke`. Browser commands consume existing SSR/REST artifacts and do not rebuild them. The managed runner allocates run-owned database and Redis namespaces, runtime/public paths, application identity, API origin, and listener, so ordinary same-checkout development services are not a blocker by themselves. Coordinate shared build/dependency writes and check custom external resources separately; a free port alone is not ownership proof. Externally managed `E2E_BASE_URL` targets are unsupported.
|
|
48
|
+
|
|
49
|
+
For native Windows managed tests, use the same public npm commands from CMD or PowerShell. PowerShell 7 on `PATH`, local NTFS storage, and reachable Redis are required; E2E additionally needs installed Chromium (`npx playwright install chromium`) and ready SSR/REST artifacts. Windows workers and browsers must prove membership in their designated registered Job, and normal Vona teardown uses authenticated cooperative shutdown before whole-Job drainage is accepted. POSIX browsers retain independent process-group verification. Failed or interrupted runs retain claims and resources for inspection; force-stop or missing PIDs do not establish successful cleanup. See [Native Windows managed tests](https://cabloy.com/reference/repo-scripts#native-windows-managed-tests) for the lifecycle and limits: the browser scope is Cabloy Basic Chromium Basic/Commerce baselines, not all browsers, documentation E2E, or Cabloy Start.
|
|
48
50
|
|
|
49
51
|
The tracked pre-commit hook runs `lint-staged`. It may format staged Markdown and configuration files, and may lint-fix and format staged source files. Review the resulting staged changes and run the relevant explicit checks before submitting.
|
|
50
52
|
|
package/package.json
CHANGED
|
@@ -79,6 +79,8 @@ npm run test:e2e a-commerce -- --tag @web --tag @smoke
|
|
|
79
79
|
|
|
80
80
|
For the complete Basic tag vocabulary and managed-runner argument boundaries, see [Repo Scripts](/reference/repo-scripts#ssr-browser-checks). `E2E_BASE_URL` must be unset: externally managed targets are unsupported.
|
|
81
81
|
|
|
82
|
+
Cabloy Basic's Chromium Basic/Commerce CI baselines configure Ubuntu, Windows, and macOS; a matrix declaration is not evidence that all six combinations passed. On native Windows, public commands work from CMD or PowerShell and require PowerShell 7 on `PATH`, local NTFS storage, reachable Redis, installed Chromium, and ready paired SSR/REST artifacts. Managed browsers inherit a registered non-breakaway Windows Job; both worker and browser membership are verified through its owning helper, and cleanup requires whole-Job drainage rather than browser close alone. Normal Windows server teardown uses authenticated cooperative shutdown; failures or interruption retain claims and resources. POSIX browsers retain independent process-group verification. See [Native Windows managed tests](/reference/repo-scripts#native-windows-managed-tests) for the prerequisites, lifecycle, and coverage limits; this is not a claim about Cabloy Start's Windows browser support.
|
|
83
|
+
|
|
82
84
|
## Cabloy Start root wrappers
|
|
83
85
|
|
|
84
86
|
Cabloy Start is the public MIT-licensed edition. Its default root wrappers use the `cabloyStartAdmin` and `cabloyStartWeb` flavors:
|
|
@@ -60,7 +60,7 @@ Root `npm run test` delegates to Vona's `npm run test`; both it and direct `cd v
|
|
|
60
60
|
|
|
61
61
|
Claims contain a versioned, nonsecret resource inventory: owned local roots, exact database allocation intents, Redis namespaces and endpoint identities, process ownership, and cleanup progress. Allocators must receive an acknowledgment that their intent is recorded before creating a database or using framework-managed Redis resources. Credentials, full environment/configuration snapshots, and readiness secrets are not stored in claims.
|
|
62
62
|
|
|
63
|
-
Successful unit, coverage, and managed application E2E runs release inventoried database and Redis resources and remove their owned local roots after process shutdown is verified. Managed E2E uses a registered browser fixture
|
|
63
|
+
Successful unit, coverage, and managed application E2E runs release inventoried database and Redis resources and remove their owned local roots after process shutdown is verified. Managed E2E uses a registered browser fixture: POSIX browsers have independently verified process groups, while Windows workers and browsers must prove membership in the designated registered guardian Job. Cleanup requires verified process-group or whole-Job drainage, respectively; browser close or a missing PID is not sufficient proof. Run-owned reports, traces, and temporary profiles are deleted on success too. The claim is deleted last. Failed or interrupted runs retain their claim and resources for inspection. Cleanup failure also makes the command fail and retains the journal for retry.
|
|
64
64
|
|
|
65
65
|
Before each new invocation, the runner checks retained claims against their immutable UTC creation timestamp. Claims older than one day are eligible for cleanup; set `VONA_TEST_CLAIM_TTL_SECONDS` to a positive integer to change the default `86400` seconds. Eligibility is not permission to delete a live or unowned resource: the runner retains malformed claims, mismatched owner markers, symlinked paths, changed service endpoints, live or uncertain process identities, incomplete launch registrations, and claims with an existing cleanup lock. Inspect these warnings manually; a lock is never taken over merely because it is old. Cleanup resolves current trusted configuration without starting the application, running seeds, or opening a listener. It drops only recorded database names and scans only exact recorded Redis prefixes, never flushing Redis. Partial progress is journaled so a later eligible cleanup can retry safely. Legacy `.vona-test-claim-<id>` directories and inventory-less run roots are not automatically migrated or deleted.
|
|
66
66
|
|
|
@@ -86,6 +86,32 @@ For Vona integrated SSR, the allocated E2E origin is supplied to Zova at runtime
|
|
|
86
86
|
|
|
87
87
|
For deliberate **dev-mode** database preparation, `npm run db:reset` is a separate, destructive command: in a single process, it clears validated application-owned Redis keys, drops and recreates managed disposable development databases, reruns migrations and instance initialization, then runs module `seed()` hooks. It requires `database.testDatabase: true` in the dev configuration and a connection outside the disposable database. Ordinary `npm run dev` and `npm run dev:one` only reuse existing managed databases; if any required target is missing, startup fails promptly with an instruction to run `npm run db:reset`. They do not create databases, perform creation-time Redis cleanup, or seed. Never run `db:reset` against development data you need to retain. Upgrades do not reset existing development data or repeat its seeds. See [Development database preparation](#development-database-preparation) and [Migration and Changes](/backend/migration-and-changes#local-development-workflow).
|
|
88
88
|
|
|
89
|
+
### Native Windows managed tests {#native-windows-managed-tests}
|
|
90
|
+
|
|
91
|
+
<span id="native-windows-unit-tests"></span>
|
|
92
|
+
|
|
93
|
+
The Cabloy Basic SQLite unit-test workflow and Chromium application E2E workflow configure native Windows runners alongside Ubuntu and macOS. Each E2E suite has its own three-OS matrix with `fail-fast: false`, Node.js 24, pnpm 11.5.2, and SQLite. Basic selects `cabloy-basic home-user-account`; Commerce selects `a-commerce markdown` and retains its explicit Commerce artifact build and Vona dependency synchronization. A configured matrix is not proof of a successful run: inspect the focused gates, real Windows Chromium lifecycle regressions, and all six hosted Basic/Commerce suite-and-OS results before claiming verified platform compatibility.
|
|
94
|
+
|
|
95
|
+
On Windows, the managed test entrypoints require Node.js with npm, PowerShell 7 (`pwsh.exe` on `PATH`), local NTFS storage, and a reachable Redis service. E2E also requires Playwright's Chromium installation and current SSR/REST artifacts with Vona dependencies synchronized. PowerShell is an internal helper dependency, not a required interactive terminal. From either CMD or PowerShell, use the same public commands:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
npm run test
|
|
99
|
+
npx playwright install chromium
|
|
100
|
+
npm run test:e2e -- cabloy-basic home-user-account
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The Node runner explicitly invokes its checked-in Windows helper; it does not interpret PowerShell code in the caller's shell or download a helper runtime. It uses a non-breakaway Windows Job Object to contain the test process tree, with job membership established at process creation and kill-on-close ownership. Claim directories and files use protected private Windows ACLs and reject unsafe reparse points and hardlinks. Flushed file contents and same-volume atomic updates provide the supported local-NTFS process-crash publication contract; this is not a claim of POSIX directory-fsync equivalence or power-loss durability.
|
|
104
|
+
|
|
105
|
+
A missing native capability, unsafe claim storage, ambiguous process identity, or unproven job drainage fails closed. Failed/interrupted runs retain their claims; helper disappearance alone never authorizes cleanup. Do not bypass these checks with raw test commands or remove retained resources without verifying ownership. The npm CLI is resolved from the npm invocation or its standard location beside the Node installation; a pnpm executable is not substituted for npm's JavaScript entrypoint.
|
|
106
|
+
|
|
107
|
+
The Windows CI Redis setup uses a pinned, checksum-verified [third-party Redis Windows development distribution](https://github.com/redis-windows/redis-windows/releases/tag/8.2.10) with its bundled Cygwin runtime, bound to loopback. This is not an official Redis Windows port or a production deployment recommendation. Linux CI uses a Redis container with a health check; macOS starts native loopback Redis with bounded readiness polling. The native setups do not install persistent services. Local users must provision Redis deliberately; neither managed test command installs it. Chromium's `--with-deps` installation is Linux-only in CI; Windows and macOS use `playwright install chromium`.
|
|
108
|
+
|
|
109
|
+
For Windows E2E, the managed fixture verifies the Playwright worker's membership before browser launch, then verifies the actual browser child's membership before connecting. The claim broker authenticates the query and asks the live helper owning that registered Job to check exact PID/creation-time identity and membership in that specific Job. An inherited registration locator, an environment flag, or membership in some other Job is not ownership proof. The Job handle stays private, and Windows browsers do not fabricate POSIX process-group claims. Inherited or custom `TEMP`, `TMP`, and `TMPDIR` names are handled case-insensitively and normalized to the run-owned temporary root.
|
|
110
|
+
|
|
111
|
+
After authenticated readiness, normal Windows Vona teardown uses an authenticated, nonce-bound loopback shutdown request and acknowledgment, then awaits a zero server exit and the helper's whole-Job drainage proof. This path reuses the worker's awaited application-close lifecycle; it does not emulate POSIX graceful signals. Refusal, invalid proof, timeout, interruption, unexpected exit, or uncertain drainage fails the run and invokes failure-stop where possible. Force-stop is never reclassified as successful teardown. Controller/helper death and cancellation retain claims and resources; disappearing PIDs alone never authorize cleanup.
|
|
112
|
+
|
|
113
|
+
The supported application browser scope here is Cabloy Basic's Chromium Basic/Commerce baselines, not every browser, documentation E2E, Cabloy Start, or generic POSIX signal parity. The E2E workflow uploads failure-only reports from `vona/.app-test-*/playwright-report/` and `vona/.app-test-*/test-results/`, including hidden run directories, under suite-and-OS-specific artifact names. It never uploads whole run roots. Failed runs retain local claims/resources for inspection, while successful runs remove their run-owned reports and profiles along with other inventoried resources.
|
|
114
|
+
|
|
89
115
|
## Specification planning and derived charts
|
|
90
116
|
|
|
91
117
|
The current Cabloy Basic root scripts also expose:
|
|
@@ -17,7 +17,7 @@ test('rejects a defined E2E_BASE_URL before loading local configuration', () =>
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
test('isolated E2E target follows the assigned listener', () => {
|
|
20
|
-
const entrypoint =
|
|
20
|
+
const entrypoint = new URL('./e2e.ts', import.meta.url).href;
|
|
21
21
|
const result = spawnSync(
|
|
22
22
|
process.execPath,
|
|
23
23
|
[
|
|
@@ -21,6 +21,7 @@ async function fixture(
|
|
|
21
21
|
group?: number;
|
|
22
22
|
noProfile?: boolean;
|
|
23
23
|
exited?: boolean;
|
|
24
|
+
platform?: NodeJS.Platform;
|
|
24
25
|
} = {},
|
|
25
26
|
) {
|
|
26
27
|
const root = await realpath(await mkdtemp(join(tmpdir(), 'cabloy-managed-browser-')));
|
|
@@ -72,7 +73,9 @@ async function fixture(
|
|
|
72
73
|
VONA_TEST_CLAIM_URL: 'http://127.0.0.1:1234/event',
|
|
73
74
|
VONA_TEST_CLAIM_TOKEN: 'a'.repeat(64),
|
|
74
75
|
TMPDIR: temporaryRoot,
|
|
76
|
+
...(options.platform === 'win32' ? { TEMP: temporaryRoot, TMP: temporaryRoot } : {}),
|
|
75
77
|
},
|
|
78
|
+
platform: options.platform ?? 'linux',
|
|
76
79
|
projectPath,
|
|
77
80
|
sendEvent: async (_env: NodeJS.ProcessEnv, event: TestClaimEvent) => {
|
|
78
81
|
const stage = event.type === 'process' ? event.process.stage : event.type;
|
|
@@ -85,13 +88,24 @@ async function fixture(
|
|
|
85
88
|
if (options.fault === 'inspect') throw new Error('inspection failed');
|
|
86
89
|
return {
|
|
87
90
|
pid,
|
|
88
|
-
pgid: options.group ?? pid,
|
|
89
|
-
startTime: 'Wed Oct 14 01:02:03 2026',
|
|
91
|
+
...(options.platform === 'win32' ? {} : { pgid: options.group ?? pid }),
|
|
92
|
+
startTime: options.platform === 'win32' ? '134000000000000000' : 'Wed Oct 14 01:02:03 2026',
|
|
90
93
|
host: 'test-host',
|
|
91
94
|
state: 'S',
|
|
92
95
|
args: '',
|
|
93
96
|
};
|
|
94
97
|
},
|
|
98
|
+
assertWindowsJob: async (
|
|
99
|
+
_env: NodeJS.ProcessEnv,
|
|
100
|
+
identity: { pid: number; startTime: string; host: string },
|
|
101
|
+
) => {
|
|
102
|
+
const role = identity.pid === process.pid ? 'worker' : 'browser';
|
|
103
|
+
calls.push(`job-${role}`);
|
|
104
|
+
assert.equal(identity.startTime, '134000000000000000');
|
|
105
|
+
assert.equal(identity.host, 'test-host');
|
|
106
|
+
assert.equal(Object.hasOwn(identity, 'pgid'), false);
|
|
107
|
+
if (options.fault === `job-${role}`) throw new Error('designated Job membership unavailable');
|
|
108
|
+
},
|
|
95
109
|
waitForGroupExit: async (pgid: number) => {
|
|
96
110
|
calls.push('wait');
|
|
97
111
|
assert.equal(pgid, 4242);
|
|
@@ -277,7 +291,7 @@ for (const invalid of ['tmpdir', 'owner', 'transport', 'connect', 'symlink']) {
|
|
|
277
291
|
if (invalid === 'transport') f.dependencies.env.VONA_TEST_CLAIM_TOKEN = '';
|
|
278
292
|
if (invalid === 'symlink') {
|
|
279
293
|
await rm(f.temporaryRoot, { recursive: true });
|
|
280
|
-
await symlink(f.root, f.temporaryRoot);
|
|
294
|
+
await symlink(f.root, f.temporaryRoot, process.platform === 'win32' ? 'junction' : 'dir');
|
|
281
295
|
}
|
|
282
296
|
const options =
|
|
283
297
|
invalid === 'connect'
|
|
@@ -364,6 +378,104 @@ test('rejects an explicit external profile argument before launch', async () =>
|
|
|
364
378
|
}
|
|
365
379
|
});
|
|
366
380
|
|
|
381
|
+
test('Windows proves worker and browser Job membership without per-browser POSIX claims', async () => {
|
|
382
|
+
const f = await fixture({ platform: 'win32' });
|
|
383
|
+
try {
|
|
384
|
+
await useManagedBrowser(
|
|
385
|
+
f.browserOptions,
|
|
386
|
+
async () => {
|
|
387
|
+
f.calls.push('use');
|
|
388
|
+
},
|
|
389
|
+
f.dependencies,
|
|
390
|
+
);
|
|
391
|
+
assert.deepEqual(f.calls, [
|
|
392
|
+
'inspect',
|
|
393
|
+
'job-worker',
|
|
394
|
+
'launch',
|
|
395
|
+
'inspect',
|
|
396
|
+
'job-browser',
|
|
397
|
+
'connect',
|
|
398
|
+
'use',
|
|
399
|
+
'close',
|
|
400
|
+
]);
|
|
401
|
+
assert.deepEqual(f.events, []);
|
|
402
|
+
} finally {
|
|
403
|
+
await f.dispose();
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
for (const fault of ['job-worker', 'job-browser', 'launch', 'inspect', 'connect', 'close']) {
|
|
408
|
+
test(`Windows fails closed on ${fault} without acknowledging browser drainage`, async () => {
|
|
409
|
+
const f = await fixture({ platform: 'win32', fault });
|
|
410
|
+
try {
|
|
411
|
+
await assert.rejects(useManagedBrowser(f.browserOptions, async () => {}, f.dependencies));
|
|
412
|
+
assert.deepEqual(f.events, []);
|
|
413
|
+
assert.ok(!f.calls.includes('wait'));
|
|
414
|
+
if (['job-worker', 'inspect'].includes(fault)) assert.ok(!f.calls.includes('launch'));
|
|
415
|
+
if (fault === 'job-browser') {
|
|
416
|
+
assert.ok(f.calls.includes('close'));
|
|
417
|
+
assert.ok(!f.calls.includes('connect'));
|
|
418
|
+
}
|
|
419
|
+
} finally {
|
|
420
|
+
await f.dispose();
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
for (const variable of ['TEMP', 'TMP', 'TMPDIR', 'temp']) {
|
|
426
|
+
test(`Windows rejects inherited ${variable} outside its run before browser launch`, async () => {
|
|
427
|
+
const f = await fixture({ platform: 'win32' });
|
|
428
|
+
try {
|
|
429
|
+
const env: NodeJS.ProcessEnv = { ...f.dependencies.env, [variable]: '/unowned' };
|
|
430
|
+
await assert.rejects(
|
|
431
|
+
useManagedBrowser(f.browserOptions, async () => {}, { ...f.dependencies, env }),
|
|
432
|
+
/run-owned temporary paths/,
|
|
433
|
+
);
|
|
434
|
+
assert.deepEqual(f.calls, []);
|
|
435
|
+
} finally {
|
|
436
|
+
await f.dispose();
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
test('Windows normalizes custom temporary environment names without mutating caller options', async () => {
|
|
442
|
+
const f = await fixture({ platform: 'win32' });
|
|
443
|
+
const env = { temp: '/unowned', Tmp: '/unowned', tmpdir: '/unowned', CUSTOM: 'keep' };
|
|
444
|
+
try {
|
|
445
|
+
await useManagedBrowser(
|
|
446
|
+
{ ...f.browserOptions, launchOptions: { env } },
|
|
447
|
+
async () => {},
|
|
448
|
+
f.dependencies,
|
|
449
|
+
);
|
|
450
|
+
assert.deepEqual(f.launchedOptions()?.env, {
|
|
451
|
+
TEMP: f.temporaryRoot,
|
|
452
|
+
TMP: f.temporaryRoot,
|
|
453
|
+
TMPDIR: f.temporaryRoot,
|
|
454
|
+
CUSTOM: 'keep',
|
|
455
|
+
});
|
|
456
|
+
assert.equal(env.temp, '/unowned');
|
|
457
|
+
assert.equal(env.Tmp, '/unowned');
|
|
458
|
+
assert.equal(env.tmpdir, '/unowned');
|
|
459
|
+
} finally {
|
|
460
|
+
await f.dispose();
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test('Windows closes a browser with an escaping actual profile without connecting', async () => {
|
|
465
|
+
const f = await fixture({ platform: 'win32', profile: '/unowned/profile' });
|
|
466
|
+
try {
|
|
467
|
+
await assert.rejects(
|
|
468
|
+
useManagedBrowser(f.browserOptions, async () => {}, f.dependencies),
|
|
469
|
+
/outside its run-owned area/,
|
|
470
|
+
);
|
|
471
|
+
assert.deepEqual(f.events, []);
|
|
472
|
+
assert.ok(f.calls.includes('close'));
|
|
473
|
+
assert.ok(!f.calls.includes('connect'));
|
|
474
|
+
} finally {
|
|
475
|
+
await f.dispose();
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
367
479
|
test('discovers exact Chromium and Firefox argv profiles; uses the temporary area for WebKit', () => {
|
|
368
480
|
assert.equal(
|
|
369
481
|
discoverBrowserProfile(['browser', '--user-data-dir=/run/tmp/with spaces']),
|