fix: provider-scoped snapshot refresh with self-healing invalidation
This commit is contained in:
@@ -588,6 +588,50 @@ describe("ProviderSnapshotManager", () => {
|
||||
manager.destroy();
|
||||
});
|
||||
|
||||
test("settings refresh invalidation self-heals workspace snapshots through the next read without force", async () => {
|
||||
const fetchModels = vi
|
||||
.fn<(cwd: string, force: boolean) => Promise<AgentModelDefinition[]>>()
|
||||
.mockImplementation(async (cwd) => [createModel("codex", cwd)]);
|
||||
const { registry } = createRegistry([
|
||||
createMockProvider({
|
||||
provider: "codex",
|
||||
fetchModels: async (cwd, force) => fetchModels(cwd, force),
|
||||
fetchModes: async () => [createMode("auto")],
|
||||
}),
|
||||
]);
|
||||
const manager = new ProviderSnapshotManager(registry, createTestLogger());
|
||||
|
||||
manager.getSnapshot(projectCwd);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(getProviderEntry(manager.getSnapshot(projectCwd), "codex")?.status).toBe("ready");
|
||||
});
|
||||
|
||||
await manager.refreshSettingsSnapshot({ providers: ["codex"] });
|
||||
|
||||
expect(fetchModels.mock.calls.map(([cwd]) => cwd)).toEqual([projectCwd, homedir()]);
|
||||
expect(fetchModels.mock.calls.map(([, force]) => force)).toEqual([false, true]);
|
||||
|
||||
const invalidatedSnapshot = manager.getSnapshot(projectCwd);
|
||||
|
||||
expect(getProviderEntry(invalidatedSnapshot, "codex")).toMatchObject({
|
||||
provider: "codex",
|
||||
status: "loading",
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(fetchModels).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
expect(fetchModels.mock.calls[2]).toEqual([projectCwd, false]);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(getProviderEntry(manager.getSnapshot(projectCwd), "codex")?.status).toBe("ready");
|
||||
});
|
||||
|
||||
manager.destroy();
|
||||
});
|
||||
|
||||
test("refresh marks a slow provider as error after the timeout", async () => {
|
||||
const fetchModels = deferred<AgentModelDefinition[]>();
|
||||
const { registry } = createRegistry([
|
||||
|
||||
@@ -63,6 +63,13 @@ export class ProviderSnapshotManager {
|
||||
this.resetSnapshotToLoading(resolvedCwd, missingProviders);
|
||||
void this.warmUp(resolvedCwd, missingProviders);
|
||||
}
|
||||
const providerLoads = this.providerLoads.get(resolvedCwd);
|
||||
const loadingProviders = Array.from(entries.values())
|
||||
.filter((entry) => entry.status === "loading" && !providerLoads?.has(entry.provider))
|
||||
.map((entry) => entry.provider);
|
||||
if (loadingProviders.length > 0) {
|
||||
void this.warmUp(resolvedCwd, loadingProviders);
|
||||
}
|
||||
if (this.shouldRevalidate(resolvedCwd)) {
|
||||
void this.warmUp(resolvedCwd);
|
||||
}
|
||||
@@ -392,10 +399,10 @@ export class ProviderSnapshotManager {
|
||||
}
|
||||
|
||||
if (!options.providers) {
|
||||
this.snapshots.delete(cwd);
|
||||
this.resetSnapshotToLoading(cwd);
|
||||
this.lastCheckedAts.delete(cwd);
|
||||
this.providerLoads.delete(cwd);
|
||||
this.events.emit("change", [], cwd);
|
||||
this.emitChange(cwd);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -405,9 +412,10 @@ export class ProviderSnapshotManager {
|
||||
}
|
||||
let changed = false;
|
||||
for (const provider of options.providers) {
|
||||
changed = snapshot.delete(provider) || changed;
|
||||
changed = snapshot.has(provider) || changed;
|
||||
this.providerLoads.get(cwd)?.delete(provider);
|
||||
}
|
||||
this.resetSnapshotToLoading(cwd, options.providers);
|
||||
if (this.providerLoads.get(cwd)?.size === 0) {
|
||||
this.providerLoads.delete(cwd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user