"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const globals_1 = require("@jest/globals");
const database_1 = require("../../utils/database");
const RepositoryManager_1 = require("../../repositories/RepositoryManager");
const validation_1 = require("../../utils/validation");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
(0, globals_1.describe)('Issue #13: Add validation for special characters in keys', () => {
    let dbManager;
    let repositories;
    let tempDbPath;
    let testSessionId;
    (0, globals_1.beforeEach)(() => {
        tempDbPath = path.join(os.tmpdir(), `test-context-${Date.now()}.db`);
        dbManager = new database_1.DatabaseManager({
            filename: tempDbPath,
            maxSize: 10 * 1024 * 1024,
            walMode: true,
        });
        repositories = new RepositoryManager_1.RepositoryManager(dbManager);
        // Create test session
        const session = repositories.sessions.create({
            name: 'Test Session',
            description: 'Session for key validation tests',
        });
        testSessionId = session.id;
    });
    (0, globals_1.afterEach)(() => {
        dbManager.close();
        try {
            fs.unlinkSync(tempDbPath);
            fs.unlinkSync(`${tempDbPath}-wal`);
            fs.unlinkSync(`${tempDbPath}-shm`);
        }
        catch (_e) {
            // Ignore
        }
    });
    (0, globals_1.describe)('validateKey function', () => {
        (0, globals_1.describe)('Invalid characters that should be rejected', () => {
            (0, globals_1.it)('should reject keys with spaces', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key with spaces')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key with spaces')).toThrow(/special characters/i);
            });
            (0, globals_1.it)('should reject keys with tabs', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\twith\ttabs')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\twith\ttabs')).toThrow(/special characters/i);
            });
            (0, globals_1.it)('should reject keys with newlines', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\nwith\nnewlines')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\nwith\nnewlines')).toThrow(/special characters/i);
            });
            (0, globals_1.it)('should reject keys with carriage returns', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\rwith\rreturns')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\rwith\rreturns')).toThrow(/special characters/i);
            });
            (0, globals_1.it)('should reject keys with null bytes', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\0with\0null')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\0with\0null')).toThrow(/invalid characters/i);
            });
            (0, globals_1.it)('should reject keys with control characters', () => {
                // Test various control characters
                const controlChars = [
                    '\x00',
                    '\x01',
                    '\x02',
                    '\x03',
                    '\x04',
                    '\x05',
                    '\x06',
                    '\x07',
                    '\x08',
                    '\x0B',
                    '\x0C',
                    '\x0E',
                    '\x0F',
                    '\x10',
                    '\x11',
                    '\x12',
                    '\x13',
                    '\x14',
                    '\x15',
                    '\x16',
                    '\x17',
                    '\x18',
                    '\x19',
                    '\x1A',
                    '\x1B',
                    '\x1C',
                    '\x1D',
                    '\x1E',
                    '\x1F',
                ];
                controlChars.forEach(char => {
                    (0, globals_1.expect)(() => (0, validation_1.validateKey)(`key${char}test`)).toThrow(validation_1.ValidationError);
                });
            });
            (0, globals_1.it)('should reject keys with backslashes', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\\with\\backslashes')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key\\with\\backslashes')).toThrow(/special characters/i);
            });
            (0, globals_1.it)('should reject keys with quotes', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key"with"quotes')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)("key'with'quotes")).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key`with`backticks')).toThrow(validation_1.ValidationError);
            });
            (0, globals_1.it)('should reject keys with special shell characters', () => {
                const shellChars = [
                    '|',
                    '&',
                    ';',
                    '<',
                    '>',
                    '(',
                    ')',
                    '{',
                    '}',
                    '[',
                    ']',
                    '$',
                    '#',
                    '!',
                    '~',
                ];
                shellChars.forEach(char => {
                    (0, globals_1.expect)(() => (0, validation_1.validateKey)(`key${char}test`)).toThrow(validation_1.ValidationError);
                    (0, globals_1.expect)(() => (0, validation_1.validateKey)(`key${char}test`)).toThrow(/special characters/i);
                });
            });
            (0, globals_1.it)('should reject keys with wildcards', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key*with*asterisks')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key?with?questions')).toThrow(validation_1.ValidationError);
            });
            (0, globals_1.it)('should reject keys that start or end with whitespace', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(' leading_space')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('trailing_space ')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('\tleading_tab')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('trailing_tab\t')).toThrow(validation_1.ValidationError);
            });
        });
        (0, globals_1.describe)('Valid characters that should be accepted', () => {
            (0, globals_1.it)('should accept alphanumeric characters', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('simpleKey123')).toBe('simpleKey123');
                (0, globals_1.expect)((0, validation_1.validateKey)('UPPERCASE')).toBe('UPPERCASE');
                (0, globals_1.expect)((0, validation_1.validateKey)('lowercase')).toBe('lowercase');
                (0, globals_1.expect)((0, validation_1.validateKey)('MixedCase123')).toBe('MixedCase123');
            });
            (0, globals_1.it)('should accept underscores', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('key_with_underscores')).toBe('key_with_underscores');
                (0, globals_1.expect)((0, validation_1.validateKey)('_leading_underscore')).toBe('_leading_underscore');
                (0, globals_1.expect)((0, validation_1.validateKey)('trailing_underscore_')).toBe('trailing_underscore_');
            });
            (0, globals_1.it)('should accept hyphens', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('key-with-hyphens')).toBe('key-with-hyphens');
                (0, globals_1.expect)((0, validation_1.validateKey)('kebab-case-key')).toBe('kebab-case-key');
            });
            (0, globals_1.it)('should accept dots', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('key.with.dots')).toBe('key.with.dots');
                (0, globals_1.expect)((0, validation_1.validateKey)('namespace.key')).toBe('namespace.key');
            });
            (0, globals_1.it)('should accept forward slashes for path-like keys', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('path/to/key')).toBe('path/to/key');
                (0, globals_1.expect)((0, validation_1.validateKey)('feature/component/item')).toBe('feature/component/item');
            });
            (0, globals_1.it)('should accept colons for namespace separation', () => {
                (0, globals_1.expect)((0, validation_1.validateKey)('namespace:key')).toBe('namespace:key');
                (0, globals_1.expect)((0, validation_1.validateKey)('module:component:item')).toBe('module:component:item');
            });
        });
        (0, globals_1.describe)('Edge cases', () => {
            (0, globals_1.it)('should reject empty keys', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('')).toThrow(/empty/i);
            });
            (0, globals_1.it)('should reject null or undefined', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(null)).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(undefined)).toThrow(validation_1.ValidationError);
            });
            (0, globals_1.it)('should reject non-string values', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(123)).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)({})).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)([])).toThrow(validation_1.ValidationError);
            });
            (0, globals_1.it)('should reject keys that are too long', () => {
                const longKey = 'a'.repeat(256);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(longKey)).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(longKey)).toThrow(/too long/i);
            });
            (0, globals_1.it)('should accept keys at maximum length', () => {
                const maxKey = 'a'.repeat(255);
                (0, globals_1.expect)((0, validation_1.validateKey)(maxKey)).toBe(maxKey);
            });
            (0, globals_1.it)('should handle Unicode characters appropriately', () => {
                // Should reject emoji and other non-ASCII Unicode
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key_with_😀_emoji')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key_with_中文_characters')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key_with_🔥_fire')).toThrow(validation_1.ValidationError);
                // Should reject other Unicode special characters
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key_with_©_copyright')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('key_with_™_trademark')).toThrow(validation_1.ValidationError);
            });
            (0, globals_1.it)('should properly validate keys that become empty after trimming', () => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('   ')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('\t\t\t')).toThrow(validation_1.ValidationError);
                (0, globals_1.expect)(() => (0, validation_1.validateKey)('\n\n\n')).toThrow(validation_1.ValidationError);
            });
        });
    });
    (0, globals_1.describe)('context_save with key validation', () => {
        (0, globals_1.it)('should reject invalid keys when saving context', () => {
            const invalidKeys = [
                'key with spaces',
                'key\twith\ttabs',
                'key\nwith\nnewlines',
                'key|with|pipes',
                'key;with;semicolons',
                'key$with$dollars',
            ];
            invalidKeys.forEach(invalidKey => {
                (0, globals_1.expect)(() => {
                    repositories.contexts.save(testSessionId, {
                        key: invalidKey,
                        value: 'test value',
                        category: 'task',
                        priority: 'normal',
                    });
                }).toThrow(validation_1.ValidationError);
            });
        });
        (0, globals_1.it)('should accept valid keys when saving context', () => {
            const validKeys = [
                'simple_key',
                'key-with-hyphens',
                'key.with.dots',
                'path/to/key',
                'namespace:key',
                'MixedCase123',
            ];
            validKeys.forEach(validKey => {
                const result = repositories.contexts.save(testSessionId, {
                    key: validKey,
                    value: 'test value',
                    category: 'task',
                    priority: 'normal',
                });
                (0, globals_1.expect)(result.key).toBe(validKey);
                (0, globals_1.expect)(result.value).toBe('test value');
            });
        });
        (0, globals_1.it)('should provide clear error messages for invalid keys', () => {
            try {
                repositories.contexts.save(testSessionId, {
                    key: 'key with spaces',
                    value: 'test value',
                });
                fail('Expected ValidationError to be thrown');
            }
            catch (error) {
                (0, globals_1.expect)(error).toBeInstanceOf(validation_1.ValidationError);
                (0, globals_1.expect)(error.message).toMatch(/special characters/i);
                (0, globals_1.expect)(error.message).toMatch(/spaces/i);
            }
        });
    });
    (0, globals_1.describe)('context_batch_save with key validation', () => {
        (0, globals_1.it)('should validate all keys in batch save', () => {
            const items = [
                { key: 'valid_key_1', value: 'value1' },
                { key: 'key with spaces', value: 'value2' },
                { key: 'valid_key_3', value: 'value3' },
                { key: 'key\twith\ttabs', value: 'value4' },
            ];
            // Note: Current implementation doesn't validate special characters in batch operations
            // This test expects validation to be added
            const result = repositories.contexts.batchSave(testSessionId, items);
            // Once validation is implemented, we expect some items to fail
            const failedItems = result.results.filter(r => !r.success);
            (0, globals_1.expect)(failedItems).toHaveLength(2); // Should fail for items with spaces and tabs
            // Check that the error messages mention special characters
            (0, globals_1.expect)(failedItems[0].error).toMatch(/special characters/i);
            (0, globals_1.expect)(failedItems[1].error).toMatch(/special characters/i);
        });
        (0, globals_1.it)('should accept batch with all valid keys', () => {
            const items = [
                { key: 'valid_key_1', value: 'value1' },
                { key: 'valid-key-2', value: 'value2' },
                { key: 'valid.key.3', value: 'value3' },
                { key: 'valid/key/4', value: 'value4' },
            ];
            const result = repositories.contexts.batchSave(testSessionId, items);
            const successCount = result.results.filter(r => r.success).length;
            (0, globals_1.expect)(successCount).toBe(4);
            const savedItems = repositories.contexts.getBySessionId(testSessionId);
            (0, globals_1.expect)(savedItems).toHaveLength(4);
        });
        (0, globals_1.it)('should provide detailed error report for batch validation failures', () => {
            const items = [
                { key: 'valid_key', value: 'value1' },
                { key: 'invalid key', value: 'value2' },
                { key: 'another|invalid', value: 'value3' },
            ];
            const result = repositories.contexts.batchSave(testSessionId, items);
            // Once validation is implemented, we expect some items to fail
            const failedItems = result.results.filter(r => !r.success);
            (0, globals_1.expect)(failedItems).toHaveLength(2); // Should fail for items with special characters
            // Check specific failures
            const invalidKeyResult = result.results.find(r => r.key === 'invalid key');
            (0, globals_1.expect)(invalidKeyResult?.success).toBe(false);
            (0, globals_1.expect)(invalidKeyResult?.error).toMatch(/special characters/i);
            const pipeKeyResult = result.results.find(r => r.key === 'another|invalid');
            (0, globals_1.expect)(pipeKeyResult?.success).toBe(false);
            (0, globals_1.expect)(pipeKeyResult?.error).toMatch(/special characters/i);
        });
    });
    (0, globals_1.describe)('Protection against malicious keys', () => {
        (0, globals_1.it)('should prevent path traversal attempts in keys', () => {
            const pathTraversalKeys = [
                '../../../etc/passwd',
                '..\\..\\..\\windows\\system32',
                'key/../../../secret',
                'key/../../admin',
            ];
            pathTraversalKeys.forEach(key => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(key)).toThrow(validation_1.ValidationError);
            });
        });
        (0, globals_1.it)('should prevent SQL injection attempts in keys', () => {
            const sqlInjectionKeys = [
                "key'; DROP TABLE sessions; --",
                'key" OR "1"="1',
                'key`; DELETE FROM context_items; --',
            ];
            sqlInjectionKeys.forEach(key => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(key)).toThrow(validation_1.ValidationError);
            });
        });
        (0, globals_1.it)('should prevent script injection attempts in keys', () => {
            const scriptInjectionKeys = [
                '<script>alert("xss")</script>',
                'key<img src=x onerror=alert(1)>',
                'javascript:alert(1)',
            ];
            scriptInjectionKeys.forEach(key => {
                (0, globals_1.expect)(() => (0, validation_1.validateKey)(key)).toThrow(validation_1.ValidationError);
            });
        });
    });
    (0, globals_1.describe)('Performance considerations', () => {
        (0, globals_1.it)('should validate keys efficiently for large batches', () => {
            const largeItems = Array.from({ length: 100 }, (_, i) => ({
                key: `valid_key_${i}`,
                value: `value_${i}`,
            }));
            const startTime = Date.now();
            const result = repositories.contexts.batchSave(testSessionId, largeItems);
            const endTime = Date.now();
            const successCount = result.results.filter(r => r.success).length;
            (0, globals_1.expect)(successCount).toBe(100);
            (0, globals_1.expect)(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
        });
    });
    (0, globals_1.describe)('Error message quality', () => {
        (0, globals_1.it)('should provide helpful error messages for common mistakes', () => {
            const testCases = [
                {
                    key: 'key with spaces',
                    expectedError: /spaces are not allowed/i,
                },
                {
                    key: 'key\twith\ttabs',
                    expectedError: /tabs are not allowed/i,
                },
                {
                    key: 'key|pipe',
                    expectedError: /special characters/i,
                },
                {
                    key: '',
                    expectedError: /cannot be empty/i,
                },
                {
                    key: 'a'.repeat(256),
                    expectedError: /too long.*255 characters/i,
                },
            ];
            testCases.forEach(({ key, expectedError }) => {
                try {
                    (0, validation_1.validateKey)(key);
                    fail(`Expected ValidationError for key: ${key}`);
                }
                catch (error) {
                    (0, globals_1.expect)(error).toBeInstanceOf(validation_1.ValidationError);
                    (0, globals_1.expect)(error.message).toMatch(expectedError);
                }
            });
        });
    });
});
