diff --git a/src/renderer/components/services/upload-manager.js b/src/renderer/components/services/upload-manager.js index b1bfce9b..6df5812a 100755 --- a/src/renderer/components/services/upload-manager.js +++ b/src/renderer/components/services/upload-manager.js @@ -373,29 +373,6 @@ webModule.factory(UPLOAD_MGR_FACTORY_NAME, [ progs = JSON.parse(data); } catch (e) {} - Object.entries(progs).forEach(([jobId, job]) => { - if (!job.uploadedParts) { - job.uploadedParts = []; - } - job.uploadedParts = job.uploadedParts. - filter(part => part && part.PartNumber && part.ETag). - map((part) => { - return { partNumber: part.PartNumber, etag: part.ETag }; - }); - if (job.from && job.from.size && job.from.mtime) { - if (fs.existsSync(job.from.path)) { - const fileStat = fs.statSync(job.from.path); - if (fileStat.size === job.from.size && job.from.mtime === fileStat.mtimeMs) { - return; - } - } - } - job.uploadedParts = []; - if (job.prog) { - delete job.prog.loaded; - } - }); - Object.entries(progs) .forEach(([jobId, briefJob]) => { if (!briefJob.from || !briefJob.from.size || !briefJob.from.mtime) { diff --git a/src/renderer/models/job/download-job.test.ts b/src/renderer/models/job/download-job.test.ts index 1fd479af..6c498bde 100644 --- a/src/renderer/models/job/download-job.test.ts +++ b/src/renderer/models/job/download-job.test.ts @@ -9,12 +9,13 @@ jest.mock("electron", () => ({ import { ipcRenderer } from "electron"; import * as AppConfig from "@/const/app-config"; -import { IpcJobEvent, Status } from "./types"; + +import { EventKey, IpcJobEvent, Status } from "./types"; import { downloadOptionsFromResumeJob } from "./_mock-helpers_/data"; import DownloadJob from "./download-job"; -describe("test models/job/download-job.ts", () => { +describe("test models/job/download-job.ts", () => { describe("test stop", () => { it("stop", () => { const downloadJob = new DownloadJob(downloadOptionsFromResumeJob); @@ -90,4 +91,64 @@ describe("test models/job/download-job.ts", () => { downloadJob.stop(); }); }); + + describe("test resume download job", () => { + it("getInfoForSave()", () => { + const downloadJob = new DownloadJob(downloadOptionsFromResumeJob); + + // stat + const fakeProgressTotal = 1024; + const fakeProgressResumable = true; + downloadJob.startDownload(null, { + key: EventKey.Stat, + data: { + progressTotal: fakeProgressTotal, + progressResumable: fakeProgressResumable, + }, + }); + expect(downloadJob.prog.total).toBe(fakeProgressTotal); + expect(downloadJob.prog.resumable).toBe(fakeProgressResumable); + + // progress + const fakeProgressLoaded = 512; + downloadJob.startDownload(null, { + key: EventKey.Progress, + data: { + progressLoaded: fakeProgressLoaded, + progressResumable: fakeProgressResumable, + }, + }); + expect(downloadJob.prog.loaded).toBe(fakeProgressLoaded); + expect(downloadJob.prog.resumable).toBe(fakeProgressResumable); + + // part downloaded + const lastDownloadedSize = downloadJob.prog.loaded; + const fakeDownloadedSize = 512; + downloadJob.startDownload(null, { + key: EventKey.PartDownloaded, + data: { + size: fakeDownloadedSize, + }, + }); + expect(downloadJob.prog.loaded).toBe(lastDownloadedSize + fakeDownloadedSize); + + // info should in disk + expect(downloadJob.getInfoForSave()) + .toEqual({ + storageClasses: downloadOptionsFromResumeJob.storageClasses, + region: downloadOptionsFromResumeJob.region, + to: downloadOptionsFromResumeJob.to, + from: downloadOptionsFromResumeJob.from, + backendMode: downloadOptionsFromResumeJob.backendMode, + + prog: { + loaded: downloadJob.prog.loaded, + total: fakeProgressTotal, + resumable: fakeProgressResumable, + }, + status: Status.Waiting, + message: "", + }); + }); + }) }); diff --git a/src/renderer/models/job/download-job.ts b/src/renderer/models/job/download-job.ts index 1f1cf010..ba29e71b 100644 --- a/src/renderer/models/job/download-job.ts +++ b/src/renderer/models/job/download-job.ts @@ -335,17 +335,20 @@ export default class DownloadJob extends Base { getInfoForSave() { return { + // read-only info storageClasses: this.options.storageClasses, region: this.options.region, to: this.options.to, from: this.options.from, + backendMode: this.options.backendMode, + domain: this.options.domain, + + // real-time info prog: { loaded: this.prog.loaded, total: this.prog.total, resumable: this.prog.resumable }, - backendMode: this.options.backendMode, - domain: this.options.domain, status: this.status, message: this.message, }; diff --git a/src/renderer/models/job/upload-job.test.ts b/src/renderer/models/job/upload-job.test.ts index 8f0ccbc4..a55d0f6b 100644 --- a/src/renderer/models/job/upload-job.test.ts +++ b/src/renderer/models/job/upload-job.test.ts @@ -10,12 +10,12 @@ jest.mock("electron", () => ({ import { ipcRenderer } from "electron"; import * as AppConfig from "@/const/app-config" -import { IpcJobEvent, Status } from "./types"; +import { EventKey, IpcJobEvent, Status } from "./types"; import { uploadOptionsFromNewJob } from "./_mock-helpers_/data"; import UploadJob from "./upload-job"; -describe("test models/job/upload-job.ts", () => { +describe("test models/job/upload-job.ts", () => { describe("test stop", () => { it("stop", () => { const uploadJob = new UploadJob(uploadOptionsFromNewJob); @@ -94,4 +94,87 @@ describe("test models/job/upload-job.ts", () => { uploadJob.stop(); }); }); + + describe("test resume upload job", () => { + it("getInfoForSave()", () => { + const uploadJob = new UploadJob(uploadOptionsFromNewJob); + uploadJob.on('partcomplete', (data) => { + uploadJob.uploadedId = data.uploadId; + uploadJob.uploadedParts[data.part.partNumber] = data.part; + return false; + }) + + // stat + const fakeProgressTotal = 1024; + const fakeProgressResumable = true; + uploadJob.startUpload(null, { + key: EventKey.Stat, + data: { + progressTotal: fakeProgressTotal, + progressResumable: fakeProgressResumable, + }, + }); + expect(uploadJob.prog.total).toBe(fakeProgressTotal); + expect(uploadJob.prog.resumable).toBe(fakeProgressResumable); + + // progress + const fakeProgressLoaded = 512; + uploadJob.startUpload(null, { + key: EventKey.Progress, + data: { + progressLoaded: fakeProgressLoaded, + progressResumable: fakeProgressResumable, + }, + }); + expect(uploadJob.prog.loaded).toBe(fakeProgressLoaded); + expect(uploadJob.prog.resumable).toBe(fakeProgressResumable); + + // part uploaded + const fakeUploadedId = 'fakeUploadId'; + const fakeUploadedPart = { + partNumber: 0, + etag: 'fakeETag', + }; + uploadJob.startUpload(null, { + key: EventKey.PartUploaded, + data: { + uploadId: fakeUploadedId, + part: fakeUploadedPart, + }, + }); + expect(uploadJob.uploadedParts.length).toBe(1); + expect(uploadJob.uploadedId).toBe(fakeUploadedId); + expect(uploadJob.uploadedParts).toEqual([ + fakeUploadedPart, + ]); + + // info should in disk + expect(uploadJob.getInfoForSave({})) + .toEqual({ + from: uploadOptionsFromNewJob.from, + + backendMode: uploadOptionsFromNewJob.backendMode, + overwrite: uploadOptionsFromNewJob.overwrite, + to: uploadOptionsFromNewJob.to, + region: uploadOptionsFromNewJob.region, + storageClassName: uploadOptionsFromNewJob.storageClassName, + storageClasses: uploadOptionsFromNewJob.storageClasses, + + prog: { + loaded: fakeProgressLoaded, + total: fakeProgressTotal, + resumable: fakeProgressResumable, + }, + status: Status.Waiting, + uploadedId: fakeUploadedId, + uploadedParts: [ + { + PartNumber: fakeUploadedPart.partNumber, + ETag: fakeUploadedPart.etag, + }, + ], + message: "", + }); + }); + }); }); diff --git a/src/renderer/models/job/upload-job.ts b/src/renderer/models/job/upload-job.ts index 565a7fe0..46c0a630 100644 --- a/src/renderer/models/job/upload-job.ts +++ b/src/renderer/models/job/upload-job.ts @@ -355,29 +355,37 @@ export default class UploadJob extends Base { getInfoForSave({ from }: { - from: { - size: number, - mtime: number, + from?: { + size?: number, + mtime?: number, } }) { return { - storageClasses: this.options.storageClasses, - region: this.options.region, - to: this.options.to, from: { ...this.options.from, ...from, }, - prog: this.options.prog, - status: this.options.status, - message: this.options.message, - uploadedId: this.options.uploadedId, - uploadedParts: this.options.uploadedParts.map((part) => { - return { PartNumber: part.partNumber, ETag: part.etag }; - }), + + // read-only info + storageClasses: this.options.storageClasses, + region: this.options.region, + to: this.options.to, overwrite: this.options.overwrite, storageClassName: this.options.storageClassName, backendMode: this.options.backendMode, + + // real-time info + prog: { + loaded: this.prog.loaded, + total: this.prog.total, + resumable: this.prog.resumable + }, + status: this.status, + message: this.message, + uploadedId: this.uploadedId, + uploadedParts: this.uploadedParts.map((part) => { + return { PartNumber: part.partNumber, ETag: part.etag }; + }), }; } }