Skip to content

Commit 29536db

Browse files
committed
fix: refresh codebase index on config change
1 parent df2dc11 commit 29536db

File tree

3 files changed

+437
-30
lines changed

3 files changed

+437
-30
lines changed

core/indexing/CodebaseIndexer.test.ts

Lines changed: 295 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* eslint-disable max-lines-per-function */
2+
/* lint is not useful for test classes */
13
import { jest } from "@jest/globals";
24
import { execSync } from "node:child_process";
35
import fs from "node:fs";
@@ -14,8 +16,11 @@ import {
1416
} from "../test/testDir.js";
1517
import { getIndexSqlitePath } from "../util/paths.js";
1618

19+
import { ConfigResult } from "@continuedev/config-yaml";
20+
import CodebaseContextProvider from "../context/providers/CodebaseContextProvider.js";
21+
import { ContinueConfig } from "../index.js";
1722
import { localPathToUri } from "../util/pathToUri.js";
18-
import { CodebaseIndexer, PauseToken } from "./CodebaseIndexer.js";
23+
import { CodebaseIndexer } from "./CodebaseIndexer.js";
1924
import { getComputeDeleteAddRemove } from "./refreshIndex.js";
2025
import { TestCodebaseIndex } from "./TestCodebaseIndex.js";
2126
import { CodebaseIndex } from "./types.js";
@@ -57,6 +62,17 @@ class TestCodebaseIndexer extends CodebaseIndexer {
5762
protected async getIndexesToBuild(): Promise<CodebaseIndex[]> {
5863
return [new TestCodebaseIndex()];
5964
}
65+
66+
// Add public methods to test private methods
67+
public testHasCodebaseContextProvider() {
68+
return (this as any).hasCodebaseContextProvider();
69+
}
70+
71+
public async testHandleConfigUpdate(
72+
configResult: ConfigResult<ContinueConfig>,
73+
) {
74+
return (this as any).handleConfigUpdate({ config: configResult.config });
75+
}
6076
}
6177

6278
// Create a mock messenger type that doesn't require actual protocol imports
@@ -71,8 +87,6 @@ type MockMessengerType = {
7187
// These are more like integration tests, whereas we should separately test
7288
// the individual CodebaseIndex classes
7389
describe("CodebaseIndexer", () => {
74-
const pauseToken = new PauseToken(false);
75-
7690
// Replace mockProgressReporter with mockMessenger
7791
const mockMessenger: MockMessengerType = {
7892
send: jest.fn(),
@@ -82,13 +96,8 @@ describe("CodebaseIndexer", () => {
8296
onError: jest.fn(),
8397
};
8498

85-
const codebaseIndexer = new TestCodebaseIndexer(
86-
testConfigHandler,
87-
testIde,
88-
mockMessenger as any,
89-
false,
90-
);
91-
const testIndex = new TestCodebaseIndex();
99+
let codebaseIndexer: TestCodebaseIndexer;
100+
let testIndex: TestCodebaseIndex;
92101

93102
beforeAll(async () => {
94103
tearDownTestDir();
@@ -99,6 +108,15 @@ describe("CodebaseIndexer", () => {
99108
cwd: TEST_DIR_PATH,
100109
});
101110
execSync('git config user.name "Test"', { cwd: TEST_DIR_PATH });
111+
112+
codebaseIndexer = new TestCodebaseIndexer(
113+
testConfigHandler,
114+
testIde,
115+
mockMessenger as any,
116+
false,
117+
);
118+
await codebaseIndexer.initPromise;
119+
testIndex = new TestCodebaseIndex();
102120
});
103121

104122
afterAll(async () => {
@@ -161,6 +179,7 @@ describe("CodebaseIndexer", () => {
161179
}
162180

163181
test("should index test folder without problem", async () => {
182+
walkDirCache.invalidate();
164183
addToTestDir([
165184
["test.ts", TEST_TS],
166185
["py/main.py", TEST_PY],
@@ -403,4 +422,270 @@ describe("CodebaseIndexer", () => {
403422
expect(codebaseIndexer.currentIndexingState).toEqual(testState);
404423
});
405424
});
425+
426+
// New describe block for testing handleConfigUpdate functionality
427+
describe("handleConfigUpdate functionality", () => {
428+
let testIndexer: TestCodebaseIndexer;
429+
let mockRefreshCodebaseIndex: jest.MockedFunction<any>;
430+
let mockGetWorkspaceDirs: jest.MockedFunction<any>;
431+
432+
beforeEach(() => {
433+
testIndexer = new TestCodebaseIndexer(
434+
testConfigHandler,
435+
testIde,
436+
mockMessenger as any,
437+
false,
438+
);
439+
440+
// Mock the refreshCodebaseIndex method to avoid actual indexing
441+
mockRefreshCodebaseIndex = jest
442+
.spyOn(testIndexer, "refreshCodebaseIndex")
443+
.mockImplementation(async () => {});
444+
445+
// Mock getWorkspaceDirs to return test directories
446+
mockGetWorkspaceDirs = jest
447+
.spyOn(testIde, "getWorkspaceDirs")
448+
.mockResolvedValue(["/test/workspace"]);
449+
});
450+
451+
afterEach(() => {
452+
jest.clearAllMocks();
453+
});
454+
455+
describe("hasCodebaseContextProvider", () => {
456+
test("should return true when codebase context provider is present", () => {
457+
// Set up config with codebase context provider
458+
(testIndexer as any).config = {
459+
contextProviders: [
460+
{
461+
description: {
462+
title: CodebaseContextProvider.description.title,
463+
},
464+
},
465+
],
466+
};
467+
468+
const result = testIndexer.testHasCodebaseContextProvider();
469+
expect(result).toBe(true);
470+
});
471+
472+
test("should return false when no context providers are configured", () => {
473+
(testIndexer as any).config = {
474+
contextProviders: undefined,
475+
};
476+
477+
const result = testIndexer.testHasCodebaseContextProvider();
478+
expect(result).toBe(false);
479+
});
480+
481+
test("should return false when context providers exist but no codebase provider", () => {
482+
(testIndexer as any).config = {
483+
contextProviders: [
484+
{
485+
description: {
486+
title: "SomeOtherProvider",
487+
},
488+
},
489+
],
490+
};
491+
492+
const result = testIndexer.testHasCodebaseContextProvider();
493+
expect(result).toBe(false);
494+
});
495+
496+
test("should return false when context providers is empty array", () => {
497+
(testIndexer as any).config = {
498+
contextProviders: [],
499+
};
500+
501+
const result = testIndexer.testHasCodebaseContextProvider();
502+
expect(result).toBe(false);
503+
});
504+
});
505+
506+
describe("handleConfigUpdate", () => {
507+
test("should return early when newConfig is null", async () => {
508+
const configResult: ConfigResult<ContinueConfig> = {
509+
config: null as any,
510+
errors: [],
511+
configLoadInterrupted: false,
512+
};
513+
514+
await testIndexer.testHandleConfigUpdate(configResult);
515+
516+
// These get called once on init, so we want them to not get called again
517+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(1);
518+
expect(mockGetWorkspaceDirs).toHaveBeenCalledTimes(1);
519+
});
520+
521+
test("should return early when newConfig is undefined", async () => {
522+
const configResult: ConfigResult<ContinueConfig> = {
523+
config: undefined as any,
524+
errors: [],
525+
configLoadInterrupted: false,
526+
};
527+
528+
await testIndexer.testHandleConfigUpdate(configResult);
529+
530+
// These get called once on init, so we want them to not get called again
531+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(1);
532+
expect(mockGetWorkspaceDirs).toHaveBeenCalledTimes(1);
533+
});
534+
535+
test("should return early when no codebase context provider is present", async () => {
536+
const configResult: ConfigResult<ContinueConfig> = {
537+
config: {
538+
contextProviders: [
539+
{
540+
description: {
541+
title: "SomeOtherProvider",
542+
},
543+
},
544+
],
545+
selectedModelByRole: {
546+
embed: {
547+
model: "test-model",
548+
provider: "test-provider",
549+
},
550+
},
551+
} as unknown as ContinueConfig,
552+
errors: [],
553+
configLoadInterrupted: false,
554+
};
555+
556+
await testIndexer.testHandleConfigUpdate(configResult);
557+
558+
// These get called once on init, so we want them to not get called again
559+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(1);
560+
expect(mockGetWorkspaceDirs).toHaveBeenCalledTimes(1);
561+
});
562+
563+
test("should return early when no embed model is configured", async () => {
564+
const configResult: ConfigResult<ContinueConfig> = {
565+
config: {
566+
contextProviders: [
567+
{
568+
description: {
569+
title: CodebaseContextProvider.description.title,
570+
},
571+
},
572+
],
573+
selectedModelByRole: {
574+
embed: undefined,
575+
},
576+
} as unknown as ContinueConfig,
577+
errors: [],
578+
configLoadInterrupted: false,
579+
};
580+
581+
await testIndexer.testHandleConfigUpdate(configResult);
582+
583+
// These get called once on init, so we want them to not get called again
584+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(1);
585+
expect(mockGetWorkspaceDirs).toHaveBeenCalledTimes(1);
586+
});
587+
588+
test("should call refreshCodebaseIndex when all conditions are met", async () => {
589+
const configResult: ConfigResult<ContinueConfig> = {
590+
config: {
591+
contextProviders: [
592+
{
593+
description: {
594+
title: CodebaseContextProvider.description.title,
595+
},
596+
},
597+
],
598+
selectedModelByRole: {
599+
embed: {
600+
model: "test-model",
601+
provider: "test-provider",
602+
},
603+
},
604+
} as unknown as ContinueConfig,
605+
errors: [],
606+
configLoadInterrupted: false,
607+
};
608+
609+
await testIndexer.testHandleConfigUpdate(configResult);
610+
611+
// These get called once on init, and we want them to get called again
612+
expect(mockGetWorkspaceDirs).toHaveBeenCalledTimes(2);
613+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(2);
614+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledWith([
615+
"/test/workspace",
616+
]);
617+
});
618+
619+
test("should set config property before checking conditions", async () => {
620+
const testConfig = {
621+
contextProviders: [
622+
{
623+
description: {
624+
title: CodebaseContextProvider.description.title,
625+
},
626+
},
627+
],
628+
selectedModelByRole: {
629+
embed: {
630+
model: "test-model",
631+
provider: "test-provider",
632+
},
633+
},
634+
} as unknown as ContinueConfig;
635+
636+
const configResult: ConfigResult<ContinueConfig> = {
637+
config: testConfig,
638+
errors: [],
639+
configLoadInterrupted: false,
640+
};
641+
642+
await testIndexer.testHandleConfigUpdate(configResult);
643+
644+
// Verify that the config was set
645+
expect((testIndexer as any).config).toBe(testConfig);
646+
// These get called once on init, and we want them to get called again
647+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(2);
648+
});
649+
650+
test("should handle multiple context providers correctly", async () => {
651+
const configResult: ConfigResult<ContinueConfig> = {
652+
config: {
653+
contextProviders: [
654+
{
655+
description: {
656+
title: "SomeOtherProvider",
657+
},
658+
},
659+
{
660+
description: {
661+
title: CodebaseContextProvider.description.title,
662+
},
663+
},
664+
{
665+
description: {
666+
title: "AnotherProvider",
667+
},
668+
},
669+
],
670+
selectedModelByRole: {
671+
embed: {
672+
model: "test-model",
673+
provider: "test-provider",
674+
},
675+
},
676+
} as unknown as ContinueConfig,
677+
errors: [],
678+
configLoadInterrupted: false,
679+
};
680+
681+
await testIndexer.testHandleConfigUpdate(configResult);
682+
683+
// These get called once on init, and we want them to get called again
684+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledTimes(2);
685+
expect(mockRefreshCodebaseIndex).toHaveBeenCalledWith([
686+
"/test/workspace",
687+
]);
688+
});
689+
});
690+
});
406691
});

0 commit comments

Comments
 (0)