fix(plugin): remediate ML-07 R2 findings

This commit is contained in:
2026-08-27 21:16:27 +08:00
parent 29cf322f1a
commit f0ac7d70d1
10 changed files with 381 additions and 29 deletions

View File

@@ -1,5 +1,11 @@
import { describe, expect, it, vi } from 'vitest';
import { createCodingPluginsStore } from '@/stores/coding-plugins';
import type {
DataServiceCollectionRemoval,
DataServiceHostResult,
DataServiceInstanceRemoval,
DataServiceInstanceState,
} from '../../shared/data-service';
function projection(enabled = false, projectId = 'local-project') {
return {
@@ -15,6 +21,31 @@ function projection(enabled = false, projectId = 'local-project') {
};
}
function deferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>((done) => { resolve = done; });
return { promise, resolve };
}
function dataServiceInstance(instanceId: string): DataServiceInstanceState {
return {
instance_id: instanceId, project_id: 'cloud-project', collections: [],
usage: { document_count: 0, total_bytes: 0 },
limits: {
max_collections: 20, max_documents: 1000, max_total_bytes: 20_971_520,
max_document_bytes: 65_536, list_default_limit: 50, list_max_limit: 100,
list_max_data_bytes: 1_048_576, mutations_per_minute: 120,
},
created_at: '2026-08-27T00:00:00Z', updated_at: '2026-08-27T00:00:00Z',
};
}
function success<T>(data: T): DataServiceHostResult<T> {
return {
success: true, status: 200, code: null, error: null, retryable: false, data,
};
}
describe('coding plugins store', () => {
it('coalesces duplicate loads and mutations while retaining the last projection on failure', async () => {
let resolveLoad!: (value: ReturnType<typeof projection>) => void;
@@ -87,6 +118,66 @@ describe('coding plugins store', () => {
});
});
it('does not let a late project A enable mutation overwrite loaded project B', async () => {
const enabledA = deferred<ReturnType<typeof projection>>();
const inspectDataService = vi.fn();
const store = createCodingPluginsStore({
list: vi.fn().mockResolvedValue(projection(false, 'project-b')),
setEnabled: vi.fn(() => enabledA.promise),
inspectDataService,
});
store.setState({ projectId: 'project-a', projection: projection(false, 'project-a') });
const mutation = store.getState().setEnabled('project-a', 'makelore.data-service', true);
await store.getState().load('project-b');
enabledA.resolve(projection(true, 'project-a'));
await mutation;
expect(store.getState()).toMatchObject({
projectId: 'project-b', projection: { project: { localProjectId: 'project-b' } },
});
expect(inspectDataService).not.toHaveBeenCalled();
});
it('does not let adjacent late Data Service mutations commit or reload over project B', async () => {
const configure = deferred<DataServiceHostResult<DataServiceInstanceState>>();
const reset = deferred<DataServiceHostResult<DataServiceInstanceState>>();
const removeCollection = deferred<DataServiceHostResult<DataServiceCollectionRemoval>>();
const removeProject = deferred<DataServiceHostResult<DataServiceInstanceRemoval>>();
const inspectDataService = vi.fn();
const list = vi.fn().mockResolvedValue(projection(false, 'project-b'));
const store = createCodingPluginsStore({
list,
inspectDataService,
configureDataService: vi.fn(() => configure.promise),
resetDataService: vi.fn(() => reset.promise),
removeCollection: vi.fn(() => removeCollection.promise),
removeProject: vi.fn(() => removeProject.promise),
});
store.setState({ projectId: 'project-a', projection: projection(false, 'project-a') });
const mutations = [
store.getState().configure(['todos']),
store.getState().reset(),
store.getState().removeCollection('todos'),
store.getState().removeProject(),
];
await store.getState().load('project-b');
configure.resolve(success(dataServiceInstance('late-configure')));
reset.resolve(success(dataServiceInstance('late-reset')));
removeCollection.resolve(success({ removed: true, usage: { document_count: 0, total_bytes: 0 } }));
removeProject.resolve(success({ removed: true }));
await Promise.all(mutations);
expect(store.getState()).toMatchObject({
projectId: 'project-b',
projection: { project: { localProjectId: 'project-b' } },
dataService: null,
});
expect(inspectDataService).not.toHaveBeenCalled();
expect(list).toHaveBeenCalledOnce();
});
it('retains ready Data Service usage when enabling an existing instance', async () => {
const readyProjection = projection(true);
readyProjection.items[0] = {
@@ -104,6 +195,7 @@ describe('coding plugins store', () => {
const store = createCodingPluginsStore({
setEnabled: vi.fn().mockResolvedValue(readyProjection), inspectDataService,
});
store.setState({ projectId: 'local-project' });
await store.getState().setEnabled('local-project', 'makelore.data-service', true);