summaryrefslogtreecommitdiff
path: root/apps/web/src/services/storage/migrations/__tests__
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-02-03 14:00:04 +0100
committerMaze Winther <mazewinther@gmail.com>2026-02-03 14:00:04 +0100
commit7366cb27e98d9e0b15e0176ac7ca32816c460284 (patch)
tree07635628d37c06489519f7daf336c7f3f4fd3a2a /apps/web/src/services/storage/migrations/__tests__
parent34daad9df7ed6dac4fbeba8619ca1958bb2dac86 (diff)
downloadopencut-7366cb27e98d9e0b15e0176ac7ca32816c460284.tar.gz
fix v1 to v2 migration
Diffstat (limited to 'apps/web/src/services/storage/migrations/__tests__')
-rw-r--r--apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts18
-rw-r--r--apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts74
-rw-r--r--apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts166
3 files changed, 145 insertions, 113 deletions
diff --git a/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts b/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts
index 24428cfe..d83b221b 100644
--- a/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts
+++ b/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts
@@ -7,24 +7,6 @@ export const v0Project = {
canvasSize: { width: 1920, height: 1080 },
backgroundColor: "#000000",
backgroundType: "color",
- tracks: [
- {
- id: "track-1",
- type: "video",
- name: "Video Track",
- elements: [
- {
- id: "element-1",
- type: "video",
- mediaId: "media-1",
- startTime: 0,
- duration: 5,
- trimStart: 0,
- trimEnd: 0,
- },
- ],
- },
- ],
bookmarks: [1.5, 3.0],
};
diff --git a/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts b/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts
index 93904947..7930dd37 100644
--- a/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts
+++ b/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts
@@ -1,3 +1,4 @@
+// v1 projects: Scenes exist but tracks stored in separate IndexedDB (video-editor-timelines-{projectId}-{sceneId})
export const v1Project = {
id: "project-v1-123",
version: 1,
@@ -15,42 +16,7 @@ export const v1Project = {
id: "scene-main",
name: "Main scene",
isMain: true,
- tracks: [
- {
- id: "track-1",
- type: "video",
- name: "Video Track",
- isMain: true,
- elements: [
- {
- id: "element-1",
- type: "video",
- mediaId: "media-1",
- startTime: 0,
- duration: 10,
- trimStart: 0,
- trimEnd: 0,
- },
- ],
- },
- {
- id: "track-2",
- type: "audio",
- name: "Audio Track",
- elements: [
- {
- id: "element-2",
- type: "audio",
- sourceType: "upload",
- mediaId: "media-2",
- startTime: 0,
- duration: 10,
- trimStart: 0,
- trimEnd: 0,
- },
- ],
- },
- ],
+ tracks: [],
bookmarks: [],
createdAt: "2024-01-15T10:00:00.000Z",
updatedAt: "2024-01-15T12:00:00.000Z",
@@ -76,22 +42,7 @@ export const v1ProjectWithMultipleScenes = {
id: "scene-1",
name: "Intro",
isMain: true,
- tracks: [
- {
- id: "track-1",
- type: "video",
- isMain: true,
- elements: [
- {
- id: "el-1",
- type: "video",
- mediaId: "m1",
- startTime: 0,
- duration: 5,
- },
- ],
- },
- ],
+ tracks: [],
bookmarks: [1.0],
createdAt: "2024-02-20T14:00:00.000Z",
updatedAt: "2024-02-20T16:00:00.000Z",
@@ -100,25 +51,10 @@ export const v1ProjectWithMultipleScenes = {
id: "scene-2",
name: "Content",
isMain: false,
- tracks: [
- {
- id: "track-2",
- type: "video",
- isMain: true,
- elements: [
- {
- id: "el-2",
- type: "video",
- mediaId: "m2",
- startTime: 0,
- duration: 20,
- },
- ],
- },
- ],
+ tracks: [],
bookmarks: [],
createdAt: "2024-02-20T14:30:00.000Z",
updatedAt: "2024-02-20T16:00:00.000Z",
},
],
-}; \ No newline at end of file
+};
diff --git a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts
index ff6a31c2..31f7dae8 100644
--- a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts
+++ b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts
@@ -5,6 +5,7 @@ import {
DEFAULT_COLOR,
DEFAULT_FPS,
} from "@/constants/project-constants";
+import type { MediaAssetData } from "@/services/storage/types";
import { getProjectId, transformProjectV1ToV2 } from "../transformers/v1-to-v2";
import {
projectWithNoId,
@@ -16,8 +17,8 @@ import {
describe("V1 to V2 Migration", () => {
describe("transformProjectV1ToV2", () => {
- test("creates metadata object from flat properties", () => {
- const result = transformProjectV1ToV2({ project: v1Project });
+ test("creates metadata object from flat properties", async () => {
+ const result = await transformProjectV1ToV2({ project: v1Project });
expect(result.skipped).toBe(false);
expect(result.project.version).toBe(2);
@@ -29,16 +30,17 @@ describe("V1 to V2 Migration", () => {
expect(typeof metadata.updatedAt).toBe("string");
});
- test("creates settings object from flat properties", () => {
- const result = transformProjectV1ToV2({ project: v1Project });
+ test("creates settings object from flat properties", async () => {
+ const result = await transformProjectV1ToV2({ project: v1Project });
const settings = result.project.settings as Record<string, unknown>;
expect(settings.fps).toBe(v1Project.fps);
expect(settings.canvasSize).toEqual(v1Project.canvasSize);
+ expect(settings.originalCanvasSize).toBe(null);
});
- test("converts color background correctly", () => {
- const result = transformProjectV1ToV2({ project: v1Project });
+ test("converts color background correctly", async () => {
+ const result = await transformProjectV1ToV2({ project: v1Project });
const settings = result.project.settings as Record<string, unknown>;
const background = settings.background as Record<string, unknown>;
@@ -46,13 +48,13 @@ describe("V1 to V2 Migration", () => {
expect(background.color).toBe(v1Project.backgroundColor);
});
- test("converts blur background correctly", () => {
+ test("converts blur background correctly", async () => {
const projectWithBlur = {
...v1Project,
backgroundType: "blur",
blurIntensity: 30,
};
- const result = transformProjectV1ToV2({ project: projectWithBlur });
+ const result = await transformProjectV1ToV2({ project: projectWithBlur });
const settings = result.project.settings as Record<string, unknown>;
const background = settings.background as Record<string, unknown>;
@@ -60,16 +62,16 @@ describe("V1 to V2 Migration", () => {
expect(background.blurIntensity).toBe(30);
});
- test("applies legacy bookmarks to main scene", () => {
- const result = transformProjectV1ToV2({ project: v1Project });
+ test("applies legacy bookmarks to main scene", async () => {
+ const result = await transformProjectV1ToV2({ project: v1Project });
const scenes = result.project.scenes as Array<Record<string, unknown>>;
const mainScene = scenes.find((s) => s.isMain === true);
expect(mainScene?.bookmarks).toEqual(v1Project.bookmarks);
});
- test("preserves existing scene bookmarks", () => {
- const result = transformProjectV1ToV2({
+ test("preserves existing scene bookmarks", async () => {
+ const result = await transformProjectV1ToV2({
project: v1ProjectWithMultipleScenes,
});
@@ -78,22 +80,24 @@ describe("V1 to V2 Migration", () => {
expect(introScene?.bookmarks).toEqual([1.0]);
});
- test("skips project that already has v2 structure", () => {
- const result = transformProjectV1ToV2({ project: v2Project });
+ test("skips project that already has v2 structure", async () => {
+ const result = await transformProjectV1ToV2({ project: v2Project });
expect(result.skipped).toBe(true);
expect(result.reason).toBe("already v2");
});
- test("skips project with no id", () => {
- const result = transformProjectV1ToV2({ project: projectWithNoId });
+ test("skips project with no id", async () => {
+ const result = await transformProjectV1ToV2({ project: projectWithNoId });
expect(result.skipped).toBe(true);
expect(result.reason).toBe("no project id");
});
- test("handles null values gracefully", () => {
- const result = transformProjectV1ToV2({ project: projectWithNullValues });
+ test("handles null values gracefully", async () => {
+ const result = await transformProjectV1ToV2({
+ project: projectWithNullValues,
+ });
expect(result.skipped).toBe(false);
const settings = result.project.settings as Record<string, unknown>;
@@ -101,13 +105,13 @@ describe("V1 to V2 Migration", () => {
expect(settings.canvasSize).toEqual(DEFAULT_CANVAS_SIZE);
});
- test("uses default values for missing properties", () => {
+ test("uses default values for missing properties", async () => {
const minimalProject = {
id: "minimal",
version: 1,
scenes: [],
};
- const result = transformProjectV1ToV2({ project: minimalProject });
+ const result = await transformProjectV1ToV2({ project: minimalProject });
const settings = result.project.settings as Record<string, unknown>;
expect(settings.fps).toBe(DEFAULT_FPS);
@@ -118,14 +122,14 @@ describe("V1 to V2 Migration", () => {
expect(background.color).toBe(DEFAULT_COLOR);
});
- test("uses default blur intensity when missing", () => {
+ test("uses default blur intensity when missing", async () => {
const projectWithBlurNoIntensity = {
id: "blur-no-intensity",
version: 1,
backgroundType: "blur",
scenes: [],
};
- const result = transformProjectV1ToV2({
+ const result = await transformProjectV1ToV2({
project: projectWithBlurNoIntensity,
});
@@ -134,21 +138,131 @@ describe("V1 to V2 Migration", () => {
expect(background.blurIntensity).toBe(DEFAULT_BLUR_INTENSITY);
});
- test("preserves currentSceneId", () => {
- const result = transformProjectV1ToV2({ project: v1Project });
+ test("preserves currentSceneId", async () => {
+ const result = await transformProjectV1ToV2({ project: v1Project });
expect(result.project.currentSceneId).toBe(v1Project.currentSceneId);
});
- test("finds main scene id when currentSceneId missing", () => {
+ test("finds main scene id when currentSceneId missing", async () => {
const projectWithoutCurrentScene = {
...v1Project,
currentSceneId: undefined,
};
- const result = transformProjectV1ToV2({
+ const result = await transformProjectV1ToV2({
project: projectWithoutCurrentScene,
});
expect(result.project.currentSceneId).toBe("scene-main");
});
+
+ test("skips loading tracks if scene already has tracks", async () => {
+ const projectWithTracks = {
+ ...v1Project,
+ scenes: [
+ {
+ id: "scene-main",
+ name: "Main scene",
+ isMain: true,
+ tracks: [
+ {
+ id: "track-1",
+ type: "video",
+ name: "Existing Track",
+ elements: [],
+ },
+ ],
+ bookmarks: [],
+ createdAt: "2024-01-15T10:00:00.000Z",
+ updatedAt: "2024-01-15T12:00:00.000Z",
+ },
+ ],
+ };
+
+ const result = await transformProjectV1ToV2({
+ project: projectWithTracks,
+ });
+
+ const scenes = result.project.scenes as Array<Record<string, unknown>>;
+ const mainScene = scenes[0];
+ const tracks = mainScene.tracks as Array<Record<string, unknown>>;
+ expect(tracks.length).toBe(1);
+ expect(tracks[0].name).toBe("Existing Track");
+ });
+ });
+
+ describe("Track Loading and Transformation", () => {
+ test("loads tracks from legacy DB and transforms media track to video track", async () => {
+ const mockLoadMediaAsset = async ({
+ mediaId,
+ }: {
+ mediaId: string;
+ }): Promise<MediaAssetData | null> => {
+ if (mediaId === "media-1") {
+ return {
+ id: "media-1",
+ name: "Test Video",
+ type: "video",
+ size: 1000,
+ lastModified: Date.now(),
+ };
+ }
+ return null;
+ };
+
+ const projectWithLegacyTracks = {
+ ...v1Project,
+ scenes: [
+ {
+ id: "scene-main",
+ name: "Main scene",
+ isMain: true,
+ tracks: [],
+ bookmarks: [],
+ createdAt: "2024-01-15T10:00:00.000Z",
+ updatedAt: "2024-01-15T12:00:00.000Z",
+ },
+ ],
+ };
+
+ // mock IndexedDB for this test would require setting up a test environment
+ // for now, we test that the transformer handles empty tracks gracefully
+ const result = await transformProjectV1ToV2({
+ project: projectWithLegacyTracks,
+ options: { loadMediaAsset: mockLoadMediaAsset },
+ });
+
+ const scenes = result.project.scenes as Array<Record<string, unknown>>;
+ const mainScene = scenes[0];
+ expect(Array.isArray(mainScene.tracks)).toBe(true);
+ });
+
+ test("transforms text element preserving opacity and migrating position", async () => {
+ const projectWithTextTrack = {
+ id: "project-text",
+ version: 1,
+ name: "Text Project",
+ scenes: [
+ {
+ id: "scene-1",
+ name: "Scene",
+ isMain: true,
+ tracks: [],
+ bookmarks: [],
+ createdAt: "2024-01-01T00:00:00.000Z",
+ updatedAt: "2024-01-01T00:00:00.000Z",
+ },
+ ],
+ };
+
+ // since tracks are empty, transformation won't happen
+ // but we verify the structure is correct
+ const result = await transformProjectV1ToV2({
+ project: projectWithTextTrack,
+ });
+
+ expect(result.skipped).toBe(false);
+ const scenes = result.project.scenes as Array<Record<string, unknown>>;
+ expect(scenes.length).toBe(1);
+ });
});
describe("getProjectId", () => {