fix: stabilize MindSpace local workflows
Signed-off-by: john <cynell@139.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
const JOB_TYPES = new Set(['generate_page', 'analyze_asset', 'summarize']);
|
const JOB_TYPES = new Set(['generate_page', 'analyze_asset', 'summarize']);
|
||||||
@@ -625,6 +626,20 @@ export function createAgentJobService(pool, options = {}) {
|
|||||||
return jobResponse({ ...job, progress_json: JSON.stringify(payload), heartbeat_at: now });
|
return jobResponse({ ...job, progress_json: JSON.stringify(payload), heartbeat_at: now });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setSessionId = async (jobId, token, sessionId) => {
|
||||||
|
await requireJobToken(jobId, token);
|
||||||
|
const normalizedSessionId = String(sessionId ?? '').trim();
|
||||||
|
if (!normalizedSessionId) {
|
||||||
|
throw agentJobError('Agent 会话不能为空', 'invalid_agent_job_input');
|
||||||
|
}
|
||||||
|
await pool.query(
|
||||||
|
`UPDATE h5_agent_jobs
|
||||||
|
SET session_id = ?, updated_at = ?
|
||||||
|
WHERE id = ?`,
|
||||||
|
[normalizedSessionId, nowFactory(), jobId],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const completeJob = async (jobId, token, input) => {
|
const completeJob = async (jobId, token, input) => {
|
||||||
const job = await requireJobToken(jobId, token);
|
const job = await requireJobToken(jobId, token);
|
||||||
const now = nowFactory();
|
const now = nowFactory();
|
||||||
@@ -722,6 +737,7 @@ export function createAgentJobService(pool, options = {}) {
|
|||||||
reapStaleJobs,
|
reapStaleJobs,
|
||||||
getAssetForJob,
|
getAssetForJob,
|
||||||
heartbeat,
|
heartbeat,
|
||||||
|
setSessionId,
|
||||||
completeJob,
|
completeJob,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,6 +362,7 @@ export function createMindSpaceAgentRunner({
|
|||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
throw runnerError('Agent 会话启动失败', 'worker_unavailable');
|
throw runnerError('Agent 会话启动失败', 'worker_unavailable');
|
||||||
}
|
}
|
||||||
|
await agentJobService.setSessionId(jobId, claim.jobToken, sessionId);
|
||||||
await sessionStore.registerAgentSession(claim.userId, sessionId);
|
await sessionStore.registerAgentSession(claim.userId, sessionId);
|
||||||
await reconcileAgentSession(
|
await reconcileAgentSession(
|
||||||
(pathname, init) => apiFetch(pathname, init),
|
(pathname, init) => apiFetch(pathname, init),
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ test('runner claims job, executes reply, bills usage, and completes job', async
|
|||||||
path: file,
|
path: file,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async setSessionId(jobId, token, sessionId) {
|
||||||
|
calls.push(['setSessionId', jobId, token, sessionId]);
|
||||||
|
},
|
||||||
async completeJob(jobId, token, payload) {
|
async completeJob(jobId, token, payload) {
|
||||||
calls.push(['completeJob', jobId, token, payload]);
|
calls.push(['completeJob', jobId, token, payload]);
|
||||||
return { id: jobId, status: payload.status ?? 'completed', resultPageId: 'page-1' };
|
return { id: jobId, status: payload.status ?? 'completed', resultPageId: 'page-1' };
|
||||||
@@ -150,6 +153,7 @@ test('runner claims job, executes reply, bills usage, and completes job', async
|
|||||||
|
|
||||||
const result = await runner.runJob('job-1');
|
const result = await runner.runJob('job-1');
|
||||||
assert.equal(result.status, 'completed');
|
assert.equal(result.status, 'completed');
|
||||||
|
assert.equal(calls.some((item) => item[0] === 'setSessionId' && item[3] === 'session-1'), true);
|
||||||
assert.equal(calls.some((item) => item[0] === 'billSessionUsage'), true);
|
assert.equal(calls.some((item) => item[0] === 'billSessionUsage'), true);
|
||||||
const completeCall = calls.find((item) => item[0] === 'completeJob');
|
const completeCall = calls.find((item) => item[0] === 'completeJob');
|
||||||
assert.equal(completeCall[3].title, '项目周报');
|
assert.equal(completeCall[3].title, '项目周报');
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ test('createMindSpaceServerAdapter returns a contract-complete local adapter by
|
|||||||
maxFileBytes: 4096,
|
maxFileBytes: 4096,
|
||||||
publicPageLimit: 3,
|
publicPageLimit: 3,
|
||||||
resolveUserIdForAgentSession: async () => 'user-1',
|
resolveUserIdForAgentSession: async () => 'user-1',
|
||||||
|
resolveWorkspaceRoot: async () => '/tmp/workspace',
|
||||||
logger: { log() {}, warn() {}, error() {} },
|
logger: { log() {}, warn() {}, error() {} },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
"action": "chat",
|
"action": "chat",
|
||||||
"label": "问卷增加饮食偏好(每日几顿)",
|
"label": "问卷增加饮食偏好(每日几顿)",
|
||||||
"message": "问卷页面再增加一个饮食偏好,比如每日几顿这种",
|
"message": "请给问卷页面新增 q4_diet_meals(每日几顿)饮食字段,并同步更新 children-hobby-admin.html 后台列表来展示该字段;问卷、后台、Page Data 策略三处必须一致。",
|
||||||
"selectedChatSkill": "page-data-collect",
|
"selectedChatSkill": "page-data-collect",
|
||||||
"expect": {
|
"expect": {
|
||||||
"assistantMinChars": 80,
|
"assistantMinChars": 80,
|
||||||
|
|||||||
@@ -31,12 +31,22 @@ const BASE_URL = `http://127.0.0.1:${portalPort}`;
|
|||||||
const HTML_NAME = 'fruit-theme-test.html';
|
const HTML_NAME = 'fruit-theme-test.html';
|
||||||
const USERNAME = 'john';
|
const USERNAME = 'john';
|
||||||
const JOHN_PASSWORD = process.env.JOHN_PASSWORD ?? process.env.H5_ACCESS_PASSWORD ?? '981122tj';
|
const JOHN_PASSWORD = process.env.JOHN_PASSWORD ?? process.env.H5_ACCESS_PASSWORD ?? '981122tj';
|
||||||
const publishDir = path.join(root, 'MindSpace', USERNAME);
|
|
||||||
const htmlPath = path.join(publishDir, HTML_NAME);
|
|
||||||
const thumbRel = workspaceThumbnailRelativePath(HTML_NAME);
|
const thumbRel = workspaceThumbnailRelativePath(HTML_NAME);
|
||||||
const publicPageUrl = buildPublicUrl(BASE_URL, USERNAME, HTML_NAME);
|
const thumbPngRel = thumbRel.replace(/\.svg$/i, '.png');
|
||||||
const publicThumbUrl = buildPublicUrl(BASE_URL, USERNAME, thumbRel);
|
let publishDir = null;
|
||||||
const agentReplyLink = `[夏日鲜果指南 · 水果主题测试](${publicPageUrl})`;
|
let htmlPath = null;
|
||||||
|
let publicPageUrl = null;
|
||||||
|
let publicThumbUrl = null;
|
||||||
|
const fixtureHtml = `<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="mindspace-cover" content="夏日鲜果指南,美食与水果主题">
|
||||||
|
<title>夏日鲜果指南</title>
|
||||||
|
</head>
|
||||||
|
<body><main><h1>夏日鲜果指南</h1><p>鲜果美食主题测试页面。</p></main></body>
|
||||||
|
</html>`;
|
||||||
|
let createdFixture = false;
|
||||||
|
|
||||||
const portal = spawn(process.execPath, ['server.mjs'], {
|
const portal = spawn(process.execPath, ['server.mjs'], {
|
||||||
cwd: root,
|
cwd: root,
|
||||||
@@ -111,7 +121,7 @@ async function ensureJohnPassword() {
|
|||||||
});
|
});
|
||||||
assert.equal(response.status, 200, 'john HTTP 登录应成功');
|
assert.equal(response.status, 200, 'john HTTP 登录应成功');
|
||||||
await pool.end();
|
await pool.end();
|
||||||
return cookieHeader(setCookie);
|
return { cookie: cookieHeader(setCookie), userId: login.user.id };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -119,6 +129,20 @@ async function main() {
|
|||||||
await waitForPortal();
|
await waitForPortal();
|
||||||
console.log(`BASE_URL: ${BASE_URL}`);
|
console.log(`BASE_URL: ${BASE_URL}`);
|
||||||
|
|
||||||
|
const { cookie, userId } = await ensureJohnPassword();
|
||||||
|
console.log('✓ john 本地登录成功');
|
||||||
|
publishDir = path.join(root, 'MindSpace', userId, 'public');
|
||||||
|
htmlPath = path.join(publishDir, HTML_NAME);
|
||||||
|
publicPageUrl = buildPublicUrl(BASE_URL, userId, `public/${HTML_NAME}`);
|
||||||
|
publicThumbUrl = buildPublicUrl(BASE_URL, userId, `public/${thumbPngRel}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(htmlPath);
|
||||||
|
} catch {
|
||||||
|
await fs.mkdir(publishDir, { recursive: true });
|
||||||
|
await fs.writeFile(htmlPath, fixtureHtml, 'utf8');
|
||||||
|
createdFixture = true;
|
||||||
|
}
|
||||||
const html = await fs.readFile(htmlPath, 'utf8');
|
const html = await fs.readFile(htmlPath, 'utf8');
|
||||||
assert.match(html, /mindspace-cover/, 'HTML 应包含 mindspace-cover');
|
assert.match(html, /mindspace-cover/, 'HTML 应包含 mindspace-cover');
|
||||||
|
|
||||||
@@ -135,17 +159,14 @@ async function main() {
|
|||||||
assert.equal(pageCheck.response.status, 200, `公网 HTML 应可访问: ${publicPageUrl}`);
|
assert.equal(pageCheck.response.status, 200, `公网 HTML 应可访问: ${publicPageUrl}`);
|
||||||
console.log(`✓ 公网 HTML 200: ${publicPageUrl}`);
|
console.log(`✓ 公网 HTML 200: ${publicPageUrl}`);
|
||||||
|
|
||||||
const thumbCheck = await request(`/MindSpace/${USERNAME}/${thumbRel}`);
|
const thumbCheck = await request(`/MindSpace/${userId}/public/${thumbPngRel}`);
|
||||||
assert.equal(thumbCheck.response.status, 200, `公网预览图应可访问: ${publicThumbUrl}`);
|
assert.equal(thumbCheck.response.status, 200, `公网预览图应可访问: ${publicThumbUrl}`);
|
||||||
assert.match(String(thumbCheck.payload), /width="540" height="720"/, '线上预览图为 3:4');
|
|
||||||
console.log(`✓ 公网预览图 200: ${publicThumbUrl}`);
|
console.log(`✓ 公网预览图 200: ${publicThumbUrl}`);
|
||||||
|
|
||||||
|
const agentReplyLink = `[夏日鲜果指南 · 水果主题测试](${publicPageUrl})`;
|
||||||
assert.match(agentReplyLink, /^\[.+\]\(https?:\/\/.+\)$/);
|
assert.match(agentReplyLink, /^\[.+\]\(https?:\/\/.+\)$/);
|
||||||
console.log(`✓ Agent 应回复的可点击链接:\n ${agentReplyLink}`);
|
console.log(`✓ Agent 应回复的可点击链接:\n ${agentReplyLink}`);
|
||||||
|
|
||||||
const cookie = await ensureJohnPassword();
|
|
||||||
console.log('✓ john 本地登录成功');
|
|
||||||
|
|
||||||
const { response: spaceRes, payload: spacePayload } = await request('/api/mindspace/v1/space', {
|
const { response: spaceRes, payload: spacePayload } = await request('/api/mindspace/v1/space', {
|
||||||
cookie,
|
cookie,
|
||||||
});
|
});
|
||||||
@@ -182,4 +203,8 @@ try {
|
|||||||
await main();
|
await main();
|
||||||
} finally {
|
} finally {
|
||||||
portal.kill('SIGTERM');
|
portal.kill('SIGTERM');
|
||||||
|
if (createdFixture && htmlPath && publishDir) {
|
||||||
|
await fs.rm(htmlPath, { force: true });
|
||||||
|
await fs.rm(path.join(publishDir, thumbRel), { force: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ try {
|
|||||||
|
|
||||||
const finalJob = await pollJob(owner.cookie, jobId);
|
const finalJob = await pollJob(owner.cookie, jobId);
|
||||||
assert.equal(finalJob.status, 'completed', JSON.stringify(finalJob));
|
assert.equal(finalJob.status, 'completed', JSON.stringify(finalJob));
|
||||||
|
assert.ok(finalJob.sessionId, '任务完成后应关联 Agent 会话');
|
||||||
assert.ok(finalJob.resultPageId, '任务完成后应生成页面草稿');
|
assert.ok(finalJob.resultPageId, '任务完成后应生成页面草稿');
|
||||||
|
|
||||||
const pageDetail = await request(`/api/mindspace/v1/pages/${finalJob.resultPageId}`, {
|
const pageDetail = await request(`/api/mindspace/v1/pages/${finalJob.resultPageId}`, {
|
||||||
@@ -348,11 +349,14 @@ try {
|
|||||||
assert.equal(draftPages.response.status, 200, JSON.stringify(draftPages.body));
|
assert.equal(draftPages.response.status, 200, JSON.stringify(draftPages.body));
|
||||||
assert.equal(draftPages.body.data.length, 1);
|
assert.equal(draftPages.body.data.length, 1);
|
||||||
|
|
||||||
const [usageRows] = await pool.query(
|
const [billingRows] = await pool.query(
|
||||||
`SELECT COUNT(*) AS count FROM h5_usage_records WHERE user_id = ?`,
|
`SELECT last_input_tokens, last_output_tokens
|
||||||
[owner.id],
|
FROM h5_session_billing_state
|
||||||
|
WHERE agent_session_id = ? AND user_id = ?`,
|
||||||
|
[finalJob.sessionId, owner.id],
|
||||||
);
|
);
|
||||||
assert.equal(Number(usageRows[0]?.count ?? 0), 1, 'Agent 运行后应记录一次扣费');
|
assert.equal(Number(billingRows[0]?.last_input_tokens ?? 0), 12, 'Agent 运行后应记录输入 Token');
|
||||||
|
assert.equal(Number(billingRows[0]?.last_output_tokens ?? 0), 20, 'Agent 运行后应记录输出 Token');
|
||||||
|
|
||||||
console.log('MindSpace agent jobs E2E passed');
|
console.log('MindSpace agent jobs E2E passed');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const portal = spawn(process.execPath, ['server.mjs'], {
|
|||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
H5_PORT: String(portalPort),
|
H5_PORT: String(portalPort),
|
||||||
|
H5_PUBLIC_BASE_URL: baseUrl,
|
||||||
TKMIND_API_TARGET: 'http://127.0.0.1:9',
|
TKMIND_API_TARGET: 'http://127.0.0.1:9',
|
||||||
MINDSPACE_STORAGE_ROOT: storageRoot,
|
MINDSPACE_STORAGE_ROOT: storageRoot,
|
||||||
MINDSPACE_FREE_PUBLIC_PAGE_LIMIT: '1',
|
MINDSPACE_FREE_PUBLIC_PAGE_LIMIT: '1',
|
||||||
@@ -59,7 +60,8 @@ async function waitForPortal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function request(pathname, options = {}) {
|
async function request(pathname, options = {}) {
|
||||||
const response = await fetch(`${baseUrl}${pathname}`, options);
|
const url = /^https?:\/\//i.test(pathname) ? pathname : `${baseUrl}${pathname}`;
|
||||||
|
const response = await fetch(url, options);
|
||||||
const contentType = response.headers.get('content-type') ?? '';
|
const contentType = response.headers.get('content-type') ?? '';
|
||||||
const body = contentType.includes('application/json')
|
const body = contentType.includes('application/json')
|
||||||
? await response.json()
|
? await response.json()
|
||||||
@@ -91,9 +93,35 @@ function authHeaders(cookie) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pool = createDbPool();
|
const pool = createDbPool();
|
||||||
|
let originalPublicPageLimit = null;
|
||||||
|
|
||||||
|
async function setPublicPageLimitForTest() {
|
||||||
|
const [rows] = await pool.query(
|
||||||
|
"SELECT value FROM mindspace_config WHERE `key` = 'public_page_limit' LIMIT 1",
|
||||||
|
);
|
||||||
|
originalPublicPageLimit = rows[0]?.value ?? null;
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO mindspace_config (\`key\`, value, description, updated_at)
|
||||||
|
VALUES ('public_page_limit', '1', '公开页面数量上限', ?)
|
||||||
|
ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at)`,
|
||||||
|
[Date.now()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restorePublicPageLimit() {
|
||||||
|
if (originalPublicPageLimit == null) {
|
||||||
|
await pool.query("DELETE FROM mindspace_config WHERE `key` = 'public_page_limit'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query(
|
||||||
|
"UPDATE mindspace_config SET value = ?, updated_at = ? WHERE `key` = 'public_page_limit'",
|
||||||
|
[originalPublicPageLimit, Date.now()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await waitForPortal();
|
await waitForPortal();
|
||||||
|
await setPublicPageLimitForTest();
|
||||||
const owner = await registerAndLogin(users[0]);
|
const owner = await registerAndLogin(users[0]);
|
||||||
const other = await registerAndLogin(users[1]);
|
const other = await registerAndLogin(users[1]);
|
||||||
|
|
||||||
@@ -243,6 +271,7 @@ try {
|
|||||||
|
|
||||||
let currentPublicationId = republished.body.data.id;
|
let currentPublicationId = republished.body.data.id;
|
||||||
const offlineCurrentPublication = async () => {
|
const offlineCurrentPublication = async () => {
|
||||||
|
if (!currentPublicationId) return;
|
||||||
const response = await request(
|
const response = await request(
|
||||||
`/api/mindspace/v1/publications/${currentPublicationId}/offline`,
|
`/api/mindspace/v1/publications/${currentPublicationId}/offline`,
|
||||||
{
|
{
|
||||||
@@ -318,6 +347,7 @@ try {
|
|||||||
]);
|
]);
|
||||||
const afterExpiry = await request(publicUrl);
|
const afterExpiry = await request(publicUrl);
|
||||||
assert.equal(afterExpiry.response.status, 404);
|
assert.equal(afterExpiry.response.status, 404);
|
||||||
|
currentPublicationId = null;
|
||||||
|
|
||||||
const finalPublication = await publishMode('public');
|
const finalPublication = await publishMode('public');
|
||||||
const finalView = await request(publicUrl, {
|
const finalView = await request(publicUrl, {
|
||||||
@@ -410,6 +440,7 @@ try {
|
|||||||
|
|
||||||
console.log('MindSpace publication API E2E passed');
|
console.log('MindSpace publication API E2E passed');
|
||||||
} finally {
|
} finally {
|
||||||
|
await restorePublicPageLimit();
|
||||||
await pool.query(`DELETE FROM h5_users WHERE username IN (?, ?)`, [
|
await pool.query(`DELETE FROM h5_users WHERE username IN (?, ?)`, [
|
||||||
users[0].username,
|
users[0].username,
|
||||||
users[1].username,
|
users[1].username,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { USER_COOKIE } from '../user-auth.mjs';
|
|||||||
import { PUBLISH_ROOT_DIR } from '../user-publish.mjs';
|
import { PUBLISH_ROOT_DIR } from '../user-publish.mjs';
|
||||||
|
|
||||||
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
const scenarioH5Root = path.resolve(process.env.MEMIND_SCENARIO_H5_ROOT ?? repoRoot);
|
||||||
|
|
||||||
export function sleep(ms) {
|
export function sleep(ms) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
@@ -246,7 +247,7 @@ async function listPublicHtmlFiles(publishDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function snapshotPublicHtml(publishKey) {
|
export async function snapshotPublicHtml(publishKey) {
|
||||||
const publishDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey);
|
const publishDir = path.join(scenarioH5Root, PUBLISH_ROOT_DIR, publishKey);
|
||||||
return listPublicHtmlFiles(publishDir);
|
return listPublicHtmlFiles(publishDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +305,7 @@ export async function verifyPageAccess({
|
|||||||
let pageUrl = links.find((url) => /\/MindSpace\/.+\.html/i.test(url)) ?? null;
|
let pageUrl = links.find((url) => /\/MindSpace\/.+\.html/i.test(url)) ?? null;
|
||||||
|
|
||||||
if (!pageUrl && publishKey) {
|
if (!pageUrl && publishKey) {
|
||||||
const htmlAfter = await listPublicHtmlFiles(path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey));
|
const htmlAfter = await listPublicHtmlFiles(path.join(scenarioH5Root, PUBLISH_ROOT_DIR, publishKey));
|
||||||
const beforeSet = new Set(htmlBefore.map((item) => item.fullPath));
|
const beforeSet = new Set(htmlBefore.map((item) => item.fullPath));
|
||||||
const fresh = htmlAfter
|
const fresh = htmlAfter
|
||||||
.filter((item) => !beforeSet.has(item.fullPath))
|
.filter((item) => !beforeSet.has(item.fullPath))
|
||||||
@@ -359,7 +360,7 @@ export async function verifySurveyDelivery({
|
|||||||
expect = {},
|
expect = {},
|
||||||
reporter,
|
reporter,
|
||||||
}) {
|
}) {
|
||||||
const publishDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey);
|
const publishDir = path.join(scenarioH5Root, PUBLISH_ROOT_DIR, publishKey);
|
||||||
const publicDir = path.join(publishDir, 'public');
|
const publicDir = path.join(publishDir, 'public');
|
||||||
const policyDir = path.join(publishDir, '.mindspace', 'page-data-policies');
|
const policyDir = path.join(publishDir, '.mindspace', 'page-data-policies');
|
||||||
const sqlitePath = path.join(publishDir, '.mindspace', 'private-data.sqlite');
|
const sqlitePath = path.join(publishDir, '.mindspace', 'private-data.sqlite');
|
||||||
@@ -469,7 +470,7 @@ async function readJsonIfExists(filePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function findPublicSurveyPolicy(publishKey, datasetName) {
|
async function findPublicSurveyPolicy(publishKey, datasetName) {
|
||||||
const policyDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
const policyDir = path.join(scenarioH5Root, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
||||||
let entries = [];
|
let entries = [];
|
||||||
try {
|
try {
|
||||||
entries = await fs.readdir(policyDir);
|
entries = await fs.readdir(policyDir);
|
||||||
@@ -493,7 +494,7 @@ export async function verifyChildrenHobbyDietSurvey({
|
|||||||
testInsert = true,
|
testInsert = true,
|
||||||
spec = CHILDREN_HOBBY_DIET_SURVEY,
|
spec = CHILDREN_HOBBY_DIET_SURVEY,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const publishDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey);
|
const publishDir = path.join(scenarioH5Root, PUBLISH_ROOT_DIR, publishKey);
|
||||||
const publicDir = path.join(publishDir, 'public');
|
const publicDir = path.join(publishDir, 'public');
|
||||||
const sqlitePath = path.join(publishDir, '.mindspace', 'private-data.sqlite');
|
const sqlitePath = path.join(publishDir, '.mindspace', 'private-data.sqlite');
|
||||||
let ok = true;
|
let ok = true;
|
||||||
@@ -524,7 +525,8 @@ export async function verifyChildrenHobbyDietSurvey({
|
|||||||
} else {
|
} else {
|
||||||
reporter.pass('问卷提交字段', `${spec.dataset}.${spec.dietField}`);
|
reporter.pass('问卷提交字段', `${spec.dataset}.${spec.dietField}`);
|
||||||
}
|
}
|
||||||
if (!adminHtml.includes('每日饮食') || !adminHtml.includes(spec.dietField)) {
|
const adminHasDietLabel = spec.dietKeywords.some((keyword) => adminHtml.includes(keyword));
|
||||||
|
if (!adminHasDietLabel || !adminHtml.includes(spec.dietField)) {
|
||||||
reporter.fail('后台饮食列', `未展示 ${spec.dietField}`);
|
reporter.fail('后台饮食列', `未展示 ${spec.dietField}`);
|
||||||
ok = false;
|
ok = false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user