QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow d20a578c7d818bc7ca32a2ac2c675da4e4f27385] (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 4s
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 5s
maven / build (push) Failing after 42s
build / maven (push) Failing after 42s
One reviewable integration on current develop 7d61f29e. It starts from
feat/record-files-standalone (attachment lifecycle, upload journal, retention
SPI; descends from develop) and adds the owner stack content from
feature/collab, feature/collab-int-20261029, feature/field-values-1196,
feature/presence-recovery-1300, test/presence-capacity-1302 and the
fix/attachment-transactions-1289 / fix/mywork-query-bounds-1291 work, plus
KOF22 adaptations: CollaborationQBitConfig.attachmentLifecycle,
CollaborationRecords.insertActivity/query(transaction), ActivityWriter source
feature routing, and the Redis/fixture tests.
Build: develop CI files are preserved. pom.xml only adds the Redis API
guardrail allow-list and an optional qbit-redis dependency; the core and
qbit-redis versions stay on the canonical SNAPSHOT lines with markers to repin
after the core and redis integration PRs are published (order CORE -> REDIS ->
COLLAB). No PRIVATE or feature-snapshot coordinate is introduced. Not yet
compiled: it needs the core integration artifact (virtual-field providers).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
76 lines
3.0 KiB
JavaScript
76 lines
3.0 KiB
JavaScript
// Bounded real HTTP load using caller-owned authenticated browser contexts.
|
|
import { performance } from 'node:perf_hooks';
|
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
|
|
export async function startPresenceLoad(browser, base, expect) {
|
|
const state = { workers: 16, completed: 0, errors: [], nodes: {}, elapsed_ms: 0 };
|
|
const contexts = [];
|
|
const leases = [];
|
|
let running = true;
|
|
let stopped = false;
|
|
let tasks = [];
|
|
const began = performance.now();
|
|
const endpoint = `${base}/qqq/v1/qbit/collaboration/presence/leases`;
|
|
async function post(context, path, data) {
|
|
const response = await context.request.post(endpoint + path, { data, timeout: 10000 });
|
|
try {
|
|
expect(response.status()).toBe(200);
|
|
const node = response.headers()['x-acceptance-node'];
|
|
state.nodes[node] = (state.nodes[node] ?? 0) + 1;
|
|
return await response.json();
|
|
} finally { await response.dispose(); }
|
|
}
|
|
async function stop() {
|
|
if (stopped) return;
|
|
stopped = true;
|
|
running = false;
|
|
await Promise.all(tasks);
|
|
state.elapsed_ms = performance.now() - began;
|
|
for (const lease of leases) {
|
|
try {
|
|
const response = await lease.context.request.delete(`${base}/qqq/v1/qbit/collaboration/presence`, {
|
|
data: { ...lease.target, connectionId: lease.id, sequence: ++lease.sequence }, timeout: 10000,
|
|
});
|
|
try { expect(response.status()).toBe(200); } finally { await response.dispose(); }
|
|
} catch (error) { state.errors.push(String(error)); }
|
|
}
|
|
for (const context of contexts) await context.close();
|
|
}
|
|
try {
|
|
for (const [index, user] of ['bob', 'carol', 'dave', 'erin'].entries()) {
|
|
const context = await browser.newContext();
|
|
contexts.push(context);
|
|
await context.addCookies([
|
|
{ name: 'collab_dev_user', value: user, url: base },
|
|
{ name: 'presence_node', value: index % 2 ? 'second' : 'first', url: base },
|
|
]);
|
|
const page = await context.newPage();
|
|
await page.goto(`${base}/app/person/1?createMockSession=true`);
|
|
await expect(page.locator('[data-qqq-id="record-view-title-person"]')).toBeVisible();
|
|
await page.close();
|
|
for (let tab = 0; tab < 4; tab++) {
|
|
const target = { table: 'person', pk: '1' };
|
|
const opened = await post(context, '', target);
|
|
expect(typeof opened.connectionId).toBe('string');
|
|
leases.push({ context, target, id: opened.connectionId, sequence: 0, completed: 0 });
|
|
}
|
|
}
|
|
tasks = leases.map(async lease => {
|
|
try {
|
|
while (running) {
|
|
await post(lease.context, '/renew', { ...lease.target, connectionId: lease.id,
|
|
sequence: ++lease.sequence, mode: 'VIEWING' });
|
|
lease.completed++;
|
|
state.completed++;
|
|
await delay(100);
|
|
}
|
|
} catch (error) { state.errors.push(String(error)); running = false; }
|
|
});
|
|
await expect.poll(() => leases.filter(lease => lease.completed > 0).length).toBe(16);
|
|
return { state, stop };
|
|
} catch (error) {
|
|
await stop();
|
|
throw error;
|
|
}
|
|
}
|