test(desktop): keep host-bridge mocks whole and assert the review contract

The memhub CI job failed four desktop tests after the issue #29 change. Both
were test-side contracts that the new ownership model intentionally moves, not
regressions in the fix itself.

1. `host-control-ownership.test.ts` mocked `@one-memory/host-bridge` with a
   whitelist that carried only `RecallBroker`. Main now imports `domainCodeOf`,
   which runtime-control re-exports from host-bridge, so the mock handed back
   undefined and every error path that called it threw a TypeError instead of
   the error the test was asserting on. The mock now spreads the real module
   and overrides only `RecallBroker`, which is the part it was written for.

2. `host-permissions.test.ts` asserted the pre-#29 contract, where a change
   anywhere in a shared registry made uninstall throw
   `host_uninstall_modified_files`. Ownership is now the value at our own key,
   so a change to that value is reported instead of guessed at: the plan
   schedules no removal, names the file in a review finding, and applying it
   refuses with `host_uninstall_needs_review`. The test keeps the guarantee it
   existed for, that those bytes are never overwritten, and now also asserts
   that apply refuses, so the safety is checked at both steps.

Verification: full desktop suite 38 files / 436 tests pass, `pnpm -r typecheck`
clean, biome reports only two pre-existing warnings on lines this change does
not touch.
This commit is contained in:
kristinhe committed 2026-10-17 23:22:48 +08:00
1 parent 16953ed49e
commit 183b61e736
2 files changed
+18 -4

No files matched your search

@@ -191,7 +191,12 @@ vi.mock("@one-memory/host-bridge/installer", () => ({
renderConnectionMaterial: fake.localRead,
verifyRecallInstall: fake.localRead,
}));
vi.mock("@one-memory/host-bridge", () => ({
// Spread the real module instead of whitelisting it. Main imports domain
// helpers that runtime-control re-exports from host-bridge, so a partial mock
// silently hands back undefined for them and every error path that calls one
// fails with a TypeError instead of the code under test.
vi.mock("@one-memory/host-bridge", async (importOriginal) => ({
...(await importOriginal<typeof import("@one-memory/host-bridge")>()),
RecallBroker: class {
constructor(options: unknown) {
fake.brokerConstructed(options);
@@ -985,7 +985,7 @@ describe.skipIf(process.platform === "win32")(
);
});
it("does not overwrite modified plugin registry ownership during uninstall", async () => {
it("reports a modified managed registry entry instead of overwriting it", async () => {
const context = await fixture();
const layout = workbuddyLayout(path.join(context.root, "workbuddy"));
const plugin = required(layout.plugin);
@@ -1004,9 +1004,18 @@ describe.skipIf(process.platform === "win32")(
config["one-memory"].installLocation = "changed-by-user";
const changed = JSON.stringify(config);
await writeFile(plugin.knownMarketplacesPath, changed);
// The value at our own key changed. Overwriting it would be a guess, so
// uninstall schedules nothing, names the file in a review finding, and
// refuses when the preview is applied. The bytes stay as the host left
// them either way.
const removal = await context.permissions.previewUninstall(layout);
expect(removal.files).toEqual([]);
expect((removal.review ?? []).map((finding) => finding.path)).toContain(
plugin.knownMarketplacesPath,
);
await expect(
context.permissions.previewUninstall(layout),
).rejects.toThrow("host_uninstall_modified_files");
context.permissions.uninstall(layout, removal.fingerprint),
).rejects.toThrow("host_uninstall_needs_review");
expect(await readFile(plugin.knownMarketplacesPath, "utf8")).toBe(
changed,
);