Files
connectai/tests/bridgeLauncher.test.ts
T
koriweb b61e06ef41 NotebookLM CLI 로그인 버튼 — nlm 실행 파일 자동 탐색
- 사이드바 도구 메뉴에 'NotebookLM 로그인' 버튼 추가, 파이썬 Scripts 폴더에서
  nlm(.exe) 자동 탐색(버전 하드코딩 없음) 후 login 인자로 실행
- g1nation.notebookLmCliPath 설정으로 수동 경로 지정도 가능
2026-08-06 18:51:22 +09:00

93 lines
4.2 KiB
TypeScript

/**
* bridgeLauncher — NotebookLM 로컬 백엔드 원클릭 실행/종료의 순수 로직 검증.
* (터미널 생성·프로세스 kill 같은 부수효과는 VS Code/OS 의존이라 여기선 순수 함수만.)
*/
import * as path from 'path';
import { candidateProjectDirs, buildPortKillCommand, candidateNlmPaths } from '../src/features/datacollect/bridgeLauncher';
jest.mock('vscode', () => ({
workspace: { getConfiguration: jest.fn(), workspaceFolders: [] },
window: {},
}), { virtual: true });
describe('candidateProjectDirs — 프로젝트 자동 탐색 후보 순서', () => {
it('설정값이 있으면 항상 1순위다', () => {
const dirs = candidateProjectDirs('/custom/Datacollector', ['/ws/ConnectAI']);
expect(dirs[0]).toBe('/custom/Datacollector');
});
it('워크스페이스 자신 → 부모의 형제 폴더(이름 후보들) 순으로 나열한다', () => {
const dirs = candidateProjectDirs('', ['/proj/ConnectAI']);
expect(dirs[0]).toBe('/proj/ConnectAI');
expect(dirs).toContain(path.join('/proj', 'Datacollector_MAC'));
expect(dirs).toContain(path.join('/proj', 'Datacollector'));
expect(dirs).toContain(path.join('/proj', 'Datacollect'));
});
it('중복 경로는 한 번만 나온다', () => {
const dirs = candidateProjectDirs('/proj/Datacollector_MAC', ['/proj/ConnectAI', '/proj/ConnectAI']);
expect(dirs.filter(d => d === path.join('/proj', 'Datacollector_MAC')).length).toBe(1);
expect(dirs.filter(d => d === '/proj/ConnectAI').length).toBe(1);
});
it('설정값 공백/빈 문자열은 무시한다', () => {
expect(candidateProjectDirs(' ', [])).toEqual([]);
expect(candidateProjectDirs('', [])).toEqual([]);
});
});
describe('candidateNlmPaths — nlm 실행 파일 탐색 순서', () => {
it('설정값이 있으면 항상 1순위다', () => {
const p = candidateNlmPaths('win32', {}, ['C:/py/Scripts'], 'D:/custom/nlm.exe');
expect(p[0]).toBe('D:/custom/nlm.exe');
});
it('윈도우는 파이썬 Scripts 폴더의 nlm.exe 를 찾는다 (버전 하드코딩 없이)', () => {
const p = candidateNlmPaths('win32', {}, ['C:/AppData/Python/Python314/Scripts']);
expect(p).toContain(path.join('C:/AppData/Python/Python314/Scripts', 'nlm.exe'));
});
it('맥/리눅스는 확장자 없는 nlm 과 ~/.local/bin 을 본다', () => {
const p = candidateNlmPaths('darwin', { HOME: '/Users/me' }, ['/Users/me/Library/Python/3.11/bin']);
expect(p).toContain(path.join('/Users/me/Library/Python/3.11/bin', 'nlm'));
expect(p).toContain(path.join('/Users/me', '.local', 'bin', 'nlm'));
expect(p.some(x => x.endsWith('nlm.exe'))).toBe(false);
});
it('마지막 후보는 PATH 폴백용 맨 이름이다', () => {
expect(candidateNlmPaths('win32', {}, []).at(-1)).toBe('nlm');
expect(candidateNlmPaths('linux', {}, []).at(-1)).toBe('nlm');
});
it('중복 경로는 한 번만 나온다', () => {
const p = candidateNlmPaths('win32', {}, ['C:/s', 'C:/s']);
expect(p.filter(x => x === path.join('C:/s', 'nlm.exe')).length).toBe(1);
});
it('설정값 공백은 무시한다', () => {
expect(candidateNlmPaths('win32', {}, [], ' ')[0]).not.toBe(' ');
});
});
describe('buildPortKillCommand — OS 자동 감지 분기', () => {
it('윈도우는 PowerShell Stop-Process 를 쓴다', () => {
const cmd = buildPortKillCommand('win32', 3002);
expect(cmd).toContain('powershell');
expect(cmd).toContain('-LocalPort 3002');
expect(cmd).toContain('Stop-Process');
});
it('맥/리눅스는 lsof + kill 을 쓴다', () => {
for (const platform of ['darwin', 'linux'] as NodeJS.Platform[]) {
const cmd = buildPortKillCommand(platform, 3002);
expect(cmd).toContain('lsof -ti :3002');
expect(cmd).toContain('kill');
}
});
it('포트 번호가 명령에 반영된다', () => {
expect(buildPortKillCommand('win32', 4100)).toContain('4100');
expect(buildPortKillCommand('darwin', 4100)).toContain(':4100');
});
});