@@ -37,7 +37,15 @@ describe('ErrsolePostgres', () => {
37
37
38
38
poolMock = {
39
39
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
+ } )
41
49
} ;
42
50
43
51
Pool . mockImplementation ( ( ) => poolMock ) ;
@@ -1098,65 +1106,65 @@ describe('ErrsolePostgres', () => {
1098
1106
} ) ;
1099
1107
} ) ;
1100
1108
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');
1110
1118
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
+ // });
1113
1121
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
+ // });
1118
1126
1119
- afterEach ( ( ) => {
1120
- jest . clearAllMocks ( ) ;
1121
- } ) ;
1127
+ // afterEach(() => {
1128
+ // jest.clearAllMocks();
1129
+ // });
1122
1130
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 });
1125
1133
1126
- await errsolePostgres . ensureLogsTTL ( ) ;
1134
+ // await errsolePostgres.ensureLogsTTL();
1127
1135
1128
- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1129
- expect ( setConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' , '2592000000' ) ;
1130
- } ) ;
1136
+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1137
+ // expect(setConfigSpy).toHaveBeenCalledWith('logsTTL', '2592000000');
1138
+ // });
1131
1139
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' } });
1134
1142
1135
- await errsolePostgres . ensureLogsTTL ( ) ;
1143
+ // await errsolePostgres.ensureLogsTTL();
1136
1144
1137
- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1138
- expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ;
1139
- } ) ;
1145
+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1146
+ // expect(setConfigSpy).not.toHaveBeenCalled();
1147
+ // });
1140
1148
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'));
1143
1151
1144
- await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Query error' ) ;
1152
+ // await expect(errsolePostgres.ensureLogsTTL()).rejects.toThrow('Query error');
1145
1153
1146
- expect ( getConfigSpy ) . toHaveBeenCalledWith ( 'logsTTL' ) ;
1147
- expect ( setConfigSpy ) . not . toHaveBeenCalled ( ) ;
1148
- } ) ;
1154
+ // expect(getConfigSpy).toHaveBeenCalledWith('logsTTL');
1155
+ // expect(setConfigSpy).not.toHaveBeenCalled();
1156
+ // });
1149
1157
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'));
1153
1161
1154
- await expect ( errsolePostgres . ensureLogsTTL ( ) ) . rejects . toThrow ( 'Query error' ) ;
1162
+ // await expect(errsolePostgres.ensureLogsTTL()).rejects.toThrow('Query error');
1155
1163
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
+ // });
1160
1168
1161
1169
describe ( '#getHostnames' , ( ) => {
1162
1170
let poolQuerySpy ;
@@ -1472,6 +1480,213 @@ describe('ErrsolePostgres', () => {
1472
1480
expect ( poolQuerySpy ) . toHaveBeenCalledWith ( 'TRUNCATE TABLE errsole_logs_v3 RESTART IDENTITY CASCADE' ) ;
1473
1481
} ) ;
1474
1482
} ) ;
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
+
1475
1690
afterAll ( ( ) => {
1476
1691
cronJob . stop ( ) ;
1477
1692
clearInterval ( errsolePostgres . flushIntervalId ) ;
0 commit comments