const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const { pathToFileURL } = require('node:url') const vm = require('node:vm') const rules = require('../utils/teamBackgroundFile') assert.equal(rules.validateEnterpriseWebsite(' 无 '), '') assert.equal(rules.validateEnterpriseWebsite('https://example.com'), '') assert.match(rules.validateEnterpriseWebsite('ftp://example.com'), /http/) assert.equal(rules.validateBackgroundFile({ name: 'brief.PDF', size: 1 }), '') assert.match(rules.validateBackgroundFile({ name: 'brief.zip', size: 1 }), /类型/) assert.match(rules.validateBackgroundFile({ name: 'brief.pdf', size: 50 * 1024 * 1024 + 1 }), /50MB/) assert.equal(rules.validateBackgroundFile({ name: 'brief.pdf', size: 50 * 1024 * 1024 }), '') assert.equal(rules.validateBackgroundFile({ name: `${'a'.repeat(251)}.pdf`, size: 1 }), '') assert.match(rules.validateBackgroundFile({ name: `${'a'.repeat(252)}.pdf`, size: 1 }), /255/) assert.match(rules.validateBackgroundFile({ name: 'empty.pdf', size: 0 }), /非空/) const backendExpiry = '2026-07-21 15:30:00' const explicitOffsetExpiry = '2026-07-21T15:30:00+08:00' const utcExpiry = '2026-07-21T07:30:00.250Z' assert.equal(rules.parseSessionExpiresAt(backendExpiry), Date.UTC(2026, 6, 21, 7, 30, 0)) assert.equal(rules.parseSessionExpiresAt(explicitOffsetExpiry), Date.UTC(2026, 6, 21, 7, 30, 0)) assert.equal(rules.parseSessionExpiresAt(utcExpiry), Date.UTC(2026, 6, 21, 7, 30, 0, 250)) assert.equal(rules.getSessionRemainingSeconds(backendExpiry, Date.UTC(2026, 6, 21, 7, 29, 59, 1)), 1) assert.equal(rules.getSessionRemainingSeconds(backendExpiry, Date.UTC(2026, 6, 21, 7, 30, 1)), 0) assert.equal(rules.getSessionRemainingSeconds('2026-07-21T15:30:00', 0), 0) assert.equal(rules.getSessionRemainingSeconds('not-a-date', 0), 0) const validQueueItem = rules.createBackgroundQueueItem( { name: 'brief.pdf', path: '/tmp/brief.pdf', size: 1 }, 'valid-file' ) const invalidQueueItem = rules.createBackgroundQueueItem( { name: 'archive.zip', path: '/tmp/archive.zip', size: 1 }, 'invalid-file' ) assert.equal(validQueueItem.status, 'pending') assert.equal(validQueueItem.progress, 0) assert.equal(validQueueItem.validationError, '') assert.equal(invalidQueueItem.status, 'failed') assert.match(invalidQueueItem.validationError, /类型/) validQueueItem.status = 'success' const uploadFailure = { id: 'upload-failure', status: 'failed', validationError: '', error: '上传失败' } assert.deepEqual( rules.getUploadableBackgroundFiles([validQueueItem, invalidQueueItem, uploadFailure]), [uploadFailure] ) const originalTeam = { id: 12, teamName: '研发团队', enterpriseWebsite: ' https://example.com/team ', coachId: 3, uploaderId: 4, source: 'WECHAT' } const teamSubmitPayload = rules.createTeamSubmitPayload(originalTeam) assert.equal(teamSubmitPayload.id, 12) assert.equal(teamSubmitPayload.enterpriseWebsite, 'https://example.com/team') assert.equal(Object.hasOwn(teamSubmitPayload, 'coachId'), false) assert.equal(Object.hasOwn(teamSubmitPayload, 'uploaderId'), false) assert.equal(Object.hasOwn(teamSubmitPayload, 'source'), false) assert.equal(originalTeam.coachId, 3, 'payload creation must not mutate the displayed detail') const textPreviewState = rules.createTextPreviewState({ mode: 'text', fileName: '背景说明.md', content: '# 团队背景', truncated: true }, 'fallback.md') assert.deepEqual(textPreviewState, { visible: true, fileName: '背景说明.md', content: '# 团队背景', truncated: true }) assert.deepEqual(rules.createEmptyTextPreviewState(), { visible: false, fileName: '', content: '', truncated: false }) assert.notEqual(rules.createEmptyTextPreviewState(), rules.createEmptyTextPreviewState()) console.log('team background rules: PASS') const transportPath = path.join(__dirname, '../http/teamBackgroundFile.js') async function loadTransport({ api = {}, uni = {}, wx = {} } = {}) { const source = fs.readFileSync(transportPath, 'utf8') const context = vm.createContext({ uni, wx }) const baseApiModule = new vm.SyntheticModule(['BaseApi'], function () { this.setExport('BaseApi', 'https://api.example.test/app') }, { context, identifier: 'test:baseApi' }) const apiModule = new vm.SyntheticModule(['default'], function () { this.setExport('default', api) }, { context, identifier: 'test:api' }) const transportModule = new vm.SourceTextModule(source, { context, identifier: pathToFileURL(transportPath).href }) await transportModule.link(specifier => { if (specifier === './baseApi.js') return baseApiModule if (specifier === './index.js') return apiModule throw new Error(`unexpected transport import: ${specifier}`) }) await transportModule.evaluate() return transportModule.namespace } function assertNoIdentityFields(value) { if (!value || typeof value !== 'object') return for (const [key, child] of Object.entries(value)) { assert.ok(!['source', 'coachId', 'uploaderId'].includes(key), `unexpected identity field: ${key}`) assertNoIdentityFields(child) } } async function createUploadHarness(response, progress = 37) { let request const progressValues = [] const uni = { getStorageSync(key) { assert.equal(key, 'token') return 'token-123' }, uploadFile(options) { request = options return { onProgressUpdate(callback) { callback({ progress }) setImmediate(() => options.success(response)) } } } } const transport = await loadTransport({ uni }) return { request: () => request, progressValues, promise: transport.uploadTeamBackgroundFile(12, '/tmp/brief.pdf', value => progressValues.push(value)) } } async function testUploadTransport() { const stringHarness = await createUploadHarness({ statusCode: 200, data: JSON.stringify({ code: 0, data: { id: 7 } }) }) assert.equal((await stringHarness.promise).id, 7) assert.equal(stringHarness.request().url, 'https://api.example.test/app/core/user/team/12/background-files') assert.equal(stringHarness.request().filePath, '/tmp/brief.pdf') assert.equal(stringHarness.request().name, 'file') assert.equal(stringHarness.request().header.token, 'token-123') assert.deepEqual(stringHarness.progressValues, [37]) assertNoIdentityFields(stringHarness.request()) const objectHarness = await createUploadHarness({ statusCode: 200, data: { code: 0, data: { id: 8 } } }) assert.equal((await objectHarness.promise).id, 8) const statusHarness = await createUploadHarness({ statusCode: 500, data: { code: 0 } }) await assert.rejects(statusHarness.promise, /上传失败,请重试/) const codeHarness = await createUploadHarness({ statusCode: 200, data: { code: 9, msg: '上传业务失败' } }) await assert.rejects(codeHarness.promise, /上传业务失败/) const invalidJsonHarness = await createUploadHarness({ statusCode: 200, data: '' }) await assert.rejects(invalidJsonHarness.promise, /上传失败,请重试/) } async function testApiTransport() { const calls = [] const api = { get(...args) { calls.push(['get', ...args]) return Promise.resolve({ data: { code: 0, data: [{ id: 1 }] } }) }, del(...args) { calls.push(['del', ...args]) return Promise.resolve({ data: { code: 0, data: true } }) }, post(...args) { calls.push(['post', ...args]) return Promise.resolve({ data: { code: 0, data: { sessionId: 'session-1' } } }) } } const transport = await loadTransport({ api }) assert.deepEqual(await transport.listTeamBackgroundFiles(12), [{ id: 1 }]) assert.equal(await transport.disableTeamBackgroundFile(12, 34), true) assert.deepEqual(await transport.createTeamBackgroundSession(12), { sessionId: 'session-1' }) assert.equal(calls[0][0], 'get') assert.equal(calls[0][1], '/core/user/team/12/background-files') assert.equal(calls[1][0], 'del') assert.equal(calls[1][1], '/core/user/team/12/background-files/34') assert.equal(calls[2][0], 'post') assert.equal(calls[2][1], '/core/user/team/12/background-upload-session') for (const call of calls) { assert.equal(Object.keys(call[2]).length, 0) assert.equal(call[3], false) assertNoIdentityFields(call) } const failingTransport = await loadTransport({ api: { get: () => Promise.resolve({ data: { code: 3, msg: '列表业务失败' } }) } }) await assert.rejects(failingTransport.listTeamBackgroundFiles(12), /列表业务失败/) assert.equal(transport.uploadTeamBackgroundFile.length, 2) assert.equal(transport.listTeamBackgroundFiles.length, 1) assert.equal(transport.disableTeamBackgroundFile.length, 2) assert.equal(transport.createTeamBackgroundSession.length, 1) assert.equal(transport.downloadTeamBackgroundFile.length, 2) } const TEXT_PREVIEW_LIMIT = 256 * 1024 async function createDownloadHarness({ result, responseHeaders, downloadError, openError, fileSize, fileContent = '', fileInfoError, readError, withHeadersListener = true } = {}) { let request let openRequest let fileInfoRequest let readRequest let fileSystemManagerCalls = 0 const resolvedFileSize = fileSize === undefined ? Buffer.byteLength(fileContent, 'utf8') : fileSize const uni = { getStorageSync(key) { assert.equal(key, 'token') return 'token-456' }, downloadFile(options) { request = options setImmediate(() => { if (downloadError) options.fail(downloadError) else options.success(result) }) if (!withHeadersListener) return {} return { onHeadersReceived(callback) { if (responseHeaders) callback({ header: responseHeaders }) } } } } const wx = { openDocument(options) { openRequest = options setImmediate(() => { if (openError) options.fail(openError) else options.success('opened') }) }, getFileSystemManager() { fileSystemManagerCalls += 1 return { getFileInfo(options) { fileInfoRequest = options setImmediate(() => { if (fileInfoError) options.fail(fileInfoError) else options.success({ size: resolvedFileSize }) }) }, readFile(options) { readRequest = options setImmediate(() => { if (readError) options.fail(readError) else options.success({ data: fileContent }) }) } } } } const transport = await loadTransport({ uni, wx }) return { request: () => request, openRequest: () => openRequest, fileInfoRequest: () => fileInfoRequest, readRequest: () => readRequest, fileSystemManagerCalls: () => fileSystemManagerCalls, promise: transport.downloadTeamBackgroundFile(12, 34) } } async function testDownloadTransport() { const successHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/download.pdf' }, responseHeaders: { 'cOnTeNt-TyPe': 'application/pdf', 'CONTENT-disPOSITION': 'attachment; filename="download.pdf"' } }) assert.equal(await successHarness.promise, 'opened') assert.equal(successHarness.request().url, 'https://api.example.test/app/core/user/team/12/background-files/34/download') assert.equal(successHarness.request().header.token, 'token-456') assert.equal(successHarness.openRequest().filePath, '/tmp/download.pdf') assert.equal(successHarness.openRequest().showMenu, true) assertNoIdentityFields(successHarness.request()) const resultHeaderHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/result-header.docx', header: { 'content-type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'content-disposition': 'attachment; filename="result-header.docx"' } }, withHeadersListener: false }) assert.equal(await resultHeaderHarness.promise, 'opened') assert.equal(resultHeaderHarness.fileSystemManagerCalls(), 0) const txtHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/text-preview.txt', headers: { 'Content-Type': 'text/plain; charset=UTF-8', 'Content-Disposition': 'attachment; filename="fallback.txt"; ' + "filename*=UTF-8''%E8%83%8C%E6%99%AF%20%E8%B5%84%E6%96%99.txt" } }, fileSize: 5, fileContent: 'hello', withHeadersListener: false }) const txtPreview = await txtHarness.promise assert.equal(txtHarness.openRequest(), undefined, 'txt must not call openDocument') assert.equal(txtPreview.mode, 'text') assert.equal(txtPreview.fileName, '背景 资料.txt') assert.equal(txtPreview.content, 'hello') assert.equal(txtPreview.truncated, false) assert.equal(txtHarness.fileInfoRequest().filePath, '/tmp/text-preview.txt') assert.equal(txtHarness.readRequest().filePath, '/tmp/text-preview.txt') assert.equal(txtHarness.readRequest().encoding, 'utf8') assert.equal(txtHarness.readRequest().position, 0) assert.equal(txtHarness.readRequest().length, 5) const mdContent = 'm'.repeat(TEXT_PREVIEW_LIMIT) const mdHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/guide.md' }, responseHeaders: { 'Content-Type': 'text/markdown; charset=UTF-8', 'Content-Disposition': 'attachment; filename="guide.md"' }, fileSize: 50 * 1024 * 1024, fileContent: mdContent }) const mdPreview = await mdHarness.promise assert.equal(mdHarness.openRequest(), undefined) assert.equal(mdPreview.mode, 'text') assert.equal(mdPreview.fileName, 'guide.md') assert.equal(mdPreview.content.length, TEXT_PREVIEW_LIMIT) assert.equal(mdPreview.truncated, true) assert.equal(mdHarness.readRequest().length, TEXT_PREVIEW_LIMIT) const csvContent = 'c'.repeat(TEXT_PREVIEW_LIMIT) const csvHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/data.csv' }, responseHeaders: { 'Content-Type': 'text/csv', 'Content-Disposition': 'attachment; filename=data.csv' }, fileSize: TEXT_PREVIEW_LIMIT, fileContent: csvContent }) const csvPreview = await csvHarness.promise assert.equal(csvHarness.openRequest(), undefined) assert.equal(csvPreview.mode, 'text') assert.equal(csvPreview.fileName, 'data.csv') assert.equal(csvPreview.content.length, TEXT_PREVIEW_LIMIT) assert.equal(csvPreview.truncated, false) assert.equal(csvHarness.readRequest().length, TEXT_PREVIEW_LIMIT) const fileInfoFailureHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/file-info-failure.txt' }, responseHeaders: { 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename="file-info-failure.txt"' }, fileInfoError: new Error('getFileInfo failed') }) await assert.rejects(fileInfoFailureHarness.promise, /文件读取失败,请重试/) assert.equal(fileInfoFailureHarness.openRequest(), undefined) assert.equal(fileInfoFailureHarness.readRequest(), undefined) const readFailureHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/read-failure.md' }, responseHeaders: { 'Content-Type': 'text/markdown', 'Content-Disposition': 'attachment; filename="read-failure.md"' }, fileSize: 10, readError: new Error('readFile failed') }) await assert.rejects(readFailureHarness.promise, /文件读取失败,请重试/) assert.equal(readFailureHarness.openRequest(), undefined) const unknownExtensionHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/unknown-file' }, responseHeaders: { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="archive.zip"' } }) await assert.rejects(unknownExtensionHarness.promise, /文件类型不支持预览/) assert.equal(unknownExtensionHarness.openRequest(), undefined) assert.equal(unknownExtensionHarness.fileSystemManagerCalls(), 0) const jsonHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/error.json' }, responseHeaders: { 'Content-Type': 'application/json;charset=UTF-8', 'Content-Disposition': 'attachment; filename="error.json"' } }) await assert.rejects(jsonHarness.promise, /下载失败,请重试/) assert.equal(jsonHarness.openRequest(), undefined) const htmlHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/error.html' }, responseHeaders: { 'content-type': 'text/html; charset=UTF-8', 'content-disposition': 'attachment; filename="error.html"' } }) await assert.rejects(htmlHarness.promise, /下载失败,请重试/) assert.equal(htmlHarness.openRequest(), undefined) const missingHeadersHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/unknown.pdf' }, withHeadersListener: false }) await assert.rejects(missingHeadersHarness.promise, /下载失败,请重试/) assert.equal(missingHeadersHarness.openRequest(), undefined) const statusHarness = await createDownloadHarness({ result: { statusCode: 500 } }) await assert.rejects(statusHarness.promise, /下载失败,请重试/) assert.equal(statusHarness.openRequest(), undefined) const downloadError = new Error('download network error') const downloadFailureHarness = await createDownloadHarness({ downloadError }) await assert.rejects(downloadFailureHarness.promise, /download network error/) const openError = new Error('open document error') const openFailureHarness = await createDownloadHarness({ result: { statusCode: 200, tempFilePath: '/tmp/download.pdf' }, responseHeaders: { 'Content-Type': 'application/pdf', 'Content-Disposition': 'attachment; filename="download.pdf"' }, openError }) await assert.rejects(openFailureHarness.promise, /open document error/) } async function main() { const loadedTransport = await loadTransport() assert.equal(typeof loadedTransport.downloadTeamBackgroundFile, 'function') await testUploadTransport() await testApiTransport() await testDownloadTransport() console.log('team background transport: PASS') } main().catch(error => { console.error(error) process.exitCode = 1 })