mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-29 08:13:12 +00:00
Squashed superset takeover of the FTS-language trilogy by @rafaelreis-r (#580 env-var language for query+write side, #581 migration backfill, #582 gbrain reindex-search-vector), rebased onto current master with the security review's required fixes applied: - Migration renumbered v116 -> v123 (master's v116 was already claimed by code_edges_source_backfill; master is at v122). - Restored the v120/#1647 search_path hardening: all four CREATE OR REPLACE trigger-function bodies (migration handler + reindex command) now carry SET search_path = pg_catalog, public, since CREATE OR REPLACE resets proconfig and would otherwise strip the hardening on upgrade. - reindex-search-vector: shared progress reporter (stderr phases reindex_search_vector.pages/.chunks), id-keyset batched backfill (5000 rows/UPDATE) instead of single whole-table statements, and --json no longer bypasses the --yes/TTY confirmation gate. - Allowlist validation regex + injection tests kept exactly as authored. - Stale v33/v116 comments swept; docs/guides/multi-language-fts.md written (README referenced it but no PR added it); llms bundles regenerated via bun run build:llms. - Trilogy tests quarantined as *.serial.test.ts (env mutation, per check-test-isolation). Verified live on PGLite: fresh init with GBRAIN_FTS_LANGUAGE=portuguese produces portuguese-stemmed vectors with search_path pinned; the reindex command retokenizes an english brain to portuguese in place. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Rafael Reis <rafael.reis@contabilizei.com.br> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
|
import { getFtsLanguage, resetFtsLanguageCache } from '../src/core/fts-language.ts';
|
|
|
|
const ENV_KEY = 'GBRAIN_FTS_LANGUAGE';
|
|
|
|
beforeEach(() => {
|
|
delete process.env[ENV_KEY];
|
|
resetFtsLanguageCache();
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env[ENV_KEY];
|
|
resetFtsLanguageCache();
|
|
});
|
|
|
|
describe('getFtsLanguage', () => {
|
|
test('defaults to english when env is unset', () => {
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('defaults to english when env is empty string', () => {
|
|
process.env[ENV_KEY] = '';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('defaults to english when env is whitespace', () => {
|
|
process.env[ENV_KEY] = ' ';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('reads valid pt_br config', () => {
|
|
process.env[ENV_KEY] = 'pt_br';
|
|
expect(getFtsLanguage()).toBe('pt_br');
|
|
});
|
|
|
|
test('reads valid simple language name', () => {
|
|
process.env[ENV_KEY] = 'spanish';
|
|
expect(getFtsLanguage()).toBe('spanish');
|
|
});
|
|
|
|
test('reads name with underscores and digits', () => {
|
|
process.env[ENV_KEY] = 'custom_lang_v2';
|
|
expect(getFtsLanguage()).toBe('custom_lang_v2');
|
|
});
|
|
|
|
test('rejects names with quotes (SQL injection guard)', () => {
|
|
process.env[ENV_KEY] = "english'; DROP TABLE pages; --";
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('rejects names with spaces', () => {
|
|
process.env[ENV_KEY] = 'pt br';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('rejects names with hyphens', () => {
|
|
process.env[ENV_KEY] = 'pt-br';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('rejects names starting with digit', () => {
|
|
process.env[ENV_KEY] = '1lang';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('rejects uppercase (Postgres config names are lowercase)', () => {
|
|
process.env[ENV_KEY] = 'English';
|
|
expect(getFtsLanguage()).toBe('english');
|
|
});
|
|
|
|
test('caches after first read', () => {
|
|
process.env[ENV_KEY] = 'pt_br';
|
|
expect(getFtsLanguage()).toBe('pt_br');
|
|
|
|
// Mutate env after first read \u2014 cached value wins.
|
|
process.env[ENV_KEY] = 'spanish';
|
|
expect(getFtsLanguage()).toBe('pt_br');
|
|
});
|
|
|
|
test('resetFtsLanguageCache clears cache', () => {
|
|
process.env[ENV_KEY] = 'pt_br';
|
|
expect(getFtsLanguage()).toBe('pt_br');
|
|
|
|
resetFtsLanguageCache();
|
|
process.env[ENV_KEY] = 'spanish';
|
|
expect(getFtsLanguage()).toBe('spanish');
|
|
});
|
|
|
|
test('trims surrounding whitespace from valid value', () => {
|
|
process.env[ENV_KEY] = ' pt_br ';
|
|
expect(getFtsLanguage()).toBe('pt_br');
|
|
});
|
|
});
|