fix: close plugin workspace acceptance gaps
This commit is contained in:
@@ -111,6 +111,7 @@ function input(overrides: Partial<PluginWorkspaceInputs> = {}): PluginWorkspaceI
|
||||
project,
|
||||
activeProject: { id: 'project-a', name: 'Project A' },
|
||||
agentNames: { 'agent-a': 'Alice' },
|
||||
authenticated: true,
|
||||
filters: { scope: 'all', source: 'all', state: 'all', search: '' },
|
||||
...overrides,
|
||||
};
|
||||
@@ -331,6 +332,62 @@ describe('buildPluginWorkspaceProjection', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps an unavailable installation as a retained device version with same-channel update and cleanup', () => {
|
||||
const item = buildPluginWorkspaceProjection(input({
|
||||
marketplaceInstallations: {
|
||||
'makelore.notes': {
|
||||
status: 'unavailable',
|
||||
pluginId: 'makelore.notes',
|
||||
releaseId: 'release-1',
|
||||
version: '1.0.0',
|
||||
channel: 'stable',
|
||||
reason: '当前客户端与新版本不兼容',
|
||||
},
|
||||
},
|
||||
})).items[0]!;
|
||||
|
||||
expect(item).toMatchObject({
|
||||
version: '1.0.0',
|
||||
updateAvailable: true,
|
||||
unavailable: true,
|
||||
deviceReason: '当前客户端与新版本不兼容',
|
||||
});
|
||||
expect(item.commands.map(({ kind }) => kind)).toEqual([
|
||||
'remove_from_library',
|
||||
'install_beta',
|
||||
'update_official',
|
||||
'remove_official_device_package',
|
||||
'disable_project',
|
||||
]);
|
||||
expect(item.commands.some(({ kind }) => kind === 'install_stable')).toBe(false);
|
||||
expect(item.commands.some(({ kind }) => kind === 'open_agent_assignment')).toBe(false);
|
||||
});
|
||||
|
||||
it('marks retained snapshots stale after refresh failure and distinguishes suspended from retired', () => {
|
||||
const item = buildPluginWorkspaceProjection(input({
|
||||
catalog: {
|
||||
...catalog,
|
||||
items: [{ ...catalog.items[0]!, runtimeStatus: 'suspended' }],
|
||||
},
|
||||
library: {
|
||||
...library,
|
||||
items: [{
|
||||
...library.items[0]!,
|
||||
runtimeStatus: 'suspended',
|
||||
catalogStatus: 'retired',
|
||||
}],
|
||||
},
|
||||
sourceFailures: { catalog: true, library: true },
|
||||
})).items[0]!;
|
||||
|
||||
expect(item).toMatchObject({
|
||||
stale: true,
|
||||
suspended: true,
|
||||
retired: true,
|
||||
unavailable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps an installed Beta update on the explicit Beta command', () => {
|
||||
expect(commandKinds({
|
||||
marketplaceInstallations: {
|
||||
@@ -419,6 +476,43 @@ describe('buildPluginWorkspaceProjection', () => {
|
||||
}]);
|
||||
});
|
||||
|
||||
it('keeps a catalog-known unknown project selection as a metadata-rich disable-only retained item', () => {
|
||||
const projectWithRecognizedUnknown = {
|
||||
...project,
|
||||
items: [],
|
||||
unknownPluginIds: ['makelore.notes'],
|
||||
};
|
||||
const projectScope = buildPluginWorkspaceProjection(input({
|
||||
project: projectWithRecognizedUnknown,
|
||||
filters: { scope: 'project', source: 'all', state: 'all', search: '' },
|
||||
}));
|
||||
|
||||
expect(projectScope.items).toHaveLength(1);
|
||||
expect(projectScope.items[0]).toMatchObject({
|
||||
key: 'retained:makelore.notes',
|
||||
source: 'retained',
|
||||
title: 'Notes',
|
||||
summary: 'Write project notes',
|
||||
category: 'productivity',
|
||||
publisher: 'MakeLore',
|
||||
commands: [{
|
||||
kind: 'disable_project',
|
||||
projectId: 'project-a',
|
||||
pluginId: 'makelore.notes',
|
||||
}],
|
||||
});
|
||||
|
||||
const allScope = buildPluginWorkspaceProjection(input({
|
||||
project: projectWithRecognizedUnknown,
|
||||
filters: { scope: 'all', source: 'all', state: 'all', search: '' },
|
||||
}));
|
||||
expect(allScope.items.map(({ key }) => key)).toEqual([
|
||||
'official:makelore.notes',
|
||||
'retained:makelore.notes',
|
||||
]);
|
||||
expect(allScope.items[1]?.commands.map(({ kind }) => kind)).toEqual(['disable_project']);
|
||||
});
|
||||
|
||||
it('applies scope, source, state, and search filters while keeping state counts stable', () => {
|
||||
const available = {
|
||||
...catalog.items[0]!,
|
||||
@@ -599,4 +693,30 @@ describe('buildPluginWorkspaceProjection', () => {
|
||||
projectState: 'unknown',
|
||||
});
|
||||
});
|
||||
|
||||
it('offers sign-in for a signed-out free catalog item but fails closed for an authenticated Library failure', () => {
|
||||
const signedOut = buildPluginWorkspaceProjection(input({
|
||||
authenticated: false,
|
||||
library: null,
|
||||
marketplaceInstallations: {},
|
||||
project: null,
|
||||
activeProject: null,
|
||||
})).items[0]!;
|
||||
expect(signedOut).toMatchObject({
|
||||
delivery: 'account_unknown',
|
||||
commands: [{ kind: 'sign_in' }],
|
||||
});
|
||||
|
||||
const authenticatedLibraryFailure = buildPluginWorkspaceProjection(input({
|
||||
authenticated: true,
|
||||
library: null,
|
||||
marketplaceInstallations: {},
|
||||
project: null,
|
||||
activeProject: null,
|
||||
})).items[0]!;
|
||||
expect(authenticatedLibraryFailure).toMatchObject({
|
||||
delivery: 'account_unknown',
|
||||
commands: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user