UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

1,047 lines (793 loc) 38.3 kB
import "reflect-metadata"; import { MetadataRegistry, SchemaBuilder } from "../../core"; import { PostgreSQLAdapter } from "../../adapters/postgresql"; import { User } from "../../examples/blog/User"; import { Post } from "../../examples/blog/Post"; import { Tag } from "../../examples/blog/Tag"; import { ComplexUser } from "../fixtures/ComplexUser"; /** * PostgreSQL Adapter Bulletproof Tests * These tests shoot AK-47s at the adapter to ensure it's bulletproof */ describe("PostgreSQLAdapter - Bulletproof Tests", () => { let registry: MetadataRegistry; let builder: SchemaBuilder; let adapter: PostgreSQLAdapter; let mockPool: any; let mockClient: any; let mockQueryResult: any; beforeEach(() => { registry = new MetadataRegistry(); builder = new SchemaBuilder(registry); builder.registerEntities([User, Post, Tag, ComplexUser]); // Debug: Check if ComplexUser was registered const entityMetadata = registry.getEntityMetadata(ComplexUser); if (!entityMetadata) { console.log("WARNING: ComplexUser not registered in metadata registry"); } // Create mock query result mockQueryResult = { rows: [], rowCount: 0 }; // Create mock client mockClient = { query: jest.fn().mockResolvedValue(mockQueryResult), release: jest.fn() }; // Create mock pool mockPool = { connect: jest.fn().mockResolvedValue(mockClient), end: jest.fn().mockResolvedValue(undefined) }; adapter = new PostgreSQLAdapter(registry, "postgresql://localhost:5432/test"); // Mock the private pool property (adapter as any).pool = mockPool; (adapter as any).initialized = true; }); afterEach(() => { jest.clearAllMocks(); }); // ============================================== // PHASE 1: FOUNDATION HARDENING - AK-47 ROUND 1 // ============================================== describe("Phase 1: Foundation Hardening", () => { describe("Connection Failure Scenarios", () => { test("should handle invalid connection string gracefully", async () => { const invalidAdapter = new PostgreSQLAdapter(registry, "invalid://connection"); // Mock the initialization to fail (invalidAdapter as any).initialize = jest.fn().mockRejectedValue(new Error("invalid connection")); // Force initialization to trigger connection await expect(invalidAdapter.query(User, { id: "test" })).rejects.toThrow("invalid connection"); }); test("should handle connection timeout", async () => { mockPool.connect.mockRejectedValueOnce(new Error("connection timeout")); await expect(adapter.query(User, { id: "test" })).rejects.toThrow("connection timeout"); }); test("should handle authentication failure", async () => { mockPool.connect.mockRejectedValueOnce(new Error("authentication failed")); await expect(adapter.query(User, { id: "test" })).rejects.toThrow("authentication failed"); }); test("should handle connection pool exhaustion", async () => { mockPool.connect.mockRejectedValueOnce(new Error("connection pool exhausted")); await expect(adapter.query(User, { id: "test" })).rejects.toThrow("connection pool exhausted"); }); test("should handle connection loss during operation", async () => { mockClient.query.mockRejectedValueOnce(new Error("connection lost")); await expect(adapter.query(User, { id: "test" })).rejects.toThrow("connection lost"); // Verify client was released despite error expect(mockClient.release).toHaveBeenCalled(); }); test("should handle null/undefined pool", async () => { (adapter as any).pool = null; (adapter as any).initialized = true; await expect(adapter.query(User, { id: "test" })).rejects.toThrow("Pool not initialized"); }); }); describe("SQL Injection Protection", () => { test("should handle malicious SQL in criteria values", async () => { const maliciousCriteria = { id: "'; DROP TABLE users; --" }; await adapter.query(User, maliciousCriteria); // Verify parameterized query was used expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining(["'; DROP TABLE users; --"]) ); }); test("should handle SQL injection in multiple criteria", async () => { const maliciousCriteria = { id: "1; DELETE FROM users WHERE 1=1; --", email: "test@example.com'; DROP TABLE posts; --" }; await adapter.query(User, maliciousCriteria); // Verify both values were parameterized expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining([ "1; DELETE FROM users WHERE 1=1; --", "test@example.com'; DROP TABLE posts; --" ]) ); }); test("should handle SQL injection in native query parameters", async () => { const maliciousParams = ["'; DROP TABLE users; --"]; await adapter.runNativeQuery("SELECT * FROM users WHERE id = $1", maliciousParams); // Verify parameters were passed through (not escaped at this level) expect(mockClient.query).toHaveBeenCalledWith( "SELECT * FROM users WHERE id = $1", maliciousParams ); }); test("should handle null/undefined criteria safely", async () => { await adapter.query(User, { id: null }); await adapter.query(User, { id: undefined }); // Should not throw and should handle null/undefined values expect(mockClient.query).toHaveBeenCalledTimes(2); }); test("should handle complex object criteria", async () => { const complexCriteria = { metadata: { key: "value; DROP TABLE users;" }, nested: { deep: "'; DELETE FROM posts; --" } }; await adapter.query(User, complexCriteria); // Should handle complex objects without injection expect(mockClient.query).toHaveBeenCalled(); }); }); describe("Entity Validation Edge Cases", () => { test("should handle circular reference in entities", async () => { const user1 = new ComplexUser(); const user2 = new ComplexUser(); user1.id = "user1"; user1.name = "User 1"; user1.email = "user1@example.com"; user1.status = "active"; user1.managers = [user2]; user2.id = "user2"; user2.name = "User 2"; user2.email = "user2@example.com"; user2.status = "active"; user2.managers = [user1]; // Circular reference // Should not cause infinite loop await expect(adapter.save(user1)).resolves.not.toThrow(); }); test("should handle deeply nested relationship validation", async () => { const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; const post = new Post(); post.id = "post1"; post.title = "Test Post"; post.content = "Content"; post.author = user; const tag = new Tag(); tag.id = "tag1"; tag.name = "Test Tag"; tag.posts = [post]; post.tags = [tag]; user.posts = [post]; // Should handle complex nested relationships await expect(adapter.save(user)).resolves.not.toThrow(); }); test("should handle entity with missing required properties", async () => { const invalidUser = new User(); invalidUser.id = "test"; // Missing required name and email await expect(adapter.save(invalidUser)).rejects.toThrow(/Invalid entity/); }); test("should handle entity with invalid property types", async () => { const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; // Invalid type assignment (user as any).createdAt = "invalid-date"; // Should not throw during save (type coercion handled by database) await expect(adapter.save(user)).resolves.not.toThrow(); }); test("should handle entity with undefined relationships", async () => { const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; user.posts = undefined as any; await expect(adapter.save(user)).resolves.not.toThrow(); }); test("should handle entity with null ID during save", async () => { const user = new User(); user.id = null as any; user.name = "Test User"; user.email = "test@example.com"; // Should handle null ID (insert operation) await expect(adapter.save(user)).resolves.not.toThrow(); }); test("should handle entity with duplicate unique properties", async () => { mockClient.query.mockRejectedValueOnce(new Error("duplicate key value violates unique constraint")); const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "existing@example.com"; await expect(adapter.save(user)).rejects.toThrow("duplicate key value violates unique constraint"); }); }); describe("Query Parameter Edge Cases", () => { test("should handle empty criteria object", async () => { await adapter.query(User, {}); // Should generate query without WHERE clause expect(mockClient.query).toHaveBeenCalledWith( expect.stringMatching(/SELECT \* FROM \w+\s*LIMIT 1/), [] ); }); test("should handle criteria with special characters", async () => { const criteria = { name: "Test User @#$%^&*()_+-=[]{}|;':,./<>?" }; await adapter.query(User, criteria); expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining(["Test User @#$%^&*()_+-=[]{}|;':,./<>?"]) ); }); test("should handle criteria with unicode characters", async () => { const criteria = { name: "用户测试 🚀 émojis" }; await adapter.query(User, criteria); expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining(["用户测试 🚀 émojis"]) ); }); test("should handle very long string criteria", async () => { const longString = "a".repeat(10000); const criteria = { name: longString }; await adapter.query(User, criteria); expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining([longString]) ); }); test("should handle array values in criteria", async () => { const criteria = { id: ["1", "2", "3"] }; await adapter.query(User, criteria); // Should handle array values appropriately expect(mockClient.query).toHaveBeenCalled(); }); test("should handle boolean values in criteria", async () => { const criteria = { active: true }; await adapter.query(User, criteria); expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$1"), expect.arrayContaining([true]) ); }); test("should handle numeric values in criteria", async () => { const criteria = { age: 25, score: 99.5, bigNumber: 9007199254740991 }; await adapter.query(User, criteria); expect(mockClient.query).toHaveBeenCalledWith( expect.stringContaining("$3"), expect.arrayContaining([25, 99.5, 9007199254740991]) ); }); }); describe("Database Operation Edge Cases", () => { test("should handle query with no results", async () => { mockQueryResult.rows = []; mockQueryResult.rowCount = 0; const result = await adapter.query(User, { id: "nonexistent" }); expect(result).toBeNull(); }); test("should handle queryMany with no results", async () => { mockQueryResult.rows = []; mockQueryResult.rowCount = 0; const results = await adapter.queryMany(User, { status: "active" }); expect(results).toEqual([]); }); test("should handle delete with no matching records", async () => { mockQueryResult.rowCount = 0; await expect(adapter.delete(User, "nonexistent")).resolves.not.toThrow(); }); test("should handle runNativeQuery with syntax error", async () => { mockClient.query.mockRejectedValueOnce(new Error("syntax error at or near")); await expect(adapter.runNativeQuery("SELEC * FROM users")).rejects.toThrow("syntax error at or near"); }); test("should handle runNativeQuery with permission error", async () => { mockClient.query.mockRejectedValueOnce(new Error("permission denied")); await expect(adapter.runNativeQuery("DROP TABLE users")).rejects.toThrow("permission denied"); }); test("should handle client release failure", async () => { mockClient.release.mockImplementation(() => { throw new Error("release failed"); }); // Should throw because release failure bubbles up await expect(adapter.query(User, { id: "test" })).rejects.toThrow("release failed"); }); test("should handle adapter close with no pool", async () => { (adapter as any).pool = null; await expect(adapter.close()).resolves.not.toThrow(); }); test("should handle adapter close with pool end failure", async () => { mockPool.end.mockRejectedValueOnce(new Error("pool end failed")); await expect(adapter.close()).rejects.toThrow("pool end failed"); }); }); }); // ============================================== // PHASE 2: TRANSACTION WARFARE - AK-47 ROUND 2 // ============================================== describe("Phase 2: Transaction Warfare", () => { describe("Transaction Rollback Scenarios", () => { test("should handle transaction rollback on save failure", async () => { // Mock successful BEGIN, then failure on INSERT, then successful ROLLBACK mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check query .mockRejectedValueOnce(new Error("constraint violation")) // INSERT fails .mockResolvedValueOnce({ rows: [], rowCount: 0 }); // ROLLBACK const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; await expect(adapter.save(user)).rejects.toThrow("constraint violation"); // Verify BEGIN, ID check, failed INSERT, and ROLLBACK were called expect(mockClient.query).toHaveBeenCalledWith("BEGIN"); expect(mockClient.query).toHaveBeenCalledWith("ROLLBACK"); }); test("should handle rollback failure after save failure", async () => { // Mock successful BEGIN, then failure on INSERT, then failure on ROLLBACK mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check query .mockRejectedValueOnce(new Error("constraint violation")) // INSERT fails .mockRejectedValueOnce(new Error("rollback failed")); // ROLLBACK fails const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; // Should throw the rollback error, not the original error await expect(adapter.save(user)).rejects.toThrow("rollback failed"); }); test("should handle nested transaction failures", async () => { // Mock a scenario where relationship saves trigger nested transactions const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; const post = new Post(); post.id = "post1"; post.title = "Test Post"; post.content = "Content"; post.author = user; user.posts = [post]; // Mock the main transaction succeeding but relationship transaction failing mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check query .mockResolvedValueOnce({ rows: [{ id: "test" }], rowCount: 1 }) // INSERT user .mockRejectedValueOnce(new Error("relationship constraint violation")) // Related entity save fails .mockResolvedValueOnce({ rows: [], rowCount: 0 }); // ROLLBACK await expect(adapter.save(user)).rejects.toThrow("relationship constraint violation"); }); test("should handle connection loss during transaction", async () => { mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockRejectedValueOnce(new Error("connection lost")); // Connection fails during transaction const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; await expect(adapter.save(user)).rejects.toThrow("connection lost"); }); test("should handle deadlock detection and retry", async () => { mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check query .mockRejectedValueOnce(new Error("deadlock detected")); // Deadlock on INSERT const user = new User(); user.id = "test"; user.name = "Test User"; user.email = "test@example.com"; await expect(adapter.save(user)).rejects.toThrow("deadlock detected"); }); }); describe("Concurrent Operation Testing", () => { test("should handle multiple saves of same entity", async () => { const user = new User(); user.id = "concurrent-test"; user.name = "Concurrent User"; user.email = "concurrent@example.com"; // Mock different responses for each concurrent save let callCount = 0; mockClient.query.mockImplementation(() => { callCount++; if (callCount <= 3) { return Promise.resolve({ rows: [], rowCount: 0 }); // First set of calls } else if (callCount === 4) { return Promise.reject(new Error("concurrent modification")); // Conflict } else { return Promise.resolve({ rows: [], rowCount: 0 }); // Remaining calls } }); // Simulate concurrent saves const savePromises = [ adapter.save(user), adapter.save(user), adapter.save(user) ]; // At least one should fail due to concurrent modification const results = await Promise.allSettled(savePromises); const hasFailure = results.some(result => result.status === "rejected"); expect(hasFailure).toBe(true); }); test("should handle race conditions in relationships", async () => { const user = new User(); user.id = "race-test"; user.name = "Race User"; user.email = "race@example.com"; const post1 = new Post(); post1.id = "post1"; post1.title = "Post 1"; post1.content = "Content 1"; post1.author = user; const post2 = new Post(); post2.id = "post2"; post2.title = "Post 2"; post2.content = "Content 2"; post2.author = user; user.posts = [post1, post2]; // Mock race condition in relationship handling mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "race-test" }], rowCount: 1 }) // INSERT user .mockRejectedValueOnce(new Error("foreign key constraint violation")); // Race in FK await expect(adapter.save(user)).rejects.toThrow("foreign key constraint violation"); }); test("should handle connection pool contention", async () => { // Simulate pool exhaustion mockPool.connect .mockRejectedValueOnce(new Error("pool exhausted")) .mockRejectedValueOnce(new Error("pool exhausted")) .mockResolvedValueOnce(mockClient); // Finally succeeds const user = new User(); user.id = "pool-test"; user.name = "Pool User"; user.email = "pool@example.com"; // First two attempts should fail await expect(adapter.query(User, { id: "test1" })).rejects.toThrow("pool exhausted"); await expect(adapter.query(User, { id: "test2" })).rejects.toThrow("pool exhausted"); // Third should succeed await expect(adapter.query(User, { id: "test3" })).resolves.toBeNull(); }); test("should handle atomic operation failures", async () => { // Test atomic operations that must either all succeed or all fail const user = new User(); user.id = "atomic-test"; user.name = "Atomic User"; user.email = "atomic@example.com"; // Mock partial success followed by failure mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "atomic-test" }], rowCount: 1 }) // User insert succeeds .mockRejectedValueOnce(new Error("index violation")) // Related operation fails .mockResolvedValueOnce({ rows: [], rowCount: 0 }); // ROLLBACK await expect(adapter.save(user)).rejects.toThrow("index violation"); // Verify rollback was called expect(mockClient.query).toHaveBeenCalledWith("ROLLBACK"); }); }); }); // ============================================== // PHASE 3: RELATIONSHIP COMPLEXITY - AK-47 ROUND 3 // ============================================== describe("Phase 3: Relationship Complexity", () => { describe("Complex Relationship Scenarios", () => { test("should handle self-referencing entity relationships", async () => { const manager = new ComplexUser(); manager.id = "manager1"; manager.name = "Manager"; manager.email = "manager@example.com"; manager.status = "active"; const employee = new ComplexUser(); employee.id = "employee1"; employee.name = "Employee"; employee.email = "employee@example.com"; employee.status = "active"; employee.managers = [manager]; manager.managedUsers = [employee]; // Should handle self-referencing relationships await expect(adapter.save(manager)).resolves.not.toThrow(); }); test("should handle orphaned relationship cleanup", async () => { const user = new User(); user.id = "orphan-test"; user.name = "Orphan User"; user.email = "orphan@example.com"; // Mock scenario where related entities become orphaned mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "orphan-test" }], rowCount: 1 }) // User insert .mockRejectedValueOnce(new Error("orphaned foreign key")) // Orphan cleanup fails .mockResolvedValueOnce({ rows: [], rowCount: 0 }); // ROLLBACK await expect(adapter.save(user)).rejects.toThrow("orphaned foreign key"); }); test("should handle deeply nested relationship saves", async () => { const user = new User(); user.id = "deep-test"; user.name = "Deep User"; user.email = "deep@example.com"; const post = new Post(); post.id = "deep-post"; post.title = "Deep Post"; post.content = "Content"; post.author = user; const tag1 = new Tag(); tag1.id = "tag1"; tag1.name = "Tag 1"; tag1.posts = [post]; const tag2 = new Tag(); tag2.id = "tag2"; tag2.name = "Tag 2"; tag2.posts = [post]; post.tags = [tag1, tag2]; user.posts = [post]; // Mock successful deep save mockClient.query.mockResolvedValue({ rows: [{ id: "test" }], rowCount: 1 }); await expect(adapter.save(user)).resolves.not.toThrow(); }); }); describe("Join Table Operations", () => { test("should handle many-to-many join table failures", async () => { const user = new User(); user.id = "join-test"; user.name = "Join User"; user.email = "join@example.com"; const follower = new User(); follower.id = "follower1"; follower.name = "Follower"; follower.email = "follower@example.com"; user.followers = [follower]; // Mock join table operation failure mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "join-test" }], rowCount: 1 }) // User insert .mockRejectedValueOnce(new Error("join table constraint violation")); // Join table fails await expect(adapter.save(user)).rejects.toThrow("join table constraint violation"); }); test("should handle join table constraint violations", async () => { // Test duplicate relationship entries mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "test" }], rowCount: 1 }) // User insert .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // Delete existing relationships .mockRejectedValueOnce(new Error("duplicate key value violates unique constraint")); // Duplicate join const user = new User(); user.id = "duplicate-test"; user.name = "Duplicate User"; user.email = "duplicate@example.com"; const follower = new User(); follower.id = "follower1"; follower.name = "Follower"; follower.email = "follower@example.com"; user.followers = [follower, follower]; // Duplicate relationship await expect(adapter.save(user)).rejects.toThrow("duplicate key value violates unique constraint"); }); test("should handle foreign key constraint violations", async () => { mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "test" }], rowCount: 1 }) // User insert .mockRejectedValueOnce(new Error("foreign key constraint violation")); // FK violation const user = new User(); user.id = "fk-test"; user.name = "FK User"; user.email = "fk@example.com"; const post = new Post(); post.id = "invalid-post"; post.title = "Invalid Post"; post.content = "Content"; post.author = user; user.posts = [post]; await expect(adapter.save(user)).rejects.toThrow("foreign key constraint violation"); }); }); }); // ============================================== // PHASE 4: DATA INTEGRITY ARTILLERY - AK-47 ROUND 4 // ============================================== describe("Phase 4: Data Integrity Artillery", () => { describe("Schema Evolution Scenarios", () => { test("should handle missing table gracefully", async () => { mockClient.query.mockRejectedValueOnce(new Error('relation "missing_table" does not exist')); await expect(adapter.query(User, { id: "test" })).rejects.toThrow('relation "missing_table" does not exist'); }); test("should handle missing column gracefully", async () => { mockClient.query.mockRejectedValueOnce(new Error('column "missing_column" does not exist')); await expect(adapter.query(User, { missingColumn: "value" })).rejects.toThrow('column "missing_column" does not exist'); }); test("should handle column type mismatch", async () => { mockClient.query.mockRejectedValueOnce(new Error("column type mismatch")); const user = new User(); user.id = "type-test"; user.name = "Type User"; user.email = "type@example.com"; await expect(adapter.save(user)).rejects.toThrow("column type mismatch"); }); test("should handle index creation failures", async () => { mockClient.query.mockRejectedValueOnce(new Error("index creation failed")); await expect(adapter.runNativeQuery("CREATE INDEX test_idx ON users(name)")).rejects.toThrow("index creation failed"); }); }); describe("Data Corruption Scenarios", () => { test("should handle partial write failures", async () => { // Mock scenario where some data is written but transaction fails mockClient.query .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // BEGIN .mockResolvedValueOnce({ rows: [], rowCount: 0 }) // ID check .mockResolvedValueOnce({ rows: [{ id: "partial" }], rowCount: 1 }) // Partial write succeeds .mockRejectedValueOnce(new Error("disk full")) // Next operation fails .mockResolvedValueOnce({ rows: [], rowCount: 0 }); // ROLLBACK const user = new User(); user.id = "partial-test"; user.name = "Partial User"; user.email = "partial@example.com"; await expect(adapter.save(user)).rejects.toThrow("disk full"); expect(mockClient.query).toHaveBeenCalledWith("ROLLBACK"); }); test("should handle encoding issues", async () => { const user = new User(); user.id = "encoding-test"; user.name = "Encoding \x00\x01\x02 User"; // Binary data user.email = "encoding@example.com"; mockClient.query.mockRejectedValueOnce(new Error("invalid byte sequence")); await expect(adapter.save(user)).rejects.toThrow("invalid byte sequence"); }); test("should handle very large data", async () => { const largeContent = "x".repeat(1000000); // 1MB string const user = new User(); user.id = "large-test"; user.name = largeContent; user.email = "large@example.com"; mockClient.query.mockRejectedValueOnce(new Error("value too long")); await expect(adapter.save(user)).rejects.toThrow("value too long"); }); test("should handle binary data edge cases", async () => { const binaryData = Buffer.from([0x00, 0x01, 0xFF, 0xFE]); const criteria = { data: binaryData }; await adapter.query(User, criteria); // Should handle binary data in parameters expect(mockClient.query).toHaveBeenCalledWith( expect.any(String), expect.arrayContaining([binaryData]) ); }); }); }); // ============================================== // PHASE 5: PERFORMANCE UNDER FIRE - AK-47 ROUND 5 // ============================================== describe("Phase 5: Performance Under Fire", () => { describe("Resource Exhaustion Testing", () => { test("should handle memory leaks in connection pool", async () => { // Simulate memory pressure const originalMemoryUsage = process.memoryUsage(); // Create many connections without proper cleanup for (let i = 0; i < 100; i++) { mockPool.connect.mockResolvedValueOnce({ query: jest.fn().mockResolvedValue({ rows: [], rowCount: 0 }), release: jest.fn() // Don't actually release }); await adapter.query(User, { id: `test${i}` }); } const currentMemoryUsage = process.memoryUsage(); // Memory usage should not grow significantly const memoryGrowth = currentMemoryUsage.heapUsed - originalMemoryUsage.heapUsed; expect(memoryGrowth).toBeLessThan(50 * 1024 * 1024); // Less than 50MB growth }); test("should handle large result set processing", async () => { // Mock a very large result set const largeResultSet = Array.from({ length: 10000 }, (_, i) => ({ id: `user${i}`, name: `User ${i}`, email: `user${i}@example.com` })); mockQueryResult.rows = largeResultSet; mockQueryResult.rowCount = largeResultSet.length; const results = await adapter.queryMany(User, { active: true }); expect(results).toHaveLength(10000); expect(results[0]).toBeInstanceOf(User); }); test("should handle query timeout scenarios", async () => { mockClient.query.mockImplementation(() => { return new Promise((_, reject) => { setTimeout(() => reject(new Error("query timeout")), 100); }); }); await expect(adapter.query(User, { id: "timeout-test" })).rejects.toThrow("query timeout"); }); test("should handle connection cleanup verification", async () => { const releaseSpy = jest.spyOn(mockClient, 'release'); // Perform multiple operations await adapter.query(User, { id: "cleanup1" }); await adapter.query(User, { id: "cleanup2" }); await adapter.query(User, { id: "cleanup3" }); // Verify all connections were properly released expect(releaseSpy).toHaveBeenCalledTimes(3); }); }); describe("Optimization Edge Cases", () => { test("should handle query plan failures", async () => { mockClient.query.mockRejectedValueOnce(new Error("could not devise a query plan")); await expect(adapter.query(User, { complexCriteria: "value" })).rejects.toThrow("could not devise a query plan"); }); test("should handle index usage verification", async () => { // Mock query execution with index information mockClient.query.mockResolvedValueOnce({ rows: [{ query_plan: "Index Scan using user_email_idx on users", execution_time: 1.5 }], rowCount: 1 }); const result = await adapter.runNativeQuery("EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1", ["test@example.com"]) as any; expect(result.rows[0].query_plan).toContain("Index Scan"); }); test("should handle bulk operation limits", async () => { // Create a large number of entities to save const users = Array.from({ length: 1000 }, (_, i) => { const user = new User(); user.id = `bulk${i}`; user.name = `Bulk User ${i}`; user.email = `bulk${i}@example.com`; return user; }); // Mock bulk operation that hits limits mockClient.query.mockRejectedValueOnce(new Error("too many parameters")); // Attempt to save all users in one transaction (should fail) for (const user of users) { await expect(adapter.save(user)).rejects.toThrow("too many parameters"); break; // Only test first one } }); test("should handle connection reuse patterns", async () => { const connectSpy = jest.spyOn(mockPool, 'connect'); // Perform multiple rapid operations const operations = [ adapter.query(User, { id: "reuse1" }), adapter.query(User, { id: "reuse2" }), adapter.query(User, { id: "reuse3" }), adapter.query(User, { id: "reuse4" }), adapter.query(User, { id: "reuse5" }) ]; await Promise.all(operations); // Verify connections were requested (reuse is handled by pool) expect(connectSpy).toHaveBeenCalledTimes(5); }); }); }); });