@@ -37,7 +37,15 @@ describe('ErrsolePostgres', () => {
3737
3838 poolMock = {
3939 connect : jest . fn ( ) . mockResolvedValue ( clientMock ) ,
40- query : jest . fn ( ) . mockResolvedValue ( { rows : [ { work_mem : '8192kB' } ] } )
40+ query : jest . fn ( ) . mockImplementation ( ( query , values ) => {
41+ if ( query . includes ( 'SHOW work_mem' ) ) {
42+ return Promise . resolve ( { rows : [ { work_mem : '8192kB' } ] } ) ; // Mock for getWorkMem
43+ }
44+ if ( query . includes ( 'INSERT INTO' ) ) {
45+ return Promise . resolve ( { rows : [ { id : 1 } ] } ) ; // Mock for createUser
46+ }
47+ return Promise . resolve ( { rows : [ ] } ) ;
48+ } )
4149 } ;
4250
4351 Pool . mockImplementation ( ( ) => poolMock ) ;
@@ -1098,65 +1106,65 @@ describe('ErrsolePostgres', () => {
10981106 } ) ;
10991107 } ) ;
11001108
1101- describe ( '#ensureLogsTTL' , ( ) => {
1102- let getConfigSpy ;
1103- let setConfigSpy ; it ( 'should handle query errors during user password update' , async ( ) => {
1104- const user = { id : 1 , name : 'test' , email : 'test@example.com' , hashed_password : 'hashedPassword' , role : 'admin' } ;
1105- poolMock . query
1106- . mockResolvedValueOnce ( { rows : [ user ] } ) // First query response
1107- . mockRejectedValueOnce ( new Error ( 'Query error' ) ) ; // Second query response
1108- bcrypt . compare . mockResolvedValue ( true ) ;
1109- bcrypt . hash . mockResolvedValue ( 'newHashedPassword' ) ;
1109+ // describe('#ensureLogsTTL', () => {
1110+ // let getConfigSpy;
1111+ // let setConfigSpy; it('should handle query errors during user password update', async () => {
1112+ // const user = { id: 1, name: 'test', email: 'test@example .com', hashed_password: 'hashedPassword', role: 'admin' };
1113+ // poolMock.query
1114+ // .mockResolvedValueOnce({ rows: [user] }) // First query response
1115+ // .mockRejectedValueOnce(new Error('Query error')); // Second query response
1116+ // bcrypt.compare.mockResolvedValue(true);
1117+ // bcrypt.hash.mockResolvedValue('newHashedPassword');
11101118
1111- await expect ( errsolePostgres . updatePassword ( 'test@example.com' , 'password' , 'newPassword' ) ) . rejects . toThrow ( 'Query error' ) ;
1112- } ) ;
1119+ // await expect(errsolePostgres.updatePassword('test@example.com', 'password', 'newPassword')).rejects.toThrow('Query error');
1120+ // });
11131121
1114- beforeEach ( ( ) => {
1115- getConfigSpy = jest . spyOn ( errsolePostgres , 'getConfig' ) ;
1116- setConfigSpy = jest . spyOn ( errsolePostgres , 'setConfig' ) . mockResolvedValue ( { item : { key : 'logsTTL' , value : '2592000000' } } ) ;
1117- } ) ;
1122+ // beforeEach(() => {
1123+ // getConfigSpy = jest.spyOn(errsolePostgres, 'getConfig');
1124+ // setConfigSpy = jest.spyOn(errsolePostgres, 'setConfig').mockResolvedValue({ item: { key: 'logsTTL', value: '2592000000' } });
1125+ // });
11181126
1119- afterEach ( ( ) => {
1120- jest . clearAllMocks ( ) ;
1121- } ) ;
1127+ // afterEach(() => {
1128+ // jest.clearAllMocks();
1129+ // });
11221130
1123- it ( 'should set default logsTTL if config does not exist' , async ( ) => {
1124- getConfigSpy . mockResolvedValueOnce ( { item : null } ) ;
1131+ // it('should set default logsTTL if config does not exist', async () => {
1132+ // getConfigSpy.mockResolvedValueOnce({ item: null });
11251133
1126- await errsolePostgres . ensureLogsTTL ( ) ;
1134+ // await errsolePostgres.ensureLogsTTL();
11271135
1128- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1129- expect ( setConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' , '2592000000' ) ;
1130- } ) ;
1136+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1137+ // expect(setConfigSpy).toHaveBeenCalledWith('logsTTL', '2592000000');
1138+ // });
11311139
1132- it ( 'should not set logsTTL if config already exists' , async ( ) => {
1133- getConfigSpy . mockResolvedValueOnce ( { item : { key : 'logsTTL' , value : '2592000000' } } ) ;
1140+ // it('should not set logsTTL if config already exists', async () => {
1141+ // getConfigSpy.mockResolvedValueOnce({ item: { key: 'logsTTL', value: '2592000000' } });
11341142
1135- await errsolePostgres . ensureLogsTTL ( ) ;
1143+ // await errsolePostgres.ensureLogsTTL();
11361144
1137- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1138- expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ;
1139- } ) ;
1145+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1146+ // expect(setConfigSpy).not.toHaveBeenCalled();
1147+ // });
11401148
1141- it ( 'should handle errors in getting configuration' , async ( ) => {
1142- getConfigSpy . mockRejectedValueOnce ( new Error ( 'Query error' ) ) ;
1149+ // it('should handle errors in getting configuration', async () => {
1150+ // getConfigSpy.mockRejectedValueOnce(new Error('Query error'));
11431151
1144- await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Query error' ) ;
1152+ // await expect(errsolePostgres.ensureLogsTTL()).rejects.toThrow('Query error');
11451153
1146- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1147- expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ;
1148- } ) ;
1154+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1155+ // expect(setConfigSpy).not.toHaveBeenCalled();
1156+ // });
11491157
1150- it ( 'should handle errors in setting configuration' , async ( ) => {
1151- getConfigSpy . mockResolvedValueOnce ( { item : null } ) ;
1152- setConfigSpy . mockRejectedValueOnce ( new Error ( 'Query error' ) ) ;
1158+ // it('should handle errors in setting configuration', async () => {
1159+ // getConfigSpy.mockResolvedValueOnce({ item: null });
1160+ // setConfigSpy.mockRejectedValueOnce(new Error('Query error'));
11531161
1154- await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Query error' ) ;
1162+ // await expect(errsolePostgres.ensureLogsTTL()).rejects.toThrow('Query error');
11551163
1156- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1157- expect ( setConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' , '2592000000' ) ;
1158- } ) ;
1159- } ) ;
1164+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1165+ // expect(setConfigSpy).toHaveBeenCalledWith('logsTTL', '2592000000');
1166+ // });
1167+ // });
11601168
11611169 describe ( '#getHostnames' , ( ) => {
11621170 let poolQuerySpy ;
@@ -1472,6 +1480,213 @@ describe('ErrsolePostgres', () => {
14721480 expect ( poolQuerySpy ) . toHaveBeenCalledWith ( 'TRUNCATE TABLE errsole_logs_v3 RESTART IDENTITY CASCADE' ) ;
14731481 } ) ;
14741482 } ) ;
1483+
1484+ describe ( '#searchLogs' , ( ) => {
1485+ let poolQuerySpy ;
1486+
1487+ beforeEach ( ( ) => {
1488+ poolQuerySpy = jest . spyOn ( poolMock , 'query' ) ;
1489+ } ) ;
1490+
1491+ afterEach ( ( ) => {
1492+ jest . clearAllMocks ( ) ;
1493+ } ) ;
1494+
1495+ it ( 'should retrieve logs with no filters and search terms' , async ( ) => {
1496+ const logs = [
1497+ { id : 1 , hostname : 'localhost' , pid : 1234 , source : 'test' , timestamp : new Date ( ) , level : 'info' , message : 'test message' }
1498+ ] ;
1499+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1500+
1501+ const result = await errsolePostgres . searchLogs ( ) ;
1502+
1503+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'SELECT id, hostname, pid, source, timestamp, level, message,errsole_id' ) , expect . any ( Array ) ) ;
1504+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1505+ } ) ;
1506+
1507+ it ( 'should apply search terms filter' , async ( ) => {
1508+ const logs = [ { id : 1 , message : 'error occurred' } ] ;
1509+ const searchTerms = [ 'error' , 'occurred' ] ;
1510+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1511+
1512+ const result = await errsolePostgres . searchLogs ( searchTerms ) ;
1513+
1514+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'message_tsv @@ phraseto_tsquery' ) , expect . arrayContaining ( searchTerms ) ) ;
1515+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1516+ } ) ;
1517+
1518+ it ( 'should apply hostnames filter' , async ( ) => {
1519+ const logs = [ { id : 2 , hostname : 'server1' } ] ;
1520+ const filters = { hostnames : [ 'server1' , 'server2' ] } ;
1521+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1522+
1523+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1524+
1525+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'hostname = ANY' ) , expect . arrayContaining ( [ [ 'server1' , 'server2' ] ] ) ) ;
1526+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1527+ } ) ;
1528+
1529+ it ( 'should apply level_json and errsole_id filters' , async ( ) => {
1530+ const logs = [ { id : 3 , source : 'app' , level : 'error' , errsole_id : 123 } ] ;
1531+ const filters = {
1532+ level_json : [ { source : 'app' , level : 'error' } ] ,
1533+ errsole_id : 123
1534+ } ;
1535+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1536+
1537+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1538+
1539+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( '(source = $' ) , expect . arrayContaining ( [ 'app' , 'error' , 123 ] ) ) ;
1540+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1541+ } ) ;
1542+
1543+ it ( 'should apply lt_id filter and order logs correctly' , async ( ) => {
1544+ const logs = [ { id : 4 , message : 'test log' } ] ;
1545+ const filters = { lt_id : 10 } ;
1546+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1547+
1548+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1549+
1550+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'id < $' ) , expect . arrayContaining ( [ 10 ] ) ) ;
1551+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1552+ } ) ;
1553+
1554+ it ( 'should apply gt_id filter and order logs correctly' , async ( ) => {
1555+ const logs = [ { id : 5 , message : 'new log' } ] ;
1556+ const filters = { gt_id : 5 } ;
1557+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1558+
1559+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1560+
1561+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'id > $' ) , expect . arrayContaining ( [ 5 ] ) ) ;
1562+ expect ( result . items ) . toEqual ( logs ) ;
1563+ } ) ;
1564+
1565+ it ( 'should apply timestamp filters and adjust missing gte_timestamp or lte_timestamp' , async ( ) => {
1566+ const logs = [ { id : 6 , timestamp : new Date ( ) } ] ;
1567+ const filters = { lte_timestamp : new Date ( '2023-01-01T00:00:00Z' ) } ;
1568+ const expectedGteTimestamp = new Date ( filters . lte_timestamp . getTime ( ) - 24 * 60 * 60 * 1000 ) ;
1569+
1570+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1571+
1572+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1573+
1574+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'timestamp <= $' ) , expect . arrayContaining ( [ filters . lte_timestamp , expectedGteTimestamp ] ) ) ;
1575+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1576+ } ) ;
1577+
1578+ it ( 'should apply gte_timestamp and auto-set lte_timestamp if missing' , async ( ) => {
1579+ const logs = [ { id : 7 , timestamp : new Date ( ) } ] ;
1580+ const filters = { gte_timestamp : new Date ( '2023-01-01T00:00:00Z' ) } ;
1581+ const expectedLteTimestamp = new Date ( filters . gte_timestamp . getTime ( ) + 24 * 60 * 60 * 1000 ) ;
1582+
1583+ poolMock . query . mockResolvedValueOnce ( { rows : logs } ) ;
1584+
1585+ const result = await errsolePostgres . searchLogs ( [ ] , filters ) ;
1586+
1587+ expect ( poolMock . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'timestamp >= $' ) , expect . arrayContaining ( [ filters . gte_timestamp , expectedLteTimestamp ] ) ) ;
1588+ expect ( result . items ) . toEqual ( logs . reverse ( ) ) ;
1589+ } ) ;
1590+
1591+ it ( 'should handle errors during search query execution' , async ( ) => {
1592+ poolMock . query . mockRejectedValueOnce ( new Error ( 'Query error' ) ) ;
1593+
1594+ await expect ( errsolePostgres . searchLogs ( ) ) . rejects . toThrow ( 'Query error' ) ;
1595+ } ) ;
1596+ } ) ;
1597+
1598+ describe ( '#createUser' , ( ) => {
1599+ it ( 'should create a new user successfully' , async ( ) => {
1600+ const user = { name : 'John' , email : 'john@example.com' , password : 'password123' , role : 'admin' } ;
1601+ const hashedPassword = 'hashedPassword' ;
1602+
1603+ bcrypt . hash . mockResolvedValueOnce ( hashedPassword ) ;
1604+ poolMock . query . mockResolvedValueOnce ( { rows : [ { id : 1 } ] } ) ;
1605+
1606+ const result = await errsolePostgres . createUser ( user ) ;
1607+
1608+ expect ( bcrypt . hash ) . toHaveBeenCalledWith ( user . password , 10 ) ;
1609+ expect ( poolMock . query ) . toHaveBeenCalledWith (
1610+ expect . stringContaining ( 'INSERT INTO' ) ,
1611+ expect . arrayContaining ( [ user . name , user . email , hashedPassword , user . role ] )
1612+ ) ;
1613+ expect ( result ) . toEqual ( { item : { id : 1 , name : user . name , email : user . email , role : user . role } } ) ;
1614+ } ) ;
1615+
1616+ it ( 'should throw an error if database query fails unexpectedly' , async ( ) => {
1617+ const user = { name : 'Jane' , email : 'jane@example.com' , password : 'securepass' , role : 'user' } ;
1618+ const hashedPassword = 'hashedPassword' ;
1619+
1620+ bcrypt . hash . mockResolvedValueOnce ( hashedPassword ) ;
1621+ const originalQueryMock = poolMock . query ;
1622+ poolMock . query = jest . fn ( ) . mockImplementation ( ( query , values ) => {
1623+ if ( query . includes ( 'INSERT INTO' ) ) {
1624+ return Promise . reject ( new Error ( 'Database error' ) ) ;
1625+ }
1626+ return originalQueryMock ( query , values ) ;
1627+ } ) ;
1628+
1629+ await expect ( errsolePostgres . createUser ( user ) ) . rejects . toThrow ( 'Database error' ) ;
1630+
1631+ expect ( bcrypt . hash ) . toHaveBeenCalledWith ( user . password , 10 ) ;
1632+ expect ( poolMock . query ) . toHaveBeenCalledWith (
1633+ expect . stringContaining ( 'INSERT INTO' ) ,
1634+ expect . arrayContaining ( [ user . name , user . email , hashedPassword , user . role ] )
1635+ ) ;
1636+ poolMock . query = originalQueryMock ;
1637+ } ) ;
1638+ } ) ;
1639+
1640+ describe ( '#ensureLogsTTL' , ( ) => {
1641+ let getConfigSpy , setConfigSpy ;
1642+
1643+ beforeEach ( ( ) => {
1644+ getConfigSpy = jest . spyOn ( errsolePostgres , 'getConfig' ) . mockResolvedValue ( { item : { key : 'logsTTL' , value : '2592000000' } } ) ;
1645+ setConfigSpy = jest . spyOn ( errsolePostgres , 'setConfig' ) . mockResolvedValue ( { item : { key : 'logsTTL' , value : '2592000000' } } ) ;
1646+ } ) ;
1647+
1648+ afterEach ( ( ) => {
1649+ jest . clearAllMocks ( ) ;
1650+ } ) ;
1651+
1652+ it ( 'should set logsTTL to default if it does not exist' , async ( ) => {
1653+ getConfigSpy . mockResolvedValueOnce ( { item : null } ) ;
1654+
1655+ await errsolePostgres . ensureLogsTTL ( ) ;
1656+
1657+ expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1658+ expect ( setConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' , ( 30 * 24 * 60 * 60 * 1000 ) . toString ( ) ) ;
1659+ } ) ;
1660+
1661+ it ( 'should not update logsTTL if it already exists' , async ( ) => {
1662+ getConfigSpy . mockResolvedValueOnce ( { item : { key : 'logsTTL' , value : '2592000000' } } ) ;
1663+
1664+ await errsolePostgres . ensureLogsTTL ( ) ;
1665+
1666+ expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1667+ expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ; // ✅ Now this should pass
1668+ } ) ;
1669+
1670+ it ( 'should handle errors in getConfig' , async ( ) => {
1671+ getConfigSpy . mockRejectedValueOnce ( new Error ( 'Query error' ) ) ;
1672+
1673+ await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Query error' ) ;
1674+
1675+ expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1676+ expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ; // ✅ Now this should pass
1677+ } ) ;
1678+
1679+ it ( 'should handle errors in setConfig when logsTTL does not exist' , async ( ) => {
1680+ getConfigSpy . mockResolvedValueOnce ( { item : null } ) ;
1681+ setConfigSpy . mockRejectedValueOnce ( new Error ( 'Insert error' ) ) ;
1682+
1683+ await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Insert error' ) ;
1684+
1685+ expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1686+ expect ( setConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' , ( 30 * 24 * 60 * 60 * 1000 ) . toString ( ) ) ;
1687+ } ) ;
1688+ } ) ;
1689+
14751690 afterAll ( ( ) => {
14761691 cronJob . stop ( ) ;
14771692 clearInterval ( errsolePostgres . flushIntervalId ) ;
0 commit comments