Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/filesystem/s3/s3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,24 @@ describe("S3FileSystem", () => {
})
);
});

it("S3FileWriter.write 应在传入 modifiedDate 时写入 createtime 元数据", async () => {
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValue(createMockResponse({ ok: true }));

const writer = await fs.create("output.txt", { modifiedDate: 1234 });
await writer.write("hello world");

expect(mockClient.request).toHaveBeenCalledWith(
"PUT",
"test-bucket",
"output.txt",
expect.objectContaining({
headers: expect.objectContaining({
"x-amz-meta-createtime": new Date(1234).toISOString(),
}),
})
);
});
});

// ---- createDir ----
Expand Down
106 changes: 106 additions & 0 deletions src/app/service/service_worker/synchronize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,112 @@ console.log("ok");`
});
});

it("passes script modifiedDate when pushing script and meta files", async () => {
const writeMock = vi.fn().mockResolvedValue(undefined);
const createMock = vi.fn().mockResolvedValue({ write: writeMock });
const fs = createFs({
create: createMock,
});
const service = new SynchronizeService(
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{
scriptCodeDAO: {
get: vi.fn().mockResolvedValue({ code: "// code" }),
},
all: vi.fn().mockResolvedValue([]),
} as any
);
const script = {
uuid: "push-uuid",
name: "push",
origin: "origin",
downloadUrl: "download-url",
checkUpdateUrl: "check-update-url",
updatetime: 1234,
createtime: 1000,
status: 1,
sort: 0,
metadata: {},
};

await service.pushScript(fs, script as any);

expect(createMock.mock.calls[0]).toEqual(["push-uuid.user.js", { modifiedDate: 1234 }]);
expect(createMock.mock.calls[1]).toEqual(["push-uuid.meta.json", { modifiedDate: 1234 }]);
});

it("uses Date.now as modifiedDate when writing scriptcat-sync.json", async () => {
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(9876);
const createMock = vi.fn().mockResolvedValue({
write: vi.fn().mockResolvedValue(undefined),
});
const fs = createFs({
create: createMock,
});
const service = new SynchronizeService(
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{
scriptCodeDAO: {},
all: vi.fn().mockResolvedValue([]),
} as any
);

try {
await service.syncOnce(syncConfig, fs);

expect(createMock).toHaveBeenCalledWith("scriptcat-sync.json", {
modifiedDate: 9876,
});
} finally {
nowSpy.mockRestore();
}
});

it("uses Date.now as modifiedDate when writing delete tombstone meta", async () => {
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(6789);
const createMock = vi.fn().mockResolvedValue({
write: vi.fn().mockResolvedValue(undefined),
});
const fs = createFs({
create: createMock,
});
const service = new SynchronizeService(
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{} as any,
{
scriptCodeDAO: {},
all: vi.fn().mockResolvedValue([]),
} as any
);

try {
await service.deleteCloudScript(fs, "delete-uuid", true);

expect(createMock).toHaveBeenCalledWith("delete-uuid.meta.json", {
modifiedDate: 6789,
});
} finally {
nowSpy.mockRestore();
}
});

it("preserves cloud-native digest and does not overwrite with pushed md5", async () => {
// 各后端 digest 格式不一致(webdav/onedrive 是 etag、dropbox 是 content_hash 等),
// 上传后再次 list 已经能拿到原生 digest 时,必须保留它,不能被本地 md5 覆盖,
Expand Down
15 changes: 10 additions & 5 deletions src/app/service/service_worker/synchronize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type ScriptcatSyncStatus = {
updatetime: number; // 更新时间
};

type PushScriptParam = TInstallScriptParams;
type PushScriptParam = TInstallScriptParams & Partial<Pick<Script, "createtime" | "updatetime">>;

type FileDigestMap = {
[key: string]: string;
Expand Down Expand Up @@ -535,7 +535,9 @@ export class SynchronizeService {
}
});
// 保存脚本猫同步状态
const syncFile = await fs.create("scriptcat-sync.json");
const syncFile = await fs.create("scriptcat-sync.json", {
modifiedDate: Date.now(),
});
await syncFile.write(JSON.stringify(scriptcatSync, null, 2));
this.logger.info("sync scriptcat-sync.json file success");
}
Expand Down Expand Up @@ -575,7 +577,9 @@ export class SynchronizeService {
await fs.delete(filename);
if (syncDelete) {
// 留下一个.meta.json删除标记
const meta = await fs.create(`${uuid}.meta.json`);
const meta = await fs.create(`${uuid}.meta.json`, {
modifiedDate: Date.now(),
});
await meta.write(
JSON.stringify(<SyncMeta>{
uuid: uuid,
Expand Down Expand Up @@ -606,12 +610,13 @@ export class SynchronizeService {
file: filename,
});
try {
const w = await fs.create(filename);
const modifiedDate = script.updatetime || script.createtime || Date.now();
const w = await fs.create(filename, { modifiedDate });
// 获取脚本代码
const code = await this.scriptCodeDAO.get(script.uuid);
const scriptCode = code!.code;
await w.write(scriptCode);
const meta = await fs.create(metaFilename);
const meta = await fs.create(metaFilename, { modifiedDate });
const metaJson = JSON.stringify(<SyncMeta>{
uuid: script.uuid,
origin: script.origin,
Expand Down
Loading