mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2253d05618
Remove the nested v3/ subdirectory by moving all active source code, tests, configs, and assets directly to the project root. Archive v2/ (dead code, 847MB) to v2-archive.tar.gz. Key changes: - Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/ - Move v3/tsconfig.json, v3/vitest.config.ts to root - Merge v3/package.json into root package.json (ESM, scripts, deps, exports) - Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/ - Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes - Fix all init installer path resolution (3-level-up → 2-level-up) - Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe" - Update settings.json ADR/DDD directories to docs/implementation/ - Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks) - Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces - Update 15 guidance shards: v3/src/ → src/ path references - Fix sync interfaces: remove v3/ and v2/ path references - Fix test path calculations (process.cwd() + '..' no longer needed) - Fix flaky perf test threshold (500ms → 1000ms for CI environments) - Fix complexity-analyzer test to match ADR-051 keyword-based eligibility Build passes, 17,901 tests pass, CLI and MCP bundles verified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
253 lines
6.8 KiB
TypeScript
253 lines
6.8 KiB
TypeScript
/**
|
|
* SQLite Reader Unit Tests
|
|
*
|
|
* Tests for reading data from SQLite databases:
|
|
* - Initialization with database path validation
|
|
* - Error handling for missing files/tables
|
|
* - Data transformation for cloud sync
|
|
* - Record count operations
|
|
*
|
|
* Note: Complex database operations are mocked to avoid CI issues.
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import * as path from 'path';
|
|
|
|
// Mock fs module before imports
|
|
const mockExistsSync = vi.fn();
|
|
vi.mock('fs', () => ({
|
|
existsSync: (...args: unknown[]) => mockExistsSync(...args),
|
|
}));
|
|
|
|
// Create mock statement and database components
|
|
const mockStmtAll = vi.fn();
|
|
const mockStmtGet = vi.fn();
|
|
const mockPragma = vi.fn();
|
|
const mockPrepare = vi.fn();
|
|
const mockClose = vi.fn();
|
|
|
|
// Mock database instance
|
|
const createMockDb = () => ({
|
|
pragma: mockPragma,
|
|
prepare: mockPrepare,
|
|
close: mockClose,
|
|
});
|
|
|
|
// Mock better-sqlite3 default export
|
|
vi.mock('better-sqlite3', () => {
|
|
const MockDatabase = vi.fn(() => createMockDb());
|
|
return { default: MockDatabase };
|
|
});
|
|
|
|
import {
|
|
SQLiteReader,
|
|
createSQLiteReader,
|
|
type SQLiteReaderConfig,
|
|
} from '../../../../src/sync/readers/sqlite-reader.js';
|
|
import type { SyncSource } from '../../../../src/sync/interfaces.js';
|
|
|
|
describe('SQLiteReader', () => {
|
|
const defaultSource: SyncSource = {
|
|
name: 'test-source',
|
|
type: 'sqlite',
|
|
path: 'test.db',
|
|
targetTable: 'aqe.test_table',
|
|
priority: 'high',
|
|
mode: 'full',
|
|
enabled: true,
|
|
};
|
|
|
|
const defaultConfig: SQLiteReaderConfig = {
|
|
source: defaultSource,
|
|
baseDir: '/test/base',
|
|
environment: 'test-env',
|
|
};
|
|
|
|
let reader: SQLiteReader;
|
|
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
|
|
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
|
|
// Default mock implementations
|
|
mockExistsSync.mockReturnValue(true);
|
|
mockStmtAll.mockReturnValue([]);
|
|
mockStmtGet.mockReturnValue({ count: 0 });
|
|
|
|
mockPrepare.mockReturnValue({
|
|
all: mockStmtAll,
|
|
get: mockStmtGet,
|
|
});
|
|
|
|
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
consoleLogSpy.mockRestore();
|
|
consoleWarnSpy.mockRestore();
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should create reader with config', () => {
|
|
reader = new SQLiteReader(defaultConfig);
|
|
|
|
expect(reader.name).toBe('test-source');
|
|
expect(reader.type).toBe('sqlite');
|
|
});
|
|
|
|
it('should resolve path relative to baseDir', () => {
|
|
reader = new SQLiteReader(defaultConfig);
|
|
|
|
const expectedPath = path.resolve('/test/base', 'test.db');
|
|
const info = reader.getInfo();
|
|
|
|
expect(info.path).toBe(expectedPath);
|
|
});
|
|
});
|
|
|
|
describe('initialize', () => {
|
|
it('should throw error if database file not found', async () => {
|
|
mockExistsSync.mockReturnValue(false);
|
|
|
|
reader = new SQLiteReader(defaultConfig);
|
|
|
|
await expect(reader.initialize()).rejects.toThrow('SQLite database not found');
|
|
});
|
|
});
|
|
|
|
describe('readAll', () => {
|
|
it('should throw error if not initialized', async () => {
|
|
const uninitReader = new SQLiteReader(defaultConfig);
|
|
|
|
await expect(uninitReader.readAll()).rejects.toThrow('Reader not initialized');
|
|
});
|
|
});
|
|
|
|
describe('readChanged', () => {
|
|
it('should throw error if not initialized', async () => {
|
|
const uninitReader = new SQLiteReader(defaultConfig);
|
|
|
|
await expect(uninitReader.readChanged(new Date())).rejects.toThrow(
|
|
'Reader not initialized'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('count', () => {
|
|
it('should throw error if not initialized', async () => {
|
|
const uninitReader = new SQLiteReader(defaultConfig);
|
|
|
|
await expect(uninitReader.count()).rejects.toThrow('Reader not initialized');
|
|
});
|
|
});
|
|
|
|
describe('close', () => {
|
|
it('should handle close when not initialized', async () => {
|
|
reader = new SQLiteReader(defaultConfig);
|
|
|
|
await expect(reader.close()).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('getInfo', () => {
|
|
it('should handle missing database file', () => {
|
|
mockExistsSync.mockReturnValue(false);
|
|
|
|
reader = new SQLiteReader(defaultConfig);
|
|
const info = reader.getInfo();
|
|
|
|
expect(info.exists).toBe(false);
|
|
expect(info.tables).toEqual([]);
|
|
});
|
|
|
|
it('should return correct path', () => {
|
|
reader = new SQLiteReader(defaultConfig);
|
|
const info = reader.getInfo();
|
|
|
|
expect(info.path).toBe(path.resolve('/test/base', 'test.db'));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('createSQLiteReader', () => {
|
|
it('should create SQLiteReader instance', () => {
|
|
const config: SQLiteReaderConfig = {
|
|
source: {
|
|
name: 'test',
|
|
type: 'sqlite',
|
|
path: 'test.db',
|
|
targetTable: 'test_table',
|
|
priority: 'high',
|
|
mode: 'full',
|
|
},
|
|
baseDir: '/test',
|
|
environment: 'test',
|
|
};
|
|
|
|
const reader = createSQLiteReader(config);
|
|
|
|
expect(reader).toBeInstanceOf(SQLiteReader);
|
|
});
|
|
});
|
|
|
|
describe('SQLiteReader data transformation helpers', () => {
|
|
// Test the transformation logic through the reader's public interface
|
|
// These tests validate the transformation rules without requiring a full database
|
|
|
|
it('should configure reader for JSON parsing', () => {
|
|
const reader = new SQLiteReader({
|
|
source: {
|
|
name: 'test',
|
|
type: 'sqlite',
|
|
path: 'test.db',
|
|
targetTable: 'aqe.test_table',
|
|
priority: 'high',
|
|
mode: 'full',
|
|
},
|
|
baseDir: '/test',
|
|
environment: 'test-env',
|
|
});
|
|
|
|
expect(reader.name).toBe('test');
|
|
expect(reader.type).toBe('sqlite');
|
|
});
|
|
|
|
it('should configure reader with custom query', () => {
|
|
const reader = new SQLiteReader({
|
|
source: {
|
|
name: 'custom-query-source',
|
|
type: 'sqlite',
|
|
path: 'test.db',
|
|
targetTable: 'aqe.custom_table',
|
|
priority: 'medium',
|
|
mode: 'incremental',
|
|
query: 'SELECT id, name FROM custom_table WHERE active = 1',
|
|
},
|
|
baseDir: '/test',
|
|
environment: 'production',
|
|
});
|
|
|
|
expect(reader.name).toBe('custom-query-source');
|
|
});
|
|
|
|
it('should extract table name from target table', () => {
|
|
const reader = new SQLiteReader({
|
|
source: {
|
|
name: 'schema-test',
|
|
type: 'sqlite',
|
|
path: 'test.db',
|
|
targetTable: 'schema.table_name',
|
|
priority: 'high',
|
|
mode: 'full',
|
|
},
|
|
baseDir: '/test',
|
|
environment: 'test',
|
|
});
|
|
|
|
// The table extraction logic is internal but affects readAll behavior
|
|
expect(reader).toBeDefined();
|
|
});
|
|
});
|