fix(scheduler): harden daily start reconciliation

Preserve legacy one-time schedules across internal account state saves and clear them only for explicit renderer daily-rule updates.

Enforce monotone handled and pending calendar receipts across backward local-date changes, invalidate late in-flight start results on shutdown, and align queue eligibility with enabled non-cancelled packages.

Replace the in-memory restart assertion with real settings persistence and add RED/GREEN regression coverage for every reviewed behavior.
This commit is contained in:
Sucukdeluxe
2026-08-22 09:50:37 +02:00
parent 4eb95c92b9
commit 0ca00149eb
5 changed files with 182 additions and 24 deletions
+26 -2
View File
@@ -5,6 +5,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { AppController } from "../src/main/app-controller";
import { defaultSettings } from "../src/main/constants";
import { configureCredentialProtector } from "../src/main/credential-protection";
import { prepareDailyStartSettingsPatch } from "../src/main/daily-start-scheduler";
import { createStoragePaths } from "../src/main/storage";
import type { AppSettings } from "../src/shared/types";
@@ -55,11 +56,11 @@ describe("AppController daily start settings", () => {
scheduledStartEpochMs: 1_800_000_000_000
});
const updated = controller.updateSettings({
const updated = controller.updateSettings(prepareDailyStartSettingsPatch({
dailyStartEnabled: true,
dailyStartMinuteOfDay: 18 * 60 + 45,
dailyStartFirstLocalDate: "2026-08-23"
});
}));
expect(updated.scheduledStartEpochMs).toBe(0);
expect(updated).toMatchObject({
@@ -68,4 +69,27 @@ describe("AppController daily start settings", () => {
dailyStartFirstLocalDate: "2026-08-23"
});
});
it("preserves a legacy one-time schedule when an account mutation saves a complete settings state", async () => {
configureCredentialProtector({
isEncryptionAvailable: () => false,
encryptString: (value) => Buffer.from(value, "utf8"),
decryptString: (value) => Buffer.from(value).toString("utf8")
});
const scheduledStartEpochMs = 1_800_000_000_000;
const controller = createController({
...defaultSettings(),
ddownloadLogin: "account@example.test",
ddownloadPassword: "secret",
scheduledStartEpochMs
});
await controller.executeAccountCommand({
action: "delete",
kind: "ddownload-login",
accountId: "svc-ddownload"
});
expect(controller.getSettings().scheduledStartEpochMs).toBe(scheduledStartEpochMs);
});
});