diff --git a/src/tools/advanced-caching/cache-compression.ts b/src/tools/advanced-caching/cache-compression.ts index dde5220..7190eec 100644 --- a/src/tools/advanced-caching/cache-compression.ts +++ b/src/tools/advanced-caching/cache-compression.ts @@ -470,7 +470,7 @@ export class CacheCompressionTool { timestamp: Date.now(), }; - const metadataBuffer = JSON.stringify(metadata); + const metadataBuffer = Buffer.from(JSON.stringify(metadata), 'utf-8'); const metadataLength = Buffer.allocUnsafe(4); metadataLength.writeUInt32LE(metadataBuffer.length, 0); @@ -1011,7 +1011,7 @@ export class CacheCompressionTool { // Replace repeated strings with dictionary references const compressed = this.compressWithDictionary(obj, dict); - return JSON.stringify(compressed); + return Buffer.from(JSON.stringify(compressed), 'utf-8'); } catch { // Not JSON, just use gzip return Buffer.from(str); @@ -1032,7 +1032,7 @@ export class CacheCompressionTool { compressed.data, dict, ); - return JSON.stringify(decompressed); + return Buffer.from(JSON.stringify(decompressed), 'utf-8'); } return data; @@ -1272,7 +1272,7 @@ export class CacheCompressionTool { } else if (typeof data === "string") { return Buffer.from(data, "utf-8"); } else { - return JSON.stringify(data); + return Buffer.from(JSON.stringify(data), 'utf-8'); } } @@ -1292,7 +1292,7 @@ export class CacheCompressionTool { active: i % 2 === 0, })), }; - return JSON.stringify(obj); + return Buffer.from(JSON.stringify(obj), 'utf-8'); } case "text": { diff --git a/src/tools/api-database/smart-cache-api.ts b/src/tools/api-database/smart-cache-api.ts index 23fb662..3df54f8 100644 --- a/src/tools/api-database/smart-cache-api.ts +++ b/src/tools/api-database/smart-cache-api.ts @@ -241,9 +241,9 @@ export class SmartCacheAPI { } const cacheKey = this.generateCacheKey(options.request, options); - const cachedBuffer = this.cache.get(cacheKey); - const cached = cachedBuffer - ? this.deserializeCachedResponse(cachedBuffer) + const cachedString = this.cache.get(cacheKey); + const cached = cachedString + ? this.deserializeCachedResponse(Buffer.from(cachedString, 'utf-8')) : null; // Update stats @@ -379,9 +379,9 @@ export class SmartCacheAPI { for (const key of allKeys) { if (regex.test(key)) { - const cachedBuffer = this.cache.get(key); - const cached = cachedBuffer - ? this.deserializeCachedResponse(cachedBuffer) + const cachedString = this.cache.get(key); + const cached = cachedString + ? this.deserializeCachedResponse(Buffer.from(cachedString, 'utf-8')) : null; if (cached) { totalSize += cached.size; @@ -402,9 +402,9 @@ export class SmartCacheAPI { const allKeysForTags = this.getAllCacheKeys(); for (const key of allKeysForTags) { - const cachedBuffer = this.cache.get(key); - const cached = cachedBuffer - ? this.deserializeCachedResponse(cachedBuffer) + const cachedString = this.cache.get(key); + const cached = cachedString + ? this.deserializeCachedResponse(Buffer.from(cachedString, 'utf-8')) : null; if (cached && cached.tags) { const hasMatchingTag = cached.tags.some((tag: string) => @@ -422,9 +422,9 @@ export class SmartCacheAPI { case "manual": if (options.request) { const cacheKey = this.generateCacheKey(options.request, options); - const cachedBuffer = this.cache.get(cacheKey); - const cached = cachedBuffer - ? this.deserializeCachedResponse(cachedBuffer) + const cachedString = this.cache.get(cacheKey); + const cached = cachedString + ? this.deserializeCachedResponse(Buffer.from(cachedString, 'utf-8')) : null; if (cached) { totalSize += cached.size; @@ -440,9 +440,9 @@ export class SmartCacheAPI { const now = Date.now(); for (const key of allKeysForTime) { - const cachedBuffer = this.cache.get(key); - const cached = cachedBuffer - ? this.deserializeCachedResponse(cachedBuffer) + const cachedString = this.cache.get(key); + const cached = cachedString + ? this.deserializeCachedResponse(Buffer.from(cachedString, 'utf-8')) : null; if (cached) { const age = Math.floor((now - cached.timestamp) / 1000); diff --git a/src/tools/api-database/smart-migration.test.ts b/src/tools/api-database/smart-migration.test.ts new file mode 100644 index 0000000..0013fc5 --- /dev/null +++ b/src/tools/api-database/smart-migration.test.ts @@ -0,0 +1,194 @@ +/** + * Unit Tests for Smart Migration Tool + * Testing import corrections and core functionality + */ + +import { describe, it, expect, beforeEach } from '@jest/globals'; +import { SmartMigration, getSmartMigration, runSmartMigration } from './smart-migration'; +import { CacheEngine } from '../../core/cache-engine'; +import { TokenCounter } from '../../core/token-counter'; +import { MetricsCollector } from '../../core/metrics'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +describe('Smart Migration - Import Type Corrections', () => { + let cacheEngine: CacheEngine; + let tokenCounter: TokenCounter; + let metricsCollector: MetricsCollector; + let smartMigration: SmartMigration; + + beforeEach(() => { + // Initialize dependencies - verifying that imports work as values + cacheEngine = new CacheEngine(join(tmpdir(), '.test-cache', 'test.db'), 100); + tokenCounter = new TokenCounter(); + metricsCollector = new MetricsCollector(); + smartMigration = new SmartMigration(cacheEngine, tokenCounter, metricsCollector); + }); + + describe('Class Instantiation', () => { + it('should instantiate SmartMigration with TokenCounter as value', () => { + expect(smartMigration).toBeInstanceOf(SmartMigration); + expect(tokenCounter).toBeInstanceOf(TokenCounter); + }); + + it('should instantiate SmartMigration with MetricsCollector as value', () => { + expect(metricsCollector).toBeInstanceOf(MetricsCollector); + }); + + it('should instantiate SmartMigration with CacheEngine as value', () => { + expect(cacheEngine).toBeInstanceOf(CacheEngine); + }); + }); + + describe('Factory Function', () => { + it('should create SmartMigration instance via factory function', () => { + const instance = getSmartMigration(cacheEngine, tokenCounter, metricsCollector); + expect(instance).toBeInstanceOf(SmartMigration); + }); + }); + + describe('Migration List Action', () => { + it('should list migrations', async () => { + const result = await smartMigration.run({ action: 'list', limit: 5 }); + + expect(result).toBeDefined(); + expect(result.result).toBeDefined(); + expect(result.tokens).toBeDefined(); + expect(result.tokens.baseline).toBeGreaterThan(0); + expect(result.tokens.actual).toBeGreaterThan(0); + }); + + it('should return token reduction metrics', async () => { + const result = await smartMigration.run({ action: 'list' }); + + expect(result.tokens.reduction).toBeGreaterThanOrEqual(0); + expect(result.tokens.saved).toBeGreaterThanOrEqual(0); + }); + }); + + describe('Migration Status Action', () => { + it('should return migration status summary', async () => { + const result = await smartMigration.run({ action: 'status' }); + + expect(result).toBeDefined(); + expect(result.result).toContain('Status'); + expect(result.cached).toBe(false); + }); + }); + + describe('Migration History Action', () => { + it('should return migration history', async () => { + const result = await smartMigration.run({ action: 'history', limit: 10 }); + + expect(result).toBeDefined(); + expect(result.result).toBeDefined(); + expect(result.executionTime).toBeGreaterThanOrEqual(0); + }); + }); + + describe('Generate Migration Action', () => { + it('should generate migration file', async () => { + const result = await smartMigration.run({ + action: 'generate', + migrationId: 'test_migration' + }); + + expect(result).toBeDefined(); + expect(result.result).toContain('test_migration'); + }); + }); + + describe('Rollback Migration Action', () => { + it('should rollback migration', async () => { + const result = await smartMigration.run({ + action: 'rollback', + migrationId: 'test_rollback', + direction: 'down' + }); + + expect(result).toBeDefined(); + expect(result.result).toContain('Rollback'); + }); + }); + + describe('Caching Behavior', () => { + it('should cache read-only operations', async () => { + const result1 = await smartMigration.run({ action: 'list' }); + const result2 = await smartMigration.run({ action: 'list' }); + + expect(result1).toBeDefined(); + expect(result2).toBeDefined(); + // Second call may be cached (implementation-dependent) + }); + + it('should respect force flag to bypass cache', async () => { + const result1 = await smartMigration.run({ action: 'list', force: false }); + const result2 = await smartMigration.run({ action: 'list', force: true }); + + expect(result1).toBeDefined(); + expect(result2).toBeDefined(); + expect(result2.cached).toBe(false); + }); + }); + + describe('Error Handling', () => { + it('should throw error for invalid action', async () => { + await expect( + smartMigration.run({ action: 'invalid' as any }) + ).rejects.toThrow(); + }); + + it('should throw error when migrationId missing for rollback', async () => { + await expect( + smartMigration.run({ action: 'rollback' }) + ).rejects.toThrow('migrationId is required'); + }); + + it('should throw error when migrationId missing for generate', async () => { + await expect( + smartMigration.run({ action: 'generate' }) + ).rejects.toThrow('migrationId is required'); + }); + }); + + describe('CLI Function', () => { + it('should run migration via CLI function', async () => { + const result = await runSmartMigration({ action: 'status' }); + + expect(result).toBeDefined(); + expect(typeof result).toBe('string'); + expect(result).toContain('Status'); + }); + }); +}); + +describe('Import Type Verification', () => { + it('should verify TokenCounter can be instantiated', () => { + const counter = new TokenCounter(); + expect(counter).toBeInstanceOf(TokenCounter); + + const result = counter.count('test string'); + expect(result).toBeDefined(); + expect(result.tokens).toBeGreaterThan(0); + }); + + it('should verify MetricsCollector can be instantiated', () => { + const metrics = new MetricsCollector(); + expect(metrics).toBeInstanceOf(MetricsCollector); + + metrics.record({ + operation: 'test', + duration: 100, + success: true, + cacheHit: false, + inputTokens: 10, + outputTokens: 5, + savedTokens: 0 + }); + }); + + it('should verify CacheEngine can be instantiated', () => { + const cache = new CacheEngine(join(tmpdir(), '.test-cache-verify', 'test.db'), 100); + expect(cache).toBeInstanceOf(CacheEngine); + }); +}); diff --git a/src/tools/api-database/smart-migration.ts b/src/tools/api-database/smart-migration.ts index dd97b5c..da880a2 100644 --- a/src/tools/api-database/smart-migration.ts +++ b/src/tools/api-database/smart-migration.ts @@ -19,10 +19,7 @@ */ import { createHash } from "crypto"; -import type { CacheEngine } from "../../core/cache-engine"; -import type { TokenCounter } from "../../core/token-counter"; -import type { MetricsCollector } from "../../core/metrics"; -import { CacheEngine as CacheEngineClass } from "../../core/cache-engine"; +import { CacheEngine, CacheEngine as CacheEngineClass } from "../../core/cache-engine"; import { TokenCounter } from "../../core/token-counter"; import { MetricsCollector } from "../../core/metrics"; diff --git a/src/tools/api-database/smart-schema.test.ts b/src/tools/api-database/smart-schema.test.ts new file mode 100644 index 0000000..17a4803 --- /dev/null +++ b/src/tools/api-database/smart-schema.test.ts @@ -0,0 +1,269 @@ +/** + * Unit Tests for Smart Schema Tool + * Testing import corrections and core functionality + */ + +import { describe, it, expect, beforeEach } from '@jest/globals'; +import { SmartSchema, getSmartSchema, runSmartSchema } from './smart-schema'; +import { CacheEngine } from '../../core/cache-engine'; +import { TokenCounter } from '../../core/token-counter'; +import { MetricsCollector } from '../../core/metrics'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +describe('Smart Schema - Import Type Corrections', () => { + let cacheEngine: CacheEngine; + let tokenCounter: TokenCounter; + let metricsCollector: MetricsCollector; + let smartSchema: SmartSchema; + + beforeEach(() => { + // Initialize dependencies - verifying that imports work as values + cacheEngine = new CacheEngine(join(tmpdir(), '.test-schema-cache', 'test.db'), 100); + tokenCounter = new TokenCounter(); + metricsCollector = new MetricsCollector(); + smartSchema = new SmartSchema(cacheEngine, tokenCounter, metricsCollector); + }); + + describe('Class Instantiation', () => { + it('should instantiate SmartSchema with TokenCounter as value', () => { + expect(smartSchema).toBeInstanceOf(SmartSchema); + expect(tokenCounter).toBeInstanceOf(TokenCounter); + }); + + it('should instantiate SmartSchema with MetricsCollector as value', () => { + expect(metricsCollector).toBeInstanceOf(MetricsCollector); + }); + + it('should instantiate SmartSchema with CacheEngine as value', () => { + expect(cacheEngine).toBeInstanceOf(CacheEngine); + }); + }); + + describe('Factory Function', () => { + it('should create SmartSchema instance via factory function', () => { + const instance = getSmartSchema(cacheEngine, tokenCounter, metricsCollector); + expect(instance).toBeInstanceOf(SmartSchema); + }); + }); + + describe('Schema Introspection', () => { + it('should introspect PostgreSQL schema', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://user:pass@localhost/testdb', + mode: 'summary' + }); + + expect(result).toBeDefined(); + expect(result.result).toBeDefined(); + expect(result.tokens).toBeDefined(); + expect(result.tokens.baseline).toBeGreaterThan(0); + }); + + it('should introspect MySQL schema', async () => { + const result = await smartSchema.run({ + connectionString: 'mysql://user:pass@localhost/testdb', + mode: 'summary' + }); + + expect(result).toBeDefined(); + expect(result.result).toContain('Schema Summary'); + }); + + it('should introspect SQLite schema', async () => { + const result = await smartSchema.run({ + connectionString: '/path/to/database.sqlite', + mode: 'summary' + }); + + expect(result).toBeDefined(); + expect(result.result).toContain('Schema Summary'); + }); + }); + + describe('Output Modes', () => { + it('should generate summary output with token reduction', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary' + }); + + expect(result.result).toContain('Schema Summary'); + expect(result.result).toContain('95% Token Reduction'); + expect(result.tokens.reduction).toBeGreaterThan(0); + }); + + it('should generate analysis output', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'analysis' + }); + + expect(result.result).toContain('Schema Analysis'); + expect(result.result).toContain('85% Token Reduction'); + }); + + it('should generate full output', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'full' + }); + + expect(result).toBeDefined(); + expect(result.result).toBeDefined(); + }); + }); + + describe('Caching Behavior', () => { + it('should cache schema introspection results', async () => { + const result1 = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary', + forceRefresh: false + }); + + const result2 = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary', + forceRefresh: false + }); + + expect(result1).toBeDefined(); + expect(result2).toBeDefined(); + }); + + it('should bypass cache when forceRefresh is true', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary', + forceRefresh: true + }); + + expect(result.cached).toBe(false); + }); + }); + + describe('Schema Analysis Features', () => { + it('should detect unused indexes when requested', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'analysis', + detectUnusedIndexes: true + }); + + expect(result).toBeDefined(); + expect(result.result).toContain('Schema Analysis'); + }); + + it('should analyze specific tables', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'analysis', + analyzeTables: ['users', 'orders'] + }); + + expect(result).toBeDefined(); + }); + + it('should include data (row counts) when requested', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary', + includeData: true + }); + + expect(result).toBeDefined(); + }); + }); + + describe('Token Reduction Metrics', () => { + it('should provide token reduction statistics', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary' + }); + + expect(result.tokens).toBeDefined(); + expect(result.tokens.baseline).toBeGreaterThan(0); + expect(result.tokens.actual).toBeGreaterThan(0); + expect(result.tokens.saved).toBeGreaterThanOrEqual(0); + expect(result.tokens.reduction).toBeGreaterThanOrEqual(0); + }); + + it('should show high reduction in summary mode', async () => { + const result = await smartSchema.run({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary' + }); + + // Summary mode should achieve high token reduction + expect(result.tokens.reduction).toBeGreaterThan(50); + }); + }); + + describe('Error Handling', () => { + it('should throw error for invalid connection string', async () => { + await expect( + smartSchema.run({ + connectionString: 'invalid://connection/string' + }) + ).rejects.toThrow(); + }); + + it('should handle missing connection string', async () => { + await expect( + smartSchema.run({ + connectionString: '' as any + }) + ).rejects.toThrow(); + }); + }); + + describe('CLI Function', () => { + it('should run schema analysis via CLI function', async () => { + const result = await runSmartSchema({ + connectionString: 'postgresql://localhost/testdb', + mode: 'summary' + }); + + expect(result).toBeDefined(); + expect(typeof result).toBe('string'); + expect(result).toContain('Schema Summary'); + }); + }); +}); + +describe('Import Type Verification - Smart Schema', () => { + it('should verify TokenCounter can be used as value in runSmartSchema', async () => { + const tokenCounter = new TokenCounter(); + expect(tokenCounter).toBeInstanceOf(TokenCounter); + + const result = tokenCounter.count('test schema data'); + expect(result.tokens).toBeGreaterThan(0); + }); + + it('should verify MetricsCollector can be used as value in runSmartSchema', () => { + const metrics = new MetricsCollector(); + expect(metrics).toBeInstanceOf(MetricsCollector); + + metrics.record({ + operation: 'smart_schema', + duration: 150, + success: true, + cacheHit: false, + inputTokens: 20, + outputTokens: 10, + savedTokens: 10 + }); + }); + + it('should verify CacheEngine can be used as value in constructor', () => { + const cache = new CacheEngine(join(tmpdir(), '.test-schema-cache-verify', 'test.db'), 100); + expect(cache).toBeInstanceOf(CacheEngine); + + const tokenCounter = new TokenCounter(); + const metrics = new MetricsCollector(); + const schema = new SmartSchema(cache, tokenCounter, metrics); + expect(schema).toBeInstanceOf(SmartSchema); + }); +}); diff --git a/src/tools/api-database/smart-schema.ts b/src/tools/api-database/smart-schema.ts index 8ae8428..5f95f2c 100644 --- a/src/tools/api-database/smart-schema.ts +++ b/src/tools/api-database/smart-schema.ts @@ -18,10 +18,7 @@ */ import { createHash } from "crypto"; -import type { CacheEngine } from "../../core/cache-engine"; -import type { TokenCounter } from "../../core/token-counter"; -import type { MetricsCollector } from "../../core/metrics"; -import { CacheEngine as CacheEngineClass } from "../../core/cache-engine"; +import { CacheEngine, CacheEngine as CacheEngineClass } from "../../core/cache-engine"; import { TokenCounter } from "../../core/token-counter"; import { MetricsCollector } from "../../core/metrics"; import { generateCacheKey } from "../shared/hash-utils";