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
2 changes: 0 additions & 2 deletions apps/server/src/orchestration-v2/RunFinalizationQueue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as SqlitePersistence from "../persistence/Sqlite.ts";
import * as PullRequestService from "../pullRequest/PullRequestService.ts";
import * as ServerSettings from "../serverSettings.ts";
import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";
import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";
import * as CheckpointCapture from "./CheckpointCaptureService.ts";
import * as CheckpointRollback from "./CheckpointRollbackService.ts";
import * as EffectOutbox from "./EffectOutbox.ts";
Expand Down Expand Up @@ -48,7 +47,6 @@ it.effect.each([
Layer.provide(
Layer.mergeAll(
projections,
Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }),
Layer.mock(PullRequestService.PullRequestService)({ refreshAfterTurn: () => Effect.void }),
Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({
refreshLocalStatus: () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import * as Layer from "effect/Layer";

import * as PullRequestService from "../pullRequest/PullRequestService.ts";
import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";
import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";
import * as CheckpointCapture from "./CheckpointCaptureService.ts";
import * as ProjectionStore from "./ProjectionStore.ts";
import * as RunFinalization from "./RunFinalizationService.ts";
Expand Down Expand Up @@ -98,7 +97,6 @@ it.effect.each(
const layer = RunFinalization.layerObserver.pipe(
Layer.provide(
Layer.mergeAll(
Layer.mock(WorkspaceEntries.WorkspaceEntries)({ refresh: () => Effect.void }),
Layer.mock(PullRequestService.PullRequestService)({
refreshAfterTurn: () => Effect.void,
}),
Expand Down
9 changes: 3 additions & 6 deletions apps/server/src/orchestration-v2/RunFinalizationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as Schema from "effect/Schema";

import * as PullRequestService from "../pullRequest/PullRequestService.ts";
import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";
import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";
import * as CheckpointCapture from "./CheckpointCaptureService.ts";
import * as ProjectionStore from "./ProjectionStore.ts";

Expand Down Expand Up @@ -92,18 +91,16 @@ export const layer = Layer.effect(RunFinalizationService, make);
export const layerObserver = Layer.effect(
RunFinalizationObserver,
Effect.gen(function* () {
const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries;
const vcsStatus = yield* VcsStatusBroadcaster.VcsStatusBroadcaster;
const projections = yield* ProjectionStore.ProjectionStoreV2;
const pullRequests = yield* PullRequestService.PullRequestService;
return {
refreshAfterTurn: pullRequests.refreshAfterTurn,
refresh: Effect.fn("RunFinalizationService.refresh")(
function* ({ cwd, threadId, runId }) {
const [, local] = yield* Effect.all(
[workspaceEntries.refresh(cwd), vcsStatus.refreshLocalStatus(cwd)],
{ concurrency: "unbounded" },
);
// The search indexes already watch file changes. A forced `scanFiles()` here waits on
// fff's picker lock on the event loop, freezing the server while the watcher holds it.
const local = yield* vcsStatus.refreshLocalStatus(cwd);
if (local.refName === null || local.isDefaultRef) return;
const thread = yield* projections.getThreadShell(threadId);
if (!thread || thread.branch !== local.refName) return;
Expand Down
39 changes: 39 additions & 0 deletions apps/server/src/workspace/WorkspaceSearchIndex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@ff-labs/fff-node";
import { afterEach, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as TestClock from "effect/testing/TestClock";
import { vi } from "vite-plus/test";

import * as WorkspaceSearchIndex from "./WorkspaceSearchIndex.ts";
Expand All @@ -28,6 +29,44 @@ function fileItem(relativePath: string): FileItem {
};
}

it.effect("reads never rescan after the former recovery interval", () =>
Effect.scoped(
Effect.gen(function* () {
const scanFiles = vi.fn(() => ({ ok: true as const, value: undefined }));
const finder = {
destroy: vi.fn(),
waitForIndexReady: vi.fn(async () => ({ ok: true as const, value: true })),
scanFiles,
mixedSearch: () => ({ ok: true as const, value: { items: [], totalMatched: 0 } }),
fileSearch: () => ({
ok: true as const,
value: { items: [fileItem("watched.ts")], scores: [], totalMatched: 1, totalFiles: 1 },
}),
grep: () => ({ ok: true as const, value: { items: [], nextCursor: null } }),
} as unknown as FileFinder;
vi.spyOn(FileFinder, "create").mockReturnValueOnce({ ok: true, value: finder });
const index = yield* WorkspaceSearchIndex.make("/workspace/project");
yield* TestClock.adjust("10 minutes");
expect(yield* index.list()).toEqual({ entries: [], truncated: false });
expect((yield* index.search("", 10, "file")).entries).toEqual([
{ path: "watched.ts", kind: "file" },
]);
expect(
yield* index.searchContents({
query: "needle",
limit: 1,
caseSensitive: true,
wholeWord: false,
useRegex: false,
}),
).toEqual({ matches: [], truncated: false });
expect(scanFiles).not.toHaveBeenCalled();
yield* index.refresh();
expect(scanFiles).toHaveBeenCalledTimes(1);
}),
),
);

it.effect("filters image searches before applying the result limit", () =>
Effect.scoped(
Effect.gen(function* () {
Expand Down
Loading