139 lines · 5.2 KB · TypeScript
RawDownload
import { EnvironmentId, type VcsListRefsResult } from "@t3tools/contracts";import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Option from "effect/Option"; import { type ClientCacheKind, MobileDatabase } from "../persistence/mobile-database";import { make } from "./environment-cache-store"; const ENVIRONMENT_ID = EnvironmentId.make("environment-1");const REFS: VcsListRefsResult = {  refs: [    {      name: "main",      current: true,      isDefault: true,      worktreePath: "/repo",    },  ],  isRepo: true,  hasPrimaryRemote: true,  nextCursor: null,  totalCount: 1,}; function cacheId(environmentId: EnvironmentId, kind: ClientCacheKind, cacheKey: string) {  return `${environmentId}:${kind}:${cacheKey}`;} function makeDatabase() {  const values = new Map<string, string>();  const removed: Array<string> = [];  const database = MobileDatabase.of({    loadCache: (environmentId, kind, cacheKey) =>      Effect.succeed(Option.fromUndefinedOr(values.get(cacheId(environmentId, kind, cacheKey)))),    listCache: (kind) =>      Effect.sync(() =>        [...values.entries()]          .filter(([key]) => key.split(":")[1] === kind)          .map(([, payload]) => payload),      ),    saveCache: (environmentId, kind, cacheKey, _schemaVersion, payload) =>      Effect.sync(() => {        values.set(cacheId(environmentId, kind, cacheKey), payload);      }),    removeCache: (environmentId, kind, cacheKey) =>      Effect.sync(() => {        const id = cacheId(environmentId, kind, cacheKey);        removed.push(id);        values.delete(id);      }),    clearCacheKind: (environmentId, kind) =>      Effect.sync(() => {        for (const key of values.keys()) {          if (key.startsWith(`${environmentId}:${kind}:`)) values.delete(key);        }      }),    clearEnvironmentCache: (environmentId) =>      Effect.sync(() => {        for (const key of values.keys()) {          if (key.startsWith(`${environmentId}:`)) values.delete(key);        }      }),    clearAllCaches: Effect.sync(() => values.clear()),    inspectCaches: Effect.succeed([]),    loadPreferencesJson: Effect.succeed(Option.none()),    savePreferencesJson: () => Effect.void,  });  return { database, removed, values };} describe("mobile SQLite environment cache store", () => {  it.effect("round-trips schema-validated VCS refs", () =>    Effect.gen(function* () {      const memory = makeDatabase();      const store = yield* make().pipe(Effect.provideService(MobileDatabase, memory.database));       yield* store.saveVcsRefs(ENVIRONMENT_ID, "/repo", REFS);       expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo")).toEqual(Option.some(REFS));    }),  );   it.effect("deletes a corrupt cache record and treats it as a miss", () =>    Effect.gen(function* () {      const memory = makeDatabase();      const store = yield* make().pipe(Effect.provideService(MobileDatabase, memory.database));      const id = cacheId(ENVIRONMENT_ID, "vcs-refs", "/repo");      memory.values.set(id, "{not-json");       expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo")).toEqual(Option.none());      expect(memory.removed).toEqual([id]);    }),  );   it.effect("removes one persisted VCS ref snapshot", () =>    Effect.gen(function* () {      const memory = makeDatabase();      const store = yield* make().pipe(Effect.provideService(MobileDatabase, memory.database));      yield* store.saveVcsRefs(ENVIRONMENT_ID, "/repo", REFS);       yield* store.removeVcsRefs(ENVIRONMENT_ID, "/repo");       expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo")).toEqual(Option.none());      expect(memory.removed).toContain(cacheId(ENVIRONMENT_ID, "vcs-refs", "/repo"));    }),  );   it.effect("clears every persisted VCS ref snapshot in one environment", () =>    Effect.gen(function* () {      const memory = makeDatabase();      const store = yield* make().pipe(Effect.provideService(MobileDatabase, memory.database));      const otherEnvironmentId = EnvironmentId.make("environment-2");      yield* store.saveVcsRefs(ENVIRONMENT_ID, "/repo", REFS);      yield* store.saveVcsRefs(ENVIRONMENT_ID, "/repo-worktree", REFS);      yield* store.saveVcsRefs(otherEnvironmentId, "/repo", REFS);       yield* store.clearVcsRefs(ENVIRONMENT_ID);       expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo")).toEqual(Option.none());      expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo-worktree")).toEqual(Option.none());      expect(yield* store.loadVcsRefs(otherEnvironmentId, "/repo")).toEqual(Option.some(REFS));    }),  );   it.effect("clears one environment without touching another", () =>    Effect.gen(function* () {      const memory = makeDatabase();      const store = yield* make().pipe(Effect.provideService(MobileDatabase, memory.database));      const otherEnvironmentId = EnvironmentId.make("environment-2");      yield* store.saveVcsRefs(ENVIRONMENT_ID, "/repo", REFS);      yield* store.saveVcsRefs(otherEnvironmentId, "/repo", REFS);       yield* store.clear(ENVIRONMENT_ID);       expect(yield* store.loadVcsRefs(ENVIRONMENT_ID, "/repo")).toEqual(Option.none());      expect(yield* store.loadVcsRefs(otherEnvironmentId, "/repo")).toEqual(Option.some(REFS));    }),  );});