Skip to content
Closed
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
76 changes: 69 additions & 7 deletions apps/server/src/orchestration/ThreadSettlementReactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options:
const snapshotReadCount = yield* Ref.make(0);
const snapshotReads = yield* Queue.unbounded<number>();
const settings = yield* Ref.make(options.settings ?? DEFAULT_SERVER_SETTINGS);
const settingsReads = yield* Queue.unbounded<ServerSettings>();
const settingsChanges = yield* PubSub.unbounded<ServerSettings>();
const mergedPullRequests = yield* PubSub.unbounded<PullRequestMergeEvent>();
const commands = yield* Ref.make<ReadonlyArray<AutoSettleCommand>>([]);
Expand Down Expand Up @@ -208,7 +209,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options:
const serverSettings = ServerSettingsService.of({
start: Effect.void,
ready: Effect.void,
getSettings: Ref.get(settings),
getSettings: Ref.get(settings).pipe(Effect.tap((value) => Queue.offer(settingsReads, value))),
updateSettings,
streamChanges: Stream.fromPubSub(settingsChanges),
subscribeChanges: PubSub.subscribe(settingsChanges).pipe(
Expand Down Expand Up @@ -253,6 +254,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options:
snapshots,
snapshotReadCount,
snapshotReads,
settingsReads,
commands,
branchCalls,
summaryCalls,
Expand Down Expand Up @@ -281,6 +283,63 @@ const startHarness = Effect.fn("startThreadSettlementHarness")(function* (
});

describe("ThreadSettlementReactor", () => {
it.effect("skips PR work on startup, timer and merge sweeps when settlement is disabled", () =>
Effect.scoped(
Effect.gen(function* () {
yield* TestClock.setTime(Date.parse(NOW));
const fixture = yield* makeHarness({
snapshot: makeSnapshot([
makeThread("branch-thread", { branch: "feature" }),
makeThread("linked-thread", {
linkedPullRequest: {
projectId: PROJECT_ID,
repository: "owner/repository",
number: 42,
url: "https://example.test/owner/repository/pull/42",
},
}),
]),
settings: {
...DEFAULT_SERVER_SETTINGS,
sidebarAutoSettleAfterDays: null,
sidebarAutoSettleOnMerge: false,
},
});

yield* Effect.gen(function* () {
const reactor = yield* ThreadSettlementReactor.ThreadSettlementReactor;
yield* reactor.start();
yield* Queue.take(fixture.settingsReads);
yield* Deferred.succeed(fixture.activation, undefined);
yield* Queue.take(fixture.settingsReads);
yield* reactor.drain;
yield* TestClock.adjust("1 minute");
yield* Queue.take(fixture.settingsReads);
yield* reactor.drain;
yield* fixture.publishMerge;
yield* Queue.take(fixture.settingsReads);
yield* reactor.drain;

assert.deepStrictEqual(yield* Ref.get(fixture.branchCalls), []);
assert.deepStrictEqual(yield* Ref.get(fixture.summaryCalls), []);
assert.deepStrictEqual(yield* Ref.get(fixture.invalidatedCwds), []);
assert.deepStrictEqual(yield* Ref.get(fixture.commands), []);
assert.strictEqual(yield* Ref.get(fixture.snapshotReadCount), 0);

yield* fixture.updateSettings({ sidebarAutoSettleAfterDays: 1 });
yield* Queue.take(fixture.snapshotReads);
yield* reactor.drain;
assert.strictEqual((yield* Ref.get(fixture.branchCalls)).length, 1);
assert.strictEqual((yield* Ref.get(fixture.summaryCalls)).length, 1);
assert.deepStrictEqual(
(yield* Ref.get(fixture.commands)).map((command) => command.threadId).toSorted(),
[ThreadId.make("branch-thread"), ThreadId.make("linked-thread")],
);
}).pipe(Effect.provide(fixture.layer));
}),
),
);

it.effect("starts without clients and skips protected threads before pull request lookup", () =>
Effect.scoped(
Effect.gen(function* () {
Expand Down Expand Up @@ -600,14 +659,14 @@ describe("ThreadSettlementReactor", () => {
Effect.tap((count) =>
count === 1
? Deferred.succeed(firstLookupStarted, undefined)
: count === 3
: count === 2
? Deferred.succeed(laterLookupStarted, undefined)
: Effect.void,
),
Effect.tap((count) =>
count === 1
? Deferred.await(releaseFirstLookup)
: count === 3
: count === 2
? Deferred.await(releaseLaterLookup)
: Effect.void,
),
Expand All @@ -627,13 +686,16 @@ describe("ThreadSettlementReactor", () => {
yield* Deferred.succeed(fixture.activation, undefined);
yield* Queue.take(fixture.snapshotReads);
yield* Deferred.await(firstLookupStarted);
yield* Queue.clear(fixture.settingsReads);

yield* fixture.updateSettings({ sidebarAutoSettleOnMerge: false });
yield* Deferred.succeed(releaseFirstLookup, undefined);
yield* Queue.take(fixture.snapshotReads);
// The in-flight decision and the newly queued sweep both read the disabled settings.
yield* Queue.take(fixture.settingsReads);
yield* Queue.take(fixture.settingsReads);
yield* reactor.drain;
assert.deepStrictEqual(yield* Ref.get(fixture.commands), []);
assert.strictEqual(yield* Ref.get(fixture.snapshotReadCount), 2);
assert.strictEqual(yield* Ref.get(fixture.snapshotReadCount), 1);

yield* Ref.set(state, "closed");
yield* fixture.updateSettings({ enableAgentBrowserAccess: false });
Expand All @@ -642,8 +704,8 @@ describe("ThreadSettlementReactor", () => {
yield* Deferred.succeed(releaseLaterLookup, undefined);
yield* reactor.drain;

assert.strictEqual(yield* Ref.get(fixture.snapshotReadCount), 3);
assert.strictEqual(yield* Ref.get(lookupCount), 3);
assert.strictEqual(yield* Ref.get(fixture.snapshotReadCount), 2);
assert.strictEqual(yield* Ref.get(lookupCount), 2);
assert.deepStrictEqual(
(yield* Ref.get(fixture.commands)).map((command) => command.threadId),
[ThreadId.make("settings-thread")],
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/orchestration/ThreadSettlementReactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export const make = Effect.gen(function* () {
const sweep = Effect.fn("ThreadSettlementReactor.sweep")(function* (
mergedPullRequest: PullRequestService.PullRequestMergeEvent | null,
) {
const settings = yield* settingsService.getSettings;
if (!settings.sidebarAutoSettleOnMerge && settings.sidebarAutoSettleAfterDays === null) {
return;
}
const snapshot = yield* snapshots.getShellSnapshot();
const now = DateTime.formatIso(yield* DateTime.now);
const projects = new Map(snapshot.projects.map((project) => [project.id, project]));
Expand Down
Loading