mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-04-04 17:56:19 +08:00
feat(webdav): follow-up 补齐自动同步与大文件防护 (#1043)
* feat(webdav): add robust auto sync with failure feedback (cherry picked from commit bb6760124a62a964b36902c004e173534910728f) * fix(webdav): enforce bounded download and extraction size (cherry picked from commit 7777d6ec2b9bba07c8bbba9b04fe3ea6b15e0e79) * fix(webdav): only show auto-sync callout for auto-source errors * refactor(webdav): remove services->commands auto-sync dependency
This commit is contained in:
@@ -34,6 +34,17 @@ vi.mock("@/components/ui/input", () => ({
|
||||
Input: (props: any) => <input {...props} />,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ui/switch", () => ({
|
||||
Switch: ({ checked, onCheckedChange, ...props }: any) => (
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
onClick={() => onCheckedChange?.(!checked)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ui/select", () => ({
|
||||
Select: ({ value, onValueChange, children }: any) => (
|
||||
<select
|
||||
@@ -82,6 +93,7 @@ const baseConfig: WebDavSyncSettings = {
|
||||
password: "secret",
|
||||
remoteRoot: "cc-switch-sync",
|
||||
profile: "default",
|
||||
autoSync: false,
|
||||
status: {},
|
||||
};
|
||||
|
||||
@@ -128,6 +140,49 @@ describe("WebdavSyncSection", () => {
|
||||
settingsApiMock.webdavSyncDownload.mockResolvedValue({ status: "downloaded" });
|
||||
});
|
||||
|
||||
it("shows auto sync error callout when last auto sync failed", () => {
|
||||
renderSection({
|
||||
...baseConfig,
|
||||
status: {
|
||||
lastError: "network timeout",
|
||||
lastErrorSource: "auto",
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByText("settings.webdavSync.autoSyncLastErrorTitle"),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("network timeout")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show auto sync error callout for manual sync errors", () => {
|
||||
renderSection({
|
||||
...baseConfig,
|
||||
status: {
|
||||
lastError: "manual upload failed",
|
||||
lastErrorSource: "manual",
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.queryByText("settings.webdavSync.autoSyncLastErrorTitle"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show auto sync error callout when source is missing", () => {
|
||||
renderSection({
|
||||
...baseConfig,
|
||||
autoSync: true,
|
||||
status: {
|
||||
lastError: "legacy error without source",
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.queryByText("settings.webdavSync.autoSyncLastErrorTitle"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows validation error when saving without base url", async () => {
|
||||
renderSection({ ...baseConfig, baseUrl: "" });
|
||||
|
||||
@@ -150,6 +205,7 @@ describe("WebdavSyncSection", () => {
|
||||
baseUrl: "https://dav.example.com/dav/",
|
||||
username: "alice",
|
||||
password: "secret",
|
||||
autoSync: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
@@ -166,6 +222,24 @@ describe("WebdavSyncSection", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("saves auto sync as true after toggle", async () => {
|
||||
renderSection(baseConfig);
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole("switch", { name: "settings.webdavSync.autoSync" }),
|
||||
);
|
||||
fireEvent.click(screen.getByRole("button", { name: "settings.webdavSync.save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(settingsApiMock.webdavSyncSaveSettings).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
autoSync: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("blocks upload when there are unsaved changes", async () => {
|
||||
renderSection(baseConfig);
|
||||
|
||||
|
||||
@@ -209,4 +209,25 @@ describe("App integration with MSW", () => {
|
||||
expect(toastErrorMock).not.toHaveBeenCalled();
|
||||
expect(toastSuccessMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows toast when auto sync fails in background", async () => {
|
||||
const { default: App } = await import("@/App");
|
||||
renderApp(App);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId("provider-list").textContent).toContain(
|
||||
"claude-1",
|
||||
),
|
||||
);
|
||||
|
||||
emitTauriEvent("webdav-sync-status-updated", {
|
||||
source: "auto",
|
||||
status: "error",
|
||||
error: "network timeout",
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastErrorMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user