@skillsmith/core 0.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +233 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/__tests__/incremental.test.d.ts +13 -0
- package/dist/src/analysis/__tests__/incremental.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/incremental.test.js +515 -0
- package/dist/src/analysis/__tests__/incremental.test.js.map +1 -0
- package/dist/src/analysis/__tests__/integration.test.d.ts +14 -0
- package/dist/src/analysis/__tests__/integration.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/integration.test.js +1059 -0
- package/dist/src/analysis/__tests__/integration.test.js.map +1 -0
- package/dist/src/analysis/__tests__/metrics.test.d.ts +9 -0
- package/dist/src/analysis/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/metrics.test.js +369 -0
- package/dist/src/analysis/__tests__/metrics.test.js.map +1 -0
- package/dist/src/analysis/__tests__/performance.test.d.ts +15 -0
- package/dist/src/analysis/__tests__/performance.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/performance.test.js +402 -0
- package/dist/src/analysis/__tests__/performance.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/go.test.d.ts +12 -0
- package/dist/src/analysis/adapters/__tests__/go.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/go.test.js +561 -0
- package/dist/src/analysis/adapters/__tests__/go.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/python.test.d.ts +11 -0
- package/dist/src/analysis/adapters/__tests__/python.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/python.test.js +669 -0
- package/dist/src/analysis/adapters/__tests__/python.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.d.ts +12 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.js +676 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +14 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.js +381 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.js.map +1 -0
- package/dist/src/analysis/adapters/base.d.ts +83 -0
- package/dist/src/analysis/adapters/base.d.ts.map +1 -0
- package/dist/src/analysis/adapters/base.js +40 -0
- package/dist/src/analysis/adapters/base.js.map +1 -0
- package/dist/src/analysis/adapters/factory.d.ts +150 -0
- package/dist/src/analysis/adapters/factory.d.ts.map +1 -0
- package/dist/src/analysis/adapters/factory.js +244 -0
- package/dist/src/analysis/adapters/factory.js.map +1 -0
- package/dist/src/analysis/adapters/go.d.ts +131 -0
- package/dist/src/analysis/adapters/go.d.ts.map +1 -0
- package/dist/src/analysis/adapters/go.js +414 -0
- package/dist/src/analysis/adapters/go.js.map +1 -0
- package/dist/src/analysis/adapters/index.d.ts +20 -0
- package/dist/src/analysis/adapters/index.d.ts.map +1 -0
- package/dist/src/analysis/adapters/index.js +23 -0
- package/dist/src/analysis/adapters/index.js.map +1 -0
- package/dist/src/analysis/adapters/java.d.ts +154 -0
- package/dist/src/analysis/adapters/java.d.ts.map +1 -0
- package/dist/src/analysis/adapters/java.js +407 -0
- package/dist/src/analysis/adapters/java.js.map +1 -0
- package/dist/src/analysis/adapters/python.d.ts +165 -0
- package/dist/src/analysis/adapters/python.d.ts.map +1 -0
- package/dist/src/analysis/adapters/python.js +475 -0
- package/dist/src/analysis/adapters/python.js.map +1 -0
- package/dist/src/analysis/adapters/rust.d.ts +116 -0
- package/dist/src/analysis/adapters/rust.d.ts.map +1 -0
- package/dist/src/analysis/adapters/rust.js +476 -0
- package/dist/src/analysis/adapters/rust.js.map +1 -0
- package/dist/src/analysis/adapters/typescript.d.ts +68 -0
- package/dist/src/analysis/adapters/typescript.d.ts.map +1 -0
- package/dist/src/analysis/adapters/typescript.js +79 -0
- package/dist/src/analysis/adapters/typescript.js.map +1 -0
- package/dist/src/analysis/aggregator.d.ts +193 -0
- package/dist/src/analysis/aggregator.d.ts.map +1 -0
- package/dist/src/analysis/aggregator.js +283 -0
- package/dist/src/analysis/aggregator.js.map +1 -0
- package/dist/src/analysis/cache.d.ts +180 -0
- package/dist/src/analysis/cache.d.ts.map +1 -0
- package/dist/src/analysis/cache.js +279 -0
- package/dist/src/analysis/cache.js.map +1 -0
- package/dist/src/analysis/file-streamer.d.ts +136 -0
- package/dist/src/analysis/file-streamer.d.ts.map +1 -0
- package/dist/src/analysis/file-streamer.js +291 -0
- package/dist/src/analysis/file-streamer.js.map +1 -0
- package/dist/src/analysis/incremental-parser.d.ts +186 -0
- package/dist/src/analysis/incremental-parser.d.ts.map +1 -0
- package/dist/src/analysis/incremental-parser.js +291 -0
- package/dist/src/analysis/incremental-parser.js.map +1 -0
- package/dist/src/analysis/incremental.d.ts +186 -0
- package/dist/src/analysis/incremental.d.ts.map +1 -0
- package/dist/src/analysis/incremental.js +247 -0
- package/dist/src/analysis/incremental.js.map +1 -0
- package/dist/src/analysis/index.d.ts +25 -3
- package/dist/src/analysis/index.d.ts.map +1 -1
- package/dist/src/analysis/index.js +45 -3
- package/dist/src/analysis/index.js.map +1 -1
- package/dist/src/analysis/language-detector.d.ts +92 -0
- package/dist/src/analysis/language-detector.d.ts.map +1 -0
- package/dist/src/analysis/language-detector.js +602 -0
- package/dist/src/analysis/language-detector.js.map +1 -0
- package/dist/src/analysis/memory-monitor.d.ts +199 -0
- package/dist/src/analysis/memory-monitor.d.ts.map +1 -0
- package/dist/src/analysis/memory-monitor.js +271 -0
- package/dist/src/analysis/memory-monitor.js.map +1 -0
- package/dist/src/analysis/metrics.d.ts +300 -0
- package/dist/src/analysis/metrics.d.ts.map +1 -0
- package/dist/src/analysis/metrics.js +537 -0
- package/dist/src/analysis/metrics.js.map +1 -0
- package/dist/src/analysis/router.d.ts +264 -0
- package/dist/src/analysis/router.d.ts.map +1 -0
- package/dist/src/analysis/router.js +398 -0
- package/dist/src/analysis/router.js.map +1 -0
- package/dist/src/analysis/tree-cache.d.ts +208 -0
- package/dist/src/analysis/tree-cache.d.ts.map +1 -0
- package/dist/src/analysis/tree-cache.js +288 -0
- package/dist/src/analysis/tree-cache.js.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts +141 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.js +239 -0
- package/dist/src/analysis/tree-sitter/manager.js.map +1 -0
- package/dist/src/analysis/types.d.ts +69 -6
- package/dist/src/analysis/types.d.ts.map +1 -1
- package/dist/src/analysis/types.js +23 -2
- package/dist/src/analysis/types.js.map +1 -1
- package/dist/src/analysis/worker-pool.d.ts +141 -0
- package/dist/src/analysis/worker-pool.d.ts.map +1 -0
- package/dist/src/analysis/worker-pool.js +418 -0
- package/dist/src/analysis/worker-pool.js.map +1 -0
- package/dist/src/analytics/schema.d.ts +1 -1
- package/dist/src/analytics/schema.d.ts.map +1 -1
- package/dist/src/analytics/schema.js +72 -0
- package/dist/src/analytics/schema.js.map +1 -1
- package/dist/src/api/cache.d.ts +24 -1
- package/dist/src/api/cache.d.ts.map +1 -1
- package/dist/src/api/cache.js +50 -2
- package/dist/src/api/cache.js.map +1 -1
- package/dist/src/api/client.d.ts +132 -2
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +214 -18
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +7 -0
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/types.d.ts +251 -0
- package/dist/src/api/types.d.ts.map +1 -0
- package/dist/src/api/types.js +9 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/benchmarks/memory/MemoryProfiler.d.ts.map +1 -1
- package/dist/src/benchmarks/memory/MemoryProfiler.js.map +1 -1
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +1 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/repositories/IndexerRepository.d.ts.map +1 -1
- package/dist/src/repositories/IndexerRepository.js +1 -0
- package/dist/src/repositories/IndexerRepository.js.map +1 -1
- package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
- package/dist/src/repositories/SkillRepository.js +1 -0
- package/dist/src/repositories/SkillRepository.js.map +1 -1
- package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
- package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
- package/dist/src/repositories/quarantine/query-builder.d.ts.map +1 -1
- package/dist/src/repositories/quarantine/query-builder.js +1 -1
- package/dist/src/repositories/quarantine/query-builder.js.map +1 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +3 -3
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/scripts/github-import/index.js.map +1 -1
- package/dist/src/scripts/import-github-skills.js +1 -1
- package/dist/src/scripts/import-github-skills.js.map +1 -1
- package/dist/src/scripts/skill-scanner/reporter.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/reporter.js.map +1 -1
- package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
- package/dist/src/scripts/validation/index.js +1 -2
- package/dist/src/scripts/validation/index.js.map +1 -1
- package/dist/src/scripts/validation/pipeline.d.ts.map +1 -1
- package/dist/src/scripts/validation/pipeline.js.map +1 -1
- package/dist/src/scripts/validation/types.d.ts +2 -2
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/services/SearchService.d.ts.map +1 -1
- package/dist/src/services/SearchService.js +1 -0
- package/dist/src/services/SearchService.js.map +1 -1
- package/dist/src/session/SessionHealthMonitor.d.ts +1 -1
- package/dist/src/session/SessionHealthMonitor.d.ts.map +1 -1
- package/dist/src/session/SessionHealthMonitor.js +1 -1
- package/dist/src/session/SessionHealthMonitor.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +1 -1
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/index.js +2 -2
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +27 -5
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +20 -5
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/types/skill.d.ts +3 -0
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/types.d.ts +2 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +2 -2
- package/dist/src/types.js.map +1 -1
- package/dist/tests/adapters-factory.test.d.ts +13 -0
- package/dist/tests/adapters-factory.test.d.ts.map +1 -0
- package/dist/tests/adapters-factory.test.js +308 -0
- package/dist/tests/adapters-factory.test.js.map +1 -0
- package/dist/tests/adapters-java.test.d.ts +13 -0
- package/dist/tests/adapters-java.test.d.ts.map +1 -0
- package/dist/tests/adapters-java.test.js +925 -0
- package/dist/tests/adapters-java.test.js.map +1 -0
- package/dist/tests/api/client.validation.test.d.ts +7 -0
- package/dist/tests/api/client.validation.test.d.ts.map +1 -0
- package/dist/tests/api/client.validation.test.js +183 -0
- package/dist/tests/api/client.validation.test.js.map +1 -0
- package/dist/tests/language-detector.test.d.ts +13 -0
- package/dist/tests/language-detector.test.d.ts.map +1 -0
- package/dist/tests/language-detector.test.js +674 -0
- package/dist/tests/language-detector.test.js.map +1 -0
- package/dist/tests/telemetry/posthog.test.d.ts +13 -0
- package/dist/tests/telemetry/posthog.test.d.ts.map +1 -0
- package/dist/tests/telemetry/posthog.test.js +600 -0
- package/dist/tests/telemetry/posthog.test.js.map +1 -0
- package/package.json +5 -6
- package/dist/src/security/RateLimiter.d.ts +0 -337
- package/dist/src/security/RateLimiter.d.ts.map +0 -1
- package/dist/src/security/RateLimiter.js +0 -782
- package/dist/src/security/RateLimiter.js.map +0 -1
- package/dist/src/security/scanner.d.ts +0 -151
- package/dist/src/security/scanner.d.ts.map +0 -1
- package/dist/src/security/scanner.js +0 -599
- package/dist/src/security/scanner.js.map +0 -1
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1340: Language Detector Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the LanguageDetector class, verifying:
|
|
5
|
+
* - Shebang detection
|
|
6
|
+
* - Content pattern analysis
|
|
7
|
+
* - Statistical keyword analysis
|
|
8
|
+
* - Confidence scoring
|
|
9
|
+
*
|
|
10
|
+
* @see docs/architecture/multi-language-analysis.md
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { LanguageDetector, detectLanguage } from '../src/analysis/language-detector.js';
|
|
14
|
+
describe('LanguageDetector', () => {
|
|
15
|
+
describe('shebang detection', () => {
|
|
16
|
+
const detector = new LanguageDetector();
|
|
17
|
+
it('detects Python from shebang', () => {
|
|
18
|
+
const result = detector.detect('#!/usr/bin/python\nprint("hello")');
|
|
19
|
+
expect(result.language).toBe('python');
|
|
20
|
+
expect(result.confidence).toBe(1.0);
|
|
21
|
+
expect(result.method).toBe('shebang');
|
|
22
|
+
expect(result.evidence[0]).toContain('#!/usr/bin/python');
|
|
23
|
+
});
|
|
24
|
+
it('detects Python3 from shebang', () => {
|
|
25
|
+
const result = detector.detect('#!/usr/bin/python3\nimport sys');
|
|
26
|
+
expect(result.language).toBe('python');
|
|
27
|
+
expect(result.confidence).toBe(1.0);
|
|
28
|
+
expect(result.method).toBe('shebang');
|
|
29
|
+
});
|
|
30
|
+
it('detects Python from env shebang', () => {
|
|
31
|
+
const result = detector.detect('#!/usr/bin/env python\nprint("hello")');
|
|
32
|
+
expect(result.language).toBe('python');
|
|
33
|
+
expect(result.confidence).toBe(1.0);
|
|
34
|
+
});
|
|
35
|
+
it('detects Node.js from shebang', () => {
|
|
36
|
+
const result = detector.detect('#!/usr/bin/node\nconsole.log("hello")');
|
|
37
|
+
expect(result.language).toBe('javascript');
|
|
38
|
+
expect(result.confidence).toBe(1.0);
|
|
39
|
+
});
|
|
40
|
+
it('detects Node.js from env shebang', () => {
|
|
41
|
+
const result = detector.detect('#!/usr/bin/env node\nmodule.exports = {}');
|
|
42
|
+
expect(result.language).toBe('javascript');
|
|
43
|
+
expect(result.confidence).toBe(1.0);
|
|
44
|
+
});
|
|
45
|
+
it('detects TypeScript from ts-node shebang', () => {
|
|
46
|
+
const result = detector.detect('#!/usr/bin/env ts-node\nconst x: number = 1');
|
|
47
|
+
expect(result.language).toBe('typescript');
|
|
48
|
+
expect(result.confidence).toBe(1.0);
|
|
49
|
+
});
|
|
50
|
+
it('detects TypeScript from deno shebang', () => {
|
|
51
|
+
const result = detector.detect('#!/usr/bin/env deno run\nDeno.writeTextFile("test.txt", "hello")');
|
|
52
|
+
expect(result.language).toBe('typescript');
|
|
53
|
+
expect(result.confidence).toBe(1.0);
|
|
54
|
+
});
|
|
55
|
+
it('detects TypeScript from bun shebang', () => {
|
|
56
|
+
const result = detector.detect('#!/usr/bin/env bun\nawait Bun.write("output.txt", "hello")');
|
|
57
|
+
expect(result.language).toBe('typescript');
|
|
58
|
+
expect(result.confidence).toBe(1.0);
|
|
59
|
+
});
|
|
60
|
+
it('detects TypeScript from npx tsx shebang', () => {
|
|
61
|
+
const result = detector.detect('#!/usr/bin/env npx tsx\nconst x: string = "hello"');
|
|
62
|
+
expect(result.language).toBe('typescript');
|
|
63
|
+
expect(result.confidence).toBe(1.0);
|
|
64
|
+
});
|
|
65
|
+
it('returns null for unknown shebang', () => {
|
|
66
|
+
const result = detector.detect('#!/bin/bash\necho "hello"');
|
|
67
|
+
// Should not detect as our supported languages
|
|
68
|
+
expect(result.method).not.toBe('shebang');
|
|
69
|
+
});
|
|
70
|
+
it('returns null for no shebang', () => {
|
|
71
|
+
const result = detector.detectByShebang('print("hello")');
|
|
72
|
+
expect(result.language).toBeNull();
|
|
73
|
+
expect(result.confidence).toBe(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('pattern detection', () => {
|
|
77
|
+
const detector = new LanguageDetector();
|
|
78
|
+
describe('TypeScript patterns', () => {
|
|
79
|
+
it('detects type-only imports', () => {
|
|
80
|
+
const content = `
|
|
81
|
+
import type { User } from './types'
|
|
82
|
+
import type { Config } from './config'
|
|
83
|
+
|
|
84
|
+
const user: User = { id: 1, name: 'test' }
|
|
85
|
+
`;
|
|
86
|
+
const result = detector.detect(content);
|
|
87
|
+
expect(result.language).toBe('typescript');
|
|
88
|
+
expect(result.method).toBe('pattern');
|
|
89
|
+
expect(result.evidence).toContain('type-only import');
|
|
90
|
+
});
|
|
91
|
+
it('detects interface declarations', () => {
|
|
92
|
+
const content = `
|
|
93
|
+
interface User {
|
|
94
|
+
id: number
|
|
95
|
+
name: string
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const result = detector.detect(content);
|
|
99
|
+
expect(result.language).toBe('typescript');
|
|
100
|
+
expect(result.evidence).toContain('interface declaration');
|
|
101
|
+
});
|
|
102
|
+
it('detects type annotations', () => {
|
|
103
|
+
const content = `
|
|
104
|
+
const count: number = 0
|
|
105
|
+
const name: string = 'test'
|
|
106
|
+
function greet(name: string): void {}
|
|
107
|
+
const callback: (x: number) => boolean = (x) => x > 0
|
|
108
|
+
`;
|
|
109
|
+
const result = detector.detect(content);
|
|
110
|
+
// Type annotations might not be strong enough on their own
|
|
111
|
+
// Combined with other patterns they should detect TypeScript
|
|
112
|
+
expect(['typescript', null]).toContain(result.language);
|
|
113
|
+
if (result.language === 'typescript') {
|
|
114
|
+
expect(result.evidence).toContain('type annotation');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('Python patterns', () => {
|
|
119
|
+
it('detects from-import statements', () => {
|
|
120
|
+
const content = `
|
|
121
|
+
from typing import List, Dict
|
|
122
|
+
from dataclasses import dataclass
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class User:
|
|
126
|
+
name: str
|
|
127
|
+
`;
|
|
128
|
+
const result = detector.detect(content);
|
|
129
|
+
expect(result.language).toBe('python');
|
|
130
|
+
expect(result.method).toBe('pattern');
|
|
131
|
+
expect(result.evidence).toContain('from import');
|
|
132
|
+
});
|
|
133
|
+
it('detects function definitions', () => {
|
|
134
|
+
const content = `
|
|
135
|
+
def greet(name: str) -> str:
|
|
136
|
+
return f"Hello, {name}"
|
|
137
|
+
|
|
138
|
+
async def fetch_data():
|
|
139
|
+
pass
|
|
140
|
+
`;
|
|
141
|
+
const result = detector.detect(content);
|
|
142
|
+
expect(result.language).toBe('python');
|
|
143
|
+
expect(result.evidence).toContain('function definition');
|
|
144
|
+
});
|
|
145
|
+
it('detects main guard', () => {
|
|
146
|
+
const content = `
|
|
147
|
+
def main():
|
|
148
|
+
print("Hello")
|
|
149
|
+
|
|
150
|
+
if __name__ == "__main__":
|
|
151
|
+
main()
|
|
152
|
+
`;
|
|
153
|
+
const result = detector.detect(content);
|
|
154
|
+
expect(result.language).toBe('python');
|
|
155
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
156
|
+
});
|
|
157
|
+
it('detects class definitions', () => {
|
|
158
|
+
const content = `
|
|
159
|
+
class UserService:
|
|
160
|
+
def __init__(self):
|
|
161
|
+
self.users = []
|
|
162
|
+
|
|
163
|
+
def get_user(self, id):
|
|
164
|
+
return self.users[id]
|
|
165
|
+
`;
|
|
166
|
+
const result = detector.detect(content);
|
|
167
|
+
expect(result.language).toBe('python');
|
|
168
|
+
expect(result.evidence).toContain('class definition');
|
|
169
|
+
});
|
|
170
|
+
it('detects elif keyword', () => {
|
|
171
|
+
const content = `
|
|
172
|
+
if x > 10:
|
|
173
|
+
print("big")
|
|
174
|
+
elif x > 5:
|
|
175
|
+
print("medium")
|
|
176
|
+
else:
|
|
177
|
+
print("small")
|
|
178
|
+
`;
|
|
179
|
+
const result = detector.detect(content);
|
|
180
|
+
expect(result.language).toBe('python');
|
|
181
|
+
expect(result.evidence).toContain('elif keyword');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe('Go patterns', () => {
|
|
185
|
+
it('detects package declaration', () => {
|
|
186
|
+
const content = `
|
|
187
|
+
package main
|
|
188
|
+
|
|
189
|
+
import "fmt"
|
|
190
|
+
|
|
191
|
+
func main() {
|
|
192
|
+
fmt.Println("Hello")
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
const result = detector.detect(content);
|
|
196
|
+
expect(result.language).toBe('go');
|
|
197
|
+
expect(result.method).toBe('pattern');
|
|
198
|
+
expect(result.evidence).toContain('package declaration');
|
|
199
|
+
});
|
|
200
|
+
it('detects method receivers', () => {
|
|
201
|
+
const content = `
|
|
202
|
+
type User struct {
|
|
203
|
+
Name string
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
func (u *User) Greet() string {
|
|
207
|
+
return "Hello, " + u.Name
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
const result = detector.detect(content);
|
|
211
|
+
expect(result.language).toBe('go');
|
|
212
|
+
expect(result.evidence).toContain('method with receiver');
|
|
213
|
+
});
|
|
214
|
+
it('detects short variable declaration', () => {
|
|
215
|
+
const content = `
|
|
216
|
+
func main() {
|
|
217
|
+
name := "test"
|
|
218
|
+
count := 0
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
const result = detector.detect(content);
|
|
222
|
+
expect(result.language).toBe('go');
|
|
223
|
+
expect(result.evidence).toContain('short variable declaration');
|
|
224
|
+
});
|
|
225
|
+
it('detects goroutines and channels', () => {
|
|
226
|
+
const content = `
|
|
227
|
+
func main() {
|
|
228
|
+
ch := make(chan int)
|
|
229
|
+
go processData(ch)
|
|
230
|
+
defer close(ch)
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
233
|
+
const result = detector.detect(content);
|
|
234
|
+
expect(result.language).toBe('go');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('Rust patterns', () => {
|
|
238
|
+
it('detects use statements', () => {
|
|
239
|
+
const content = `
|
|
240
|
+
use std::io::Read;
|
|
241
|
+
use std::collections::HashMap;
|
|
242
|
+
|
|
243
|
+
fn main() {
|
|
244
|
+
let map: HashMap<String, i32> = HashMap::new();
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
const result = detector.detect(content);
|
|
248
|
+
expect(result.language).toBe('rust');
|
|
249
|
+
expect(result.method).toBe('pattern');
|
|
250
|
+
expect(result.evidence).toContain('use statement');
|
|
251
|
+
});
|
|
252
|
+
it('detects impl blocks', () => {
|
|
253
|
+
const content = `
|
|
254
|
+
struct User {
|
|
255
|
+
name: String,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
impl User {
|
|
259
|
+
fn new(name: String) -> Self {
|
|
260
|
+
User { name }
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
const result = detector.detect(content);
|
|
265
|
+
expect(result.language).toBe('rust');
|
|
266
|
+
expect(result.evidence).toContain('impl block');
|
|
267
|
+
});
|
|
268
|
+
it('detects derive macros', () => {
|
|
269
|
+
const content = `
|
|
270
|
+
#[derive(Debug, Clone, Serialize)]
|
|
271
|
+
struct Config {
|
|
272
|
+
host: String,
|
|
273
|
+
port: u16,
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
276
|
+
const result = detector.detect(content);
|
|
277
|
+
expect(result.language).toBe('rust');
|
|
278
|
+
expect(result.evidence).toContain('derive macro');
|
|
279
|
+
});
|
|
280
|
+
it('detects Result and Option types', () => {
|
|
281
|
+
const content = `
|
|
282
|
+
fn read_file(path: &str) -> Result<String, io::Error> {
|
|
283
|
+
let content: Option<String> = Some("test".to_string());
|
|
284
|
+
content.unwrap()
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
const result = detector.detect(content);
|
|
288
|
+
expect(result.language).toBe('rust');
|
|
289
|
+
});
|
|
290
|
+
it('detects match expressions', () => {
|
|
291
|
+
const content = `
|
|
292
|
+
fn get_value(opt: Option<i32>) -> i32 {
|
|
293
|
+
match opt {
|
|
294
|
+
Some(x) => x,
|
|
295
|
+
None => 0,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
`;
|
|
299
|
+
const result = detector.detect(content);
|
|
300
|
+
expect(result.language).toBe('rust');
|
|
301
|
+
expect(result.evidence).toContain('match expression');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
describe('Java patterns', () => {
|
|
305
|
+
it('detects package declaration', () => {
|
|
306
|
+
const content = `
|
|
307
|
+
package com.example.app;
|
|
308
|
+
|
|
309
|
+
import java.util.List;
|
|
310
|
+
|
|
311
|
+
public class Main {
|
|
312
|
+
public static void main(String[] args) {
|
|
313
|
+
System.out.println("Hello");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
const result = detector.detect(content);
|
|
318
|
+
expect(result.language).toBe('java');
|
|
319
|
+
expect(result.method).toBe('pattern');
|
|
320
|
+
expect(result.evidence).toContain('package declaration');
|
|
321
|
+
});
|
|
322
|
+
it('detects public class', () => {
|
|
323
|
+
const content = `
|
|
324
|
+
public class UserService {
|
|
325
|
+
private List<User> users;
|
|
326
|
+
|
|
327
|
+
public User findById(int id) {
|
|
328
|
+
return users.get(id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
`;
|
|
332
|
+
const result = detector.detect(content);
|
|
333
|
+
expect(result.language).toBe('java');
|
|
334
|
+
expect(result.evidence).toContain('public class');
|
|
335
|
+
});
|
|
336
|
+
it('detects Override annotation', () => {
|
|
337
|
+
const content = `
|
|
338
|
+
class User {
|
|
339
|
+
@Override
|
|
340
|
+
public String toString() {
|
|
341
|
+
return "User{}";
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
const result = detector.detect(content);
|
|
346
|
+
expect(result.language).toBe('java');
|
|
347
|
+
expect(result.evidence).toContain('Override annotation');
|
|
348
|
+
});
|
|
349
|
+
it('detects System.out.println', () => {
|
|
350
|
+
const content = `
|
|
351
|
+
public class Main {
|
|
352
|
+
public void test() {
|
|
353
|
+
System.out.println("test");
|
|
354
|
+
System.out.print("test");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
public static void main(String[] args) {
|
|
358
|
+
new Main().test();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
`;
|
|
362
|
+
const result = detector.detect(content);
|
|
363
|
+
expect(result.language).toBe('java');
|
|
364
|
+
expect(result.evidence).toContain('System.out');
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
describe('JavaScript patterns', () => {
|
|
368
|
+
it('detects ES module imports', () => {
|
|
369
|
+
const content = `
|
|
370
|
+
import React from 'react'
|
|
371
|
+
import { useState, useEffect } from 'react'
|
|
372
|
+
|
|
373
|
+
export default function App() {
|
|
374
|
+
return <div>Hello</div>
|
|
375
|
+
}
|
|
376
|
+
`;
|
|
377
|
+
const result = detector.detect(content);
|
|
378
|
+
// Should upgrade to TypeScript since TypeScript patterns match too
|
|
379
|
+
expect(['typescript', 'javascript']).toContain(result.language);
|
|
380
|
+
});
|
|
381
|
+
it('detects CommonJS require', () => {
|
|
382
|
+
const content = `
|
|
383
|
+
const fs = require('fs')
|
|
384
|
+
const path = require('path')
|
|
385
|
+
|
|
386
|
+
module.exports = {
|
|
387
|
+
readFile: fs.readFileSync
|
|
388
|
+
}
|
|
389
|
+
`;
|
|
390
|
+
const result = detector.detect(content);
|
|
391
|
+
expect(['typescript', 'javascript']).toContain(result.language);
|
|
392
|
+
});
|
|
393
|
+
it('detects async/await', () => {
|
|
394
|
+
const content = `
|
|
395
|
+
async function fetchData() {
|
|
396
|
+
const response = await fetch('/api/data')
|
|
397
|
+
return await response.json()
|
|
398
|
+
}
|
|
399
|
+
`;
|
|
400
|
+
const result = detector.detect(content);
|
|
401
|
+
expect(['typescript', 'javascript']).toContain(result.language);
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
describe('statistical detection', () => {
|
|
406
|
+
const detector = new LanguageDetector();
|
|
407
|
+
it('detects Python by keyword frequency', () => {
|
|
408
|
+
const content = `
|
|
409
|
+
def function_one():
|
|
410
|
+
pass
|
|
411
|
+
|
|
412
|
+
def function_two():
|
|
413
|
+
if True:
|
|
414
|
+
return None
|
|
415
|
+
elif False:
|
|
416
|
+
raise Exception
|
|
417
|
+
else:
|
|
418
|
+
yield 1
|
|
419
|
+
|
|
420
|
+
class MyClass:
|
|
421
|
+
def __init__(self):
|
|
422
|
+
self.value = True
|
|
423
|
+
`;
|
|
424
|
+
const result = detector.detect(content);
|
|
425
|
+
expect(result.language).toBe('python');
|
|
426
|
+
});
|
|
427
|
+
it('detects Go by keyword frequency', () => {
|
|
428
|
+
const content = `
|
|
429
|
+
package main
|
|
430
|
+
|
|
431
|
+
func main() {
|
|
432
|
+
var x int
|
|
433
|
+
const y = 10
|
|
434
|
+
|
|
435
|
+
for i := range []int{1, 2, 3} {
|
|
436
|
+
go processItem(i)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
select {
|
|
440
|
+
case <-done:
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
`;
|
|
445
|
+
const result = detector.detect(content);
|
|
446
|
+
expect(result.language).toBe('go');
|
|
447
|
+
});
|
|
448
|
+
it('returns lower confidence for statistical detection', () => {
|
|
449
|
+
// Content that relies only on statistical detection
|
|
450
|
+
const content = `
|
|
451
|
+
fn test
|
|
452
|
+
let mut
|
|
453
|
+
const static
|
|
454
|
+
struct enum
|
|
455
|
+
impl use
|
|
456
|
+
`;
|
|
457
|
+
const result = detector.detectByStatistics(content);
|
|
458
|
+
expect(result.confidence).toBeLessThanOrEqual(0.7);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
describe('confidence threshold', () => {
|
|
462
|
+
it('uses default minimum confidence', () => {
|
|
463
|
+
const detector = new LanguageDetector();
|
|
464
|
+
expect(detector.getMinConfidence()).toBe(0.3);
|
|
465
|
+
});
|
|
466
|
+
it('respects custom minimum confidence', () => {
|
|
467
|
+
const detector = new LanguageDetector({ minConfidence: 0.5 });
|
|
468
|
+
expect(detector.getMinConfidence()).toBe(0.5);
|
|
469
|
+
});
|
|
470
|
+
it('can update confidence threshold', () => {
|
|
471
|
+
const detector = new LanguageDetector();
|
|
472
|
+
detector.setMinConfidence(0.8);
|
|
473
|
+
expect(detector.getMinConfidence()).toBe(0.8);
|
|
474
|
+
});
|
|
475
|
+
it('clamps confidence to valid range', () => {
|
|
476
|
+
const detector = new LanguageDetector();
|
|
477
|
+
detector.setMinConfidence(1.5);
|
|
478
|
+
expect(detector.getMinConfidence()).toBe(1);
|
|
479
|
+
detector.setMinConfidence(-0.5);
|
|
480
|
+
expect(detector.getMinConfidence()).toBe(0);
|
|
481
|
+
});
|
|
482
|
+
it('returns null when confidence below threshold', () => {
|
|
483
|
+
const detector = new LanguageDetector({ minConfidence: 0.99 });
|
|
484
|
+
// Even strong patterns might not reach 0.99
|
|
485
|
+
const result = detector.detect('const x = 1');
|
|
486
|
+
// Should likely return null due to high threshold
|
|
487
|
+
if (result.confidence < 0.99) {
|
|
488
|
+
expect(result.language).toBeNull();
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
describe('detectLanguage convenience function', () => {
|
|
493
|
+
it('detects language from content', () => {
|
|
494
|
+
const result = detectLanguage('#!/usr/bin/env python\nprint("hello")');
|
|
495
|
+
expect(result.language).toBe('python');
|
|
496
|
+
expect(result.confidence).toBe(1.0);
|
|
497
|
+
});
|
|
498
|
+
it('accepts options', () => {
|
|
499
|
+
const result = detectLanguage('const x = 1', { minConfidence: 0.1 });
|
|
500
|
+
expect(result).toBeDefined();
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
describe('edge cases', () => {
|
|
504
|
+
const detector = new LanguageDetector();
|
|
505
|
+
it('handles empty content', () => {
|
|
506
|
+
const result = detector.detect('');
|
|
507
|
+
expect(result.language).toBeNull();
|
|
508
|
+
expect(result.confidence).toBe(0);
|
|
509
|
+
expect(result.method).toBe('none');
|
|
510
|
+
});
|
|
511
|
+
it('handles whitespace-only content', () => {
|
|
512
|
+
const result = detector.detect(' \n\t\n ');
|
|
513
|
+
expect(result.language).toBeNull();
|
|
514
|
+
expect(result.confidence).toBe(0);
|
|
515
|
+
});
|
|
516
|
+
it('handles content with no clear language', () => {
|
|
517
|
+
const result = detector.detect('Hello World\nThis is just text\nNo code here');
|
|
518
|
+
expect(result.confidence).toBeLessThan(0.5);
|
|
519
|
+
});
|
|
520
|
+
it('handles binary-like content', () => {
|
|
521
|
+
const result = detector.detect('\x00\x01\x02\x03');
|
|
522
|
+
expect(result.confidence).toBeLessThan(0.5);
|
|
523
|
+
});
|
|
524
|
+
it('handles mixed language patterns', () => {
|
|
525
|
+
// Content with both Python and JavaScript patterns
|
|
526
|
+
const content = `
|
|
527
|
+
def function():
|
|
528
|
+
pass
|
|
529
|
+
|
|
530
|
+
function test() {
|
|
531
|
+
return 1
|
|
532
|
+
}
|
|
533
|
+
`;
|
|
534
|
+
const result = detector.detect(content);
|
|
535
|
+
// Should pick one, likely Python due to stronger patterns
|
|
536
|
+
expect(result.language).not.toBeNull();
|
|
537
|
+
expect(result.confidence).toBeGreaterThan(0);
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
describe('real-world examples', () => {
|
|
541
|
+
const detector = new LanguageDetector();
|
|
542
|
+
it('detects Python Django view', () => {
|
|
543
|
+
const content = `
|
|
544
|
+
from django.http import JsonResponse
|
|
545
|
+
from django.views import View
|
|
546
|
+
|
|
547
|
+
class UserView(View):
|
|
548
|
+
def get(self, request, user_id):
|
|
549
|
+
user = User.objects.get(id=user_id)
|
|
550
|
+
return JsonResponse({'name': user.name})
|
|
551
|
+
|
|
552
|
+
def post(self, request):
|
|
553
|
+
data = json.loads(request.body)
|
|
554
|
+
return JsonResponse({'status': 'ok'})
|
|
555
|
+
`;
|
|
556
|
+
const result = detector.detect(content);
|
|
557
|
+
expect(result.language).toBe('python');
|
|
558
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
559
|
+
});
|
|
560
|
+
it('detects TypeScript React component', () => {
|
|
561
|
+
const content = `
|
|
562
|
+
import React, { useState, useEffect } from 'react'
|
|
563
|
+
import type { User } from './types'
|
|
564
|
+
|
|
565
|
+
interface Props {
|
|
566
|
+
userId: number
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export const UserProfile: React.FC<Props> = ({ userId }) => {
|
|
570
|
+
const [user, setUser] = useState<User | null>(null)
|
|
571
|
+
|
|
572
|
+
useEffect(() => {
|
|
573
|
+
fetchUser(userId).then(setUser)
|
|
574
|
+
}, [userId])
|
|
575
|
+
|
|
576
|
+
if (!user) return <div>Loading...</div>
|
|
577
|
+
|
|
578
|
+
return <div>{user.name}</div>
|
|
579
|
+
}
|
|
580
|
+
`;
|
|
581
|
+
const result = detector.detect(content);
|
|
582
|
+
expect(result.language).toBe('typescript');
|
|
583
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
584
|
+
});
|
|
585
|
+
it('detects Go HTTP handler', () => {
|
|
586
|
+
const content = `
|
|
587
|
+
package handlers
|
|
588
|
+
|
|
589
|
+
import (
|
|
590
|
+
"encoding/json"
|
|
591
|
+
"net/http"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
type User struct {
|
|
595
|
+
ID int \`json:"id"\`
|
|
596
|
+
Name string \`json:"name"\`
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
func GetUser(w http.ResponseWriter, r *http.Request) {
|
|
600
|
+
user := &User{ID: 1, Name: "test"}
|
|
601
|
+
|
|
602
|
+
w.Header().Set("Content-Type", "application/json")
|
|
603
|
+
json.NewEncoder(w).Encode(user)
|
|
604
|
+
}
|
|
605
|
+
`;
|
|
606
|
+
const result = detector.detect(content);
|
|
607
|
+
expect(result.language).toBe('go');
|
|
608
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
609
|
+
});
|
|
610
|
+
it('detects Rust CLI application', () => {
|
|
611
|
+
const content = `
|
|
612
|
+
use clap::Parser;
|
|
613
|
+
use std::fs;
|
|
614
|
+
use std::io::Result;
|
|
615
|
+
|
|
616
|
+
#[derive(Parser, Debug)]
|
|
617
|
+
#[command(author, version, about)]
|
|
618
|
+
struct Args {
|
|
619
|
+
#[arg(short, long)]
|
|
620
|
+
input: String,
|
|
621
|
+
|
|
622
|
+
#[arg(short, long, default_value_t = false)]
|
|
623
|
+
verbose: bool,
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
fn main() -> Result<()> {
|
|
627
|
+
let args = Args::parse();
|
|
628
|
+
|
|
629
|
+
let content = fs::read_to_string(&args.input)?;
|
|
630
|
+
|
|
631
|
+
if args.verbose {
|
|
632
|
+
println!("Read {} bytes", content.len());
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
Ok(())
|
|
636
|
+
}
|
|
637
|
+
`;
|
|
638
|
+
const result = detector.detect(content);
|
|
639
|
+
expect(result.language).toBe('rust');
|
|
640
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
641
|
+
});
|
|
642
|
+
it('detects Java Spring Boot controller', () => {
|
|
643
|
+
const content = `
|
|
644
|
+
package com.example.demo.controller;
|
|
645
|
+
|
|
646
|
+
import org.springframework.web.bind.annotation.*;
|
|
647
|
+
import org.springframework.beans.factory.annotation.Autowired;
|
|
648
|
+
import java.util.List;
|
|
649
|
+
|
|
650
|
+
@RestController
|
|
651
|
+
@RequestMapping("/api/users")
|
|
652
|
+
public class UserController {
|
|
653
|
+
|
|
654
|
+
@Autowired
|
|
655
|
+
private UserService userService;
|
|
656
|
+
|
|
657
|
+
@GetMapping("/{id}")
|
|
658
|
+
public User getUser(@PathVariable Long id) {
|
|
659
|
+
return userService.findById(id);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
@PostMapping
|
|
663
|
+
public User createUser(@RequestBody User user) {
|
|
664
|
+
return userService.save(user);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
`;
|
|
668
|
+
const result = detector.detect(content);
|
|
669
|
+
expect(result.language).toBe('java');
|
|
670
|
+
expect(result.confidence).toBeGreaterThan(0.5);
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
//# sourceMappingURL=language-detector.test.js.map
|