mirror of
https://github.com/immich-app/immich.git
synced 2025-11-02 21:27:39 +09:00
feat: audit cleanup (#21567)
This commit is contained in:
3
mobile/openapi/lib/model/sync_entity_type.dart
generated
3
mobile/openapi/lib/model/sync_entity_type.dart
generated
@@ -69,6 +69,7 @@ class SyncEntityType {
|
|||||||
static const userMetadataDeleteV1 = SyncEntityType._(r'UserMetadataDeleteV1');
|
static const userMetadataDeleteV1 = SyncEntityType._(r'UserMetadataDeleteV1');
|
||||||
static const syncAckV1 = SyncEntityType._(r'SyncAckV1');
|
static const syncAckV1 = SyncEntityType._(r'SyncAckV1');
|
||||||
static const syncResetV1 = SyncEntityType._(r'SyncResetV1');
|
static const syncResetV1 = SyncEntityType._(r'SyncResetV1');
|
||||||
|
static const syncCompleteV1 = SyncEntityType._(r'SyncCompleteV1');
|
||||||
|
|
||||||
/// List of all possible values in this [enum][SyncEntityType].
|
/// List of all possible values in this [enum][SyncEntityType].
|
||||||
static const values = <SyncEntityType>[
|
static const values = <SyncEntityType>[
|
||||||
@@ -118,6 +119,7 @@ class SyncEntityType {
|
|||||||
userMetadataDeleteV1,
|
userMetadataDeleteV1,
|
||||||
syncAckV1,
|
syncAckV1,
|
||||||
syncResetV1,
|
syncResetV1,
|
||||||
|
syncCompleteV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
static SyncEntityType? fromJson(dynamic value) => SyncEntityTypeTypeTransformer().decode(value);
|
static SyncEntityType? fromJson(dynamic value) => SyncEntityTypeTypeTransformer().decode(value);
|
||||||
@@ -202,6 +204,7 @@ class SyncEntityTypeTypeTransformer {
|
|||||||
case r'UserMetadataDeleteV1': return SyncEntityType.userMetadataDeleteV1;
|
case r'UserMetadataDeleteV1': return SyncEntityType.userMetadataDeleteV1;
|
||||||
case r'SyncAckV1': return SyncEntityType.syncAckV1;
|
case r'SyncAckV1': return SyncEntityType.syncAckV1;
|
||||||
case r'SyncResetV1': return SyncEntityType.syncResetV1;
|
case r'SyncResetV1': return SyncEntityType.syncResetV1;
|
||||||
|
case r'SyncCompleteV1': return SyncEntityType.syncCompleteV1;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
|
|||||||
@@ -15416,6 +15416,10 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"SyncCompleteV1": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"SyncEntityType": {
|
"SyncEntityType": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"AuthUserV1",
|
"AuthUserV1",
|
||||||
@@ -15463,7 +15467,8 @@
|
|||||||
"UserMetadataV1",
|
"UserMetadataV1",
|
||||||
"UserMetadataDeleteV1",
|
"UserMetadataDeleteV1",
|
||||||
"SyncAckV1",
|
"SyncAckV1",
|
||||||
"SyncResetV1"
|
"SyncResetV1",
|
||||||
|
"SyncCompleteV1"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4921,7 +4921,8 @@ export enum SyncEntityType {
|
|||||||
UserMetadataV1 = "UserMetadataV1",
|
UserMetadataV1 = "UserMetadataV1",
|
||||||
UserMetadataDeleteV1 = "UserMetadataDeleteV1",
|
UserMetadataDeleteV1 = "UserMetadataDeleteV1",
|
||||||
SyncAckV1 = "SyncAckV1",
|
SyncAckV1 = "SyncAckV1",
|
||||||
SyncResetV1 = "SyncResetV1"
|
SyncResetV1 = "SyncResetV1",
|
||||||
|
SyncCompleteV1 = "SyncCompleteV1"
|
||||||
}
|
}
|
||||||
export enum SyncRequestType {
|
export enum SyncRequestType {
|
||||||
AlbumsV1 = "AlbumsV1",
|
AlbumsV1 = "AlbumsV1",
|
||||||
|
|||||||
@@ -336,6 +336,9 @@ export class SyncAckV1 {}
|
|||||||
@ExtraModel()
|
@ExtraModel()
|
||||||
export class SyncResetV1 {}
|
export class SyncResetV1 {}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
|
export class SyncCompleteV1 {}
|
||||||
|
|
||||||
export type SyncItem = {
|
export type SyncItem = {
|
||||||
[SyncEntityType.AuthUserV1]: SyncAuthUserV1;
|
[SyncEntityType.AuthUserV1]: SyncAuthUserV1;
|
||||||
[SyncEntityType.UserV1]: SyncUserV1;
|
[SyncEntityType.UserV1]: SyncUserV1;
|
||||||
@@ -382,6 +385,7 @@ export type SyncItem = {
|
|||||||
[SyncEntityType.UserMetadataV1]: SyncUserMetadataV1;
|
[SyncEntityType.UserMetadataV1]: SyncUserMetadataV1;
|
||||||
[SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1;
|
[SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1;
|
||||||
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
||||||
|
[SyncEntityType.SyncCompleteV1]: SyncCompleteV1;
|
||||||
[SyncEntityType.SyncResetV1]: SyncResetV1;
|
[SyncEntityType.SyncResetV1]: SyncResetV1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -530,6 +530,7 @@ export enum JobName {
|
|||||||
AssetGenerateThumbnails = 'AssetGenerateThumbnails',
|
AssetGenerateThumbnails = 'AssetGenerateThumbnails',
|
||||||
|
|
||||||
AuditLogCleanup = 'AuditLogCleanup',
|
AuditLogCleanup = 'AuditLogCleanup',
|
||||||
|
AuditTableCleanup = 'AuditTableCleanup',
|
||||||
|
|
||||||
DatabaseBackup = 'DatabaseBackup',
|
DatabaseBackup = 'DatabaseBackup',
|
||||||
|
|
||||||
@@ -708,6 +709,7 @@ export enum SyncEntityType {
|
|||||||
|
|
||||||
SyncAckV1 = 'SyncAckV1',
|
SyncAckV1 = 'SyncAckV1',
|
||||||
SyncResetV1 = 'SyncResetV1',
|
SyncResetV1 = 'SyncResetV1',
|
||||||
|
SyncCompleteV1 = 'SyncCompleteV1',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NotificationLevel {
|
export enum NotificationLevel {
|
||||||
|
|||||||
@@ -957,7 +957,7 @@ where
|
|||||||
order by
|
order by
|
||||||
"stack"."updateId" asc
|
"stack"."updateId" asc
|
||||||
|
|
||||||
-- SyncRepository.people.getDeletes
|
-- SyncRepository.person.getDeletes
|
||||||
select
|
select
|
||||||
"id",
|
"id",
|
||||||
"personId"
|
"personId"
|
||||||
@@ -970,7 +970,7 @@ where
|
|||||||
order by
|
order by
|
||||||
"person_audit"."id" asc
|
"person_audit"."id" asc
|
||||||
|
|
||||||
-- SyncRepository.people.getUpserts
|
-- SyncRepository.person.getUpserts
|
||||||
select
|
select
|
||||||
"id",
|
"id",
|
||||||
"createdAt",
|
"createdAt",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Kysely } from 'kysely';
|
import { Kysely, sql } from 'kysely';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { columns } from 'src/database';
|
import { columns } from 'src/database';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
@@ -62,7 +62,7 @@ export class SyncRepository {
|
|||||||
partnerAsset: PartnerAssetsSync;
|
partnerAsset: PartnerAssetsSync;
|
||||||
partnerAssetExif: PartnerAssetExifsSync;
|
partnerAssetExif: PartnerAssetExifsSync;
|
||||||
partnerStack: PartnerStackSync;
|
partnerStack: PartnerStackSync;
|
||||||
people: PersonSync;
|
person: PersonSync;
|
||||||
stack: StackSync;
|
stack: StackSync;
|
||||||
user: UserSync;
|
user: UserSync;
|
||||||
userMetadata: UserMetadataSync;
|
userMetadata: UserMetadataSync;
|
||||||
@@ -84,7 +84,7 @@ export class SyncRepository {
|
|||||||
this.partnerAsset = new PartnerAssetsSync(this.db);
|
this.partnerAsset = new PartnerAssetsSync(this.db);
|
||||||
this.partnerAssetExif = new PartnerAssetExifsSync(this.db);
|
this.partnerAssetExif = new PartnerAssetExifsSync(this.db);
|
||||||
this.partnerStack = new PartnerStackSync(this.db);
|
this.partnerStack = new PartnerStackSync(this.db);
|
||||||
this.people = new PersonSync(this.db);
|
this.person = new PersonSync(this.db);
|
||||||
this.stack = new StackSync(this.db);
|
this.stack = new StackSync(this.db);
|
||||||
this.user = new UserSync(this.db);
|
this.user = new UserSync(this.db);
|
||||||
this.userMetadata = new UserMetadataSync(this.db);
|
this.userMetadata = new UserMetadataSync(this.db);
|
||||||
@@ -117,6 +117,15 @@ class BaseSync {
|
|||||||
.orderBy(idRef, 'asc');
|
.orderBy(idRef, 'asc');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected auditCleanup<T extends keyof DB>(t: T, days: number) {
|
||||||
|
const { table, ref } = this.db.dynamic;
|
||||||
|
|
||||||
|
return this.db
|
||||||
|
.deleteFrom(table(t).as(t))
|
||||||
|
.where(ref(`${t}.deletedAt`), '<', sql.raw(`now() - interval '${days} days'`))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
protected upsertQuery<T extends keyof DB>(t: T, { nowId, ack }: SyncQueryOptions) {
|
protected upsertQuery<T extends keyof DB>(t: T, { nowId, ack }: SyncQueryOptions) {
|
||||||
const { table, ref } = this.db.dynamic;
|
const { table, ref } = this.db.dynamic;
|
||||||
const updateIdRef = ref(`${t}.updateId`);
|
const updateIdRef = ref(`${t}.updateId`);
|
||||||
@@ -150,6 +159,10 @@ class AlbumSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('album_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
const userId = options.userId;
|
const userId = options.userId;
|
||||||
@@ -286,6 +299,10 @@ class AlbumToAssetSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('album_asset_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
const userId = options.userId;
|
const userId = options.userId;
|
||||||
@@ -334,6 +351,10 @@ class AlbumUserSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('album_user_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
const userId = options.userId;
|
const userId = options.userId;
|
||||||
@@ -371,6 +392,10 @@ class AssetSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('asset_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('asset', options)
|
return this.upsertQuery('asset', options)
|
||||||
@@ -400,6 +425,10 @@ class PersonSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('person_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('person', options)
|
return this.upsertQuery('person', options)
|
||||||
@@ -431,6 +460,10 @@ class AssetFaceSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('asset_face_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('asset_face', options)
|
return this.upsertQuery('asset_face', options)
|
||||||
@@ -473,6 +506,10 @@ class MemorySync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('memory_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('memory', options)
|
return this.upsertQuery('memory', options)
|
||||||
@@ -505,6 +542,10 @@ class MemoryToAssetSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('memory_asset_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('memory_asset', options)
|
return this.upsertQuery('memory_asset', options)
|
||||||
@@ -537,6 +578,10 @@ class PartnerSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('partner_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
const userId = options.userId;
|
const userId = options.userId;
|
||||||
@@ -616,6 +661,10 @@ class StackSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('stack_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('stack', options)
|
return this.upsertQuery('stack', options)
|
||||||
@@ -664,6 +713,10 @@ class UserSync extends BaseSync {
|
|||||||
return this.auditQuery('user_audit', options).select(['id', 'userId']).stream();
|
return this.auditQuery('user_audit', options).select(['id', 'userId']).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('user_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('user', options).select(columns.syncUser).stream();
|
return this.upsertQuery('user', options).select(columns.syncUser).stream();
|
||||||
@@ -679,6 +732,10 @@ class UserMetadataSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('user_metadata_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions) {
|
getUpserts(options: SyncQueryOptions) {
|
||||||
return this.upsertQuery('user_metadata', options)
|
return this.upsertQuery('user_metadata', options)
|
||||||
@@ -698,6 +755,10 @@ class AssetMetadataSync extends BaseSync {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupAuditTable(daysAgo: number) {
|
||||||
|
return this.auditCleanup('asset_metadata_audit', daysAgo);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true })
|
@GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true })
|
||||||
getUpserts(options: SyncQueryOptions, userId: string) {
|
getUpserts(options: SyncQueryOptions, userId: string) {
|
||||||
return this.upsertQuery('asset_metadata', options)
|
return this.upsertQuery('asset_metadata', options)
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ export interface DB {
|
|||||||
api_key: ApiKeyTable;
|
api_key: ApiKeyTable;
|
||||||
|
|
||||||
asset: AssetTable;
|
asset: AssetTable;
|
||||||
|
asset_audit: AssetAuditTable;
|
||||||
asset_exif: AssetExifTable;
|
asset_exif: AssetExifTable;
|
||||||
asset_face: AssetFaceTable;
|
asset_face: AssetFaceTable;
|
||||||
asset_face_audit: AssetFaceAuditTable;
|
asset_face_audit: AssetFaceAuditTable;
|
||||||
@@ -173,7 +174,6 @@ export interface DB {
|
|||||||
asset_metadata: AssetMetadataTable;
|
asset_metadata: AssetMetadataTable;
|
||||||
asset_metadata_audit: AssetMetadataAuditTable;
|
asset_metadata_audit: AssetMetadataAuditTable;
|
||||||
asset_job_status: AssetJobStatusTable;
|
asset_job_status: AssetJobStatusTable;
|
||||||
asset_audit: AssetAuditTable;
|
|
||||||
|
|
||||||
audit: AuditTable;
|
audit: AuditTable;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||||
import { Column, CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools';
|
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||||
|
|
||||||
@Table('memory_asset_audit')
|
@Table('memory_asset_audit')
|
||||||
export class MemoryAssetAuditTable {
|
export class MemoryAssetAuditTable {
|
||||||
@PrimaryGeneratedUuidV7Column()
|
@PrimaryGeneratedUuidV7Column()
|
||||||
id!: string;
|
id!: Generated<string>;
|
||||||
|
|
||||||
@ForeignKeyColumn(() => MemoryTable, { type: 'uuid', onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
@ForeignKeyColumn(() => MemoryTable, { type: 'uuid', onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||||
memoryId!: string;
|
memoryId!: string;
|
||||||
@@ -14,5 +14,5 @@ export class MemoryAssetAuditTable {
|
|||||||
assetId!: string;
|
assetId!: string;
|
||||||
|
|
||||||
@CreateDateColumn({ default: () => 'clock_timestamp()', index: true })
|
@CreateDateColumn({ default: () => 'clock_timestamp()', index: true })
|
||||||
deletedAt!: Date;
|
deletedAt!: Generated<Timestamp>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ describe(JobService.name, () => {
|
|||||||
{ name: JobName.PersonCleanup },
|
{ name: JobName.PersonCleanup },
|
||||||
{ name: JobName.MemoryCleanup },
|
{ name: JobName.MemoryCleanup },
|
||||||
{ name: JobName.SessionCleanup },
|
{ name: JobName.SessionCleanup },
|
||||||
|
{ name: JobName.AuditTableCleanup },
|
||||||
{ name: JobName.AuditLogCleanup },
|
{ name: JobName.AuditLogCleanup },
|
||||||
{ name: JobName.MemoryGenerate },
|
{ name: JobName.MemoryGenerate },
|
||||||
{ name: JobName.UserSyncUsage },
|
{ name: JobName.UserSyncUsage },
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ export class JobService extends BaseService {
|
|||||||
{ name: JobName.PersonCleanup },
|
{ name: JobName.PersonCleanup },
|
||||||
{ name: JobName.MemoryCleanup },
|
{ name: JobName.MemoryCleanup },
|
||||||
{ name: JobName.SessionCleanup },
|
{ name: JobName.SessionCleanup },
|
||||||
|
{ name: JobName.AuditTableCleanup },
|
||||||
{ name: JobName.AuditLogCleanup },
|
{ name: JobName.AuditLogCleanup },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common';
|
import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
import { Insertable } from 'kysely';
|
import { Insertable } from 'kysely';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime, Duration } from 'luxon';
|
||||||
import { Writable } from 'node:stream';
|
import { Writable } from 'node:stream';
|
||||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
||||||
|
import { OnJob } from 'src/decorators';
|
||||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import {
|
import {
|
||||||
@@ -15,7 +16,16 @@ import {
|
|||||||
SyncItem,
|
SyncItem,
|
||||||
SyncStreamDto,
|
SyncStreamDto,
|
||||||
} from 'src/dtos/sync.dto';
|
} from 'src/dtos/sync.dto';
|
||||||
import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum';
|
import {
|
||||||
|
AssetVisibility,
|
||||||
|
DatabaseAction,
|
||||||
|
EntityType,
|
||||||
|
JobName,
|
||||||
|
Permission,
|
||||||
|
QueueName,
|
||||||
|
SyncEntityType,
|
||||||
|
SyncRequestType,
|
||||||
|
} from 'src/enum';
|
||||||
import { SyncQueryOptions } from 'src/repositories/sync.repository';
|
import { SyncQueryOptions } from 'src/repositories/sync.repository';
|
||||||
import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table';
|
import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
@@ -32,6 +42,8 @@ type AssetLike = Omit<SyncAssetV1, 'checksum' | 'thumbhash'> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const COMPLETE_ID = 'complete';
|
const COMPLETE_ID = 'complete';
|
||||||
|
const MAX_DAYS = 30;
|
||||||
|
const MAX_DURATION = Duration.fromObject({ days: MAX_DAYS });
|
||||||
|
|
||||||
const mapSyncAssetV1 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV1 => ({
|
const mapSyncAssetV1 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV1 => ({
|
||||||
...data,
|
...data,
|
||||||
@@ -137,19 +149,24 @@ export class SyncService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isPendingSyncReset = await this.sessionRepository.isPendingSyncReset(session.id);
|
const isPendingSyncReset = await this.sessionRepository.isPendingSyncReset(session.id);
|
||||||
|
|
||||||
if (isPendingSyncReset) {
|
if (isPendingSyncReset) {
|
||||||
send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} });
|
send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} });
|
||||||
response.end();
|
response.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkpoints = await this.syncCheckpointRepository.getAll(session.id);
|
||||||
|
const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)]));
|
||||||
|
|
||||||
|
if (this.needsFullSync(checkpointMap)) {
|
||||||
|
send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} });
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { nowId } = await this.syncCheckpointRepository.getNow();
|
const { nowId } = await this.syncCheckpointRepository.getNow();
|
||||||
const options: SyncQueryOptions = { nowId, userId: auth.user.id };
|
const options: SyncQueryOptions = { nowId, userId: auth.user.id };
|
||||||
|
|
||||||
const checkpoints = await this.syncCheckpointRepository.getAll(session.id);
|
|
||||||
const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)]));
|
|
||||||
|
|
||||||
const handlers: Record<SyncRequestType, () => Promise<void>> = {
|
const handlers: Record<SyncRequestType, () => Promise<void>> = {
|
||||||
[SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(options, response, checkpointMap),
|
[SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(options, response, checkpointMap),
|
||||||
[SyncRequestType.UsersV1]: () => this.syncUsersV1(options, response, checkpointMap),
|
[SyncRequestType.UsersV1]: () => this.syncUsersV1(options, response, checkpointMap),
|
||||||
@@ -180,9 +197,41 @@ export class SyncService extends BaseService {
|
|||||||
await handler();
|
await handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send(response, { type: SyncEntityType.SyncCompleteV1, ids: [nowId], data: {} });
|
||||||
|
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnJob({ name: JobName.AuditTableCleanup, queue: QueueName.BackgroundTask })
|
||||||
|
async onAuditTableCleanup() {
|
||||||
|
const pruneThreshold = MAX_DAYS + 1;
|
||||||
|
|
||||||
|
await this.syncRepository.album.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.albumUser.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.albumToAsset.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.asset.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.assetFace.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.assetMetadata.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.memory.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.memoryToAsset.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.partner.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.person.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.stack.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.user.cleanupAuditTable(pruneThreshold);
|
||||||
|
await this.syncRepository.userMetadata.cleanupAuditTable(pruneThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
private needsFullSync(checkpointMap: CheckpointMap) {
|
||||||
|
const completeAck = checkpointMap[SyncEntityType.SyncCompleteV1];
|
||||||
|
if (!completeAck) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const milliseconds = Number.parseInt(completeAck.updateId.replaceAll('-', '').slice(0, 12), 16);
|
||||||
|
|
||||||
|
return DateTime.fromMillis(milliseconds) < DateTime.now().minus(MAX_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
private async syncAuthUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
|
private async syncAuthUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
|
||||||
const upsertType = SyncEntityType.AuthUserV1;
|
const upsertType = SyncEntityType.AuthUserV1;
|
||||||
const upserts = this.syncRepository.authUser.getUpserts({ ...options, ack: checkpointMap[upsertType] });
|
const upserts = this.syncRepository.authUser.getUpserts({ ...options, ack: checkpointMap[upsertType] });
|
||||||
@@ -719,13 +768,13 @@ export class SyncService extends BaseService {
|
|||||||
|
|
||||||
private async syncPeopleV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
|
private async syncPeopleV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
|
||||||
const deleteType = SyncEntityType.PersonDeleteV1;
|
const deleteType = SyncEntityType.PersonDeleteV1;
|
||||||
const deletes = this.syncRepository.people.getDeletes({ ...options, ack: checkpointMap[deleteType] });
|
const deletes = this.syncRepository.person.getDeletes({ ...options, ack: checkpointMap[deleteType] });
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: deleteType, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const upsertType = SyncEntityType.PersonV1;
|
const upsertType = SyncEntityType.PersonV1;
|
||||||
const upserts = this.syncRepository.people.getUpserts({ ...options, ack: checkpointMap[upsertType] });
|
const upserts = this.syncRepository.person.getUpserts({ ...options, ack: checkpointMap[upsertType] });
|
||||||
for await (const { updateId, ...data } of upserts) {
|
for await (const { updateId, ...data } of upserts) {
|
||||||
send(response, { type: upsertType, ids: [updateId], data });
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,9 @@ export interface QueueStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type JobItem =
|
export type JobItem =
|
||||||
|
// Audit
|
||||||
|
| { name: JobName.AuditTableCleanup; data?: IBaseJob }
|
||||||
|
|
||||||
// Backups
|
// Backups
|
||||||
| { name: JobName.DatabaseBackup; data?: IBaseJob }
|
| { name: JobName.DatabaseBackup; data?: IBaseJob }
|
||||||
|
|
||||||
|
|||||||
2
server/test/fixtures/asset.stub.ts
vendored
2
server/test/fixtures/asset.stub.ts
vendored
@@ -35,7 +35,7 @@ export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif
|
|||||||
primaryAssetId: assets[0].id,
|
primaryAssetId: assets[0].id,
|
||||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||||
updateId: 'uuid-v7',
|
updateId: expect.any(String),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
6
server/test/fixtures/tag.stub.ts
vendored
6
server/test/fixtures/tag.stub.ts
vendored
@@ -1,5 +1,6 @@
|
|||||||
import { Tag } from 'src/database';
|
import { Tag } from 'src/database';
|
||||||
import { TagResponseDto } from 'src/dtos/tag.dto';
|
import { TagResponseDto } from 'src/dtos/tag.dto';
|
||||||
|
import { newUuidV7 } from 'test/small.factory';
|
||||||
|
|
||||||
const parent = Object.freeze<Tag>({
|
const parent = Object.freeze<Tag>({
|
||||||
id: 'tag-parent',
|
id: 'tag-parent',
|
||||||
@@ -37,7 +38,10 @@ const color = {
|
|||||||
parentId: null,
|
parentId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const upsert = { userId: 'tag-user', updateId: 'uuid-v7' };
|
const upsert = {
|
||||||
|
userId: 'tag-user',
|
||||||
|
updateId: newUuidV7(),
|
||||||
|
};
|
||||||
|
|
||||||
export const tagStub = {
|
export const tagStub = {
|
||||||
tag,
|
tag,
|
||||||
|
|||||||
@@ -258,6 +258,12 @@ export class SyncTestContext extends MediumTestContext<SyncService> {
|
|||||||
return stream.getResponse();
|
return stream.getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async assertSyncIsComplete(auth: AuthDto, types: SyncRequestType[]) {
|
||||||
|
await expect(this.syncStream(auth, types)).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
async syncAckAll(auth: AuthDto, response: Array<{ type: string; ack: string }>) {
|
async syncAckAll(auth: AuthDto, response: Array<{ type: string; ack: string }>) {
|
||||||
const acks: Record<string, string> = {};
|
const acks: Record<string, string> = {};
|
||||||
const syncAcks: string[] = [];
|
const syncAcks: string[] = [];
|
||||||
|
|||||||
226
server/test/medium/specs/services/sync.service.spec.ts
Normal file
226
server/test/medium/specs/services/sync.service.spec.ts
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
import { Kysely } from 'kysely';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { AssetMetadataKey, UserMetadataKey } from 'src/enum';
|
||||||
|
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { SyncRepository } from 'src/repositories/sync.repository';
|
||||||
|
import { DB } from 'src/schema';
|
||||||
|
import { SyncService } from 'src/services/sync.service';
|
||||||
|
import { newMediumService } from 'test/medium.factory';
|
||||||
|
import { getKyselyDB } from 'test/utils';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
let defaultDatabase: Kysely<DB>;
|
||||||
|
|
||||||
|
const setup = (db?: Kysely<DB>) => {
|
||||||
|
return newMediumService(SyncService, {
|
||||||
|
database: db || defaultDatabase,
|
||||||
|
real: [DatabaseRepository, SyncRepository],
|
||||||
|
mock: [LoggingRepository],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
defaultDatabase = await getKyselyDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
const deletedLongAgo = DateTime.now().minus({ days: 35 }).toISO();
|
||||||
|
|
||||||
|
const assertTableCount = async <T extends keyof DB>(db: Kysely<DB>, t: T, count: number) => {
|
||||||
|
const { table } = db.dynamic;
|
||||||
|
const results = await db.selectFrom(table(t).as(t)).selectAll().execute();
|
||||||
|
expect(results).toHaveLength(count);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe(SyncService.name, () => {
|
||||||
|
describe('onAuditTableCleanup', () => {
|
||||||
|
it('should work', async () => {
|
||||||
|
const { sut } = setup();
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the album_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'album_audit';
|
||||||
|
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ albumId: v4(), userId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the album_asset_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'album_asset_audit';
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ albumId: album.id, assetId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the album_user_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'album_user_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ albumId: v4(), userId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the asset_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
await ctx.database
|
||||||
|
.insertInto('asset_audit')
|
||||||
|
.values({ assetId: v4(), ownerId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, 'asset_audit', 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, 'asset_audit', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the asset_face_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'asset_face_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ assetFaceId: v4(), assetId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the asset_metadata_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'asset_metadata_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ assetId: v4(), key: AssetMetadataKey.MobileApp, deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the memory_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'memory_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ memoryId: v4(), userId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the memory_asset_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'memory_asset_audit';
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ memoryId: memory.id, assetId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the partner_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'partner_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ sharedById: v4(), sharedWithId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the stack_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'stack_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ stackId: v4(), userId: v4(), deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the user_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'user_audit';
|
||||||
|
await ctx.database.insertInto(tableName).values({ userId: v4(), deletedAt: deletedLongAgo }).execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cleanup the user_metadata_audit table', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
const tableName = 'user_metadata_audit';
|
||||||
|
await ctx.database
|
||||||
|
.insertInto(tableName)
|
||||||
|
.values({ userId: v4(), key: UserMetadataKey.Onboarding, deletedAt: deletedLongAgo })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await assertTableCount(ctx.database, tableName, 1);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
await assertTableCount(ctx.database, tableName, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip recent records', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
const keep = {
|
||||||
|
id: v4(),
|
||||||
|
assetId: v4(),
|
||||||
|
ownerId: v4(),
|
||||||
|
deletedAt: DateTime.now().minus({ days: 25 }).toISO(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const remove = {
|
||||||
|
id: v4(),
|
||||||
|
assetId: v4(),
|
||||||
|
ownerId: v4(),
|
||||||
|
deletedAt: DateTime.now().minus({ days: 35 }).toISO(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await ctx.database.insertInto('asset_audit').values([keep, remove]).execute();
|
||||||
|
await assertTableCount(ctx.database, 'asset_audit', 2);
|
||||||
|
await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
const after = await ctx.database.selectFrom('asset_audit').select(['id']).execute();
|
||||||
|
expect(after).toHaveLength(1);
|
||||||
|
expect(after[0].id).toBe(keep.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -74,11 +74,11 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync album asset exif for own user', async () => {
|
it('should sync album asset exif for own user', async () => {
|
||||||
@@ -88,8 +88,15 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(2);
|
expect.objectContaining({ type: SyncEntityType.AssetExifV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncAckV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumAssetExifCreateV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync album asset exif for unrelated user', async () => {
|
it('should not sync album asset exif for unrelated user', async () => {
|
||||||
@@ -104,8 +111,11 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
const { session } = await ctx.newSession({ userId: user3.id });
|
const { session } = await ctx.newSession({ userId: user3.id });
|
||||||
const authUser3 = factory.auth({ session, user: user3 });
|
const authUser3 = factory.auth({ session, user: user3 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetExifV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill album assets exif when a user shares an album with you', async () => {
|
it('should backfill album assets exif when a user shares an album with you', async () => {
|
||||||
@@ -139,8 +149,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
|
|
||||||
// ack initial album asset exif sync
|
// ack initial album asset exif sync
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -174,11 +184,11 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(newResponse).toHaveLength(5);
|
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync old asset exif when a user adds them to an album they share you', async () => {
|
it('should sync old asset exif when a user adds them to an album they share you', async () => {
|
||||||
@@ -207,8 +217,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(firstAlbumResponse).toHaveLength(2);
|
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, firstAlbumResponse);
|
await ctx.syncAckAll(auth, firstAlbumResponse);
|
||||||
|
|
||||||
@@ -224,8 +234,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
type: SyncEntityType.AlbumAssetExifBackfillV1,
|
type: SyncEntityType.AlbumAssetExifBackfillV1,
|
||||||
},
|
},
|
||||||
backfillSyncAck,
|
backfillSyncAck,
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
|
|
||||||
// ack initial album asset sync
|
// ack initial album asset sync
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -244,11 +254,11 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(newResponse).toHaveLength(2);
|
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync asset exif updates for an album shared with you', async () => {
|
it('should sync asset exif updates for an album shared with you', async () => {
|
||||||
@@ -262,7 +272,6 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
updateSyncAck,
|
updateSyncAck,
|
||||||
{
|
{
|
||||||
@@ -272,6 +281,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -283,9 +293,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
city: 'New City',
|
city: 'New City',
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([
|
||||||
expect(updateResponse).toHaveLength(1);
|
|
||||||
expect(updateResponse).toEqual([
|
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({
|
data: expect.objectContaining({
|
||||||
@@ -294,6 +302,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifUpdateV1,
|
type: SyncEntityType.AlbumAssetExifUpdateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -330,8 +339,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifCreateV1,
|
type: SyncEntityType.AlbumAssetExifCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(response).toHaveLength(3);
|
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
@@ -342,8 +351,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
city: 'Delayed Exif',
|
city: 'Delayed Exif',
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([
|
||||||
expect(updateResponse).toEqual([
|
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({
|
data: expect.objectContaining({
|
||||||
@@ -352,7 +360,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetExifUpdateV1,
|
type: SyncEntityType.AlbumAssetExifUpdateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
expect(updateResponse).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
updateSyncAck,
|
updateSyncAck,
|
||||||
{
|
{
|
||||||
@@ -83,10 +82,11 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync album asset for own user', async () => {
|
it('should sync album asset for own user', async () => {
|
||||||
@@ -95,8 +95,15 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(2);
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncAckV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumAssetCreateV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync album asset for unrelated user', async () => {
|
it('should not sync album asset for unrelated user', async () => {
|
||||||
@@ -110,8 +117,11 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
const { session } = await ctx.newSession({ userId: user3.id });
|
const { session } = await ctx.newSession({ userId: user3.id });
|
||||||
const authUser3 = factory.auth({ session, user: user3 });
|
const authUser3 = factory.auth({ session, user: user3 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill album assets when a user shares an album with you', async () => {
|
it('should backfill album assets when a user shares an album with you', async () => {
|
||||||
@@ -133,7 +143,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
updateSyncAck,
|
updateSyncAck,
|
||||||
{
|
{
|
||||||
@@ -143,6 +152,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ack initial album asset sync
|
// ack initial album asset sync
|
||||||
@@ -176,10 +186,11 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync old assets when a user adds them to an album they share you', async () => {
|
it('should sync old assets when a user adds them to an album they share you', async () => {
|
||||||
@@ -196,7 +207,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
expect(firstAlbumResponse).toHaveLength(2);
|
|
||||||
expect(firstAlbumResponse).toEqual([
|
expect(firstAlbumResponse).toEqual([
|
||||||
updateSyncAck,
|
updateSyncAck,
|
||||||
{
|
{
|
||||||
@@ -206,6 +216,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, firstAlbumResponse);
|
await ctx.syncAckAll(auth, firstAlbumResponse);
|
||||||
@@ -213,7 +224,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
// expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -223,6 +233,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
type: SyncEntityType.AlbumAssetBackfillV1,
|
type: SyncEntityType.AlbumAssetBackfillV1,
|
||||||
},
|
},
|
||||||
backfillSyncAck,
|
backfillSyncAck,
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ack initial album asset sync
|
// ack initial album asset sync
|
||||||
@@ -242,10 +253,11 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync asset updates for an album shared with you', async () => {
|
it('should sync asset updates for an album shared with you', async () => {
|
||||||
@@ -258,7 +270,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
updateSyncAck,
|
updateSyncAck,
|
||||||
{
|
{
|
||||||
@@ -268,6 +279,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetCreateV1,
|
type: SyncEntityType.AlbumAssetCreateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -280,7 +292,6 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]);
|
||||||
expect(updateResponse).toHaveLength(1);
|
|
||||||
expect(updateResponse).toEqual([
|
expect(updateResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -290,6 +301,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumAssetUpdateV1,
|
type: SyncEntityType.AlbumAssetUpdateV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -38,10 +37,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync album to asset for owned albums', async () => {
|
it('should sync album to asset for owned albums', async () => {
|
||||||
@@ -51,7 +51,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -61,10 +60,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync the album to asset for shared albums', async () => {
|
it('should detect and sync the album to asset for shared albums', async () => {
|
||||||
@@ -76,7 +76,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -86,10 +85,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync album to asset for an album owned by another user', async () => {
|
it('should not sync album to asset for an album owned by another user', async () => {
|
||||||
@@ -98,7 +98,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
const { album } = await ctx.newAlbum({ ownerId: user2.id });
|
const { album } = await ctx.newAlbum({ ownerId: user2.id });
|
||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill album to assets when a user shares an album with you', async () => {
|
it('should backfill album to assets when a user shares an album with you', async () => {
|
||||||
@@ -114,7 +114,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id });
|
await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -124,6 +123,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ack initial album to asset sync
|
// ack initial album to asset sync
|
||||||
@@ -148,10 +148,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
data: {},
|
data: {},
|
||||||
type: SyncEntityType.SyncAckV1,
|
type: SyncEntityType.SyncAckV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted album to asset relation', async () => {
|
it('should detect and sync a deleted album to asset relation', async () => {
|
||||||
@@ -162,7 +163,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -172,6 +172,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -179,7 +180,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await wait(2);
|
await wait(2);
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -189,10 +189,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetDeleteV1,
|
type: SyncEntityType.AlbumToAssetDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted album to asset relation when an asset is deleted', async () => {
|
it('should detect and sync a deleted album to asset relation when an asset is deleted', async () => {
|
||||||
@@ -203,7 +204,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -213,6 +213,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -220,7 +221,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await wait(2);
|
await wait(2);
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -230,10 +230,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetDeleteV1,
|
type: SyncEntityType.AlbumToAssetDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a deleted album to asset relation when the album is deleted', async () => {
|
it('should not sync a deleted album to asset relation when the album is deleted', async () => {
|
||||||
@@ -244,7 +245,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -254,11 +254,12 @@ describe(SyncRequestType.AlbumToAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumToAssetV1,
|
type: SyncEntityType.AlbumToAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await albumRepo.delete(album.id);
|
await albumRepo.delete(album.id);
|
||||||
await wait(2);
|
await wait(2);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,7 +46,6 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor });
|
const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -56,10 +56,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an updated shared user', async () => {
|
it('should detect and sync an updated shared user', async () => {
|
||||||
@@ -71,11 +72,10 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
|
|
||||||
await albumUserRepo.update({ albumsId: album.id, usersId: user1.id }, { role: AlbumUserRole.Viewer });
|
await albumUserRepo.update({ albumsId: album.id, usersId: user1.id }, { role: AlbumUserRole.Viewer });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -86,10 +86,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted shared user', async () => {
|
it('should detect and sync a deleted shared user', async () => {
|
||||||
@@ -100,9 +101,8 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor });
|
const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
|
|
||||||
await albumUserRepo.delete({ albumsId: album.id, usersId: user1.id });
|
await albumUserRepo.delete({ albumsId: album.id, usersId: user1.id });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
@@ -115,10 +115,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserDeleteV1,
|
type: SyncEntityType.AlbumUserDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,7 +135,6 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -145,10 +145,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an updated shared user', async () => {
|
it('should detect and sync an updated shared user', async () => {
|
||||||
@@ -161,10 +162,14 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(2);
|
expect(response).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
|
|
||||||
await albumUserRepo.update({ albumsId: album.id, usersId: user.id }, { role: AlbumUserRole.Viewer });
|
await albumUserRepo.update({ albumsId: album.id, usersId: user.id }, { role: AlbumUserRole.Viewer });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
@@ -178,10 +183,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted shared user', async () => {
|
it('should detect and sync a deleted shared user', async () => {
|
||||||
@@ -194,10 +200,14 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(2);
|
expect(response).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
await albumUserRepo.delete({ albumsId: album.id, usersId: user.id });
|
await albumUserRepo.delete({ albumsId: album.id, usersId: user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
@@ -210,10 +220,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserDeleteV1,
|
type: SyncEntityType.AlbumUserDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill album users when a user shares an album with you', async () => {
|
it('should backfill album users when a user shares an album with you', async () => {
|
||||||
@@ -232,7 +243,6 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album1.id, userId: user2.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album1.id, userId: user2.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -243,6 +253,7 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ack initial user
|
// ack initial user
|
||||||
@@ -285,10 +296,11 @@ describe(SyncRequestType.AlbumUsersV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumUserV1,
|
type: SyncEntityType.AlbumUserV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -35,10 +34,11 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a new album', async () => {
|
it('should detect and sync a new album', async () => {
|
||||||
@@ -46,7 +46,6 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -55,10 +54,11 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an album delete', async () => {
|
it('should detect and sync an album delete', async () => {
|
||||||
@@ -67,7 +67,6 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
const { album } = await ctx.newAlbum({ ownerId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -76,12 +75,12 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await albumRepo.delete(album.id);
|
await albumRepo.delete(album.id);
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -90,10 +89,11 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AlbumDeleteV1,
|
type: SyncEntityType.AlbumDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('shared albums', () => {
|
describe('shared albums', () => {
|
||||||
@@ -104,17 +104,17 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: album.id }),
|
data: expect.objectContaining({ id: album.id }),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an album share (share before sync)', async () => {
|
it('should detect and sync an album share (share before sync)', async () => {
|
||||||
@@ -124,17 +124,17 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: album.id }),
|
data: expect.objectContaining({ id: album.id }),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an album share (share after sync)', async () => {
|
it('should detect and sync an album share (share after sync)', async () => {
|
||||||
@@ -150,23 +150,24 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
data: expect.objectContaining({ id: userAlbum.id }),
|
data: expect.objectContaining({ id: userAlbum.id }),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await ctx.newAlbumUser({ userId: auth.user.id, albumId: user2Album.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ userId: auth.user.id, albumId: user2Album.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: user2Album.id }),
|
data: expect.objectContaining({ id: user2Album.id }),
|
||||||
type: SyncEntityType.AlbumV1,
|
type: SyncEntityType.AlbumV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an album delete`', async () => {
|
it('should detect and sync an album delete`', async () => {
|
||||||
@@ -177,24 +178,27 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
expect(response).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
|
|
||||||
await albumRepo.delete(album.id);
|
await albumRepo.delete(album.id);
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: { albumId: album.id },
|
data: { albumId: album.id },
|
||||||
type: SyncEntityType.AlbumDeleteV1,
|
type: SyncEntityType.AlbumDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync an album unshare as an album delete', async () => {
|
it('should detect and sync an album unshare as an album delete', async () => {
|
||||||
@@ -205,10 +209,13 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
expect(response).toHaveLength(1);
|
expect(response).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AlbumV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
|
|
||||||
await albumUserRepo.delete({ albumsId: album.id, usersId: auth.user.id });
|
await albumUserRepo.delete({ albumsId: album.id, usersId: auth.user.id });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]);
|
||||||
@@ -218,10 +225,11 @@ describe(SyncRequestType.AlbumsV1, () => {
|
|||||||
data: { albumId: album.id },
|
data: { albumId: album.id },
|
||||||
type: SyncEntityType.AlbumDeleteV1,
|
type: SyncEntityType.AlbumDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ describe(SyncRequestType.AssetExifsV1, () => {
|
|||||||
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetExifsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -57,10 +56,11 @@ describe(SyncRequestType.AssetExifsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.AssetExifV1,
|
type: SyncEntityType.AssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only sync asset exif for own user', async () => {
|
it('should only sync asset exif for own user', async () => {
|
||||||
@@ -72,7 +72,10 @@ describe(SyncRequestType.AssetExifsV1, () => {
|
|||||||
const { session } = await ctx.newSession({ userId: user2.id });
|
const { session } = await ctx.newSession({ userId: user2.id });
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetExifsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetExifV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetExifsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ describe(SyncEntityType.AssetFaceV1, () => {
|
|||||||
const { assetFace } = await ctx.newAssetFace({ assetId: asset.id, personId: person.id });
|
const { assetFace } = await ctx.newAssetFace({ assetId: asset.id, personId: person.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -44,10 +43,11 @@ describe(SyncEntityType.AssetFaceV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: 'AssetFaceV1',
|
type: 'AssetFaceV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted asset face', async () => {
|
it('should detect and sync a deleted asset face', async () => {
|
||||||
@@ -58,7 +58,6 @@ describe(SyncEntityType.AssetFaceV1, () => {
|
|||||||
await personRepo.deleteAssetFace(assetFace.id);
|
await personRepo.deleteAssetFace(assetFace.id);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -67,10 +66,11 @@ describe(SyncEntityType.AssetFaceV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetFaceDeleteV1',
|
type: 'AssetFaceDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync an asset face or asset face delete for an unrelated user', async () => {
|
it('should not sync an asset face or asset face delete for an unrelated user', async () => {
|
||||||
@@ -82,11 +82,18 @@ describe(SyncEntityType.AssetFaceV1, () => {
|
|||||||
const { assetFace } = await ctx.newAssetFace({ assetId: asset.id });
|
const { assetFace } = await ctx.newAssetFace({ assetId: asset.id });
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toEqual([
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetFaceV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
|
|
||||||
await personRepo.deleteAssetFace(assetFace.id);
|
await personRepo.deleteAssetFace(assetFace.id);
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1);
|
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AssetFaceDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ describe(SyncEntityType.AssetMetadataV1, () => {
|
|||||||
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -37,10 +36,11 @@ describe(SyncEntityType.AssetMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetMetadataV1',
|
type: 'AssetMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetMetadataV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update asset metadata', async () => {
|
it('should update asset metadata', async () => {
|
||||||
@@ -51,7 +51,6 @@ describe(SyncEntityType.AssetMetadataV1, () => {
|
|||||||
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -62,6 +61,7 @@ describe(SyncEntityType.AssetMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetMetadataV1',
|
type: 'AssetMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -79,10 +79,11 @@ describe(SyncEntityType.AssetMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetMetadataV1',
|
type: 'AssetMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, updatedResponse);
|
await ctx.syncAckAll(auth, updatedResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetMetadataV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,7 +96,6 @@ describe(SyncEntityType.AssetMetadataDeleteV1, () => {
|
|||||||
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -106,6 +106,7 @@ describe(SyncEntityType.AssetMetadataDeleteV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetMetadataV1',
|
type: 'AssetMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -121,6 +122,7 @@ describe(SyncEntityType.AssetMetadataDeleteV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetMetadataDeleteV1',
|
type: 'AssetMetadataDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ describe(SyncEntityType.AssetV1, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -64,10 +63,11 @@ describe(SyncEntityType.AssetV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetV1',
|
type: 'AssetV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted asset', async () => {
|
it('should detect and sync a deleted asset', async () => {
|
||||||
@@ -77,7 +77,6 @@ describe(SyncEntityType.AssetV1, () => {
|
|||||||
await assetRepo.remove(asset);
|
await assetRepo.remove(asset);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -86,10 +85,11 @@ describe(SyncEntityType.AssetV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AssetDeleteV1',
|
type: 'AssetDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync an asset or asset delete for an unrelated user', async () => {
|
it('should not sync an asset or asset delete for an unrelated user', async () => {
|
||||||
@@ -100,11 +100,17 @@ describe(SyncEntityType.AssetV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toEqual([
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.AssetsV1])).toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
|
|
||||||
await assetRepo.remove(asset);
|
await assetRepo.remove(asset);
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toEqual([
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.AssetsV1])).toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
const { auth, user, ctx } = await setup(await getKyselyDB());
|
const { auth, user, ctx } = await setup(await getKyselyDB());
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -43,10 +42,11 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'AuthUserV1',
|
type: 'AuthUserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AuthUsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AuthUsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync a change and then another change to that same user', async () => {
|
it('should sync a change and then another change to that same user', async () => {
|
||||||
@@ -55,7 +55,6 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
const userRepo = ctx.get(UserRepository);
|
const userRepo = ctx.get(UserRepository);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -65,6 +64,7 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: 'AuthUserV1',
|
type: 'AuthUserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -72,7 +72,6 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
await userRepo.update(user.id, { isAdmin: true });
|
await userRepo.update(user.id, { isAdmin: true });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -82,6 +81,7 @@ describe(SyncEntityType.AuthUserV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: 'AuthUserV1',
|
type: 'AuthUserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
60
server/test/medium/specs/sync/sync-complete.spec.ts
Normal file
60
server/test/medium/specs/sync/sync-complete.spec.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Kysely } from 'kysely';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { SyncEntityType, SyncRequestType } from 'src/enum';
|
||||||
|
import { SyncCheckpointRepository } from 'src/repositories/sync-checkpoint.repository';
|
||||||
|
import { DB } from 'src/schema';
|
||||||
|
import { toAck } from 'src/utils/sync';
|
||||||
|
import { SyncTestContext } from 'test/medium.factory';
|
||||||
|
import { getKyselyDB } from 'test/utils';
|
||||||
|
import { v7 } from 'uuid';
|
||||||
|
|
||||||
|
let defaultDatabase: Kysely<DB>;
|
||||||
|
|
||||||
|
const setup = async (db?: Kysely<DB>) => {
|
||||||
|
const ctx = new SyncTestContext(db || defaultDatabase);
|
||||||
|
const { auth, user, session } = await ctx.newSyncAuthUser();
|
||||||
|
return { auth, user, session, ctx };
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
defaultDatabase = await getKyselyDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(SyncEntityType.SyncCompleteV1, () => {
|
||||||
|
it('should work', async () => {
|
||||||
|
const { auth, ctx } = await setup();
|
||||||
|
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect an old checkpoint and send back a reset', async () => {
|
||||||
|
const { auth, session, ctx } = await setup();
|
||||||
|
const updateId = v7({ msecs: DateTime.now().minus({ days: 60 }).toMillis() });
|
||||||
|
|
||||||
|
await ctx.get(SyncCheckpointRepository).upsertAll([
|
||||||
|
{
|
||||||
|
type: SyncEntityType.SyncCompleteV1,
|
||||||
|
sessionId: session.id,
|
||||||
|
ack: toAck({ type: SyncEntityType.SyncCompleteV1, updateId }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
||||||
|
expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not send back a reset if the checkpoint is recent', async () => {
|
||||||
|
const { auth, session, ctx } = await setup();
|
||||||
|
const updateId = v7({ msecs: DateTime.now().minus({ days: 7 }).toMillis() });
|
||||||
|
|
||||||
|
await ctx.get(SyncCheckpointRepository).upsertAll([
|
||||||
|
{
|
||||||
|
type: SyncEntityType.SyncCompleteV1,
|
||||||
|
sessionId: session.id,
|
||||||
|
ack: toAck({ type: SyncEntityType.SyncCompleteV1, updateId }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -25,7 +25,6 @@ describe(SyncEntityType.MemoryToAssetV1, () => {
|
|||||||
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -35,10 +34,11 @@ describe(SyncEntityType.MemoryToAssetV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'MemoryToAssetV1',
|
type: 'MemoryToAssetV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted memory to asset relation', async () => {
|
it('should detect and sync a deleted memory to asset relation', async () => {
|
||||||
@@ -50,7 +50,6 @@ describe(SyncEntityType.MemoryToAssetV1, () => {
|
|||||||
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -60,10 +59,11 @@ describe(SyncEntityType.MemoryToAssetV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'MemoryToAssetDeleteV1',
|
type: 'MemoryToAssetDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a memory to asset relation or delete for an unrelated user', async () => {
|
it('should not sync a memory to asset relation or delete for an unrelated user', async () => {
|
||||||
@@ -74,11 +74,18 @@ describe(SyncEntityType.MemoryToAssetV1, () => {
|
|||||||
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
||||||
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
||||||
|
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toEqual([
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1);
|
expect.objectContaining({ type: SyncEntityType.MemoryToAssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
|
|
||||||
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0);
|
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.MemoryToAssetDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
const { memory } = await ctx.newMemory({ ownerId: user1.id });
|
const { memory } = await ctx.newMemory({ ownerId: user1.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -43,10 +42,11 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'MemoryV1',
|
type: 'MemoryV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted memory', async () => {
|
it('should detect and sync a deleted memory', async () => {
|
||||||
@@ -56,7 +56,6 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
await memoryRepo.delete(memory.id);
|
await memoryRepo.delete(memory.id);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -65,10 +64,11 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'MemoryDeleteV1',
|
type: 'MemoryDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync a memory and then an update to that same memory', async () => {
|
it('should sync a memory and then an update to that same memory', async () => {
|
||||||
@@ -77,29 +77,29 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: memory.id }),
|
data: expect.objectContaining({ id: memory.id }),
|
||||||
type: 'MemoryV1',
|
type: 'MemoryV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await memoryRepo.update(memory.id, { seenAt: new Date() });
|
await memoryRepo.update(memory.id, { seenAt: new Date() });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: memory.id }),
|
data: expect.objectContaining({ id: memory.id }),
|
||||||
type: 'MemoryV1',
|
type: 'MemoryV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a memory or a memory delete for an unrelated user', async () => {
|
it('should not sync a memory or a memory delete for an unrelated user', async () => {
|
||||||
@@ -108,8 +108,8 @@ describe(SyncEntityType.MemoryV1, () => {
|
|||||||
const { user: user2 } = await ctx.newUser();
|
const { user: user2 } = await ctx.newUser();
|
||||||
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]);
|
||||||
await memoryRepo.delete(memory.id);
|
await memoryRepo.delete(memory.id);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -59,10 +58,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.PartnerAssetExifV1,
|
type: SyncEntityType.PartnerAssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync partner asset exif for own user', async () => {
|
it('should not sync partner asset exif for own user', async () => {
|
||||||
@@ -72,8 +72,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: auth.user.id });
|
const { asset } = await ctx.newAsset({ ownerId: auth.user.id });
|
||||||
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetExifV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync partner asset exif for unrelated user', async () => {
|
it('should not sync partner asset exif for unrelated user', async () => {
|
||||||
@@ -86,8 +89,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
const { session } = await ctx.newSession({ userId: user3.id });
|
const { session } = await ctx.newSession({ userId: user3.id });
|
||||||
const authUser3 = factory.auth({ session, user: user3 });
|
const authUser3 = factory.auth({ session, user: user3 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetExifV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill partner asset exif when a partner shared their library with you', async () => {
|
it('should backfill partner asset exif when a partner shared their library with you', async () => {
|
||||||
@@ -102,7 +108,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual(
|
expect(response).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{
|
{
|
||||||
@@ -112,6 +117,7 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetExifV1,
|
type: SyncEntityType.PartnerAssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -119,7 +125,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(newResponse).toHaveLength(2);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -133,10 +138,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
data: {},
|
data: {},
|
||||||
type: SyncEntityType.SyncAckV1,
|
type: SyncEntityType.SyncAckV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle partners with users ids lower than a uuidv7', async () => {
|
it('should handle partners with users ids lower than a uuidv7', async () => {
|
||||||
@@ -151,7 +157,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -160,15 +165,15 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetExifV1,
|
type: SyncEntityType.PartnerAssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
// This checks that our ack upsert is correct
|
// This checks that our ack upsert is correct
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(newResponse).toHaveLength(2);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)),
|
ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)),
|
||||||
@@ -182,10 +187,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
data: {},
|
data: {},
|
||||||
type: SyncEntityType.SyncAckV1,
|
type: SyncEntityType.SyncAckV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => {
|
it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => {
|
||||||
@@ -203,7 +209,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -212,13 +217,13 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetExifV1,
|
type: SyncEntityType.PartnerAssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
expect(newResponse).toHaveLength(3);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)),
|
ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)),
|
||||||
@@ -239,9 +244,10 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetExifV1,
|
type: SyncEntityType.PartnerAssetExifV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -70,10 +69,11 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.PartnerAssetV1,
|
type: SyncEntityType.PartnerAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted partner asset', async () => {
|
it('should detect and sync a deleted partner asset', async () => {
|
||||||
@@ -86,7 +86,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
await assetRepo.remove(asset);
|
await assetRepo.remove(asset);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -95,10 +94,11 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.PartnerAssetDeleteV1,
|
type: SyncEntityType.PartnerAssetDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a deleted partner asset due to a user delete', async () => {
|
it('should not sync a deleted partner asset due to a user delete', async () => {
|
||||||
@@ -109,7 +109,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
await ctx.newAsset({ ownerId: user2.id });
|
await ctx.newAsset({ ownerId: user2.id });
|
||||||
await userRepo.delete({ id: user2.id }, true);
|
await userRepo.delete({ id: user2.id }, true);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a deleted partner asset due to a partner delete (unshare)', async () => {
|
it('should not sync a deleted partner asset due to a partner delete (unshare)', async () => {
|
||||||
@@ -119,9 +119,12 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
const { user: user2 } = await ctx.newUser();
|
const { user: user2 } = await ctx.newUser();
|
||||||
await ctx.newAsset({ ownerId: user2.id });
|
await ctx.newAsset({ ownerId: user2.id });
|
||||||
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.PartnerAssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
await partnerRepo.remove(partner);
|
await partnerRepo.remove(partner);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync an asset or asset delete for own user', async () => {
|
it('should not sync an asset or asset delete for own user', async () => {
|
||||||
@@ -132,13 +135,19 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: auth.user.id });
|
const { asset } = await ctx.newAsset({ ownerId: auth.user.id });
|
||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
|
|
||||||
await assetRepo.remove(asset);
|
await assetRepo.remove(asset);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync an asset or asset delete for unrelated user', async () => {
|
it('should not sync an asset or asset delete for unrelated user', async () => {
|
||||||
@@ -150,13 +159,19 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
|
|
||||||
await assetRepo.remove(asset);
|
await assetRepo.remove(asset);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill partner assets when a partner shared their library with you', async () => {
|
it('should backfill partner assets when a partner shared their library with you', async () => {
|
||||||
@@ -170,7 +185,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -179,13 +193,13 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetV1,
|
type: SyncEntityType.PartnerAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(newResponse).toHaveLength(2);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -199,10 +213,11 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
data: {},
|
data: {},
|
||||||
type: SyncEntityType.SyncAckV1,
|
type: SyncEntityType.SyncAckV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => {
|
it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => {
|
||||||
@@ -218,7 +233,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -227,12 +241,12 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetV1,
|
type: SyncEntityType.PartnerAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
expect(newResponse).toHaveLength(3);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -253,9 +267,10 @@ describe(SyncRequestType.PartnerAssetsV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerAssetV1,
|
type: SyncEntityType.PartnerAssetV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -42,10 +41,11 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.PartnerStackV1,
|
type: SyncEntityType.PartnerStackV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted partner stack', async () => {
|
it('should detect and sync a deleted partner stack', async () => {
|
||||||
@@ -58,7 +58,6 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
await stackRepo.delete(stack.id);
|
await stackRepo.delete(stack.id);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining('PartnerStackDeleteV1'),
|
ack: expect.stringContaining('PartnerStackDeleteV1'),
|
||||||
@@ -67,10 +66,11 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
},
|
},
|
||||||
type: SyncEntityType.PartnerStackDeleteV1,
|
type: SyncEntityType.PartnerStackDeleteV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a deleted partner stack due to a user delete', async () => {
|
it('should not sync a deleted partner stack due to a user delete', async () => {
|
||||||
@@ -81,7 +81,7 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
await userRepo.delete({ id: user2.id }, true);
|
await userRepo.delete({ id: user2.id }, true);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a deleted partner stack due to a partner delete (unshare)', async () => {
|
it('should not sync a deleted partner stack due to a partner delete (unshare)', async () => {
|
||||||
@@ -91,9 +91,12 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.PartnerStackV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
await partnerRepo.remove(partner);
|
await partnerRepo.remove(partner);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a stack or stack delete for own user', async () => {
|
it('should not sync a stack or stack delete for own user', async () => {
|
||||||
@@ -103,11 +106,17 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset.id]);
|
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset.id]);
|
||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.StackV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
await stackRepo.delete(stack.id);
|
await stackRepo.delete(stack.id);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.StackDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a stack or stack delete for unrelated user', async () => {
|
it('should not sync a stack or stack delete for unrelated user', async () => {
|
||||||
@@ -119,13 +128,19 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.StackV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
|
||||||
await stackRepo.delete(stack.id);
|
await stackRepo.delete(stack.id);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toEqual([
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.StackDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should backfill partner stacks when a partner shared their library with you', async () => {
|
it('should backfill partner stacks when a partner shared their library with you', async () => {
|
||||||
@@ -140,7 +155,6 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining('PartnerStackV1'),
|
ack: expect.stringContaining('PartnerStackV1'),
|
||||||
@@ -149,12 +163,12 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerStackV1,
|
type: SyncEntityType.PartnerStackV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: user.id });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(newResponse).toHaveLength(2);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1),
|
ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1),
|
||||||
@@ -168,10 +182,11 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
data: {},
|
data: {},
|
||||||
type: SyncEntityType.SyncAckV1,
|
type: SyncEntityType.SyncAckV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only backfill partner stacks created prior to the current partner stack checkpoint', async () => {
|
it('should only backfill partner stacks created prior to the current partner stack checkpoint', async () => {
|
||||||
@@ -189,7 +204,6 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining(SyncEntityType.PartnerStackV1),
|
ack: expect.stringContaining(SyncEntityType.PartnerStackV1),
|
||||||
@@ -198,12 +212,12 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerStackV1,
|
type: SyncEntityType.PartnerStackV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
expect(newResponse).toHaveLength(3);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -224,9 +238,10 @@ describe(SyncRequestType.PartnerStacksV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: SyncEntityType.PartnerStackV1,
|
type: SyncEntityType.PartnerStackV1,
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id });
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -37,10 +36,11 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'PartnerV1',
|
type: 'PartnerV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted partner', async () => {
|
it('should detect and sync a deleted partner', async () => {
|
||||||
@@ -53,22 +53,20 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
await partnerRepo.remove(partner);
|
await partnerRepo.remove(partner);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
||||||
expect(response).toHaveLength(1);
|
expect(response).toEqual([
|
||||||
expect(response).toEqual(
|
{
|
||||||
expect.arrayContaining([
|
ack: expect.any(String),
|
||||||
{
|
data: {
|
||||||
ack: expect.any(String),
|
sharedById: partner.sharedById,
|
||||||
data: {
|
sharedWithId: partner.sharedWithId,
|
||||||
sharedById: partner.sharedById,
|
|
||||||
sharedWithId: partner.sharedWithId,
|
|
||||||
},
|
|
||||||
type: 'PartnerDeleteV1',
|
|
||||||
},
|
},
|
||||||
]),
|
type: 'PartnerDeleteV1',
|
||||||
);
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a partner share both to and from another user', async () => {
|
it('should detect and sync a partner share both to and from another user', async () => {
|
||||||
@@ -79,32 +77,30 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
const { partner: partner2 } = await ctx.newPartner({ sharedById: user1.id, sharedWithId: user2.id });
|
const { partner: partner2 } = await ctx.newPartner({ sharedById: user1.id, sharedWithId: user2.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
||||||
expect(response).toHaveLength(2);
|
expect(response).toEqual([
|
||||||
expect(response).toEqual(
|
{
|
||||||
expect.arrayContaining([
|
ack: expect.any(String),
|
||||||
{
|
data: {
|
||||||
ack: expect.any(String),
|
inTimeline: partner1.inTimeline,
|
||||||
data: {
|
sharedById: partner1.sharedById,
|
||||||
inTimeline: partner1.inTimeline,
|
sharedWithId: partner1.sharedWithId,
|
||||||
sharedById: partner1.sharedById,
|
|
||||||
sharedWithId: partner1.sharedWithId,
|
|
||||||
},
|
|
||||||
type: 'PartnerV1',
|
|
||||||
},
|
},
|
||||||
{
|
type: 'PartnerV1',
|
||||||
ack: expect.any(String),
|
},
|
||||||
data: {
|
{
|
||||||
inTimeline: partner2.inTimeline,
|
ack: expect.any(String),
|
||||||
sharedById: partner2.sharedById,
|
data: {
|
||||||
sharedWithId: partner2.sharedWithId,
|
inTimeline: partner2.inTimeline,
|
||||||
},
|
sharedById: partner2.sharedById,
|
||||||
type: 'PartnerV1',
|
sharedWithId: partner2.sharedWithId,
|
||||||
},
|
},
|
||||||
]),
|
type: 'PartnerV1',
|
||||||
);
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync a partner and then an update to that same partner', async () => {
|
it('should sync a partner and then an update to that same partner', async () => {
|
||||||
@@ -116,7 +112,6 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id });
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -127,6 +122,7 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'PartnerV1',
|
type: 'PartnerV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -137,7 +133,6 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -148,10 +143,11 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'PartnerV1',
|
type: 'PartnerV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a partner or partner delete for an unrelated user', async () => {
|
it('should not sync a partner or partner delete for an unrelated user', async () => {
|
||||||
@@ -163,9 +159,9 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
const { user: user3 } = await ctx.newUser();
|
const { user: user3 } = await ctx.newUser();
|
||||||
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user3.id });
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user3.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
await partnerRepo.remove(partner);
|
await partnerRepo.remove(partner);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a partner delete after a user is deleted', async () => {
|
it('should not sync a partner delete after a user is deleted', async () => {
|
||||||
@@ -177,6 +173,6 @@ describe(SyncEntityType.PartnerV1, () => {
|
|||||||
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
await userRepo.delete({ id: user2.id }, true);
|
await userRepo.delete({ id: user2.id }, true);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ describe(SyncEntityType.PersonV1, () => {
|
|||||||
const { person } = await ctx.newPerson({ ownerId: auth.user.id });
|
const { person } = await ctx.newPerson({ ownerId: auth.user.id });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -40,10 +39,11 @@ describe(SyncEntityType.PersonV1, () => {
|
|||||||
}),
|
}),
|
||||||
type: 'PersonV1',
|
type: 'PersonV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PeopleV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted person', async () => {
|
it('should detect and sync a deleted person', async () => {
|
||||||
@@ -53,7 +53,6 @@ describe(SyncEntityType.PersonV1, () => {
|
|||||||
await personRepo.delete([person.id]);
|
await personRepo.delete([person.id]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -62,10 +61,11 @@ describe(SyncEntityType.PersonV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'PersonDeleteV1',
|
type: 'PersonDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.PeopleV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a person or person delete for an unrelated user', async () => {
|
it('should not sync a person or person delete for an unrelated user', async () => {
|
||||||
@@ -76,11 +76,18 @@ describe(SyncEntityType.PersonV1, () => {
|
|||||||
const { person } = await ctx.newPerson({ ownerId: user2.id });
|
const { person } = await ctx.newPerson({ ownerId: user2.id });
|
||||||
const auth2 = factory.auth({ session, user: user2 });
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toHaveLength(1);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toEqual([
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.PeopleV1])).toHaveLength(0);
|
expect.objectContaining({ type: SyncEntityType.PersonV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]);
|
||||||
|
|
||||||
await personRepo.delete([person.id]);
|
await personRepo.delete([person.id]);
|
||||||
expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toHaveLength(1);
|
|
||||||
expect(await ctx.syncStream(auth, [SyncRequestType.PeopleV1])).toHaveLength(0);
|
expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.PersonDeleteV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ describe(SyncEntityType.SyncResetV1, () => {
|
|||||||
it('should work', async () => {
|
it('should work', async () => {
|
||||||
const { auth, ctx } = await setup();
|
const { auth, ctx } = await setup();
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]);
|
||||||
expect(response).toEqual([]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect a pending sync reset', async () => {
|
it('should detect a pending sync reset', async () => {
|
||||||
@@ -41,7 +40,10 @@ describe(SyncEntityType.SyncResetV1, () => {
|
|||||||
|
|
||||||
await ctx.newAsset({ ownerId: user.id });
|
await ctx.newAsset({ ownerId: user.id });
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1);
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
|
|
||||||
await ctx.get(SessionRepository).update(auth.session!.id, {
|
await ctx.get(SessionRepository).update(auth.session!.id, {
|
||||||
isPendingSyncReset: true,
|
isPendingSyncReset: true,
|
||||||
@@ -62,9 +64,8 @@ describe(SyncEntityType.SyncResetV1, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1], true)).resolves.toEqual([
|
await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1], true)).resolves.toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
type: SyncEntityType.AssetV1,
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -86,9 +87,8 @@ describe(SyncEntityType.SyncResetV1, () => {
|
|||||||
|
|
||||||
const postResetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
const postResetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]);
|
||||||
expect(postResetResponse).toEqual([
|
expect(postResetResponse).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({ type: SyncEntityType.AssetV1 }),
|
||||||
type: SyncEntityType.AssetV1,
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ describe(SyncEntityType.StackV1, () => {
|
|||||||
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]);
|
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining('StackV1'),
|
ack: expect.stringContaining('StackV1'),
|
||||||
@@ -38,10 +37,11 @@ describe(SyncEntityType.StackV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'StackV1',
|
type: 'StackV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted stack', async () => {
|
it('should detect and sync a deleted stack', async () => {
|
||||||
@@ -53,17 +53,17 @@ describe(SyncEntityType.StackV1, () => {
|
|||||||
await stackRepo.delete(stack.id);
|
await stackRepo.delete(stack.id);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining('StackDeleteV1'),
|
ack: expect.stringContaining('StackDeleteV1'),
|
||||||
data: { stackId: stack.id },
|
data: { stackId: stack.id },
|
||||||
type: 'StackDeleteV1',
|
type: 'StackDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync a stack and then an update to that same stack', async () => {
|
it('should sync a stack and then an update to that same stack', async () => {
|
||||||
@@ -74,22 +74,29 @@ describe(SyncEntityType.StackV1, () => {
|
|||||||
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]);
|
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
||||||
expect(response).toHaveLength(1);
|
expect(response).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.StackV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
await stackRepo.update(stack.id, { primaryAssetId: asset2.id });
|
await stackRepo.update(stack.id, { primaryAssetId: asset2.id });
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.StacksV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
expect(newResponse).toEqual([
|
||||||
|
expect.objectContaining({ type: SyncEntityType.StackV1 }),
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
|
]);
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.stringContaining('StackV1'),
|
ack: expect.stringContaining('StackV1'),
|
||||||
data: expect.objectContaining({ id: stack.id, primaryAssetId: asset2.id }),
|
data: expect.objectContaining({ id: stack.id, primaryAssetId: asset2.id }),
|
||||||
type: 'StackV1',
|
type: 'StackV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, newResponse);
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not sync a stack or stack delete for an unrelated user', async () => {
|
it('should not sync a stack or stack delete for an unrelated user', async () => {
|
||||||
@@ -100,8 +107,8 @@ describe(SyncEntityType.StackV1, () => {
|
|||||||
const { asset: asset2 } = await ctx.newAsset({ ownerId: user2.id });
|
const { asset: asset2 } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset1.id, asset2.id]);
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset1.id, asset2.id]);
|
||||||
|
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]);
|
||||||
await stackRepo.delete(stack.id);
|
await stackRepo.delete(stack.id);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ describe(SyncEntityType.UserMetadataV1, () => {
|
|||||||
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -36,10 +35,11 @@ describe(SyncEntityType.UserMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserMetadataV1',
|
type: 'UserMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.UserMetadataV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update user metadata', async () => {
|
it('should update user metadata', async () => {
|
||||||
@@ -49,7 +49,6 @@ describe(SyncEntityType.UserMetadataV1, () => {
|
|||||||
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -60,6 +59,7 @@ describe(SyncEntityType.UserMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserMetadataV1',
|
type: 'UserMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -77,10 +77,11 @@ describe(SyncEntityType.UserMetadataV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserMetadataV1',
|
type: 'UserMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, updatedResponse);
|
await ctx.syncAckAll(auth, updatedResponse);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.UserMetadataV1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,7 +93,6 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => {
|
|||||||
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } });
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -103,6 +103,7 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserMetadataV1',
|
type: 'UserMetadataV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -118,6 +119,7 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserMetadataDeleteV1',
|
type: 'UserMetadataDeleteV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -43,10 +42,11 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
},
|
},
|
||||||
type: 'UserV1',
|
type: 'UserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a soft deleted user', async () => {
|
it('should detect and sync a soft deleted user', async () => {
|
||||||
@@ -56,7 +56,6 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
||||||
|
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual(
|
expect(response).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{
|
{
|
||||||
@@ -69,11 +68,12 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
data: expect.objectContaining({ id: deleted.id }),
|
data: expect.objectContaining({ id: deleted.id }),
|
||||||
type: 'UserV1',
|
type: 'UserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect and sync a deleted user', async () => {
|
it('should detect and sync a deleted user', async () => {
|
||||||
@@ -85,7 +85,6 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
await userRepo.delete({ id: user.id }, true);
|
await userRepo.delete({ id: user.id }, true);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
||||||
expect(response).toHaveLength(2);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
@@ -99,10 +98,11 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
data: expect.objectContaining({ id: authUser.id }),
|
data: expect.objectContaining({ id: authUser.id }),
|
||||||
type: 'UserV1',
|
type: 'UserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]);
|
await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sync a user and then an update to that same user', async () => {
|
it('should sync a user and then an update to that same user', async () => {
|
||||||
@@ -111,13 +111,13 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
const userRepo = ctx.get(UserRepository);
|
const userRepo = ctx.get(UserRepository);
|
||||||
|
|
||||||
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
||||||
expect(response).toHaveLength(1);
|
|
||||||
expect(response).toEqual([
|
expect(response).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: user.id }),
|
data: expect.objectContaining({ id: user.id }),
|
||||||
type: 'UserV1',
|
type: 'UserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await ctx.syncAckAll(auth, response);
|
await ctx.syncAckAll(auth, response);
|
||||||
@@ -125,13 +125,13 @@ describe(SyncEntityType.UserV1, () => {
|
|||||||
const updated = await userRepo.update(auth.user.id, { name: 'new name' });
|
const updated = await userRepo.update(auth.user.id, { name: 'new name' });
|
||||||
|
|
||||||
const newResponse = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.UsersV1]);
|
||||||
expect(newResponse).toHaveLength(1);
|
|
||||||
expect(newResponse).toEqual([
|
expect(newResponse).toEqual([
|
||||||
{
|
{
|
||||||
ack: expect.any(String),
|
ack: expect.any(String),
|
||||||
data: expect.objectContaining({ id: user.id, name: updated.name }),
|
data: expect.objectContaining({ id: user.id, name: updated.name }),
|
||||||
type: 'UserV1',
|
type: 'UserV1',
|
||||||
},
|
},
|
||||||
|
expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { randomUUID } from 'node:crypto';
|
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
ApiKey,
|
ApiKey,
|
||||||
@@ -17,14 +16,15 @@ import { MapAsset } from 'src/dtos/asset-response.dto';
|
|||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AssetStatus, AssetType, AssetVisibility, MemoryType, Permission, UserMetadataKey, UserStatus } from 'src/enum';
|
import { AssetStatus, AssetType, AssetVisibility, MemoryType, Permission, UserMetadataKey, UserStatus } from 'src/enum';
|
||||||
import { OnThisDayData, UserMetadataItem } from 'src/types';
|
import { OnThisDayData, UserMetadataItem } from 'src/types';
|
||||||
|
import { v4, v7 } from 'uuid';
|
||||||
|
|
||||||
export const newUuid = () => randomUUID() as string;
|
export const newUuid = () => v4();
|
||||||
export const newUuids = () =>
|
export const newUuids = () =>
|
||||||
Array.from({ length: 100 })
|
Array.from({ length: 100 })
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map(() => newUuid());
|
.map(() => newUuid());
|
||||||
export const newDate = () => new Date();
|
export const newDate = () => new Date();
|
||||||
export const newUuidV7 = () => 'uuid-v7';
|
export const newUuidV7 = () => v7();
|
||||||
export const newSha1 = () => Buffer.from('this is a fake hash');
|
export const newSha1 = () => Buffer.from('this is a fake hash');
|
||||||
export const newEmbedding = () => {
|
export const newEmbedding = () => {
|
||||||
const embedding = Array.from({ length: 512 })
|
const embedding = Array.from({ length: 512 })
|
||||||
|
|||||||
Reference in New Issue
Block a user