fix(server): shard the server-test unit job to stop the intermittent crash (#23009)

## Problem

`server-test` fails intermittently: exit 1 with **no `FAIL` line and no
`Test Suites:/Tests:` summary** — the jest run is aborted mid-way,
before the reporter's `onRunComplete`.

## Root cause

The `test` target runs the entire unit suite (~6,600 tests) in **one
in-band jest process on a single runner VM** (`nx.json` sets
`maxWorkers: 1` for the `ci` configuration). A few minutes in, that
process is killed by an **external `SIGKILL`** — confirmed *not* OOM
(~15 GB free at kill time, no cgroup `oom_kill`) and *not* an in-process
crash (a Node diagnostic report armed with `--report-on-fatalerror` +
`--report-uncaught-exception` writes nothing). The whole-run kill is why
it fails intermittently with no summary. `maxWorkers=2` on one VM still
dies, so the threshold is **per-VM**, not per-process.

## Fix

Shard the unit suite across VMs, the same way `server-integration-test`
already does:

- A `twenty-server` `test:ci` target runs jest directly (so `--shard`
forwards) with `dependsOn: ["^build"]` so the workspace deps are built.
- `server-test` becomes a 4-way matrix; each shard runs a quarter of the
suite, well under the kill threshold.
- `ci-server-status-check` already aggregates `server-test`, so required
checks are unchanged.

Also provides two mocks a completed run needs but the SIGKILL had been
masking in `ApplicationRegistrationService.upsertFromCatalog` unit
tests: the `MetricsService` provider and
`applicationRegistrationRepository.createQueryBuilder`.
This commit is contained in:
Félix Malfait
2026-07-20 06:39:13 +02:00
committed by GitHub
parent cdb7e56720
commit 6e1e98f4ab
3 changed files with 27 additions and 5 deletions
@@ -10,6 +10,7 @@ import { ApplicationEntity } from 'src/engine/core-modules/application/applicati
import { CacheLockService } from 'src/engine/core-modules/cache-lock/cache-lock.service';
import { CoreEntityCacheService } from 'src/engine/core-entity-cache/services/core-entity-cache.service';
import { ServerFileStorageService } from 'src/engine/core-modules/file-storage/services/server-file-storage.service';
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
describe('ApplicationRegistrationService - upsertFromCatalog', () => {
@@ -18,6 +19,7 @@ describe('ApplicationRegistrationService - upsertFromCatalog', () => {
findOne: jest.Mock;
save: jest.Mock;
create: jest.Mock;
createQueryBuilder: jest.Mock;
};
const catalogParams = {
@@ -45,6 +47,13 @@ describe('ApplicationRegistrationService - upsertFromCatalog', () => {
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn((entity) => entity),
createQueryBuilder: jest.fn(() => ({
update: jest.fn().mockReturnThis(),
set: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
andWhere: jest.fn().mockReturnThis(),
execute: jest.fn().mockResolvedValue({ affected: 0 }),
})),
};
const module: TestingModule = await Test.createTestingModule({
@@ -82,6 +91,10 @@ describe('ApplicationRegistrationService - upsertFromCatalog', () => {
provide: CoreEntityCacheService,
useValue: { invalidate: jest.fn() },
},
{
provide: MetricsService,
useValue: { incrementCounterBy: jest.fn() },
},
],
}).compile();