diff --git a/server/src/migrations/1741027685381-ResetMemories.ts b/server/src/migrations/1741027685381-ResetMemories.ts new file mode 100644 index 0000000000..6a80372219 --- /dev/null +++ b/server/src/migrations/1741027685381-ResetMemories.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ResetMemories1741027685381 implements MigrationInterface { + name = 'ResetMemories1741027685381'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DELETE FROM "memories"`); + await queryRunner.query(`DELETE FROM "system_metadata" WHERE "key" = 'memories-state'`); + } + + public async down(): Promise { + // nothing to do + } +} diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index c5240b82c1..ee28a20d4d 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -25,11 +25,24 @@ export class VersionService extends BaseService { await this.handleVersionCheck(); await this.databaseRepository.withLock(DatabaseLock.VersionHistory, async () => { - const latest = await this.versionRepository.getLatest(); + const previous = await this.versionRepository.getLatest(); const current = serverVersion.toString(); - if (!latest || latest.version !== current) { - this.logger.log(`Version has changed, adding ${current} to history`); + + if (!previous) { await this.versionRepository.create({ version: current }); + return; + } + + if (previous.version !== current) { + const previousVersion = new SemVer(previous.version); + + this.logger.log(`Adding ${current} to upgrade history`); + await this.versionRepository.create({ version: current }); + + const needsNewMemories = semver.lt(previousVersion, '1.129.0'); + if (needsNewMemories) { + await this.jobRepository.queue({ name: JobName.MEMORIES_CREATE }); + } } }); } diff --git a/server/test/factory.ts b/server/test/factory.ts index a682ad48f2..0b96b74e8e 100644 --- a/server/test/factory.ts +++ b/server/test/factory.ts @@ -10,6 +10,7 @@ import { PartnerRepository } from 'src/repositories/partner.repository'; import { SessionRepository } from 'src/repositories/session.repository'; import { SyncRepository } from 'src/repositories/sync.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; class CustomWritable extends Writable { private data = ''; @@ -162,6 +163,7 @@ export class TestContext { sessionRepository: SessionRepository; syncRepository: SyncRepository; partnerRepository: PartnerRepository; + versionHistoryRepository: VersionHistoryRepository; private constructor(private db: Kysely) { this.userRepository = new UserRepository(this.db); @@ -170,6 +172,7 @@ export class TestContext { this.sessionRepository = new SessionRepository(this.db); this.syncRepository = new SyncRepository(this.db); this.partnerRepository = new PartnerRepository(this.db); + this.versionHistoryRepository = new VersionHistoryRepository(this.db); } static from(db: Kysely) { diff --git a/server/test/medium/specs/version.service.spec.ts b/server/test/medium/specs/version.service.spec.ts new file mode 100644 index 0000000000..dd157b6fa7 --- /dev/null +++ b/server/test/medium/specs/version.service.spec.ts @@ -0,0 +1,56 @@ +import { serverVersion } from 'src/constants'; +import { JobName } from 'src/enum'; +import { VersionService } from 'src/services/version.service'; +import { TestContext } from 'test/factory'; +import { getKyselyDB, newTestService } from 'test/utils'; + +const setup = async () => { + const db = await getKyselyDB(); + const context = await TestContext.from(db).create(); + const { sut, mocks } = newTestService(VersionService, context); + + return { + context, + sut, + jobMock: mocks.job, + }; +}; + +describe(VersionService.name, () => { + describe.concurrent('onBootstrap', () => { + it('record the current version on startup', async () => { + const { context, sut } = await setup(); + + const itemsBefore = await context.versionHistoryRepository.getAll(); + expect(itemsBefore).toHaveLength(0); + + await sut.onBootstrap(); + + const itemsAfter = await context.versionHistoryRepository.getAll(); + expect(itemsAfter).toHaveLength(1); + expect(itemsAfter[0]).toEqual({ + createdAt: expect.any(Date), + id: expect.any(String), + version: serverVersion.toString(), + }); + }); + + it('should queue memory creation when upgrading from 1.128.0', async () => { + const { context, jobMock, sut } = await setup(); + + await context.versionHistoryRepository.create({ version: 'v1.128.0' }); + await sut.onBootstrap(); + + expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.MEMORIES_CREATE }); + }); + + it('should not queue memory creation when upgrading from 1.129.0', async () => { + const { context, jobMock, sut } = await setup(); + + await context.versionHistoryRepository.create({ version: 'v1.129.0' }); + await sut.onBootstrap(); + + expect(jobMock.queue).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/server/test/utils.ts b/server/test/utils.ts index 8b3798b8b1..36f6c9f738 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -97,6 +97,7 @@ type Overrides = { metadataRepository?: MetadataRepository; syncRepository?: SyncRepository; userRepository?: UserRepository; + versionHistoryRepository?: VersionHistoryRepository; }; type BaseServiceArgs = ConstructorParameters; type Constructor> = { @@ -151,7 +152,7 @@ export const newTestService = ( Service: Constructor, overrides?: Overrides, ) => { - const { metadataRepository, userRepository, syncRepository } = overrides || {}; + const { metadataRepository, userRepository, syncRepository, versionHistoryRepository } = overrides || {}; const accessMock = newAccessRepositoryMock(); const loggerMock = newLoggingRepositoryMock(); @@ -235,7 +236,8 @@ export const newTestService = ( telemetryMock as unknown as TelemetryRepository, trashMock as RepositoryInterface as TrashRepository, userMock as RepositoryInterface as UserRepository, - versionHistoryMock as RepositoryInterface as VersionHistoryRepository, + versionHistoryRepository || + (versionHistoryMock as RepositoryInterface as VersionHistoryRepository), viewMock as RepositoryInterface as ViewRepository, );