import * as monaco from 'monaco-editor'; function createMockModel(id: string): monaco.editor.ITextModel { return ({ id } as unknown) as monaco.editor.ITextModel; } function createMockEditor(model: monaco.editor.ITextModel | null) { return ({ getModel: () => model, } as unknown) as monaco.editor.IStandaloneCodeEditor; } function createMockPosition(column: number, lineNumber = 1): monaco.Position { return { column, lineNumber } as monaco.Position; } /** * Simulates the filteringCompletionProvider wrapper used in ExprEditor. * This matches the pattern in src/expr/index.tsx. */ function createFilteringProvider(editorRef: { current: monaco.editor.IStandaloneCodeEditor | null }) { const innerProvider = { provideCompletionItems: jest.fn().mockResolvedValue({ suggestions: [{ label: 'test', insertText: 'test' }], }), }; const filteringProvider: monaco.languages.CompletionItemProvider = { ...innerProvider, provideCompletionItems: (model, position, context, token) => { if (editorRef.current?.getModel()?.id !== model.id) { return { suggestions: [] }; } return innerProvider.provideCompletionItems(model, position, context, token); }, }; return { filteringProvider, innerProvider }; } describe('Expr filteringCompletionProvider - model ownership check', () => { it('returns empty suggestions when model ID does NOT match the editor model', async () => { const editorModel = createMockModel('editor-model-1'); const otherModel = createMockModel('other-model-2'); const editorRef = { current: createMockEditor(editorModel) }; const { filteringProvider, innerProvider } = createFilteringProvider(editorRef); const result = await filteringProvider.provideCompletionItems!(otherModel, createMockPosition(1), {} as any, {} as any); expect(result).toEqual({ suggestions: [] }); expect(innerProvider.provideCompletionItems).not.toHaveBeenCalled(); }); it('proceeds with completions when model ID matches the editor model', async () => { const model = createMockModel('same-model-1'); const editorRef = { current: createMockEditor(model) }; const { filteringProvider, innerProvider } = createFilteringProvider(editorRef); const result = await filteringProvider.provideCompletionItems!(model, createMockPosition(1), {} as any, {} as any); expect(result).not.toEqual({ suggestions: [] }); expect(innerProvider.provideCompletionItems).toHaveBeenCalled(); }); it('returns empty suggestions when editorRef.current is null', async () => { const model = createMockModel('some-model'); const editorRef = { current: null }; const { filteringProvider, innerProvider } = createFilteringProvider(editorRef); const result = await filteringProvider.provideCompletionItems!(model, createMockPosition(1), {} as any, {} as any); expect(result).toEqual({ suggestions: [] }); expect(innerProvider.provideCompletionItems).not.toHaveBeenCalled(); }); it('returns empty suggestions when editor has no model', async () => { const model = createMockModel('some-model'); const editorRef = { current: createMockEditor(null) }; const { filteringProvider, innerProvider } = createFilteringProvider(editorRef); const result = await filteringProvider.provideCompletionItems!(model, createMockPosition(1), {} as any, {} as any); expect(result).toEqual({ suggestions: [] }); expect(innerProvider.provideCompletionItems).not.toHaveBeenCalled(); }); it('returns empty suggestions when editor model id is undefined', async () => { const editorModel = ({ id: undefined } as unknown) as monaco.editor.ITextModel; const model = createMockModel('some-model'); const editorRef = { current: createMockEditor(editorModel) }; const { filteringProvider, innerProvider } = createFilteringProvider(editorRef); const result = await filteringProvider.provideCompletionItems!(model, createMockPosition(1), {} as any, {} as any); expect(result).toEqual({ suggestions: [] }); expect(innerProvider.provideCompletionItems).not.toHaveBeenCalled(); }); });