mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
fix(import): disambiguate ChatLab reimports
This commit is contained in:
1 parent
af78be00db
commit
6dc35722c0
14 files changed
+110
-9
No files matched your search
@@ -12,7 +12,7 @@ interface ChatImportResult {
|
|||||||
sessionId?: string
|
sessionId?: string
|
||||||
error?: string
|
error?: string
|
||||||
importMode?: 'created' | 'incremental'
|
importMode?: 'created' | 'incremental'
|
||||||
matchedBy?: 'stable-id' | 'trailing-messages'
|
matchedBy?: 'source-session-id' | 'stable-id' | 'trailing-messages'
|
||||||
newMessageCount?: number
|
newMessageCount?: number
|
||||||
duplicateCount?: number
|
duplicateCount?: number
|
||||||
diagnostics?: {
|
diagnostics?: {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -43,7 +43,7 @@ interface ChatImportResult {
|
|||||||
sessionId?: string
|
sessionId?: string
|
||||||
error?: string
|
error?: string
|
||||||
importMode?: 'created' | 'incremental'
|
importMode?: 'created' | 'incremental'
|
||||||
matchedBy?: 'stable-id' | 'trailing-messages'
|
matchedBy?: 'source-session-id' | 'stable-id' | 'trailing-messages'
|
||||||
newMessageCount?: number
|
newMessageCount?: number
|
||||||
duplicateCount?: number
|
duplicateCount?: number
|
||||||
diagnostics?: ImportDiagnostics
|
diagnostics?: ImportDiagnostics
|
||||||
|
|||||||
@@ -106,7 +106,12 @@ function parseOptionalArray<T>(value: string | null): T[] | undefined {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportAsJson(db: DatabaseAdapter, messages: MessageRow[], sessionName: string): string {
|
function exportAsJson(
|
||||||
|
db: DatabaseAdapter,
|
||||||
|
messages: MessageRow[],
|
||||||
|
sessionName: string,
|
||||||
|
sourceSessionId: string
|
||||||
|
): string {
|
||||||
const meta = db
|
const meta = db
|
||||||
.prepare(
|
.prepare(
|
||||||
`SELECT name,
|
`SELECT name,
|
||||||
@@ -147,6 +152,7 @@ function exportAsJson(db: DatabaseAdapter, messages: MessageRow[], sessionName:
|
|||||||
groupId: meta.groupId || undefined,
|
groupId: meta.groupId || undefined,
|
||||||
groupAvatar: meta.groupAvatar || undefined,
|
groupAvatar: meta.groupAvatar || undefined,
|
||||||
ownerId: meta.ownerId || undefined,
|
ownerId: meta.ownerId || undefined,
|
||||||
|
sourceSessionId,
|
||||||
},
|
},
|
||||||
members: members.map((member) => ({
|
members: members.map((member) => ({
|
||||||
platformId: member.platformId,
|
platformId: member.platformId,
|
||||||
@@ -219,7 +225,7 @@ export function exportWithFormat(
|
|||||||
content = exportAsTxt(messages, params.sessionName)
|
content = exportAsTxt(messages, params.sessionName)
|
||||||
break
|
break
|
||||||
case 'json':
|
case 'json':
|
||||||
content = exportAsJson(db, messages, params.sessionName)
|
content = exportAsJson(db, messages, params.sessionName, params.sessionId)
|
||||||
break
|
break
|
||||||
case 'markdown':
|
case 'markdown':
|
||||||
content = exportAsMarkdown(messages, params.sessionName)
|
content = exportAsMarkdown(messages, params.sessionName)
|
||||||
|
|||||||
@@ -32,13 +32,14 @@ function writeChatLabJsonl(
|
|||||||
filePath: string,
|
filePath: string,
|
||||||
meta: SourceMeta,
|
meta: SourceMeta,
|
||||||
members: SourceMember[],
|
members: SourceMember[],
|
||||||
messages: ParsedMessage[]
|
messages: ParsedMessage[],
|
||||||
|
sourceSessionId?: string
|
||||||
): void {
|
): void {
|
||||||
const lines = [
|
const lines = [
|
||||||
{
|
{
|
||||||
_type: 'header',
|
_type: 'header',
|
||||||
chatlab: { version: '0.0.2', exportedAt: 1783840000 },
|
chatlab: { version: '0.0.2', exportedAt: 1783840000 },
|
||||||
meta,
|
meta: { ...meta, sourceSessionId },
|
||||||
},
|
},
|
||||||
...members.map((member) => ({ _type: 'member', ...member })),
|
...members.map((member) => ({ _type: 'member', ...member })),
|
||||||
...messages.map((message) => ({
|
...messages.map((message) => ({
|
||||||
@@ -171,6 +172,77 @@ test('matches a unique private session by stable owner and participant ids', asy
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('uses a validated ChatLab source session id to disambiguate stable matches', async (t) => {
|
||||||
|
const tempDir = makeTempDir()
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }))
|
||||||
|
|
||||||
|
const meta: SourceMeta = {
|
||||||
|
name: 'Private chat',
|
||||||
|
platform: 'wechat',
|
||||||
|
type: 'private',
|
||||||
|
ownerId: 'owner',
|
||||||
|
}
|
||||||
|
const members = [
|
||||||
|
{ platformId: 'owner', accountName: 'Owner' },
|
||||||
|
{ platformId: 'peer', accountName: 'Peer' },
|
||||||
|
]
|
||||||
|
const messages = [textMessage('peer', 1783860000, 'hello')]
|
||||||
|
|
||||||
|
seedSession(path.join(tempDir, 'source-session.db'), meta, members, messages)
|
||||||
|
seedSession(path.join(tempDir, 'duplicate-session.db'), meta, members, messages)
|
||||||
|
writeChatLabJsonl(path.join(tempDir, 'source.jsonl'), meta, members, messages, 'source-session')
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
await resolveAutoImportTarget(
|
||||||
|
path.join(tempDir, 'source.jsonl'),
|
||||||
|
createDeps(tempDir, ['source-session', 'duplicate-session'])
|
||||||
|
),
|
||||||
|
{
|
||||||
|
action: 'incremental',
|
||||||
|
sessionId: 'source-session',
|
||||||
|
matchedBy: 'source-session-id',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not trust a ChatLab source session id that fails identity validation', async (t) => {
|
||||||
|
const tempDir = makeTempDir()
|
||||||
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }))
|
||||||
|
|
||||||
|
const sourceMeta: SourceMeta = {
|
||||||
|
name: 'Private chat',
|
||||||
|
platform: 'wechat',
|
||||||
|
type: 'private',
|
||||||
|
ownerId: 'owner',
|
||||||
|
}
|
||||||
|
const sourceMembers = [
|
||||||
|
{ platformId: 'owner', accountName: 'Owner' },
|
||||||
|
{ platformId: 'peer', accountName: 'Peer' },
|
||||||
|
]
|
||||||
|
const messages = [textMessage('peer', 1783860000, 'hello')]
|
||||||
|
|
||||||
|
seedSession(path.join(tempDir, 'matching-a.db'), sourceMeta, sourceMembers, messages)
|
||||||
|
seedSession(path.join(tempDir, 'matching-b.db'), sourceMeta, sourceMembers, messages)
|
||||||
|
seedSession(
|
||||||
|
path.join(tempDir, 'unrelated.db'),
|
||||||
|
{ ...sourceMeta, ownerId: 'different-owner' },
|
||||||
|
[
|
||||||
|
{ platformId: 'different-owner', accountName: 'Other owner' },
|
||||||
|
{ platformId: 'different-peer', accountName: 'Other peer' },
|
||||||
|
],
|
||||||
|
[textMessage('different-peer', 1783860000, 'hello')]
|
||||||
|
)
|
||||||
|
writeChatLabJsonl(path.join(tempDir, 'source.jsonl'), sourceMeta, sourceMembers, messages, 'unrelated')
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
await resolveAutoImportTarget(
|
||||||
|
path.join(tempDir, 'source.jsonl'),
|
||||||
|
createDeps(tempDir, ['matching-a', 'matching-b', 'unrelated'])
|
||||||
|
),
|
||||||
|
{ action: 'create', reason: 'ambiguous' }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('matches a unique session when its last five business messages appear consecutively in the source', async (t) => {
|
test('matches a unique session when its last five business messages appear consecutively in the source', async (t) => {
|
||||||
const tempDir = makeTempDir()
|
const tempDir = makeTempDir()
|
||||||
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }))
|
t.after(() => fs.rmSync(tempDir, { recursive: true, force: true }))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { ImportProgressCallback } from './streaming-importer'
|
|||||||
|
|
||||||
const MATCH_WINDOW_SIZE = 5
|
const MATCH_WINDOW_SIZE = 5
|
||||||
|
|
||||||
export type AutoImportMatchMethod = 'stable-id' | 'trailing-messages'
|
export type AutoImportMatchMethod = 'source-session-id' | 'stable-id' | 'trailing-messages'
|
||||||
|
|
||||||
export type AutoImportDecision =
|
export type AutoImportDecision =
|
||||||
| { action: 'incremental'; sessionId: string; matchedBy: AutoImportMatchMethod }
|
| { action: 'incremental'; sessionId: string; matchedBy: AutoImportMatchMethod }
|
||||||
@@ -137,11 +137,17 @@ export async function resolveAutoImportTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasStableIdentity) {
|
if (hasStableIdentity) {
|
||||||
|
if (meta.sourceSessionId && stableMatches.includes(meta.sourceSessionId)) {
|
||||||
|
return { action: 'incremental', sessionId: meta.sourceSessionId, matchedBy: 'source-session-id' }
|
||||||
|
}
|
||||||
if (stableMatches.length === 1) {
|
if (stableMatches.length === 1) {
|
||||||
return { action: 'incremental', sessionId: stableMatches[0], matchedBy: 'stable-id' }
|
return { action: 'incremental', sessionId: stableMatches[0], matchedBy: 'stable-id' }
|
||||||
}
|
}
|
||||||
return { action: 'create', reason: stableMatches.length > 1 ? 'ambiguous' : 'no-match' }
|
return { action: 'create', reason: stableMatches.length > 1 ? 'ambiguous' : 'no-match' }
|
||||||
}
|
}
|
||||||
|
if (meta.sourceSessionId && trailingMatches.includes(meta.sourceSessionId)) {
|
||||||
|
return { action: 'incremental', sessionId: meta.sourceSessionId, matchedBy: 'source-session-id' }
|
||||||
|
}
|
||||||
if (trailingMatches.length === 1) {
|
if (trailingMatches.length === 1) {
|
||||||
return { action: 'incremental', sessionId: trailingMatches[0], matchedBy: 'trailing-messages' }
|
return { action: 'incremental', sessionId: trailingMatches[0], matchedBy: 'trailing-messages' }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct ChatlabMeta {
|
|||||||
group_id: Option<String>,
|
group_id: Option<String>,
|
||||||
group_avatar: Option<String>,
|
group_avatar: Option<String>,
|
||||||
owner_id: Option<String>,
|
owner_id: Option<String>,
|
||||||
|
source_session_id: Option<String>,
|
||||||
/// true: members came from the top-level `members` array (full shape);
|
/// true: members came from the top-level `members` array (full shape);
|
||||||
/// false: collected from messages (id/name/nickname only), as in TS.
|
/// false: collected from messages (id/name/nickname only), as in TS.
|
||||||
members_from_head: bool,
|
members_from_head: bool,
|
||||||
@@ -87,6 +88,7 @@ struct MetaOut {
|
|||||||
group_id: Option<String>,
|
group_id: Option<String>,
|
||||||
group_avatar: Option<String>,
|
group_avatar: Option<String>,
|
||||||
owner_id: Option<String>,
|
owner_id: Option<String>,
|
||||||
|
source_session_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_meta(meta_raw: Option<&[u8]>, file_path: &str) -> ScanResult<MetaOut> {
|
fn parse_meta(meta_raw: Option<&[u8]>, file_path: &str) -> ScanResult<MetaOut> {
|
||||||
@@ -139,6 +141,7 @@ fn parse_meta(meta_raw: Option<&[u8]>, file_path: &str) -> ScanResult<MetaOut> {
|
|||||||
let group_id = optional_str(&meta, "groupId", "meta")?;
|
let group_id = optional_str(&meta, "groupId", "meta")?;
|
||||||
let group_avatar = optional_str(&meta, "groupAvatar", "meta")?;
|
let group_avatar = optional_str(&meta, "groupAvatar", "meta")?;
|
||||||
let owner_id = optional_str(&meta, "ownerId", "meta")?;
|
let owner_id = optional_str(&meta, "ownerId", "meta")?;
|
||||||
|
let source_session_id = optional_str(&meta, "sourceSessionId", "meta")?;
|
||||||
|
|
||||||
// Filename fallback fires whenever the name resolved to the default,
|
// Filename fallback fires whenever the name resolved to the default,
|
||||||
// mirroring `if (meta.name === '未知群聊')` in the TS parser.
|
// mirroring `if (meta.name === '未知群聊')` in the TS parser.
|
||||||
@@ -153,6 +156,7 @@ fn parse_meta(meta_raw: Option<&[u8]>, file_path: &str) -> ScanResult<MetaOut> {
|
|||||||
group_id,
|
group_id,
|
||||||
group_avatar,
|
group_avatar,
|
||||||
owner_id,
|
owner_id,
|
||||||
|
source_session_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +383,7 @@ pub fn parse_chatlab(
|
|||||||
group_id: meta.group_id,
|
group_id: meta.group_id,
|
||||||
group_avatar: meta.group_avatar,
|
group_avatar: meta.group_avatar,
|
||||||
owner_id: meta.owner_id,
|
owner_id: meta.owner_id,
|
||||||
|
source_session_id: meta.source_session_id,
|
||||||
members_from_head,
|
members_from_head,
|
||||||
})
|
})
|
||||||
.map_err(|err| strict(format!("meta serialization failed: {err}")))?;
|
.map_err(|err| strict(format!("meta serialization failed: {err}")))?;
|
||||||
@@ -410,7 +415,7 @@ mod tests {
|
|||||||
fn parses_full_document() {
|
fn parses_full_document() {
|
||||||
let doc = r#"{
|
let doc = r#"{
|
||||||
"chatlab": {"version": "0.0.2", "exportedAt": 1700000000},
|
"chatlab": {"version": "0.0.2", "exportedAt": 1700000000},
|
||||||
"meta": {"name": "测试群", "platform": "weixin", "type": "group", "groupId": "g1@chatroom", "groupAvatar": "https://x/y.jpg", "ownerId": "u1"},
|
"meta": {"name": "测试群", "platform": "weixin", "type": "group", "groupId": "g1@chatroom", "groupAvatar": "https://x/y.jpg", "ownerId": "u1", "sourceSessionId": "source-session"},
|
||||||
"members": [
|
"members": [
|
||||||
{"platformId": "u1", "accountName": "Alice", "groupNickname": "小A", "aliases": ["Ally"], "avatar": "https://a", "roles": [{"id": "owner"}, {"id": "admin", "name": "管理"}]},
|
{"platformId": "u1", "accountName": "Alice", "groupNickname": "小A", "aliases": ["Ally"], "avatar": "https://a", "roles": [{"id": "owner"}, {"id": "admin", "name": "管理"}]},
|
||||||
{"platformId": "u2", "accountName": "Bob"}
|
{"platformId": "u2", "accountName": "Bob"}
|
||||||
@@ -428,6 +433,7 @@ mod tests {
|
|||||||
assert_eq!(m["chatType"], "group");
|
assert_eq!(m["chatType"], "group");
|
||||||
assert_eq!(m["groupId"], "g1@chatroom");
|
assert_eq!(m["groupId"], "g1@chatroom");
|
||||||
assert_eq!(m["ownerId"], "u1");
|
assert_eq!(m["ownerId"], "u1");
|
||||||
|
assert_eq!(m["sourceSessionId"], "source-session");
|
||||||
assert_eq!(m["membersFromHead"], true);
|
assert_eq!(m["membersFromHead"], true);
|
||||||
assert_eq!(out.members.len(), 2);
|
assert_eq!(out.members.len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ interface JsonlHeader {
|
|||||||
groupId?: string
|
groupId?: string
|
||||||
groupAvatar?: string
|
groupAvatar?: string
|
||||||
ownerId?: string
|
ownerId?: string
|
||||||
|
sourceSessionId?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +172,7 @@ async function* parseChatLabJsonl(options: ParseOptions): AsyncGenerator<ParseEv
|
|||||||
groupId: parsed.meta.groupId,
|
groupId: parsed.meta.groupId,
|
||||||
groupAvatar: parsed.meta.groupAvatar,
|
groupAvatar: parsed.meta.groupAvatar,
|
||||||
ownerId: parsed.meta.ownerId,
|
ownerId: parsed.meta.ownerId,
|
||||||
|
sourceSessionId: parsed.meta.sourceSessionId,
|
||||||
}
|
}
|
||||||
headerParsed = true
|
headerParsed = true
|
||||||
yield { type: 'meta', data: meta }
|
yield { type: 'meta', data: meta }
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ async function* parseChatLab(options: ParseOptions): AsyncGenerator<ParseEvent,
|
|||||||
groupId: metaObj.groupId,
|
groupId: metaObj.groupId,
|
||||||
groupAvatar: metaObj.groupAvatar,
|
groupAvatar: metaObj.groupAvatar,
|
||||||
ownerId: metaObj.ownerId,
|
ownerId: metaObj.ownerId,
|
||||||
|
sourceSessionId: metaObj.sourceSessionId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ const CANONICAL_FIXTURE = {
|
|||||||
groupId: 'g1@chatroom',
|
groupId: 'g1@chatroom',
|
||||||
groupAvatar: 'https://example.com/g.jpg',
|
groupAvatar: 'https://example.com/g.jpg',
|
||||||
ownerId: 'u1',
|
ownerId: 'u1',
|
||||||
|
sourceSessionId: 'source-session',
|
||||||
},
|
},
|
||||||
members: [
|
members: [
|
||||||
{
|
{
|
||||||
@@ -130,6 +131,7 @@ describe('chatlab native parser parity', { skip: !nativeAvailable() && 'native m
|
|||||||
assert.equal(nativeResult.meta.platform, 'weixin')
|
assert.equal(nativeResult.meta.platform, 'weixin')
|
||||||
assert.equal(nativeResult.meta.groupId, 'g1@chatroom')
|
assert.equal(nativeResult.meta.groupId, 'g1@chatroom')
|
||||||
assert.equal(nativeResult.meta.ownerId, 'u1')
|
assert.equal(nativeResult.meta.ownerId, 'u1')
|
||||||
|
assert.equal(nativeResult.meta.sourceSessionId, 'source-session')
|
||||||
assert.equal(nativeResult.members.length, 2)
|
assert.equal(nativeResult.members.length, 2)
|
||||||
assert.deepEqual(nativeResult.members[0].aliases, ['Ally'])
|
assert.deepEqual(nativeResult.members[0].aliases, ['Ally'])
|
||||||
assert.equal(nativeResult.members[0].avatar, 'https://example.com/a.jpg')
|
assert.equal(nativeResult.members[0].avatar, 'https://example.com/a.jpg')
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ interface ChatlabMetaJson {
|
|||||||
groupId: string | null
|
groupId: string | null
|
||||||
groupAvatar: string | null
|
groupAvatar: string | null
|
||||||
ownerId: string | null
|
ownerId: string | null
|
||||||
|
sourceSessionId: string | null
|
||||||
/**
|
/**
|
||||||
* true when members came from the top-level `members` array;
|
* true when members came from the top-level `members` array;
|
||||||
* false when collected from messages (TS emits different object shapes).
|
* false when collected from messages (TS emits different object shapes).
|
||||||
@@ -62,6 +63,7 @@ const chatlabAdapter: NativeFormatAdapter = {
|
|||||||
groupId: meta.groupId ?? undefined,
|
groupId: meta.groupId ?? undefined,
|
||||||
groupAvatar: meta.groupAvatar ?? undefined,
|
groupAvatar: meta.groupAvatar ?? undefined,
|
||||||
ownerId: meta.ownerId ?? undefined,
|
ownerId: meta.ownerId ?? undefined,
|
||||||
|
sourceSessionId: meta.sourceSessionId ?? undefined,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export interface ParsedMeta {
|
|||||||
groupId?: string // 群ID(群聊类型有值)
|
groupId?: string // 群ID(群聊类型有值)
|
||||||
groupAvatar?: string // 群头像(base64 Data URL)
|
groupAvatar?: string // 群头像(base64 Data URL)
|
||||||
ownerId?: string // 所有者/导出者的 platformId
|
ownerId?: string // 所有者/导出者的 platformId
|
||||||
|
sourceSessionId?: string // ChatLab 导出来源会话 ID,仅用于安全消歧
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface ImportOptions {
|
|||||||
// ==================== 导入结果 ====================
|
// ==================== 导入结果 ====================
|
||||||
|
|
||||||
export type AutoImportMode = 'created' | 'incremental'
|
export type AutoImportMode = 'created' | 'incremental'
|
||||||
export type AutoImportMatchMethod = 'stable-id' | 'trailing-messages'
|
export type AutoImportMatchMethod = 'source-session-id' | 'stable-id' | 'trailing-messages'
|
||||||
|
|
||||||
export interface ImportResult {
|
export interface ImportResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export interface ChatLabMeta {
|
|||||||
groupId?: string // 群ID(可选,仅群聊)
|
groupId?: string // 群ID(可选,仅群聊)
|
||||||
groupAvatar?: string // 群头像(base64 Data URL,可选)
|
groupAvatar?: string // 群头像(base64 Data URL,可选)
|
||||||
ownerId?: string // 所有者/导出者的 platformId(可选)
|
ownerId?: string // 所有者/导出者的 platformId(可选)
|
||||||
|
sourceSessionId?: string // ChatLab 导出来源会话 ID(可选)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ test('exports JSON as ChatLab format that can be parsed for re-import', async ()
|
|||||||
const exported = JSON.parse(result.content)
|
const exported = JSON.parse(result.content)
|
||||||
assert.equal(exported.chatlab.version, '0.0.2')
|
assert.equal(exported.chatlab.version, '0.0.2')
|
||||||
assert.equal(exported.meta.ownerId, 'alice')
|
assert.equal(exported.meta.ownerId, 'alice')
|
||||||
|
assert.equal(exported.meta.sourceSessionId, 'session-1')
|
||||||
assert.deepEqual(exported.members[0].aliases, ['Alice'])
|
assert.deepEqual(exported.members[0].aliases, ['Alice'])
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
exported.messages.map((message: { timestamp: number }) => message.timestamp),
|
exported.messages.map((message: { timestamp: number }) => message.timestamp),
|
||||||
@@ -87,6 +88,7 @@ test('exports JSON as ChatLab format that can be parsed for re-import', async ()
|
|||||||
groupId: 'group-1',
|
groupId: 'group-1',
|
||||||
groupAvatar: 'group-avatar',
|
groupAvatar: 'group-avatar',
|
||||||
ownerId: 'alice',
|
ownerId: 'alice',
|
||||||
|
sourceSessionId: 'session-1',
|
||||||
})
|
})
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
parsed.members.map((member) => ({
|
parsed.members.map((member) => ({
|
||||||
|
|||||||
Reference in new issue
Block a user