Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1301,12 +1301,12 @@
},
"services/code-index/__tests__/manager.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 87
"count": 84
}
},
"services/code-index/__tests__/orchestrator.spec.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 25
"count": 23
}
},
"services/code-index/__tests__/service-factory.spec.ts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,23 @@ describe("CodeIndexScanExecutor", () => {
},
)

it.each([0, 2])("preserves incremental batch-error tolerance with %s indexed blocks", async (indexed) => {
it.each([0, 2])("rejects incremental batch errors with %s indexed blocks", async (indexed) => {
const { executor, scanner } = setup()
const failures = [new Error("embedding failure"), new Error("upsert failure"), new Error("embedding failure")]
scanner.scanDirectory.mockImplementation(async (_path, onError, onIndexed, onParsed) => {
onParsed?.(3)
onIndexed?.(indexed)
onError?.(new Error("batch failure"))
for (const failure of failures) onError?.(failure)
return { stats: { processed: 1, skipped: 0 }, totalBlockCount: 3 }
})
await expect(executor.runIncrementalScan(new AbortController().signal)).resolves.toBe(true)
const result = executor.runIncrementalScan(new AbortController().signal)
await expect(result).rejects.toBeInstanceOf(AggregateError)
await expect(result).rejects.toMatchObject({
errors: failures,
message: "Incremental scan failed with 3 errors:\nembedding failure\nupsert failure",
})
await result.catch((error: AggregateError) => {
for (const [index, failure] of failures.entries()) expect(error.errors[index]).toBe(failure)
})
})
})
62 changes: 10 additions & 52 deletions src/services/code-index/__tests__/manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { makeExtensionContext } from "../../../test-utils/vscode"
import { CodeIndexManager } from "../manager"
import { CodeIndexManagerRegistry } from "../code-index-manager-registry"
import { CodeIndexServiceFactory } from "../service-factory"
import { CodeIndexOrchestrator } from "../orchestrator"
import { CodeIndexSearchService } from "../search-service"
import type { MockedClass } from "vitest"
import * as path from "path"
import { providerIdentifiers } from "@roo-code/types/provider-identifiers"
Expand Down Expand Up @@ -474,63 +476,19 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
;(manager as any)._configManager = mockConfigManager
})

it("should validate embedder during _recreateServices when validation succeeds", async () => {
// Arrange
mockServiceFactoryInstance.validateEmbedder.mockResolvedValue({ valid: true })

// Act - directly call the private method for testing
await (manager as any)._recreateServices()

// Assert
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockStateManager.setSystemState).not.toHaveBeenCalledWith("Error", expect.any(String))
})

it("should set error state when embedder validation fails", async () => {
// Arrange
it("should create indexing services without a startup embedder validation request", async () => {
mockServiceFactoryInstance.validateEmbedder.mockResolvedValue({
valid: false,
error: "embeddings:validation.authenticationFailed",
error: "Embedder unavailable",
})

// Act & Assert
await expect((manager as any)._recreateServices()).rejects.toThrow(
"embeddings:validation.authenticationFailed",
)
await manager["_recreateServices"]()

// Assert other expectations
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockStateManager.setSystemState).toHaveBeenCalledWith(
"Error",
"embeddings:validation.authenticationFailed",
)
})

it("should set generic error state when embedder validation throws", async () => {
// Arrange
// Since the real service factory catches exceptions, we should mock it to resolve with an error
mockServiceFactoryInstance.validateEmbedder.mockResolvedValue({
valid: false,
error: "embeddings:validation.configurationError",
})

// Act & Assert
await expect((manager as any)._recreateServices()).rejects.toThrow(
"embeddings:validation.configurationError",
)

// Assert other expectations
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
const createdEmbedder = mockServiceFactoryInstance.createServices.mock.results[0].value.embedder
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalledWith(createdEmbedder)
expect(mockStateManager.setSystemState).toHaveBeenCalledWith(
"Error",
"embeddings:validation.configurationError",
)
expect(mockServiceFactoryInstance.validateEmbedder).not.toHaveBeenCalled()
expect(mockStateManager.setSystemState).not.toHaveBeenCalledWith("Error", expect.any(String))
expect(manager["_orchestrator"]).toBeInstanceOf(CodeIndexOrchestrator)
expect(manager["_searchService"]).toBeInstanceOf(CodeIndexSearchService)
})

it("should handle embedder creation failure", async () => {
Expand Down Expand Up @@ -684,7 +642,7 @@ describe("CodeIndexManager - handleSettingsChange regression", () => {
// Assert - manager should be initialized again
expect(manager.isInitialized).toBe(true)
expect(mockServiceFactoryInstance.createServices).toHaveBeenCalled()
expect(mockServiceFactoryInstance.validateEmbedder).toHaveBeenCalled()
expect(mockServiceFactoryInstance.validateEmbedder).not.toHaveBeenCalled()
})

it("should be safe to call when not in error state (idempotent)", async () => {
Expand Down
Loading
Loading