Skip to content
Source
Sign inSign up

keybindings.test.ts

619 lines23.8 KB

1import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts";
2import * as NodeServices from "@effect/platform-node/NodeServices";
3import { assert, it } from "@effect/vitest";
4import { assertFailure } from "@effect/vitest/utils";
5import * as Effect from "effect/Effect";
6import * as FileSystem from "effect/FileSystem";
7import * as Layer from "effect/Layer";
8import * as Logger from "effect/Logger";
9import * as Path from "effect/Path";
10import * as Schema from "effect/Schema";
11import * as ServerConfig from "./config.ts";
12import * as Keybindings from "./keybindings.ts";
13import * as SharedKeybindings from "@t3tools/shared/keybindings";
14import { KeybindingsConfigError, MAX_KEYBINDINGS_COUNT } from "@t3tools/contracts";
15​
16const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig);
17const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson);
18const decodeKeybindingsConfigJson = Schema.decodeUnknownEffect(KeybindingsConfigJson);
19const layerKeybindings = () => {
20 return Keybindings.layer.pipe(
21 Layer.provideMerge(
22 Layer.fresh(
23 ServerConfig.layerTest(process.cwd(), {
24 prefix: "t3code-keybindings-test-",
25 }),
26 ),
27 ),
28 );
29};
30​
31const toDetailResult = <A, R>(effect: Effect.Effect<A, KeybindingsConfigError, R>) =>
32 effect.pipe(
33 Effect.mapError((error) => error.detail),
34 Effect.result,
35 );
36​
37const writeKeybindingsConfig = (configPath: string, rules: readonly KeybindingRule[]) =>
38 Effect.gen(function* () {
39 const fileSystem = yield* FileSystem.FileSystem;
40 const path = yield* Path.Path;
41 const encoded = yield* encodeKeybindingsConfigJson(rules);
42 yield* fileSystem.makeDirectory(path.dirname(configPath), { recursive: true });
43 yield* fileSystem.writeFileString(configPath, encoded);
44 });
45​
46const readKeybindingsConfig = (configPath: string) =>
47 Effect.gen(function* () {
48 const fileSystem = yield* FileSystem.FileSystem;
49 const rawConfig = yield* fileSystem.readFileString(configPath);
50 return yield* decodeKeybindingsConfigJson(rawConfig);
51 });
52​
53it.layer(NodeServices.layer)("keybindings", (it) => {
54 it.effect("parses shortcuts including plus key", () =>
55 Effect.sync(() => {
56 assert.deepEqual(SharedKeybindings.parseKeybindingShortcut("mod+j"), {
57 key: "j",
58 metaKey: false,
59 ctrlKey: false,
60 shiftKey: false,
61 altKey: false,
62 modKey: true,
63 });
64 assert.deepEqual(SharedKeybindings.parseKeybindingShortcut("mod++"), {
65 key: "+",
66 metaKey: false,
67 ctrlKey: false,
68 shiftKey: false,
69 altKey: false,
70 modKey: true,
71 });
72 }),
73 );
74​
75 it.effect("compiles valid rule with parsed when AST", () =>
76 Effect.sync(() => {
77 const compiled = SharedKeybindings.compileResolvedKeybindingRule({
78 key: "mod+d",
79 command: "terminal.split",
80 when: "terminalOpen && !terminalFocus",
81 });
82​
83 assert.deepEqual(compiled, {
84 command: "terminal.split",
85 shortcut: {
86 key: "d",
87 metaKey: false,
88 ctrlKey: false,
89 shiftKey: false,
90 altKey: false,
91 modKey: true,
92 },
93 whenAst: {
94 type: "and",
95 left: { type: "identifier", name: "terminalOpen" },
96 right: {
97 type: "not",
98 node: { type: "identifier", name: "terminalFocus" },
99 },
100 },
101 });
102 }),
103 );
104​
105 it.effect("rejects invalid rules", () =>
106 Effect.sync(() => {
107 assert.isNull(
108 SharedKeybindings.compileResolvedKeybindingRule({
109 key: "mod+shift+d+o",
110 command: "terminal.new",
111 }),
112 );
113​
114 assert.isNull(
115 SharedKeybindings.compileResolvedKeybindingRule({
116 key: "mod+d",
117 command: "terminal.split",
118 when: "terminalFocus && (",
119 }),
120 );
121​
122 assert.isNull(
123 SharedKeybindings.compileResolvedKeybindingRule({
124 key: "mod+d",
125 command: "terminal.split",
126 when: `${"!".repeat(300)}terminalFocus`,
127 }),
128 );
129 }),
130 );
131​
132 it.effect("bootstraps default keybindings when config file is missing", () =>
133 Effect.gen(function* () {
134 const fs = yield* FileSystem.FileSystem;
135 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
136 assert.isFalse(yield* fs.exists(keybindingsConfigPath));
137​
138 yield* Effect.gen(function* () {
139 const keybindings = yield* Keybindings.Keybindings;
140 yield* keybindings.syncDefaultKeybindingsOnStartup;
141 });
142​
143 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
144 assert.deepEqual(persisted, SharedKeybindings.DEFAULT_KEYBINDINGS);
145 }).pipe(Effect.provide(layerKeybindings())),
146 );
147​
148 it.effect("uses defaults in runtime when config is malformed without overriding file", () =>
149 Effect.gen(function* () {
150 const fs = yield* FileSystem.FileSystem;
151 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
152 yield* fs.writeFileString(keybindingsConfigPath, "{ not-json");
153​
154 const configState = yield* Effect.gen(function* () {
155 const keybindings = yield* Keybindings.Keybindings;
156 return yield* keybindings.loadConfigState;
157 });
158​
159 assert.deepEqual(
160 configState.keybindings,
161 SharedKeybindings.compileResolvedKeybindingsConfig(SharedKeybindings.DEFAULT_KEYBINDINGS),
162 );
163 assert.deepEqual(configState.issues, [
164 {
165 kind: "keybindings.malformed-config",
166 message: configState.issues[0]?.message ?? "",
167 },
168 ]);
169 assert.equal(yield* fs.readFileString(keybindingsConfigPath), "{ not-json");
170 }).pipe(Effect.provide(layerKeybindings())),
171 );
172​
173 it.effect("ignores invalid entries in runtime and reports them as issues", () =>
174 Effect.gen(function* () {
175 const fs = yield* FileSystem.FileSystem;
176 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
177 yield* fs.writeFileString(
178 keybindingsConfigPath,
179 JSON.stringify([
180 { key: "mod+j", command: "terminal.toggle" },
181 { key: "mod+shift+d+o", command: "terminal.new" },
182 { key: "mod+x", command: "invalid.command" },
183 ]),
184 );
185​
186 const configState = yield* Effect.gen(function* () {
187 const keybindings = yield* Keybindings.Keybindings;
188 return yield* keybindings.loadConfigState;
189 });
190​
191 assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle"));
192 assert.isFalse(
193 configState.keybindings.some((entry) => String(entry.command) === "invalid.command"),
194 );
195 assert.deepEqual(configState.issues, [
196 {
197 kind: "keybindings.invalid-entry",
198 index: 1,
199 message: configState.issues[0]?.message ?? "",
200 },
201 {
202 kind: "keybindings.invalid-entry",
203 index: 2,
204 message: configState.issues[1]?.message ?? "",
205 },
206 ]);
207 }).pipe(Effect.provide(layerKeybindings())),
208 );
209​
210 it.effect(
211 "upserts missing default keybindings on startup without overriding existing command rules",
212 () =>
213 Effect.gen(function* () {
214 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
215 yield* writeKeybindingsConfig(keybindingsConfigPath, [
216 { key: "mod+shift+y", command: "terminal.toggle" },
217 { key: "mod+shift+r", command: "script.run-tests.run" },
218 { key: "mod+shift+b", command: "script.custom-panel.run" },
219 {
220 key: "mod+shift+b",
221 command: "script.custom-composer.run",
222 when: "composerFocus",
223 },
224 ]);
225​
226 const keybindings = yield* Keybindings.Keybindings;
227 yield* keybindings.syncDefaultKeybindingsOnStartup;
228​
229 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
230 const byCommand = new Map(persisted.map((entry) => [entry.command, entry]));
231​
232 const persistedToggle = byCommand.get("terminal.toggle");
233 assert.isNotNull(persistedToggle);
234 assert.equal(persistedToggle?.key, "mod+shift+y");
235 assert.isFalse(
236 persisted.some((entry) => entry.command === "terminal.toggle" && entry.key === "mod+j"),
237 );
238​
239 for (const defaultRule of SharedKeybindings.DEFAULT_KEYBINDINGS) {
240 assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`);
241 }
242 assert.isTrue(byCommand.has("script.run-tests.run"));
243​
244 const configState = yield* keybindings.loadConfigState;
245 assert.deepEqual(
246 configState.keybindings
247 .filter((entry) => entry.shortcut.key === "b" && entry.shortcut.shiftKey)
248 .map((entry) => entry.command),
249 ["threadPanel.toggle", "script.custom-panel.run", "script.custom-composer.run"],
250 );
251 }).pipe(Effect.provide(layerKeybindings())),
252 );
253​
254 it.effect("adds a late default to an existing command once", () =>
255 Effect.gen(function* () {
256 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
257 const keybindings = yield* Keybindings.Keybindings;
258 const when = "composerFocus && draftThreadRoute";
259 const existing = { key: "mod+alt+enter", command: "composer.sendBackground", when } as const;
260 const backgroundRules = Effect.map(readKeybindingsConfig(keybindingsConfigPath), (rules) =>
261 rules.filter((entry) => entry.command === "composer.sendBackground"),
262 );
263 yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]);
264​
265 yield* keybindings.syncDefaultKeybindingsOnStartup;
266 assert.deepStrictEqual(yield* backgroundRules, [
267 { key: "mod+enter", command: "composer.sendBackground", when },
268 existing,
269 ]);
270​
271 yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]);
272 yield* keybindings.syncDefaultKeybindingsOnStartup;
273 assert.deepStrictEqual(yield* backgroundRules, [existing]);
274 }).pipe(Effect.provide(layerKeybindings())),
275 );
276​
277 it.effect("leaves a customized command without the late default", () =>
278 Effect.gen(function* () {
279 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
280 const keybindings = yield* Keybindings.Keybindings;
281 const custom = {
282 key: "alt+b",
283 command: "composer.sendBackground",
284 when: "composerFocus && draftThreadRoute",
285 } as const;
286 yield* writeKeybindingsConfig(keybindingsConfigPath, [custom]);
287​
288 yield* keybindings.syncDefaultKeybindingsOnStartup;
289 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
290 assert.deepStrictEqual(
291 persisted.filter((entry) => entry.command === "composer.sendBackground"),
292 [custom],
293 );
294 }).pipe(Effect.provide(layerKeybindings())),
295 );
296​
297 it.effect("keeps a late default pending while the config is full", () =>
298 Effect.gen(function* () {
299 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
300 const keybindings = yield* Keybindings.Keybindings;
301 const when = "composerFocus && draftThreadRoute";
302 const existing = { key: "mod+alt+enter", command: "composer.sendBackground", when } as const;
303 const fillers = Array.from({ length: MAX_KEYBINDINGS_COUNT - 1 }, (_, index) => ({
304 key: "mod+alt+f1",
305 command: `script.filler-${index}.run` as const,
306 }));
307 const hasModEnter = Effect.map(readKeybindingsConfig(keybindingsConfigPath), (rules) =>
308 rules.some(
309 (entry) => entry.command === "composer.sendBackground" && entry.key === "mod+enter",
310 ),
311 );
312​
313 yield* writeKeybindingsConfig(keybindingsConfigPath, [existing, ...fillers]);
314 yield* keybindings.syncDefaultKeybindingsOnStartup;
315 assert.isFalse(yield* hasModEnter);
316​
317 yield* writeKeybindingsConfig(keybindingsConfigPath, [existing]);
318 yield* keybindings.syncDefaultKeybindingsOnStartup;
319 assert.isTrue(yield* hasModEnter);
320 }).pipe(Effect.provide(layerKeybindings())),
321 );
322​
323 it.effect("skips conflicting default keybindings on startup and logs a detailed warning", () => {
324 const messages: string[] = [];
325 const logger = Logger.make(({ message }) => {
326 messages.push(String(message));
327 });
328​
329 return Effect.gen(function* () {
330 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
331 yield* writeKeybindingsConfig(keybindingsConfigPath, [
332 { key: "mod+j", command: "script.custom-action.run" },
333 ]);
334​
335 yield* Effect.gen(function* () {
336 const keybindings = yield* Keybindings.Keybindings;
337 yield* keybindings.syncDefaultKeybindingsOnStartup;
338 });
339​
340 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
341 assert.isFalse(persisted.some((entry) => entry.command === "terminal.toggle"));
342 assert.isTrue(persisted.some((entry) => entry.command === "script.custom-action.run"));
343​
344 assert.isTrue(
345 messages.some((message) =>
346 message.includes("skipping default keybinding due to shortcut conflict"),
347 ),
348 );
349 }).pipe(
350 Effect.provide(
351 Layer.mergeAll(layerKeybindings(), Logger.layer([logger], { mergeWithExisting: false })),
352 ),
353 );
354 });
355​
356 it.effect("appends additional custom keybindings for the same command", () =>
357 Effect.gen(function* () {
358 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
359 yield* writeKeybindingsConfig(keybindingsConfigPath, [
360 { key: "mod+r", command: "script.run-tests.run" },
361 ]);
362 yield* Effect.gen(function* () {
363 const keybindings = yield* Keybindings.Keybindings;
364 return yield* keybindings.upsertKeybindingRule({
365 key: "mod+shift+r",
366 command: "script.run-tests.run",
367 });
368 });
369​
370 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
371 const persistedView = persisted.map(({ key, command }) => ({ key, command }));
372 assert.deepEqual(persistedView, [
373 { key: "mod+r", command: "script.run-tests.run" },
374 { key: "mod+shift+r", command: "script.run-tests.run" },
375 ]);
376 }).pipe(Effect.provide(layerKeybindings())),
377 );
378​
379 it.effect("replaces only the targeted custom keybinding", () =>
380 Effect.gen(function* () {
381 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
382 yield* writeKeybindingsConfig(keybindingsConfigPath, [
383 { key: "mod+r", command: "script.run-tests.run" },
384 { key: "mod+shift+r", command: "script.run-tests.run" },
385 ]);
386 yield* Effect.gen(function* () {
387 const keybindings = yield* Keybindings.Keybindings;
388 return yield* keybindings.upsertKeybindingRule({
389 key: "mod+alt+r",
390 command: "script.run-tests.run",
391 replace: { key: "mod+r", command: "script.run-tests.run" },
392 });
393 });
394​
395 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
396 const persistedView = persisted.map(({ key, command }) => ({ key, command }));
397 assert.deepEqual(persistedView, [
398 { key: "mod+shift+r", command: "script.run-tests.run" },
399 { key: "mod+alt+r", command: "script.run-tests.run" },
400 ]);
401 }).pipe(Effect.provide(layerKeybindings())),
402 );
403​
404 it.effect("replacing with a rule that already exists elsewhere does not duplicate it", () =>
405 Effect.gen(function* () {
406 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
407 yield* writeKeybindingsConfig(keybindingsConfigPath, [
408 { key: "mod+r", command: "script.run-tests.run" },
409 { key: "mod+alt+r", command: "script.run-tests.run" },
410 ]);
411 yield* Effect.gen(function* () {
412 const keybindings = yield* Keybindings.Keybindings;
413 return yield* keybindings.upsertKeybindingRule({
414 key: "mod+alt+r",
415 command: "script.run-tests.run",
416 replace: { key: "mod+r", command: "script.run-tests.run" },
417 });
418 });
419​
420 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
421 const persistedView = persisted.map(({ key, command }) => ({ key, command }));
422 assert.deepEqual(persistedView, [{ key: "mod+alt+r", command: "script.run-tests.run" }]);
423 }).pipe(Effect.provide(layerKeybindings())),
424 );
425​
426 it.effect("removes only the targeted custom keybinding", () =>
427 Effect.gen(function* () {
428 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
429 yield* writeKeybindingsConfig(keybindingsConfigPath, [
430 { key: "mod+r", command: "script.run-tests.run" },
431 { key: "mod+shift+r", command: "script.run-tests.run" },
432 ]);
433 yield* Effect.gen(function* () {
434 const keybindings = yield* Keybindings.Keybindings;
435 return yield* keybindings.removeKeybindingRule({
436 key: "mod+r",
437 command: "script.run-tests.run",
438 });
439 });
440​
441 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
442 const persistedView = persisted.map(({ key, command }) => ({ key, command }));
443 assert.deepEqual(persistedView, [{ key: "mod+shift+r", command: "script.run-tests.run" }]);
444 }).pipe(Effect.provide(layerKeybindings())),
445 );
446​
447 it.effect("keeps entries it cannot decode when editing other keybindings", () =>
448 Effect.gen(function* () {
449 const fs = yield* FileSystem.FileSystem;
450 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
451 const futureCommand = { key: "mod+shift+k", command: "command.from-a-newer-build" };
452 const badShortcut = { key: "mod+shift+d+o", command: "terminal.new" };
453 yield* fs.writeFileString(
454 keybindingsConfigPath,
455 JSON.stringify([
456 { key: "mod+r", command: "script.run-tests.run" },
457 futureCommand,
458 badShortcut,
459 ]),
460 );
461 const keybindings = yield* Keybindings.Keybindings;
462 const persisted = Effect.map(fs.readFileString(keybindingsConfigPath), JSON.parse);
463​
464 const saved = yield* keybindings.upsertKeybindingRule({
465 key: "mod+shift+r",
466 command: "script.run-tests.run",
467 });
468 assert.equal(saved.issues.length, 2);
469 assert.deepEqual(yield* persisted, [
470 { key: "mod+r", command: "script.run-tests.run" },
471 futureCommand,
472 badShortcut,
473 { key: "mod+shift+r", command: "script.run-tests.run" },
474 ]);
475​
476 const removed = yield* keybindings.removeKeybindingRule({
477 key: "mod+r",
478 command: "script.run-tests.run",
479 });
480 assert.deepEqual(yield* persisted, [
481 futureCommand,
482 badShortcut,
483 { key: "mod+shift+r", command: "script.run-tests.run" },
484 ]);
485 const { issues } = yield* keybindings.loadConfigState;
486 assert.deepEqual(removed.issues, issues);
487 assert.deepEqual(
488 issues.map((issue) => issue.kind === "keybindings.invalid-entry" && issue.index),
489 [0, 1],
490 );
491 }).pipe(Effect.provide(layerKeybindings())),
492 );
493​
494 it.effect("removes a hand-written keybinding targeted by its settings spelling", () =>
495 Effect.gen(function* () {
496 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
497 yield* writeKeybindingsConfig(keybindingsConfigPath, [
498 {
499 key: "Shift+Cmd+R",
500 command: "script.run-tests.run",
501 when: "terminalFocus&&!previewFocus",
502 },
503 { key: "mod+shift+r", command: "script.run-tests.run" },
504 { key: "x\u0000true", command: "script.run-tests.run" },
505 { key: "x", command: "script.run-tests.run", when: "true" },
506 ]);
507 yield* Effect.gen(function* () {
508 const keybindings = yield* Keybindings.Keybindings;
509 yield* keybindings.removeKeybindingRule({
510 key: "meta+shift+r",
511 command: "script.run-tests.run",
512 when: "terminalFocus && !previewFocus",
513 });
514 yield* keybindings.removeKeybindingRule({
515 key: "x",
516 command: "script.run-tests.run",
517 when: "true",
518 });
519 });
520​
521 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
522 assert.deepEqual(persisted, [
523 { key: "mod+shift+r", command: "script.run-tests.run" },
524 { key: "x\u0000true", command: "script.run-tests.run" },
525 ]);
526 }).pipe(Effect.provide(layerKeybindings())),
527 );
528​
529 it.effect("replaces a hand-written keybinding targeted by its settings spelling", () =>
530 Effect.gen(function* () {
531 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
532 yield* writeKeybindingsConfig(keybindingsConfigPath, [
533 { key: "escape", command: "script.run-tests.run", when: "a && b && c" },
534 ]);
535 yield* Effect.gen(function* () {
536 const keybindings = yield* Keybindings.Keybindings;
537 return yield* keybindings.upsertKeybindingRule({
538 key: "mod+alt+r",
539 command: "script.run-tests.run",
540 replace: { key: "esc", command: "script.run-tests.run", when: "(a && b) && c" },
541 });
542 });
543​
544 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
545 assert.deepEqual(persisted, [{ key: "mod+alt+r", command: "script.run-tests.run" }]);
546 }).pipe(Effect.provide(layerKeybindings())),
547 );
548​
549 it.effect("refuses to overwrite malformed keybindings config", () =>
550 Effect.gen(function* () {
551 const fs = yield* FileSystem.FileSystem;
552 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
553 yield* fs.writeFileString(keybindingsConfigPath, "{ not-json");
554​
555 const result = yield* Effect.gen(function* () {
556 const keybindings = yield* Keybindings.Keybindings;
557 return yield* keybindings.upsertKeybindingRule({
558 key: "mod+shift+r",
559 command: "script.run-tests.run",
560 });
561 }).pipe(toDetailResult);
562 assertFailure(result, "expected JSON array");
563​
564 const persistedRaw = yield* fs.readFileString(keybindingsConfigPath);
565 assert.equal(persistedRaw, "{ not-json");
566 }).pipe(Effect.provide(layerKeybindings())),
567 );
568​
569 it.effect("retries a failed config read instead of keeping the failure", () =>
570 Effect.gen(function* () {
571 const fileSystem = yield* FileSystem.FileSystem;
572 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
573 yield* fileSystem.makeDirectory(keybindingsConfigPath, { recursive: true });
574​
575 const keybindings = yield* Keybindings.Keybindings;
576 const failed = yield* toDetailResult(keybindings.loadConfigState);
577 assertFailure(failed, "failed to read keybindings config");
578​
579 yield* fileSystem.remove(keybindingsConfigPath, { recursive: true });
580 yield* writeKeybindingsConfig(keybindingsConfigPath, [
581 { key: "mod+j", command: "terminal.toggle" },
582 ]);
583​
584 const configState = yield* keybindings.loadConfigState;
585 assert.deepEqual(configState.issues, []);
586 assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle"));
587 }).pipe(Effect.provide(layerKeybindings())),
588 );
589​
590 it.effect("serializes concurrent upserts to avoid lost updates", () =>
591 Effect.gen(function* () {
592 const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig;
593 yield* writeKeybindingsConfig(keybindingsConfigPath, []);
594​
595 const commands = Array.from(
596 { length: 20 },
597 (_, index): KeybindingCommand => `script.concurrent-${index}.run`,
598 );
599 yield* Effect.gen(function* () {
600 const keybindings = yield* Keybindings.Keybindings;
601 yield* Effect.forEach(
602 commands,
603 (command, index) =>
604 keybindings.upsertKeybindingRule({
605 key: `mod+${String.fromCharCode(97 + index)}`,
606 command,
607 }),
608 { concurrency: "unbounded", discard: true },
609 );
610 });
611​
612 const persisted = yield* readKeybindingsConfig(keybindingsConfigPath);
613 const persistedCommands = new Set(persisted.map((entry) => entry.command));
614 for (const command of commands) {
615 assert.isTrue(persistedCommands.has(command), `expected persisted command ${command}`);
616 }
617 }).pipe(Effect.provide(layerKeybindings())),
618 );
619});