Skip to content

Commit 44a31a5

Browse files
committed
tests
1 parent 82e1e63 commit 44a31a5

7 files changed

Lines changed: 1728 additions & 136 deletions

File tree

frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.spec.ts

Lines changed: 244 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
66
import { MatSnackBarModule } from '@angular/material/snack-bar';
77
import { Angulartics2Module } from 'angulartics2';
88
import { of } from 'rxjs';
9+
import { PageEvent } from '@angular/material/paginator';
910

1011
import { AuditLogDialogComponent } from './audit-log-dialog.component';
1112
import { SecretsService } from 'src/app/services/secrets.service';
13+
import { Secret, AuditLogEntry, AuditLogResponse } from 'src/app/models/secret';
1214

1315
describe('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

Comments
 (0)