@@ -6,17 +6,19 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
66import { MatSnackBarModule } from '@angular/material/snack-bar' ;
77import { Angulartics2Module } from 'angulartics2' ;
88import { of } from 'rxjs' ;
9+ import { PageEvent } from '@angular/material/paginator' ;
910
1011import { AuditLogDialogComponent } from './audit-log-dialog.component' ;
1112import { SecretsService } from 'src/app/services/secrets.service' ;
13+ import { Secret , AuditLogEntry , AuditLogResponse } from 'src/app/models/secret' ;
1214
1315describe ( 'AuditLogDialogComponent' , ( ) => {
1416 let component : AuditLogDialogComponent ;
1517 let fixture : ComponentFixture < AuditLogDialogComponent > ;
1618 let mockSecretsService : jasmine . SpyObj < SecretsService > ;
1719 let mockDialogRef : jasmine . SpyObj < MatDialogRef < AuditLogDialogComponent > > ;
1820
19- const mockSecret = {
21+ const mockSecret : Secret = {
2022 id : '1' ,
2123 slug : 'test-secret' ,
2224 companyId : '1' ,
@@ -25,20 +27,58 @@ describe('AuditLogDialogComponent', () => {
2527 masterEncryption : false ,
2628 } ;
2729
30+ const mockAuditLogEntry : AuditLogEntry = {
31+ id : '1' ,
32+ action : 'create' ,
33+ user : { id : '1' , email : 'user@example.com' } ,
34+ accessedAt : '2024-01-01T00:00:00Z' ,
35+ success : true ,
36+ } ;
37+
38+ const mockAuditLogResponse : AuditLogResponse = {
39+ data : [ mockAuditLogEntry ] ,
40+ pagination : { total : 1 , currentPage : 1 , perPage : 20 , lastPage : 1 } ,
41+ } ;
42+
43+ const mockMultipleLogsResponse : AuditLogResponse = {
44+ data : [
45+ mockAuditLogEntry ,
46+ {
47+ id : '2' ,
48+ action : 'view' ,
49+ user : { id : '2' , email : 'viewer@example.com' } ,
50+ accessedAt : '2024-01-02T00:00:00Z' ,
51+ success : true ,
52+ } ,
53+ {
54+ id : '3' ,
55+ action : 'update' ,
56+ user : { id : '1' , email : 'user@example.com' } ,
57+ accessedAt : '2024-01-03T00:00:00Z' ,
58+ success : true ,
59+ } ,
60+ {
61+ id : '4' ,
62+ action : 'copy' ,
63+ user : { id : '3' , email : 'copier@example.com' } ,
64+ accessedAt : '2024-01-04T00:00:00Z' ,
65+ success : true ,
66+ } ,
67+ {
68+ id : '5' ,
69+ action : 'delete' ,
70+ user : { id : '1' , email : 'user@example.com' } ,
71+ accessedAt : '2024-01-05T00:00:00Z' ,
72+ success : false ,
73+ errorMessage : 'Deletion failed' ,
74+ } ,
75+ ] ,
76+ pagination : { total : 5 , currentPage : 1 , perPage : 20 , lastPage : 1 } ,
77+ } ;
78+
2879 beforeEach ( async ( ) => {
2980 mockSecretsService = jasmine . createSpyObj ( 'SecretsService' , [ 'getAuditLog' ] ) ;
30- mockSecretsService . getAuditLog . and . returnValue ( of ( {
31- data : [
32- {
33- id : '1' ,
34- action : 'create' ,
35- user : { id : '1' , email : 'user@example.com' } ,
36- accessedAt : '2024-01-01T00:00:00Z' ,
37- success : true ,
38- } ,
39- ] ,
40- pagination : { total : 1 , currentPage : 1 , perPage : 20 , lastPage : 1 } ,
41- } ) ) ;
81+ mockSecretsService . getAuditLog . and . returnValue ( of ( mockAuditLogResponse ) ) ;
4282
4383 mockDialogRef = jasmine . createSpyObj ( 'MatDialogRef' , [ 'close' ] ) ;
4484
@@ -67,18 +107,200 @@ describe('AuditLogDialogComponent', () => {
67107 expect ( component ) . toBeTruthy ( ) ;
68108 } ) ;
69109
70- it ( 'should load audit log on init' , ( ) => {
71- expect ( mockSecretsService . getAuditLog ) . toHaveBeenCalledWith ( 'test-secret' , 1 , 20 ) ;
110+ describe ( 'component initialization' , ( ) => {
111+ it ( 'should load audit log on init' , ( ) => {
112+ expect ( mockSecretsService . getAuditLog ) . toHaveBeenCalledWith ( 'test-secret' , 1 , 20 ) ;
113+ } ) ;
114+
115+ it ( 'should display audit log entries' , ( ) => {
116+ expect ( component . logs . length ) . toBe ( 1 ) ;
117+ expect ( component . logs [ 0 ] . action ) . toBe ( 'create' ) ;
118+ } ) ;
119+
120+ it ( 'should initialize with default pagination' , ( ) => {
121+ expect ( component . pagination ) . toEqual ( {
122+ total : 1 ,
123+ currentPage : 1 ,
124+ perPage : 20 ,
125+ lastPage : 1
126+ } ) ;
127+ } ) ;
128+
129+ it ( 'should initialize loading as false after load' , ( ) => {
130+ expect ( component . loading ) . toBeFalse ( ) ;
131+ } ) ;
132+
133+ it ( 'should have correct displayed columns' , ( ) => {
134+ expect ( component . displayedColumns ) . toEqual ( [ 'action' , 'user' , 'accessedAt' , 'success' ] ) ;
135+ } ) ;
136+ } ) ;
137+
138+ describe ( 'action labels' , ( ) => {
139+ it ( 'should have label for create action' , ( ) => {
140+ expect ( component . actionLabels [ 'create' ] ) . toBe ( 'Created' ) ;
141+ } ) ;
142+
143+ it ( 'should have label for view action' , ( ) => {
144+ expect ( component . actionLabels [ 'view' ] ) . toBe ( 'Viewed' ) ;
145+ } ) ;
146+
147+ it ( 'should have label for copy action' , ( ) => {
148+ expect ( component . actionLabels [ 'copy' ] ) . toBe ( 'Copied' ) ;
149+ } ) ;
150+
151+ it ( 'should have label for update action' , ( ) => {
152+ expect ( component . actionLabels [ 'update' ] ) . toBe ( 'Updated' ) ;
153+ } ) ;
154+
155+ it ( 'should have label for delete action' , ( ) => {
156+ expect ( component . actionLabels [ 'delete' ] ) . toBe ( 'Deleted' ) ;
157+ } ) ;
158+ } ) ;
159+
160+ describe ( 'action icons' , ( ) => {
161+ it ( 'should have icon for create action' , ( ) => {
162+ expect ( component . actionIcons [ 'create' ] ) . toBe ( 'add_circle' ) ;
163+ } ) ;
164+
165+ it ( 'should have icon for view action' , ( ) => {
166+ expect ( component . actionIcons [ 'view' ] ) . toBe ( 'visibility' ) ;
167+ } ) ;
168+
169+ it ( 'should have icon for copy action' , ( ) => {
170+ expect ( component . actionIcons [ 'copy' ] ) . toBe ( 'content_copy' ) ;
171+ } ) ;
172+
173+ it ( 'should have icon for update action' , ( ) => {
174+ expect ( component . actionIcons [ 'update' ] ) . toBe ( 'edit' ) ;
175+ } ) ;
176+
177+ it ( 'should have icon for delete action' , ( ) => {
178+ expect ( component . actionIcons [ 'delete' ] ) . toBe ( 'delete' ) ;
179+ } ) ;
180+ } ) ;
181+
182+ describe ( 'action colors' , ( ) => {
183+ it ( 'should have color for create action' , ( ) => {
184+ expect ( component . actionColors [ 'create' ] ) . toBe ( 'primary' ) ;
185+ } ) ;
186+
187+ it ( 'should have color for view action' , ( ) => {
188+ expect ( component . actionColors [ 'view' ] ) . toBe ( 'accent' ) ;
189+ } ) ;
190+
191+ it ( 'should have color for copy action' , ( ) => {
192+ expect ( component . actionColors [ 'copy' ] ) . toBe ( 'accent' ) ;
193+ } ) ;
194+
195+ it ( 'should have color for update action' , ( ) => {
196+ expect ( component . actionColors [ 'update' ] ) . toBe ( 'primary' ) ;
197+ } ) ;
198+
199+ it ( 'should have color for delete action' , ( ) => {
200+ expect ( component . actionColors [ 'delete' ] ) . toBe ( 'warn' ) ;
201+ } ) ;
202+ } ) ;
203+
204+ describe ( 'loadAuditLog' , ( ) => {
205+ it ( 'should set loading to true while fetching' , ( ) => {
206+ component . loading = false ;
207+ mockSecretsService . getAuditLog . and . returnValue ( of ( mockAuditLogResponse ) ) ;
208+
209+ component . loadAuditLog ( ) ;
210+
211+ expect ( component . loading ) . toBeFalse ( ) ;
212+ } ) ;
213+
214+ it ( 'should update logs on successful fetch' , ( ) => {
215+ mockSecretsService . getAuditLog . and . returnValue ( of ( mockMultipleLogsResponse ) ) ;
216+
217+ component . loadAuditLog ( ) ;
218+
219+ expect ( component . logs . length ) . toBe ( 5 ) ;
220+ } ) ;
221+
222+ it ( 'should update pagination on successful fetch' , ( ) => {
223+ mockSecretsService . getAuditLog . and . returnValue ( of ( mockMultipleLogsResponse ) ) ;
224+
225+ component . loadAuditLog ( ) ;
226+
227+ expect ( component . pagination . total ) . toBe ( 5 ) ;
228+ } ) ;
229+
230+ it ( 'should call getAuditLog with current pagination' , ( ) => {
231+ mockSecretsService . getAuditLog . calls . reset ( ) ;
232+ component . pagination . currentPage = 2 ;
233+ component . pagination . perPage = 10 ;
234+
235+ component . loadAuditLog ( ) ;
236+
237+ expect ( mockSecretsService . getAuditLog ) . toHaveBeenCalledWith ( 'test-secret' , 2 , 10 ) ;
238+ } ) ;
72239 } ) ;
73240
74- it ( 'should display audit log entries' , ( ) => {
75- expect ( component . logs . length ) . toBe ( 1 ) ;
76- expect ( component . logs [ 0 ] . action ) . toBe ( 'create' ) ;
241+ describe ( 'onPageChange' , ( ) => {
242+ it ( 'should update pagination and reload audit log' , ( ) => {
243+ const pageEvent : PageEvent = {
244+ pageIndex : 2 ,
245+ pageSize : 50 ,
246+ length : 100
247+ } ;
248+
249+ mockSecretsService . getAuditLog . calls . reset ( ) ;
250+ component . onPageChange ( pageEvent ) ;
251+
252+ expect ( component . pagination . currentPage ) . toBe ( 3 ) ;
253+ expect ( component . pagination . perPage ) . toBe ( 50 ) ;
254+ expect ( mockSecretsService . getAuditLog ) . toHaveBeenCalledWith ( 'test-secret' , 3 , 50 ) ;
255+ } ) ;
256+
257+ it ( 'should handle first page correctly' , ( ) => {
258+ const pageEvent : PageEvent = {
259+ pageIndex : 0 ,
260+ pageSize : 20 ,
261+ length : 100
262+ } ;
263+
264+ mockSecretsService . getAuditLog . calls . reset ( ) ;
265+ component . onPageChange ( pageEvent ) ;
266+
267+ expect ( component . pagination . currentPage ) . toBe ( 1 ) ;
268+ } ) ;
77269 } ) ;
78270
79- it ( 'should have action labels' , ( ) => {
80- expect ( component . actionLabels [ 'create' ] ) . toBe ( 'Created' ) ;
81- expect ( component . actionLabels [ 'view' ] ) . toBe ( 'Viewed' ) ;
82- expect ( component . actionLabels [ 'delete' ] ) . toBe ( 'Deleted' ) ;
271+ describe ( 'with multiple audit log entries' , ( ) => {
272+ beforeEach ( ( ) => {
273+ mockSecretsService . getAuditLog . and . returnValue ( of ( mockMultipleLogsResponse ) ) ;
274+ component . loadAuditLog ( ) ;
275+ } ) ;
276+
277+ it ( 'should display all entries' , ( ) => {
278+ expect ( component . logs . length ) . toBe ( 5 ) ;
279+ } ) ;
280+
281+ it ( 'should include failed actions' , ( ) => {
282+ const failedAction = component . logs . find ( log => ! log . success ) ;
283+ expect ( failedAction ) . toBeTruthy ( ) ;
284+ expect ( failedAction ?. errorMessage ) . toBe ( 'Deletion failed' ) ;
285+ } ) ;
286+
287+ it ( 'should include all action types' , ( ) => {
288+ const actions = component . logs . map ( log => log . action ) ;
289+ expect ( actions ) . toContain ( 'create' ) ;
290+ expect ( actions ) . toContain ( 'view' ) ;
291+ expect ( actions ) . toContain ( 'update' ) ;
292+ expect ( actions ) . toContain ( 'copy' ) ;
293+ expect ( actions ) . toContain ( 'delete' ) ;
294+ } ) ;
295+ } ) ;
296+
297+ describe ( 'data binding' , ( ) => {
298+ it ( 'should have access to secret data' , ( ) => {
299+ expect ( component . data . secret ) . toEqual ( mockSecret ) ;
300+ } ) ;
301+
302+ it ( 'should have access to secret slug' , ( ) => {
303+ expect ( component . data . secret . slug ) . toBe ( 'test-secret' ) ;
304+ } ) ;
83305 } ) ;
84306} ) ;
0 commit comments