70 lines
5.8 KiB
JavaScript
70 lines
5.8 KiB
JavaScript
'use strict';
|
|
const { test } = require('node:test'), assert = require('node:assert/strict');
|
|
const fs = require('node:fs'), os = require('node:os'), path = require('node:path'), vm = require('node:vm');
|
|
const { freePort, start, stop } = require('./support.cjs');
|
|
const fx = { window: {} };
|
|
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../../h5/assets/mock/report-fixture.js'), 'utf8'), fx);
|
|
const fixture = fx.window.MOCK_FIXTURE;
|
|
|
|
test('persistent browser consent, cookies, owner isolation, reports, source, admission control and restart', async () => {
|
|
const dataRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'juzi-rednote-backend-')), port = await freePort();
|
|
const base = 'http://127.0.0.1:' + port, publicBase = base + '/rednote/';
|
|
let child;
|
|
const get = async (route, cookie = '') => fetch(base + route, { headers: { cookie } });
|
|
const post = async (route, data = {}, cookie = '', headers = {}) => {
|
|
const response = await fetch(base + route, { method: 'POST', headers: { 'Content-Type': 'application/json', cookie, ...headers }, body: JSON.stringify(data) });
|
|
return { status: response.status, cookie: response.headers.getSetCookie(), data: await response.json() };
|
|
};
|
|
try {
|
|
child = await start({ dataRoot, port, publicBase, delay: 80 });
|
|
const html = await (await get('/')).text();
|
|
assert(html.includes('window.JuziEntry={mode:"rednote"')); assert(!html.includes('data-action="login-wechat"'));
|
|
assert.equal((await (await get('/api/auth/config')).json()).wechat, false);
|
|
assert.equal((await post('/api/auth/wechat', { consent: true })).status, 404);
|
|
assert.equal((await get('/api/auth/wechat/callback?code=fake&state=fake')).status, 404);
|
|
assert.equal((await post('/api/taobao/session')).status, 404);
|
|
assert.equal((await post('/api/auth/browser', { consent: false })).status, 400);
|
|
assert.equal((await post('/api/auth/browser', { consent: true }, '', { Origin: 'https://evil.invalid' })).status, 403);
|
|
assert.equal(fs.existsSync(path.join(dataRoot, 'accounts/identities.json')), false, 'no identity before consent');
|
|
const alice = await post('/api/auth/browser', { consent: true, channel: 'taobao', user_id: 'forged' });
|
|
assert.equal(alice.data.user.kind, 'browser_persistent'); assert.notEqual(alice.data.user.id, 'forged');
|
|
assert.match(alice.cookie[0], /juzi_rednote_session=[a-f0-9]{64}; Path=\/rednote\/; HttpOnly; SameSite=Lax; Max-Age=2592000/);
|
|
const cookie = alice.cookie[0].split(';')[0];
|
|
assert.equal((await post('/api/auth/browser', { consent: true }, cookie)).data.user.id, alice.data.user.id);
|
|
assert.equal((await post('/api/auth/browser', { consent: true, token: cookie })).status, 401);
|
|
const bob = await post('/api/auth/browser', { consent: true }), bobCookie = bob.cookie[0].split(';')[0];
|
|
const body = { front_image: fixture.photos[0], left_image: fixture.photos[0], right_image: fixture.photos[0],
|
|
profile: { real_age: 44, daily_skin_type: '干性肌肤', gender: '女' }, channel: 'h5' };
|
|
const [first, busy] = await Promise.all([post('/api/skin-analyze', body, cookie), post('/api/skin-analyze', body, cookie)]);
|
|
assert.equal(first.status, 200); assert.equal(first.data.report_saved, true);
|
|
assert.equal(busy.status, 429); assert.equal(busy.data.code, 'BUSY');
|
|
assert.equal((await get('/api/reports/' + first.data.record_id, bobCookie)).status, 404);
|
|
assert.equal((await get('/api/reports', cookie.replace('juzi_rednote_session=', 'juzi_session='))).status, 401);
|
|
assert.equal((await get('/data/accounts/identities.json')).status, 403);
|
|
const detail = await (await get('/api/admin/records/' + first.data.record_id)).json();
|
|
assert.equal(detail.record.channel, 'rednote');
|
|
assert(detail.photos.front.startsWith('/rednote/api/admin/photos/'));
|
|
const ids = [first.data.record_id]; let last = first;
|
|
for (let i = 0; i < 5; i++) { last = await post('/api/skin-analyze', body, cookie); assert.equal(last.data.report_saved, true); ids.push(last.data.record_id); }
|
|
const history = await (await get('/api/reports', cookie)).json();
|
|
assert.equal(history.total, 5); assert.equal(history.items[0].id, ids.at(-1));
|
|
assert.equal((await get('/api/reports/' + ids[0], cookie)).status, 404);
|
|
assert.equal((await (await get('/api/reports', bobCookie)).json()).total, 0);
|
|
const stats = await (await get('/api/admin/stats')).json(); assert.equal(stats.by_channel.rednote.total, 6);
|
|
assert.equal((await (await get('/api/admin/records?channel=h5')).json()).total, 0);
|
|
const csv = await (await get('/api/admin/export?channel=rednote')).text(); assert.equal(csv.trim().split('\n').length, 7); assert(csv.includes(',rednote'));
|
|
const snapshot = await (await get('/api/reports/' + last.data.record_id, cookie)).json();
|
|
await stop(child); child = await start({ dataRoot, port, publicBase });
|
|
assert.equal((await (await get('/api/auth/session', cookie)).json()).user.id, alice.data.user.id);
|
|
assert.deepEqual((await (await get('/api/reports/' + last.data.record_id, cookie)).json()).report, snapshot.report);
|
|
const side = await post('/api/wrinkle-views', { token: last.data.wrinkle_job.token }, cookie);
|
|
assert.equal(side.data.ok, true); assert.equal(side.data.wrinkle_views.left.status, 'ready');
|
|
assert.equal((await post('/api/wrinkle-views', { token: last.data.wrinkle_job.token }, bobCookie)).status, 404);
|
|
assert.equal((await post('/api/auth/logout', {}, cookie)).status, 200);
|
|
assert.equal((await get('/api/reports', cookie)).status, 401);
|
|
assert.notEqual((await post('/api/auth/browser', { consent: true }, cookie)).data.user.id, alice.data.user.id);
|
|
const db = fs.readFileSync(path.join(dataRoot, 'accounts/identities.json'), 'utf8');
|
|
assert(!db.includes(cookie.split('=')[1]), 'server stores session hashes, not cookie secrets');
|
|
} finally { await stop(child); fs.rmSync(dataRoot, { recursive: true, force: true }); }
|
|
});
|