Files
juzi/h5/test/profile-store.js

49 lines
3.4 KiB
JavaScript

const assert = require('node:assert/strict');
const Profile = require('../js/profile-store');
const regions = [['北京市', ['北京市']], ['浙江省', ['杭州市', '宁波市']]];
const now = new Date(2026, 9, 12);
const user = { id: 'fixture-a', kind: 'wechat' }, other = { id: 'fixture-b', kind: 'wechat' };
const valid = { birth: '1991-11', gender: '男', region: { province: '浙江省', city: '宁波市' }, skinIndex: 3 };
const empty = { birth: '', gender: '', region: null, skinIndex: 0 };
assert.equal(Profile.age(valid.birth, now), 34);
assert.equal(Profile.age(valid.birth, new Date(2026, 10, 1)), 35);
assert.equal(Profile.age(valid.birth, new Date(2027, 9, 1)), 35);
for (const birth of [null, '', '1991-1', '1991-00', '1991-13', '1991-11-01', '2026-01', '2027-01', '1926-01']) {
assert.equal(Profile.age(birth, now), null, String(birth));
}
assert.equal(Profile.age('2025-10', now), 1);
assert.equal(Profile.age('1926-11', now), 99);
assert.deepEqual(Profile.clean(null, regions, 5, now), empty);
assert.deepEqual(Profile.clean({ ...valid, token: 'not-persisted', report: {} }, regions, 5, now), valid);
assert.deepEqual(Profile.clean({ birth: 'bad', gender: '<script>', region: { province: '北京市', city: '宁波市' }, skinIndex: 5 }, regions, 5, now), empty);
for (const skinIndex of [-1, 1.5, '2', NaN]) assert.equal(Profile.clean({ skinIndex }, regions, 5, now).skinIndex, 0);
assert.equal(Profile.clean({ gender: '女' }, regions, 5, now).gender, '女', 'partial selections can be saved');
assert.equal(Profile.key(null), null);
assert.equal(Profile.key({ id: '', kind: 'browser' }), null);
assert.equal(Profile.key({ id: 'x', kind: 'unknown' }), null);
assert.equal(Profile.key({ id: 'x'.repeat(129), kind: 'browser' }), null);
assert.equal(Profile.key({ id: 'a/b', kind: 'wechat' }), 'juzi.profile.v1.wechat.a%2Fb');
const data = new Map();
const storage = { getItem: key => data.get(key) ?? null, setItem: (key, value) => data.set(key, value) };
const store = Profile.create(() => storage);
assert.equal(store.read(user), null);
store.write(user, valid);
assert.deepEqual(Profile.create(() => storage).read(user), valid, 'survives a page reload');
assert.equal(store.read(other), null, 'different accounts are isolated');
assert.equal(store.read({ ...user, kind: 'browser' }), null, 'guests cannot read saved preferences');
store.write({ ...user, kind: 'browser' }, valid);
assert.equal(Profile.key({ ...user, kind: 'browser' }), null);
store.write(other, empty); assert.deepEqual(store.read(user), valid);
store.write(null, valid); assert.equal(data.size, 2, 'no unauthenticated writes');
for (const raw of ['broken', 'x'.repeat(4097)]) {
data.set(Profile.key(user), raw);
assert.equal(Profile.create(() => storage).read(user), null, 'corrupt/oversized data is harmless');
}
const restricted = Profile.create(() => { throw Error('SecurityError'); });
assert.equal(restricted.read(user), null);
restricted.write(user, valid); assert.deepEqual(restricted.read(user), valid);
assert.equal(restricted.read(other), null);
const quota = Profile.create(() => ({ getItem: () => JSON.stringify(empty), setItem: () => { throw Error('QuotaExceededError'); } }));
quota.write(user, valid); assert.deepEqual(quota.read(user), valid, 'stale disk data cannot overwrite latest in-page selections');
console.log('PASS: profile validation, dynamic age, reload persistence, account isolation, corrupt/restricted/quota-limited storage, no credentials/report fields');