fix(ci): restore the peer dep the swc install drops, and close a test race (#2040)

Two separate reasons v4.0.0 (#1719) is red.

The SonarQube workflow killed all 616 client test files with "Cannot find module
'@testing-library/dom'". That package is a peer dependency of
@testing-library/react and not a direct dependency of the client, and the step
that installs @swc/core's Linux binary passes --legacy-peer-deps, which tells npm
not to put peer dependencies back when it re-resolves the tree. In test.yml that
step lives in the server job, which never runs client tests, so folding the jobs
into one (#2039) is what brought the two together. It now installs alongside the
swc binary, with its version read from the peer range rather than pinned, and a
one-line resolve check right after fails fast with the module name instead of six
hundred identical failures twenty minutes later.

MAdminStoragePanel's FE-MOB-MSTOR-015 was a genuine race in the test, not in the
panel. Its POST handler incremented postCount, then awaited request.json(), and
only then pushed to migrationPosts — so between the two there was a window where
one POST had been counted but migrationPosts was still empty. The GET handler
reports `migrations: []` for an empty migrationPosts, i.e. "nothing running", so
a poll landing in that window told the queue it was free to start the second
migration, and the waitFor(postCount === 1) above could resolve inside it too.
Parsing before counting closes it. Verified by running the whole mobile suite at
--maxWorkers=8, where it used to surface.
This commit is contained in:
Maurice
2026-08-25 17:09:29 +02:00
committed by GitHub
parent 6fe5009c20
commit fe5c6f182e
2 changed files with 29 additions and 2 deletions
+20 -1
View File
@@ -45,9 +45,28 @@ jobs:
# Same reason as test.yml: the lockfile was generated on Windows and omits
# @swc/core's Linux optional native binary, so npm ci skips it on the
# runner and the server's SWC transform cannot load.
#
# @testing-library/dom rides along, and has to. This install re-resolves
# the tree, and --legacy-peer-deps means npm does not put peer
# dependencies back — @testing-library/dom is a peer of
# @testing-library/react and not a direct dependency of the client, so it
# was silently dropped and all 616 client test files died on
# "Cannot find module". In test.yml this step lives in the server job,
# which never runs client tests, so the collision only appears once the
# jobs are folded into one. The version is read from the peer range rather
# than pinned here, so a bump to @testing-library/react carries over.
run: |
SWC_VERSION=$(node -p "require('@swc/core/package.json').version")
npm install --no-save --legacy-peer-deps "@swc/core-linux-x64-gnu@$SWC_VERSION"
DOM_RANGE=$(node -p "require('@testing-library/react/package.json').peerDependencies['@testing-library/dom']")
npm install --no-save --legacy-peer-deps \
"@swc/core-linux-x64-gnu@$SWC_VERSION" \
"@testing-library/dom@$DOM_RANGE"
- name: Verify the test toolchain survived that install
# Cheap tripwire: if a future dependency bump loses another peer the same
# way, this fails here with the name of the module instead of 600
# identical test failures twenty minutes later.
run: node -e "require.resolve('@testing-library/dom'); require.resolve('@swc/core')"
- name: Build shared
# Only shared, not the root `build`. server and client run their suites
@@ -329,8 +329,16 @@ describe('MAdminStoragePanel', () => {
server.use(
http.put('/api/admin/storage', () => HttpResponse.json(savedState)),
http.post('/api/admin/storage/migrations', async ({ request }) => {
postCount += 1;
// Parse BEFORE counting, so postCount and migrationPosts move together.
// With the increment first, the await left a window where postCount was
// already 1 while migrationPosts was still empty — and the GET handler
// below reports `migrations: []` for an empty migrationPosts, i.e. "no
// migration running". A poll landing in that window told the queue it was
// free to dequeue the second candidate, and the `waitFor(postCount === 1)`
// further down could resolve inside it too. Rare locally, reliable under
// CI load, where the microtask gap is wider than the 50ms test poll.
const body = (await request.json()) as { category: StorageCategory; to: string };
postCount += 1;
migrationPosts.push(body);
// Pins the single-flight fix: the second POST must never arrive
// before the first migration is server-confirmed terminal — a