Files
lefarcen b55d443b44 fix(web): streamline Home submit preflight (#6756)
* fix(web): retry transient home balance preflight

* refactor(web): streamline home submit preflight

* fix(web): keep local submit independent from cloud identity

* fix(web): preserve local and legacy submit lanes

* fix(web): keep in-memory workspace witness valid

* fix(web): retain workspace preflight compatibility

* fix(web): require workspace witness for team plugin apply

* fix(plugins): apply exact local plugin source

* fix(web): invalidate legacy witness on identity change

* refactor: keep Home submit local-first

* test: align local-first creation coverage

* fix: preserve exact local plugin context

* fix: fence exact local plugin use

* fix: disable inexact legacy plugin fallback

* test: mock exact plugin apply in visuals

* fix: reject unsupported exact project source

* Revert "fix: reject unsupported exact project source"

This reverts commit 99e0cf23c1.

* Revert "test: mock exact plugin apply in visuals"

This reverts commit 33b36688c7.

* Revert "fix: disable inexact legacy plugin fallback"

This reverts commit d1b4d9bcaa.

* Revert "fix: fence exact local plugin use"

This reverts commit aebaf6a20b.

* Revert "fix: preserve exact local plugin context"

This reverts commit aa6ab67631.

* Revert "fix(plugins): apply exact local plugin source"

This reverts commit e776a7d079.

* fix: reconcile local submit selections

* fix: preserve cold cloud submit handoff

* test: seed active team design-system binding

* fix(plugins): preserve exact local source semantics

* test(web): align Home failure copy after main merge

* fix(home): preserve staged resource catalog scope
2026-08-13 06:33:29 +00:00

175 lines
5.7 KiB
TypeScript

import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import {
ensureWorkspaceResource,
openDatabase,
updateWorkspaceResource,
} from '../src/db.js';
import {
materializeWorkspaceScopedTeamResource,
teamResourceMaterializationDir,
} from '../src/collab/team-resource-materialization.js';
import {
localPluginRegistryScope,
resolveLocalPluginBySource,
resolvePluginFolder,
resolvePluginSnapshot,
upsertInstalledPlugin,
workspaceTeamPluginBindingResourceId,
} from '../src/plugins/index.js';
const roots: string[] = [];
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) =>
rm(root, { recursive: true, force: true }),
));
});
async function pluginManifest(folder: string, title: string): Promise<void> {
await mkdir(folder, { recursive: true });
await writeFile(
path.join(folder, 'open-design.json'),
JSON.stringify({ name: 'shared-id', title, version: '1.0.0' }),
);
}
describe('resolveLocalPluginBySource', () => {
it('derives local registry provenance only for an exact Team source', () => {
expect(localPluginRegistryScope({
id: 'shared-id',
source: 'team:plugin:workspace-a:shared-id',
})).toEqual({ workspaceId: 'workspace-a', workspaceMemberId: null });
expect(localPluginRegistryScope({
id: 'shared-id',
source: 'local:personal:shared-id',
})).toBeUndefined();
});
it('selects the exact local record when Personal and Team share an id', async () => {
const root = await mkdtemp(path.join(os.tmpdir(), 'od-local-plugin-source-'));
roots.push(root);
const dataDir = path.join(root, 'data');
const pluginsRoot = path.join(dataDir, 'plugins');
const db = openDatabase(root, { dataDir });
const personalFolder = path.join(root, 'personal-plugin');
await pluginManifest(personalFolder, 'Personal copy');
const personal = await resolvePluginFolder({
folder: personalFolder,
folderId: 'shared-id',
sourceKind: 'local',
source: 'local:personal:shared-id',
});
if (!personal.ok) throw new Error(personal.errors.join('; '));
upsertInstalledPlugin(db, personal.record);
const team = await materializeWorkspaceScopedTeamResource({
kindRoot: pluginsRoot,
identity: {
kind: 'plugin',
workspaceId: 'workspace-a',
resourceId: 'shared-id',
hubResourceId: 'hub-team-shared-id',
},
storageName: 'shared-id',
pullInto: (dir) => pluginManifest(dir, 'Team copy'),
verifyWorkspaceScope: async () => true,
verifyStillShared: async () => true,
});
if (team.status !== 'committed') throw new Error('fixture did not materialize');
const exactTeamPlugin = await resolveLocalPluginBySource({
db,
id: 'shared-id',
source: team.sourceKey,
userPluginsRoot: pluginsRoot,
});
expect(exactTeamPlugin).toMatchObject({
id: 'shared-id',
title: 'Team copy',
source: 'team:plugin:workspace-a:shared-id',
fsPath: teamResourceMaterializationDir(
pluginsRoot,
'workspace-a',
'shared-id',
'shared-id',
),
});
await expect(resolveLocalPluginBySource({
db,
id: 'shared-id',
source: 'local:personal:shared-id',
userPluginsRoot: pluginsRoot,
})).resolves.toMatchObject({ title: 'Personal copy' });
db.prepare(`
INSERT INTO projects (id, name, created_at, updated_at)
VALUES (?, ?, ?, ?)
`).run('team-project', 'Team project', 1, 1);
const snapshot = resolvePluginSnapshot({
db,
body: { pluginId: 'shared-id' },
projectId: 'team-project',
registry: { skills: [], designSystems: [], craft: [], atoms: [] },
plugin: exactTeamPlugin ?? undefined,
});
expect(snapshot?.ok).toBe(true);
if (snapshot?.ok) expect(snapshot.snapshot.pluginTitle).toBe('Team copy');
});
it('rejects a forged Team source without a local materialization marker', async () => {
const root = await mkdtemp(path.join(os.tmpdir(), 'od-local-plugin-source-'));
roots.push(root);
const dataDir = path.join(root, 'data');
const db = openDatabase(root, { dataDir });
await expect(resolveLocalPluginBySource({
db,
id: 'shared-id',
source: 'team:plugin:workspace-forged:shared-id',
userPluginsRoot: path.join(dataDir, 'plugins'),
})).resolves.toBeNull();
});
it('rejects a Team source after its local binding is tombstoned', async () => {
const root = await mkdtemp(path.join(os.tmpdir(), 'od-local-plugin-source-'));
roots.push(root);
const dataDir = path.join(root, 'data');
const pluginsRoot = path.join(dataDir, 'plugins');
const db = openDatabase(root, { dataDir });
const source = 'team:plugin:workspace-a:shared-id';
await materializeWorkspaceScopedTeamResource({
kindRoot: pluginsRoot,
identity: {
kind: 'plugin',
workspaceId: 'workspace-a',
resourceId: 'shared-id',
hubResourceId: 'hub-team-shared-id',
},
storageName: 'shared-id',
pullInto: (dir) => pluginManifest(dir, 'Retired Team copy'),
verifyWorkspaceScope: async () => true,
verifyStillShared: async () => true,
});
const bindingId = workspaceTeamPluginBindingResourceId('workspace-a', 'shared-id');
ensureWorkspaceResource(db, 'plugin', 'workspace-a', bindingId, {
visibility: 'team',
resourceState: 'active',
});
updateWorkspaceResource(db, 'plugin', 'workspace-a', bindingId, {
resourceState: 'deleted',
});
await expect(resolveLocalPluginBySource({
db,
id: 'shared-id',
source,
userPluginsRoot: pluginsRoot,
})).resolves.toBeNull();
});
});