feat: add yuntu report filling automation

This commit is contained in:
wxs
2026-03-31 18:52:34 +08:00
parent c0a531fd1d
commit c8b5d27078
17 changed files with 2996 additions and 0 deletions
@@ -0,0 +1,95 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const {
validateAndNormalizeReportInput,
toDatabaseRecord,
} = require('../src/report-service');
function createManualPayload() {
return {
reportId: 'report-001',
sourceType: 'MANUAL_CAPTURE',
sourceReportId: null,
aadvid: '1648829117232140',
name: '测试报告',
price: ['1,100', '101,100000'],
rules: [{ keywords: ['奶粉'], op: 'INCLUDE' }],
analysisDims: ['MARKETOVERVIEW'],
categories: [{ id: '20028', name: '奶粉类目' }],
channels: ['ALL'],
startTime: '2025-03-01',
endTime: '2026-02-28',
periodType: 'MONTH',
userName: 'tester@example.com',
payload: {
name: '测试报告',
startTime: '2025-03-01',
endTime: '2026-02-28',
},
};
}
test('validateAndNormalizeReportInput accepts a valid manual report payload', () => {
const input = createManualPayload();
const result = validateAndNormalizeReportInput(input);
assert.equal(result.reportId, 'report-001');
assert.equal(result.sourceType, 'MANUAL_CAPTURE');
assert.equal(result.sourceReportId, null);
assert.equal(result.aadvid, '1648829117232140');
assert.equal(result.startTime, '2025-03-01');
assert.equal(result.endTime, '2026-02-28');
});
test('validateAndNormalizeReportInput rejects AUTO_COPY without sourceReportId', () => {
const input = {
...createManualPayload(),
reportId: 'report-002',
sourceType: 'AUTO_COPY',
sourceReportId: '',
};
assert.throws(
() => validateAndNormalizeReportInput(input),
(error) => {
assert.equal(error.code, 'VALIDATION_ERROR');
assert.match(error.message, /sourceReportId/i);
return true;
},
);
});
test('toDatabaseRecord maps API payload fields into database-ready values', () => {
const normalized = validateAndNormalizeReportInput({
...createManualPayload(),
reportId: 'report-003',
sourceType: 'AUTO_COPY',
sourceReportId: 'report-001',
});
const record = toDatabaseRecord(normalized);
assert.deepEqual(record, {
report_id: 'report-003',
source_type: 'AUTO_COPY',
source_report_id: 'report-001',
aadvid: '1648829117232140',
name: '测试报告',
price: ['1,100', '101,100000'],
rules: [{ keywords: ['奶粉'], op: 'INCLUDE' }],
analysis_dims: ['MARKETOVERVIEW'],
categories: [{ id: '20028', name: '奶粉类目' }],
transaction_channels: ['ALL'],
start_time: '2025-03-01',
end_time: '2026-02-28',
period_type: 'MONTH',
user_name: 'tester@example.com',
payload: {
name: '测试报告',
startTime: '2025-03-01',
endTime: '2026-02-28',
},
});
});
@@ -0,0 +1,146 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { createApp } = require('../src/server');
function createManualPayload() {
return {
reportId: 'report-001',
sourceType: 'MANUAL_CAPTURE',
sourceReportId: null,
aadvid: '1648829117232140',
name: '测试报告',
price: ['1,100', '101,100000'],
rules: [{ keywords: ['奶粉'], op: 'INCLUDE' }],
analysisDims: ['MARKETOVERVIEW'],
categories: [{ id: '20028', name: '奶粉类目' }],
channels: ['ALL'],
startTime: '2025-03-01',
endTime: '2026-02-28',
periodType: 'MONTH',
userName: 'tester@example.com',
payload: {
name: '测试报告',
startTime: '2025-03-01',
endTime: '2026-02-28',
},
};
}
async function withServer(repository, callback) {
const app = createApp({
repository,
allowedOrigin: 'https://yuntu.oceanengine.com',
});
const server = await new Promise((resolve) => {
const instance = app.listen(0, '127.0.0.1', () => resolve(instance));
});
const address = server.address();
const baseUrl = `http://127.0.0.1:${address.port}`;
try {
await callback(baseUrl);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
}
test('GET /health returns ok status', async () => {
await withServer(
{
async save() {
throw new Error('save should not be called');
},
},
async (baseUrl) => {
const response = await fetch(`${baseUrl}/health`);
const body = await response.json();
assert.equal(response.status, 200);
assert.deepEqual(body, {
success: true,
data: {
status: 'ok',
},
});
},
);
});
test('POST /api/reports returns 400 for invalid auto copy payload', async () => {
await withServer(
{
async save() {
throw new Error('save should not be called');
},
},
async (baseUrl) => {
const response = await fetch(`${baseUrl}/api/reports`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
...createManualPayload(),
sourceType: 'AUTO_COPY',
sourceReportId: '',
}),
});
const body = await response.json();
assert.equal(response.status, 400);
assert.equal(body.success, false);
assert.equal(body.error.code, 'VALIDATION_ERROR');
},
);
});
test('POST /api/reports persists a valid payload and returns created response', async () => {
let savedRecord = null;
await withServer(
{
async save(record) {
savedRecord = record;
return {
id: 12,
reportId: record.report_id,
created: true,
};
},
},
async (baseUrl) => {
const response = await fetch(`${baseUrl}/api/reports`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(createManualPayload()),
});
const body = await response.json();
assert.equal(response.status, 201);
assert.deepEqual(body, {
success: true,
data: {
id: 12,
reportId: 'report-001',
created: true,
},
});
assert.equal(savedRecord.report_id, 'report-001');
assert.equal(savedRecord.source_type, 'MANUAL_CAPTURE');
},
);
});