Files
mnote/scripts/task515-onlyoffice-live-scope-http-smoke.js
T

189 lines
6.6 KiB
JavaScript
Raw Normal View History

2026-06-01 09:29:12 +08:00
#!/usr/bin/env node
"use strict";
const assert = require("node:assert");
const BASE_URL = (process.env.MNOTE_UI_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
async function requestJson(path, init = {}) {
const response = await fetch(`${BASE_URL}${path}`, {
...init,
headers: {
"content-type": "application/json",
"x-mnote-actor-id": "user_1",
...(init.headers || {}),
},
});
const text = await response.text();
let payload = null;
try {
payload = text ? JSON.parse(text) : null;
} catch (_) {
payload = { raw: text };
}
return {
status: response.status,
headers: Object.fromEntries(response.headers.entries()),
payload,
};
}
function callPayload(sessionId, args, toolName = "mnote.onlyoffice.sheet.set_value") {
return {
toolName,
workspaceId: "ws_demo",
documentId: "doc_1",
actorId: "user_1",
sessionId,
runId: `${sessionId}-run`,
toolCallId: `${sessionId}-call`,
traceId: `${sessionId}-trace`,
idempotencyKey: `${sessionId}-idem`,
dryRun: true,
capabilityScope: ["office.write"],
args,
};
}
async function main() {
const suffix = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
const bridgeSessionId = `mnote-oo-http-${suffix}`;
const bridgeToken = `token-${suffix}`;
const register = await requestJson("/api/onlyoffice/bridge/session", {
method: "POST",
body: JSON.stringify({
sessionId: bridgeSessionId,
token: bridgeToken,
editorType: "cell",
documentId: "doc_http",
assetId: "asset_http_allowed",
fileType: "xlsx",
}),
});
assert.equal(register.status, 200, JSON.stringify(register));
assert.equal(register.payload.sessionId, bridgeSessionId);
const implicit = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_implicit", {
address: "A1",
value: "after",
})),
});
assert.equal(implicit.status, 400, JSON.stringify(implicit));
assert.equal(implicit.headers["x-error-code"], "mnote_onlyoffice_session_explicit_required");
const forbidden = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_forbidden", {
onlyofficeSessionId: bridgeSessionId,
address: "A1",
value: "after",
aiAccessScope: {
permissionLevel: "read_write",
allowedResourceIds: ["asset_other"],
},
})),
});
assert.equal(forbidden.status, 403, JSON.stringify(forbidden));
assert.equal(forbidden.headers["x-error-code"], "mnote_onlyoffice_resource_scope_forbidden");
const missingScope = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_missing_scope", {
onlyofficeSessionId: bridgeSessionId,
address: "A1",
value: "after",
})),
});
assert.equal(missingScope.status, 403, JSON.stringify(missingScope));
assert.equal(missingScope.headers["x-error-code"], "mnote_onlyoffice_resource_scope_required");
const allowed = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_allowed", {
onlyofficeSessionId: bridgeSessionId,
address: "A1",
value: "after",
aiAccessScope: {
permissionLevel: "read_write",
allowedResourceIds: ["asset_http_allowed"],
},
})),
});
assert.equal(allowed.status, 200, JSON.stringify(allowed));
assert.equal(allowed.payload.result.schema, "mnote.onlyoffice.action_plan.v1");
assert.equal(allowed.payload.result.sessionId, bridgeSessionId);
assert.equal(allowed.payload.result.action, "sheet.set_value");
assert.equal(allowed.payload.audit.effect, "dry_run");
const currentImplicit = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_current_implicit", {
aiAccessScope: {
permissionLevel: "read",
allowedResourceIds: ["asset_http_allowed"],
},
}, "mnote.onlyoffice.session.current")),
});
assert.equal(currentImplicit.status, 400, JSON.stringify(currentImplicit));
assert.equal(currentImplicit.headers["x-error-code"], "mnote_onlyoffice_session_explicit_required");
const currentForbidden = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_current_forbidden", {
onlyofficeSessionId: bridgeSessionId,
aiAccessScope: {
permissionLevel: "read",
allowedResourceIds: ["asset_other"],
},
}, "mnote.onlyoffice.session.current")),
});
assert.equal(currentForbidden.status, 403, JSON.stringify(currentForbidden));
assert.equal(currentForbidden.headers["x-error-code"], "mnote_onlyoffice_resource_scope_forbidden");
const currentAllowed = await requestJson("/api/mnote/tools/call", {
2026-06-01 09:29:12 +08:00
method: "POST",
body: JSON.stringify(callPayload("sess_http_current_allowed", {
onlyofficeSessionId: bridgeSessionId,
aiAccessScope: {
permissionLevel: "read",
allowedResourceIds: ["asset_http_allowed"],
},
}, "mnote.onlyoffice.session.current")),
});
assert.equal(currentAllowed.status, 200, JSON.stringify(currentAllowed));
assert.equal(currentAllowed.payload.result.schema, "mnote.onlyoffice.session.v1");
assert.equal(currentAllowed.payload.result.session.sessionId, bridgeSessionId);
assert.equal(currentAllowed.payload.result.session.assetId, "asset_http_allowed");
console.log(JSON.stringify({
ok: true,
task: "task515-onlyoffice-live-scope-http-smoke",
baseUrl: BASE_URL,
bridgeSessionId,
implicit: { status: implicit.status, code: implicit.headers["x-error-code"] },
forbidden: { status: forbidden.status, code: forbidden.headers["x-error-code"] },
missingScope: { status: missingScope.status, code: missingScope.headers["x-error-code"] },
allowed: {
status: allowed.status,
schema: allowed.payload.result.schema,
action: allowed.payload.result.action,
audit: allowed.payload.audit.effect,
},
currentImplicit: { status: currentImplicit.status, code: currentImplicit.headers["x-error-code"] },
currentForbidden: { status: currentForbidden.status, code: currentForbidden.headers["x-error-code"] },
currentAllowed: {
status: currentAllowed.status,
schema: currentAllowed.payload.result.schema,
sessionId: currentAllowed.payload.result.session.sessionId,
assetId: currentAllowed.payload.result.session.assetId,
},
}, null, 2));
}
main().catch((error) => {
console.error(error && error.stack ? error.stack : error);
process.exit(1);
});