diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5bbe73ad578..eee80bdf886 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -494,85 +494,13 @@ jobs:
if: needs.change-check.outputs.realm-server == 'true' || github.ref == 'refs/heads/main' || needs.change-check.outputs.run_all == 'true'
runs-on: ubuntu-latest
concurrency:
- group: realm-server-test-${{ matrix.testModule }}-${{ github.head_ref || github.run_id }}
+ group: realm-server-test-${{ matrix.shardIndex }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
- testModule:
- [
- "auth-client-test.ts",
- "billing-test.ts",
- "card-dependencies-endpoint-test.ts",
- "card-endpoints-test.ts",
- "card-reference-resolver-test.ts",
- "card-source-endpoints-test.ts",
- "definition-lookup-test.ts",
- "file-watcher-events-test.ts",
- "indexing-event-sink-test.ts",
- "indexing-test.ts",
- "runtime-dependency-tracker-test.ts",
- "transpile-test.ts",
- "module-syntax-test.ts",
- "node-realm-test.ts",
- "permissions/permission-checker-test.ts",
- "prerendering-test.ts",
- "prerender-server-test.ts",
- "prerender-manager-test.ts",
- "prerender-proxy-test.ts",
- "remote-prerenderer-test.ts",
- "queue-test.ts",
- "realm-endpoints/cancel-indexing-job-test.ts",
- "realm-endpoints/dependencies-test.ts",
- "realm-endpoints/directory-test.ts",
- "realm-endpoints/info-test.ts",
- "realm-endpoints/invalidate-urls-test.ts",
- "realm-endpoints/lint-test.ts",
- "realm-endpoints/mtimes-test.ts",
- "realm-endpoints/permissions-test.ts",
- "realm-endpoints/publishability-test.ts",
- "realm-endpoints/reindex-test.ts",
- "realm-endpoints/search-test.ts",
- "realm-endpoints/user-test.ts",
- "realm-endpoints-test.ts",
- "sanitize-head-html-test.ts",
- "search-prerendered-test.ts",
- "types-endpoint-test.ts",
- "server-config-test.ts",
- "server-endpoints/authentication-test.ts",
- "server-endpoints/bot-commands-test.ts",
- "server-endpoints/bot-registration-test.ts",
- "server-endpoints/delete-realm-test.ts",
- "server-endpoints/download-realm-test.ts",
- "server-endpoints/index-responses-test.ts",
- "server-endpoints/maintenance-endpoints-test.ts",
- "server-endpoints/queue-status-test.ts",
- "server-endpoints/realm-lifecycle-test.ts",
- "server-endpoints/search-test.ts",
- "server-endpoints/info-test.ts",
- "server-endpoints/search-prerendered-test.ts",
- "server-endpoints/stripe-session-test.ts",
- "server-endpoints/stripe-webhook-test.ts",
- "server-endpoints/user-and-catalog-test.ts",
- "server-endpoints/incoming-webhook-test.ts",
- "server-endpoints/webhook-commands-test.ts",
- "server-endpoints/webhook-receiver-test.ts",
- "server-endpoints/federated-types-test.ts",
- "virtual-network-test.ts",
- "atomic-endpoints-test.ts",
- "request-forward-test.ts",
- "publish-unpublish-realm-test.ts",
- "boxel-domain-availability-test.ts",
- "claim-boxel-domain-test.ts",
- "command-parsing-utils-test.ts",
- "delete-boxel-claimed-domain-test.ts",
- "get-boxel-claimed-domain-test.ts",
- "realm-auth-test.ts",
- "run-command-task-test.ts",
- "queries-test.ts",
- "session-room-queries-test.ts",
- "full-reindex-test.ts",
- ]
+ shardIndex: [1, 2, 3, 4, 5, 6]
+ shardTotal: [6]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/init
@@ -586,6 +514,10 @@ jobs:
run: |
shopt -s dotglob
cp -a .test-web-assets-artifact/. ./
+ - name: Compute shard test modules
+ id: shard_modules
+ run: echo "modules=$(node scripts/shard-test-modules.js ${{ matrix.shardIndex }} 6)" >> "$GITHUB_OUTPUT"
+ working-directory: packages/realm-server
- name: Start test services (icons + host dist + realm servers)
run: mise run test-services:realm-server | tee -a /tmp/server.log &
- name: create realm users
@@ -595,7 +527,15 @@ jobs:
run: pnpm test:wait-for-servers
working-directory: packages/realm-server
env:
- TEST_MODULE: ${{matrix.testModule}}
+ TEST_MODULES: ${{ steps.shard_modules.outputs.modules }}
+ JUNIT_OUTPUT_FILE: ${{ github.workspace }}/junit/realm-server-${{ matrix.shardIndex }}.xml
+ - name: Upload junit report
+ uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 4.6.1
+ if: ${{ !cancelled() }}
+ with:
+ name: realm-server-test-report-${{ matrix.shardIndex }}
+ path: junit/realm-server-${{ matrix.shardIndex }}.xml
+ retention-days: 30
- name: Print realm server logs
if: ${{ !cancelled() }}
run: cat /tmp/server.log
@@ -603,7 +543,7 @@ jobs:
id: artifact_name
if: ${{ !cancelled() }}
run: |
- export SAFE_ARTIFACT_NAME=$(echo ${{ matrix.testModule }} | sed 's/[/]/_/g')
+ export SAFE_ARTIFACT_NAME=shard-${{ matrix.shardIndex }}
echo "artifact_name=$SAFE_ARTIFACT_NAME" >> "$GITHUB_OUTPUT"
- name: Extract worker and prerender logs
if: ${{ !cancelled() }}
@@ -672,6 +612,39 @@ jobs:
path: /tmp/host-dist.log
retention-days: 30
+ realm-server-merge-reports:
+ name: Merge Realm Server reports and publish
+ if: ${{ !cancelled() && (needs.change-check.outputs.realm-server == 'true' || github.ref == 'refs/heads/main' || needs.change-check.outputs.run_all == 'true') }}
+ concurrency:
+ group: realm-server-merge-reports-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+ needs: [change-check, realm-server-test]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - uses: ./.github/actions/init
+ - name: Download JUnit reports
+ uses: actions/download-artifact@b14cf4c92620c250e1c074ab0a5800e37df86765 # 4.2.0
+ with:
+ path: all-realm-server-reports
+ pattern: realm-server-test-report-*
+ merge-multiple: true
+ - name: Merge reports
+ run: npx junit-report-merger realm-server.xml "./all-realm-server-reports/*.xml"
+ - name: Upload merged report
+ uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # 4.6.1
+ if: ${{ !cancelled() }}
+ with:
+ name: realm-server-test-report-merged
+ path: realm-server.xml
+ retention-days: 30
+ - name: Publish test results
+ uses: EnricoMi/publish-unit-test-result-action@170bf24d20d201b842d7a52403b73ed297e6645b # 2.18.0
+ if: ${{ !cancelled() }}
+ with:
+ junit_files: realm-server.xml
+ check_name: Realm Server Test Results
+
software-factory-test:
name: Software Factory Tests
needs: [change-check, test-web-assets]
diff --git a/AGENTS.md b/AGENTS.md
index 06974549b91..1a0056340b9 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -116,6 +116,8 @@
`pnpm test`
- Run a single module:
`TEST_MODULE=card-endpoints-test.ts pnpm test-module`
+- Run a list of modules:
+ `TEST_MODULES=card-endpoints-test.ts|another-module-test.ts pnpm test`
- Focusing on single test or module:
Add `.only` to module/test declaration (`test.only('returns a 201 response', ...)`)
Then run `pnpm test`
diff --git a/README.md b/README.md
index 2f37714ed1a..2557ea8194a 100644
--- a/README.md
+++ b/README.md
@@ -462,7 +462,11 @@ First make sure to generate the host app's `dist/` output in order to support ca
To run the `packages/realm-server/` workspace tests start:
1. `mise run dev` from the repo root to serve _both_ the base realm and the realm that serves the test cards for node.
-2. Run `pnpm test` in the `packages/realm-server/` workspace to run the realm node tests. `TEST_MODULE=realm-endpoints-test.ts pnpm test-module` is an example of how to run a single test module.
+2. Run the realm server tests:
+
+- `pnpm test` in the `packages/realm-server/` workspace to run the realm node tests in full (~1hr).
+- `TEST_MODULES="types-endpoint-test.ts|another-test-module.ts" pnpm test` in the `packages/realm-server/` workspace to run tests for a subset of modules.
+- `TEST_MODULE="types-endpoint-test.ts" pnpm test-module` in the `packages/realm-server/` workspace to run tests for a specific module.
### Boxel UI
diff --git a/packages/realm-server/package.json b/packages/realm-server/package.json
index 39c1f3b623d..88e39a633f8 100644
--- a/packages/realm-server/package.json
+++ b/packages/realm-server/package.json
@@ -98,7 +98,7 @@
"start:host-dist": "./scripts/start-host-dist.sh",
"start:pg": "./scripts/start-pg.sh",
"stop:pg": "./scripts/stop-pg.sh",
- "test:wait-for-servers": "WAIT_ON_TIMEOUT=900000 NODE_NO_WARNINGS=1 start-server-and-test 'pnpm run wait' 'http-get://localhost:4201/base/_readiness-check?acceptHeader=application%2Fvnd.api%2Bjson' 'pnpm run wait' 'http-get://localhost:4202/node-test/_readiness-check?acceptHeader=application%2Fvnd.api%2Bjson|http://localhost:8008|http://localhost:5001' 'test-module'",
+ "test:wait-for-servers": "WAIT_ON_TIMEOUT=900000 NODE_NO_WARNINGS=1 start-server-and-test 'pnpm run wait' 'http-get://localhost:4201/base/_readiness-check?acceptHeader=application%2Fvnd.api%2Bjson' 'pnpm run wait' 'http-get://localhost:4202/node-test/_readiness-check?acceptHeader=application%2Fvnd.api%2Bjson|http://localhost:8008|http://localhost:5001' 'test'",
"setup:base-in-deployment": "mkdir -p /persistent/base && rsync --dry-run --itemize-changes --checksum --recursive --delete ../base/. /persistent/base/ && rsync --checksum --recursive --delete ../base/. /persistent/base/",
"setup:experiments-in-deployment": "mkdir -p /persistent/experiments && rsync --dry-run --itemize-changes --checksum --recursive ../experiments-realm/. /persistent/experiments/ && rsync --checksum --recursive ../experiments-realm/. /persistent/experiments/",
"setup:catalog-in-deployment": "mkdir -p /persistent/catalog && rsync --dry-run --itemize-changes --checksum --recursive --delete ../catalog-realm/. /persistent/catalog/ && rsync --checksum --recursive --delete ../catalog-realm/. /persistent/catalog/",
@@ -131,7 +131,6 @@
"lint:js": "eslint . --report-unused-disable-directives --cache",
"lint:js:fix": "eslint . --report-unused-disable-directives --fix",
"lint:glint": "glint",
- "lint:test-shards": "ts-node --transpileOnly scripts/lint-test-shards.ts",
"full-reset": "./scripts/full-reset.sh",
"full-reindex": "./scripts/full-reindex.sh",
"clear-modules-cache": "NODE_NO_WARNINGS=1 PGDATABASE=${PGDATABASE:-boxel} PGPORT=${PGPORT:-5435} ts-node --transpileOnly scripts/clear-modules-cache.ts",
diff --git a/packages/realm-server/scripts/junit-reporter.js b/packages/realm-server/scripts/junit-reporter.js
new file mode 100644
index 00000000000..3081188c711
--- /dev/null
+++ b/packages/realm-server/scripts/junit-reporter.js
@@ -0,0 +1,111 @@
+#!/usr/bin/env node
+/* eslint-env node */
+'use strict';
+
+// QUnit JUnit XML reporter for CI.
+//
+// Usage: qunit --reporter junit-reporter.js ...
+// Or: qunit --require ./scripts/junit-reporter.js --reporter console ...
+//
+// Set JUNIT_OUTPUT_FILE to control the output path (default: junit/realm-server.xml).
+// When used as a --require module, this attaches alongside the default console
+// reporter so you get both terminal output and a JUnit file.
+
+const fs = require('node:fs'); // eslint-disable-line @typescript-eslint/no-var-requires
+const path = require('node:path'); // eslint-disable-line @typescript-eslint/no-var-requires
+const QUnit = require('qunit'); // eslint-disable-line @typescript-eslint/no-var-requires
+
+const outputFile =
+ process.env.JUNIT_OUTPUT_FILE ||
+ path.join(process.cwd(), '..', '..', 'junit', 'realm-server.xml');
+
+const suites = new Map(); // moduleName -> { tests, failures, errors, time, testCases }
+
+function escapeXml(str) {
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+QUnit.on('testEnd', (data) => {
+ const moduleName = data.module || 'default';
+ const testName = data.name || 'unknown';
+ const runtime = (data.runtime || 0) / 1000; // ms → seconds
+ const status = data.status; // passed, failed, skipped, todo
+
+ if (!suites.has(moduleName)) {
+ suites.set(moduleName, {
+ tests: 0,
+ failures: 0,
+ errors: 0,
+ skipped: 0,
+ time: 0,
+ testCases: [],
+ });
+ }
+
+ const suite = suites.get(moduleName);
+ suite.tests++;
+ suite.time += runtime;
+
+ let caseXml = ` {
+ let msg = e.message || '';
+ if (e.actual !== undefined && e.expected !== undefined) {
+ msg += `\nExpected: ${JSON.stringify(e.expected)}\nActual: ${JSON.stringify(e.actual)}`;
+ }
+ if (e.stack) {
+ msg += `\n${e.stack}`;
+ }
+ return msg;
+ })
+ .join('\n---\n');
+ caseXml += `>\n ${escapeXml(messages)}\n `;
+ } else if (status === 'skipped' || status === 'todo') {
+ suite.skipped++;
+ caseXml += `>\n \n `;
+ } else {
+ caseXml += ` />`;
+ }
+
+ suite.testCases.push(caseXml);
+});
+
+QUnit.on('runEnd', () => {
+ const suitesXml = [];
+ let totalTests = 0;
+ let totalFailures = 0;
+ let totalErrors = 0;
+ let totalSkipped = 0;
+ let totalTime = 0;
+
+ for (const [name, suite] of suites) {
+ totalTests += suite.tests;
+ totalFailures += suite.failures;
+ totalErrors += suite.errors;
+ totalSkipped += suite.skipped;
+ totalTime += suite.time;
+
+ suitesXml.push(
+ ` \n${suite.testCases.join('\n')}\n `,
+ );
+ }
+
+ const xml = [
+ ``,
+ ``,
+ ...suitesXml,
+ ``,
+ ].join('\n');
+
+ const dir = path.dirname(outputFile);
+ fs.mkdirSync(dir, { recursive: true });
+ fs.writeFileSync(outputFile, xml, 'utf8');
+});
diff --git a/packages/realm-server/scripts/lint-test-shards.ts b/packages/realm-server/scripts/lint-test-shards.ts
deleted file mode 100755
index f0df182e839..00000000000
--- a/packages/realm-server/scripts/lint-test-shards.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { readFileSync } from 'fs-extra';
-import { glob } from 'glob';
-import yaml from 'js-yaml';
-import { join } from 'path';
-
-const YAML_FILE = join(
- __dirname,
- '..',
- '..',
- '..',
- '.github',
- 'workflows',
- 'ci.yaml',
-);
-const TEST_DIR = join(__dirname, '..', 'tests');
-
-function getCiTestModules(yamlFilePath: string) {
- try {
- const yamlContent = readFileSync(yamlFilePath, 'utf8');
- const yamlData = yaml.load(yamlContent) as Record;
-
- const shardIndexes: string[] =
- yamlData?.jobs?.['realm-server-test']?.strategy?.matrix?.testModule;
-
- if (!Array.isArray(shardIndexes)) {
- throw new Error(
- `Invalid 'jobs.realm-server-test.strategy.matrix.testModule' format in the YAML file.`,
- );
- }
-
- return shardIndexes;
- } catch (error: any) {
- console.error(`Error reading shardIndex from YAML file: ${error.message}`);
- process.exit(1);
- }
-}
-
-function getFilesystemTestModules(testDir: string) {
- try {
- const files = glob.sync(`${testDir}/**/*-test.ts`, { nodir: true });
- return files.map((file: string) => file.replace(`${testDir}/`, ''));
- } catch (error: any) {
- console.error(
- `Error reading test files from dir ${testDir}: ${error.message}`,
- );
- process.exit(1);
- }
-}
-
-function validateTestFiles(yamlFilePath: string, testDir: string) {
- const ciTestModules = getCiTestModules(yamlFilePath);
- const filesystemTestModules = getFilesystemTestModules(testDir);
-
- let errorFound = false;
-
- for (let filename of filesystemTestModules) {
- if (!ciTestModules.includes(filename)) {
- console.error(
- `Error: Test file '${filename}' exists in the filesystem but not in the ${yamlFilePath} file.`,
- );
- errorFound = true;
- }
- }
- for (let filename of ciTestModules) {
- if (!filesystemTestModules.includes(filename)) {
- console.error(
- `Error: Test file '${filename}' exists in the YAML file but not in the filesystem.`,
- );
- errorFound = true;
- }
- }
-
- if (errorFound) {
- process.exit(1);
- } else {
- console.log(
- `All test files are accounted for in the ${yamlFilePath} file for the realm-server matrix strategy.`,
- );
- }
-}
-
-validateTestFiles(YAML_FILE, TEST_DIR);
diff --git a/packages/realm-server/scripts/run-test-modules.js b/packages/realm-server/scripts/run-test-modules.js
new file mode 100644
index 00000000000..9d3d2246865
--- /dev/null
+++ b/packages/realm-server/scripts/run-test-modules.js
@@ -0,0 +1,58 @@
+#!/usr/bin/env node
+/* eslint-env node */
+'use strict';
+
+const { spawnSync } = require('node:child_process'); // eslint-disable-line @typescript-eslint/no-var-requires
+
+function buildModuleFilter(modulesToMatch) {
+ const escaped = modulesToMatch
+ .map((moduleName) => escapeRegex(moduleName))
+ .join('|');
+ const pattern = `^(?:${escaped})(?:\\s>\\s|:)`;
+ return `/${pattern}/`;
+}
+
+function escapeRegex(value) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\//g, '\\/');
+}
+
+const rawModules = process.env.TEST_MODULES ?? '';
+const cleanedRaw = rawModules.trim();
+
+if (!cleanedRaw) {
+ console.error('TEST_MODULES must be set.');
+ process.exit(1);
+}
+
+const modules = cleanedRaw
+ .split(/[|,]/)
+ .map((value) => value.trim())
+ .filter(Boolean)
+ .map((value) => value.replace(/^['"]+|['"]+$/g, ''));
+
+if (modules.length === 0) {
+ console.error('No module names found in TEST_MODULES.');
+ process.exit(1);
+}
+
+const args = ['--require', 'ts-node/register/transpile-only'];
+
+args.push('--filter', buildModuleFilter(modules));
+
+args.push('tests/index.ts');
+
+const qunitBin = require.resolve('qunit/bin/qunit.js');
+const result = spawnSync(process.execPath, [qunitBin, ...args], {
+ stdio: 'inherit',
+ env: process.env,
+});
+
+if (typeof result.status === 'number') {
+ process.exit(result.status);
+}
+
+if (result.error) {
+ console.error(result.error);
+}
+
+process.exit(1);
diff --git a/packages/realm-server/scripts/shard-test-modules.js b/packages/realm-server/scripts/shard-test-modules.js
new file mode 100644
index 00000000000..7eaeb1124c5
--- /dev/null
+++ b/packages/realm-server/scripts/shard-test-modules.js
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+/* eslint-env node */
+'use strict';
+
+// Discovers all *-test.ts files under tests/ and outputs the subset assigned
+// to the requested shard (1-based). Files are sorted alphabetically and
+// distributed round-robin so every shard gets a roughly equal share.
+//
+// Usage: node shard-test-modules.js
+// Output: module names joined by "|", suitable for TEST_MODULES.
+
+const fs = require('node:fs'); // eslint-disable-line @typescript-eslint/no-var-requires
+const path = require('node:path'); // eslint-disable-line @typescript-eslint/no-var-requires
+
+const shard = parseInt(process.argv[2], 10);
+const totalShards = parseInt(process.argv[3], 10);
+
+if (!shard || !totalShards || shard < 1 || shard > totalShards) {
+ console.error(
+ `Usage: shard-test-modules.js (got shard=${process.argv[2]}, totalShards=${process.argv[3]})`,
+ );
+ process.exit(1);
+}
+
+const testsDir = path.resolve(__dirname, '..', 'tests');
+
+function collectTestModules(dir, prefix) {
+ let modules = [];
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
+ if (entry.isDirectory()) {
+ modules = modules.concat(
+ collectTestModules(path.join(dir, entry.name), relative),
+ );
+ } else if (entry.isFile() && entry.name.endsWith('-test.ts')) {
+ modules.push(relative);
+ }
+ }
+ return modules;
+}
+
+const allModules = collectTestModules(testsDir, '').sort();
+
+const shardModules = allModules.filter(
+ (_, index) => (index % totalShards) + 1 === shard,
+);
+
+if (shardModules.length === 0) {
+ console.error(`Shard ${shard}/${totalShards} has no test modules.`);
+ process.exit(1);
+}
+
+process.stdout.write(shardModules.join('|'));
diff --git a/packages/realm-server/tests/index.ts b/packages/realm-server/tests/index.ts
index 61e5f187df4..623b331c7b6 100644
--- a/packages/realm-server/tests/index.ts
+++ b/packages/realm-server/tests/index.ts
@@ -30,6 +30,21 @@ import * as ContentTagGlobal from 'content-tag';
import QUnit from 'qunit';
QUnit.config.testTimeout = 60000;
+const testModules = process.env.TEST_MODULES?.trim();
+
+if (testModules) {
+ const modules = parseModules(testModules);
+ if (modules.length > 0) {
+ QUnit.config.filter = buildModuleFilter(modules);
+ console.log(
+ `Filtering tests to modules from TEST_MODULES: ${modules.join(', ')}`,
+ );
+ } else {
+ console.warn(
+ 'TEST_MODULES was provided but no module names were parsed. Running full suite.',
+ );
+ }
+}
// Cleanup here ensures lingering servers/prerenderers/queues don't keep the
// Node event loop alive after tests finish.
@@ -185,3 +200,21 @@ import './sanitize-head-html-test';
import './node-realm-test';
import './session-room-queries-test';
import './indexing-event-sink-test';
+
+function parseModules(value: string): string[] {
+ return value
+ .split(/[|,]/)
+ .map((entry) => entry.trim())
+ .filter(Boolean)
+ .map((entry) => entry.replace(/^['"]+|['"]+$/g, ''));
+}
+
+function buildModuleFilter(modulesToMatch: string[]): string {
+ const escaped = modulesToMatch.map((moduleName) => escapeRegex(moduleName));
+ const pattern = `^(?:${escaped.join('|')})(?:\\s>\\s|:)`;
+ return `/${pattern}/`;
+}
+
+function escapeRegex(value: string): string {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\//g, '\\/');
+}
diff --git a/packages/realm-server/tests/indexing-test.ts b/packages/realm-server/tests/indexing-test.ts
index ae92c35c415..5d620c595ce 100644
--- a/packages/realm-server/tests/indexing-test.ts
+++ b/packages/realm-server/tests/indexing-test.ts
@@ -1469,6 +1469,11 @@ module(basename(__filename), function () {
{ clientRequestId: 'burst-2' },
);
+ let expectedUrls = [
+ `${testRealm}mango`,
+ `${testRealm}post-1`,
+ `${testRealm}vangogh`,
+ ];
let row = (await waitUntil(
async () => {
let rows = (await testDbAdapter.execute(
@@ -1485,13 +1490,19 @@ module(basename(__filename), function () {
changes: { url: string; operation: 'update' | 'delete' }[];
};
}[];
- return rows.length === 1 ? rows[0] : undefined;
+ if (rows.length !== 1) {
+ return undefined;
+ }
+ let urls = rows[0].args.changes
+ .map((change) => change.url)
+ .sort();
+ return urls.length === expectedUrls.length ? rows[0] : undefined;
},
{
timeout: 3000,
interval: 50,
timeoutMessage:
- 'expected exactly one pending incremental canonical job',
+ 'expected exactly one pending incremental canonical job with all coalesced URLs',
},
)) as {
id: number;
@@ -1504,7 +1515,7 @@ module(basename(__filename), function () {
let urls = row.args.changes.map((change) => change.url).sort();
assert.deepEqual(
urls,
- [`${testRealm}mango`, `${testRealm}post-1`, `${testRealm}vangogh`],
+ expectedUrls,
'pending canonical incremental args include union of burst invalidations',
);
assert.strictEqual(
diff --git a/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh b/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh
index 4ef285c291e..1c41f9c8d8b 100755
--- a/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh
+++ b/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh
@@ -14,9 +14,14 @@ else
EFFECTIVE_LOG_LEVELS="$BASE_LOG_LEVELS"
fi
+JUNIT_REPORTER_ARGS=""
+if [ -n "${JUNIT_OUTPUT_FILE-}" ]; then
+ JUNIT_REPORTER_ARGS="--require ${SCRIPT_DIR}/../../scripts/junit-reporter.js"
+fi
+
LOG_LEVELS="$EFFECTIVE_LOG_LEVELS" \
NODE_NO_WARNINGS=1 \
PGPORT=55436 \
STRIPE_WEBHOOK_SECRET=stripe-webhook-secret \
STRIPE_API_KEY=stripe-api-key \
-qunit --require ts-node/register/transpile-only "$@" tests/index.ts
+qunit --require ts-node/register/transpile-only $JUNIT_REPORTER_ARGS "$@" tests/index.ts