diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92b1591..5b688b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ jobs: - name: Compile run: npm run compile - - name: Run Tests + - name: Run Unit Tests + run: npm run test:unit + + - name: Run Integration Tests # VS Code tests require a display server on Linux - run: xvfb-run -a npm test + run: xvfb-run -a npm run test:integration diff --git a/.vscode-test.mjs b/.vscode-test.mjs index b62ba25..f9c67eb 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -1,5 +1,5 @@ import { defineConfig } from '@vscode/test-cli'; export default defineConfig({ - files: 'out/test/**/*.test.js', + files: 'out/test/integration/**/*.test.js', }); diff --git a/package.json b/package.json index fb2c832..2ed00b3 100644 --- a/package.json +++ b/package.json @@ -113,12 +113,15 @@ "pretest": "npm run compile-tests && npm run compile && npm run lint", "check-types": "node ./node_modules/typescript/bin/tsc --noEmit", "lint": "node ./node_modules/eslint/bin/eslint.js src", - "test": "node ./node_modules/@vscode/test-cli/out/bin.mjs", + "test": "npm run test:unit && npm run test:integration", + "test:unit": "npm run compile-tests && npx mocha out/test/unit/**/*.test.js --ui tdd", + "test:integration": "node ./node_modules/@vscode/test-cli/out/bin.mjs", "prepare": "husky" }, "lint-staged": { "*.ts": [ - "eslint --fix" + "eslint --fix", + "bash -c 'npm run test:unit'" ] }, "devDependencies": { diff --git a/src/ai/registry.ts b/src/ai/registry.ts index 2affe81..ff06509 100644 --- a/src/ai/registry.ts +++ b/src/ai/registry.ts @@ -1,5 +1,5 @@ -import * as vscode from 'vscode'; -import type { PredicteCommitConfig } from '../core/config'; +import type * as vscode from 'vscode'; +import type { PredicteCommitConfig } from '../core/types'; import type { ProviderClient } from './types'; export interface ProviderDefinition { diff --git a/src/ai/selector.ts b/src/ai/selector.ts index 98214ca..8760a5c 100644 --- a/src/ai/selector.ts +++ b/src/ai/selector.ts @@ -1,6 +1,6 @@ import type * as vscode from 'vscode'; -import type { PredicteCommitConfig } from '../core/config'; -import { getEffectiveProviderId } from '../core/config'; +import type { PredicteCommitConfig } from '../core/types'; +import { getEffectiveProviderId } from '../core/logic'; import type { ProviderClient } from './types'; import { getProviderDefinition } from './registry'; diff --git a/src/commands.ts b/src/commands.ts index 1dafd1c..b95cf1e 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; -import { getConfig, getEffectiveProviderId } from './core/config'; +import { getConfig } from './core/config'; +import { getEffectiveProviderId } from './core/logic'; import { getGitExtension } from './modules/git/vscode'; import { getTargetRepository } from './modules/git/repo'; import { toRepoRelativePosixPath } from './utils/paths'; diff --git a/src/core/config.ts b/src/core/config.ts index 42d7607..2782f97 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -1,16 +1,6 @@ import * as vscode from 'vscode'; - -export type PredicteCommitConfig = { - provider: string; - models: string[]; - ignoredFiles: string[]; - useLocal: boolean; - localBaseUrl: string; - localModel: string; - debugLogging: boolean; -}; - -export const DEFAULT_LOCAL_URL = ''; +import { PredicteCommitConfig } from './types'; +import { DEFAULT_LOCAL_URL } from './constants'; export function getConfig(): PredicteCommitConfig { const cfg = vscode.workspace.getConfiguration('predicteCommit'); @@ -26,18 +16,3 @@ export function getConfig(): PredicteCommitConfig { debugLogging: cfg.get('debugLogging', false), }; } - -export const DIFF_CAPS: { maxCharsPerFile: number; maxCharsTotal: number } = { - maxCharsPerFile: 8000, - maxCharsTotal: 32000, -}; - -export const TRUNCATION_MARKER = '...TRUNCATED...'; - -export function getEffectiveProviderId(cfg: PredicteCommitConfig): string { - // Backwards compatibility: existing users may rely on useLocal. - if (cfg.useLocal) { - return 'ollama'; - } - return cfg.provider; -} diff --git a/src/core/constants.ts b/src/core/constants.ts new file mode 100644 index 0000000..7e65d52 --- /dev/null +++ b/src/core/constants.ts @@ -0,0 +1,8 @@ +export const DEFAULT_LOCAL_URL = ''; + +export const DIFF_CAPS: { maxCharsPerFile: number; maxCharsTotal: number } = { + maxCharsPerFile: 8000, + maxCharsTotal: 32000, +}; + +export const TRUNCATION_MARKER = '...TRUNCATED...'; diff --git a/src/core/logic.ts b/src/core/logic.ts new file mode 100644 index 0000000..ba3afe1 --- /dev/null +++ b/src/core/logic.ts @@ -0,0 +1,9 @@ +import { PredicteCommitConfig } from './types'; + +export function getEffectiveProviderId(cfg: PredicteCommitConfig): string { + // Backwards compatibility: existing users may rely on useLocal. + if (cfg.useLocal) { + return 'ollama'; + } + return cfg.provider; +} diff --git a/src/core/types.ts b/src/core/types.ts new file mode 100644 index 0000000..561bc4a --- /dev/null +++ b/src/core/types.ts @@ -0,0 +1,9 @@ +export type PredicteCommitConfig = { + provider: string; + models: string[]; + ignoredFiles: string[]; + useLocal: boolean; + localBaseUrl: string; + localModel: string; + debugLogging: boolean; +}; diff --git a/src/providers/local/index.ts b/src/providers/local/index.ts index bfe3426..7767521 100644 --- a/src/providers/local/index.ts +++ b/src/providers/local/index.ts @@ -1,7 +1,7 @@ import { postChatCompletion } from '../../ai/http'; import type { GenerateRequest, GenerateResult, ProviderClient } from '../../ai/types'; import { registerProvider } from '../../ai/registry'; -import { DEFAULT_LOCAL_URL } from '../../core/config'; +import { DEFAULT_LOCAL_URL } from '../../core/constants'; export class LocalProvider implements ProviderClient { constructor( diff --git a/src/test/config.test.ts b/src/test/integration/config.test.ts similarity index 88% rename from src/test/config.test.ts rename to src/test/integration/config.test.ts index 8f0e58e..59d95e3 100644 --- a/src/test/config.test.ts +++ b/src/test/integration/config.test.ts @@ -1,5 +1,8 @@ import * as assert from 'assert'; -import { getEffectiveProviderId, getConfig, PredicteCommitConfig, DEFAULT_LOCAL_URL } from '../core/config'; +import { getConfig } from '../../core/config'; +import { PredicteCommitConfig } from '../../core/types'; +import { getEffectiveProviderId } from '../../core/logic'; +import { DEFAULT_LOCAL_URL } from '../../core/constants'; suite('Config Test Suite', () => { test('getEffectiveProviderId', () => { diff --git a/src/test/extension.test.ts b/src/test/integration/extension.test.ts similarity index 83% rename from src/test/extension.test.ts rename to src/test/integration/extension.test.ts index 3147783..b433856 100644 --- a/src/test/extension.test.ts +++ b/src/test/integration/extension.test.ts @@ -3,10 +3,10 @@ import * as assert from 'assert'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode'; -// import * as myExtension from '../../extension'; +// import * as myExtension from '../../../extension'; -import { truncateWithMarker } from '../utils/truncate'; -import { isTransientStatus, isAuthOrConfigStatus } from '../ai/errors'; +import { truncateWithMarker } from '../../utils/truncate'; +import { isTransientStatus, isAuthOrConfigStatus } from '../../ai/errors'; suite('Extension Test Suite', () => { vscode.window.showInformationMessage('Start all tests.'); diff --git a/src/test/ai.test.ts b/src/test/unit/ai.test.ts similarity index 92% rename from src/test/ai.test.ts rename to src/test/unit/ai.test.ts index ae903a1..f83d7b2 100644 --- a/src/test/ai.test.ts +++ b/src/test/unit/ai.test.ts @@ -1,8 +1,8 @@ import * as assert from 'assert'; -import { selectProvider } from '../ai/selector'; -import { registerProvider } from '../ai/registry'; -import { PredicteCommitConfig } from '../core/config'; -import { ProviderClient, GenerateRequest, GenerateResult } from '../ai/types'; +import { selectProvider } from '../../ai/selector'; +import { registerProvider } from '../../ai/registry'; +import { PredicteCommitConfig } from '../../core/types'; +import { ProviderClient, GenerateRequest, GenerateResult } from '../../ai/types'; suite('AI Selector Test Suite', () => { test('selectProvider selects correct provider', async () => { diff --git a/src/test/http_error.test.ts b/src/test/unit/http_error.test.ts similarity index 93% rename from src/test/http_error.test.ts rename to src/test/unit/http_error.test.ts index 6a4d22c..f63e9a8 100644 --- a/src/test/http_error.test.ts +++ b/src/test/unit/http_error.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; -import { postChatCompletion } from '../ai/http'; -import { ProviderError } from '../ai/errors'; +import { postChatCompletion } from '../../ai/http'; +import { ProviderError } from '../../ai/errors'; suite('HTTP Error Handling', () => { test('ECONNREFUSED returns friendly message', async () => { diff --git a/src/test/utils.test.ts b/src/test/unit/utils.test.ts similarity index 96% rename from src/test/utils.test.ts rename to src/test/unit/utils.test.ts index fa6c65d..83e46c8 100644 --- a/src/test/utils.test.ts +++ b/src/test/unit/utils.test.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; -import { isIgnored } from '../utils/ignore'; -import { truncateWithMarker, capDiffsByFileAndTotal } from '../utils/truncate'; -import { DIFF_CAPS, TRUNCATION_MARKER } from '../core/config'; +import { isIgnored } from '../../utils/ignore'; +import { truncateWithMarker, capDiffsByFileAndTotal } from '../../utils/truncate'; +import { DIFF_CAPS, TRUNCATION_MARKER } from '../../core/constants'; suite('Utils Test Suite', () => { test('isIgnored', () => { diff --git a/src/utils/truncate.ts b/src/utils/truncate.ts index 38dcde6..0af0aa8 100644 --- a/src/utils/truncate.ts +++ b/src/utils/truncate.ts @@ -1,4 +1,4 @@ -import { DIFF_CAPS, TRUNCATION_MARKER } from '../core/config'; +import { DIFF_CAPS, TRUNCATION_MARKER } from '../core/constants'; export function truncateWithMarker(input: string, maxChars: number): string { if (input.length <= maxChars) {