feat(web): add spaces, grouped channels and file chat
This commit is contained in:
1 parent
c7d8ef15bc
commit
f6cfb12716
111 files changed
+4371
-506
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { exportResource, mergeVault, validatePersonal, type VaultProfile } from './accountVault';
|
||||
import type { SavedResource } from '../database';
|
||||
const resource = (modifiedAt = 10): SavedResource => ({ id: 'AAAAAAAAAAAAAAAAAAAAAA', kind: 'channel', mode: 'enc', role: 'owner',
|
||||
link: 'https://drop.test/c/a#k=secret', endpointSecret: new Uint8Array(32).fill(7), deviceLabel: 'device-a', localScope: 'account:alice',
|
||||
createdAt: 1, modifiedAt, expiresAt: 100, seats: 3, membersCanUpload: true, name: `version-${modifiedAt}` });
|
||||
const profile = (): VaultProfile => ({ v: 2, keys: { publicKey: {}, privateKey: {} }, resources: [exportResource(resource(20))], deletes: [] });
|
||||
describe('account vault portability and merge', () => {
|
||||
it('never synchronizes device identity or local scope', () => {
|
||||
const exported = exportResource(resource());
|
||||
expect(exported).not.toHaveProperty('endpointSecret'); expect(exported).not.toHaveProperty('deviceLabel');
|
||||
expect(exported).not.toHaveProperty('localScope'); expect(exported.link).toContain('secret');
|
||||
});
|
||||
it('a stale device does not overwrite a newer remote resource', () => {
|
||||
expect(mergeVault(profile(), [resource(10)], []).resources[0]?.name).toBe('version-20');
|
||||
});
|
||||
it('resource removal survives a stale device syncing its old copy', () => {
|
||||
const remote = { ...profile(), resources: [], deletes: [{ id: resource().id, modifiedAt: 30 }] };
|
||||
expect(mergeVault(remote, [resource(20)], []).resources).toEqual([]);
|
||||
expect(mergeVault(profile(), [], [{ id: resource().id, modifiedAt: 30 }]).resources).toEqual([]);
|
||||
});
|
||||
it('explicitly rejoining after removal restores the resource', () => {
|
||||
const remote = { ...profile(), resources: [], deletes: [{ id: resource().id, modifiedAt: 30 }] };
|
||||
const merged = mergeVault(remote, [resource(40)], []);
|
||||
expect(merged.resources).toHaveLength(1); expect(merged.deletes).toEqual([]);
|
||||
});
|
||||
it('keeps the newest personal profile across stale devices, including avatar removal', () => {
|
||||
const remote = { ...profile(), personal: { displayName: '最新名称', avatar: 'data:image/png;base64,AA==', modifiedAt: 20 } };
|
||||
expect(mergeVault(remote, [], [], { displayName: '旧名称', modifiedAt: 10 }).personal).toEqual(remote.personal);
|
||||
expect(mergeVault(remote, [], [], { displayName: '新名称', modifiedAt: 30 }).personal).not.toHaveProperty('avatar');
|
||||
});
|
||||
it('rejects remote executable and external avatars', () => {
|
||||
for (const avatar of ['https://tracker.test/avatar.png', 'data:image/svg+xml;base64,AA==']) expect(() => validatePersonal({ displayName: 'Alice', avatar, modifiedAt: 1 })).toThrow();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user