Skip to content

Commit 2c7db9d

Browse files
test: add unit tests for AuthenticationManager and improve hashing tests
1 parent 4363c90 commit 2c7db9d

4 files changed

Lines changed: 377 additions & 255 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
2+
import { AuthenticationManager, AuthenticationOptions } from '../../src/authenticationManager';
3+
import * as openidClient from 'openid-client';
4+
import * as jwtDecodeModule from 'jwt-decode';
5+
import * as grpc from '@grpc/grpc-js';
6+
7+
vi.mock('openid-client');
8+
vi.mock('jwt-decode');
9+
10+
const mockDiscovery = { issuer: 'https://issuer.example.com' } as any;
11+
const mockToken = {
12+
access_token: 'mock_access_token',
13+
token_type: 'Bearer',
14+
refresh_token: 'mock_refresh_token',
15+
} as any;
16+
const mockDecoded = { exp: Math.floor(Date.now() / 1000) + 3600 };
17+
18+
const options: AuthenticationOptions = {
19+
clientId: 'client-id',
20+
clientSecret: 'client-secret',
21+
issuerUrl: 'https://issuer.example.com',
22+
scope: 'manage:classify',
23+
};
24+
25+
describe('AuthenticationManager', () => {
26+
let manager: AuthenticationManager;
27+
28+
beforeEach(() => {
29+
vi.resetAllMocks();
30+
(openidClient.discovery as any).mockResolvedValue(mockDiscovery);
31+
(openidClient.clientCredentialsGrant as any).mockResolvedValue(mockToken);
32+
(openidClient.refreshTokenGrant as any).mockResolvedValue(mockToken);
33+
(jwtDecodeModule.jwtDecode as any).mockReturnValue(mockDecoded);
34+
manager = new AuthenticationManager(options);
35+
});
36+
37+
it('should discover OIDC configuration and acquire token on first use', async () => {
38+
await manager.getAuthenticationHeader();
39+
expect(openidClient.discovery).toHaveBeenCalledWith(
40+
new URL(options.issuerUrl),
41+
options.clientId,
42+
options.clientSecret,
43+
);
44+
expect(openidClient.clientCredentialsGrant).toHaveBeenCalledWith(
45+
mockDiscovery,
46+
{ audience: 'crisp-athena-dev', scope: options.scope }
47+
);
48+
});
49+
50+
it('should return a Bearer token in the authentication header', async () => {
51+
const header = await manager.getAuthenticationHeader();
52+
expect(header).toBe('Bearer mock_access_token');
53+
});
54+
55+
it('should append Authorization to grpc.Metadata', async () => {
56+
const metadata = new grpc.Metadata();
57+
await manager.appendAuthorizationToMetadata(metadata);
58+
expect(metadata.get('Authorization')).toEqual(['Bearer mock_access_token']);
59+
});
60+
61+
it('should refresh token if expired and refresh_token is present', async () => {
62+
// Simulate token already acquired and expired
63+
await manager.getAuthenticationHeader();
64+
(manager as any).tokenExpiration = new Date(Date.now() - 1000); // expired
65+
(manager as any).token.refresh_token = 'mock_refresh_token';
66+
(openidClient.refreshTokenGrant as any).mockResolvedValue({
67+
...mockToken,
68+
access_token: 'new_access_token',
69+
});
70+
(jwtDecodeModule.jwtDecode as any).mockReturnValue({ exp: Math.floor(Date.now() / 1000) + 3600 });
71+
72+
const header = await manager.getAuthenticationHeader();
73+
expect(openidClient.refreshTokenGrant).toHaveBeenCalled();
74+
expect(header).toBe('Bearer new_access_token');
75+
});
76+
77+
it('should reacquire token if expired and no refresh_token', async () => {
78+
await manager.getAuthenticationHeader();
79+
(manager as any).tokenExpiration = new Date(Date.now() - 1000); // expired
80+
(manager as any).token.refresh_token = undefined;
81+
(openidClient.clientCredentialsGrant as any).mockResolvedValue({
82+
...mockToken,
83+
access_token: 'reacquired_access_token',
84+
});
85+
(jwtDecodeModule.jwtDecode as any).mockReturnValue({ exp: Math.floor(Date.now() / 1000) + 3600 });
86+
87+
const header = await manager.getAuthenticationHeader();
88+
expect(openidClient.clientCredentialsGrant).toHaveBeenCalled();
89+
expect(header).toBe('Bearer reacquired_access_token');
90+
});
91+
92+
it('should reacquire token if refresh fails', async () => {
93+
await manager.getAuthenticationHeader();
94+
(manager as any).tokenExpiration = new Date(Date.now() - 1000); // expired
95+
(manager as any).token.refresh_token = 'mock_refresh_token';
96+
(openidClient.refreshTokenGrant as any).mockRejectedValue(new Error('refresh failed'));
97+
(openidClient.clientCredentialsGrant as any).mockResolvedValue({
98+
...mockToken,
99+
access_token: 'fallback_access_token',
100+
});
101+
(jwtDecodeModule.jwtDecode as any).mockReturnValue({ exp: Math.floor(Date.now() / 1000) + 3600 });
102+
103+
const header = await manager.getAuthenticationHeader();
104+
expect(openidClient.refreshTokenGrant).toHaveBeenCalled();
105+
expect(openidClient.clientCredentialsGrant).toHaveBeenCalled();
106+
expect(header).toBe('Bearer fallback_access_token');
107+
});
108+
109+
it('should only discover once', async () => {
110+
await manager.getAuthenticationHeader();
111+
await manager.getAuthenticationHeader();
112+
expect(openidClient.discovery).toHaveBeenCalledTimes(1);
113+
});
114+
});

__tests__/unit/hashing.test.ts

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,59 @@ import { computeHashesFromStream, HashType, ImageFormat, RequestEncoding } from
33
import fs from 'fs';
44

55
describe('hashing', () => {
6-
it('should compute MD5 and SHA1 hashes for a valid image', async ({expect}) => {
7-
// Test implementation here
8-
// Test implementation here
9-
const imagePath = __dirname + '/448x448.jpg';
10-
11-
const imageStream = fs.createReadStream(imagePath);
12-
13-
//assert error is thrown for invalid image
14-
const { data, format, md5, sha1 } = await computeHashesFromStream(imageStream,
15-
RequestEncoding.UNCOMPRESSED,
16-
ImageFormat.JPEG,
17-
false,
18-
[HashType.MD5, HashType.SHA1]
19-
);
20-
21-
expect(data).toBeDefined();
22-
expect(format).toBe(ImageFormat.JPEG);
23-
24-
expect(md5).toEqual('eb3a95fdd86ce28d9a63a68328783874');
25-
expect(sha1).toEqual('b972b222bc91c457d904ebff16134dc79b67d1c9');
26-
});
27-
28-
it('should compute MD5 and SHA1 hashes for a resized invalid image', async ({expect}) => {
29-
// Test implementation here
30-
// Test implementation here
31-
const imagePath = __dirname + '/578x478.jpg';
32-
33-
const imageStream = fs.createReadStream(imagePath);
34-
35-
//assert error is thrown for invalid image
36-
const { data, format, md5, sha1 } = await computeHashesFromStream(imageStream,
37-
RequestEncoding.UNCOMPRESSED,
38-
ImageFormat.JPEG,
39-
true,
40-
[HashType.MD5, HashType.SHA1]
41-
);
42-
43-
expect(data).toBeDefined();
44-
expect(format).toBe(ImageFormat.RAW_UINT8);
45-
46-
expect(md5).toEqual('d390b6cd7436fd41d1bcd005e7e3e652');
47-
expect(sha1).toEqual('7f979ca35cfe390bd92f5dfa4a05919da76f0e43');
48-
});
49-
50-
it('should throw an error for an invalid image', async ({expect}) => {
51-
// Test implementation here
52-
const imagePath = __dirname + '/578x478.jpg';
53-
54-
const imageStream = fs.createReadStream(imagePath);
55-
56-
//assert error is thrown for invalid image
57-
await expect(computeHashesFromStream(imageStream)).rejects.toThrow('Image must be 448x448 pixels');
6+
describe('computeHashesFromStream', () => {
7+
it('should compute MD5 and SHA1 hashes for a valid image', async ({expect}) => {
8+
// Test implementation here
9+
// Test implementation here
10+
const imagePath = __dirname + '/448x448.jpg';
11+
12+
const imageStream = fs.createReadStream(imagePath);
13+
14+
//assert error is thrown for invalid image
15+
const { data, format, md5, sha1 } = await computeHashesFromStream(imageStream,
16+
RequestEncoding.UNCOMPRESSED,
17+
ImageFormat.JPEG,
18+
false,
19+
[HashType.MD5, HashType.SHA1]
20+
);
21+
22+
expect(data).toBeDefined();
23+
expect(format).toBe(ImageFormat.JPEG);
24+
25+
expect(md5).toEqual('eb3a95fdd86ce28d9a63a68328783874');
26+
expect(sha1).toEqual('b972b222bc91c457d904ebff16134dc79b67d1c9');
27+
});
28+
29+
it('should compute MD5 and SHA1 hashes for a resized invalid image', async ({expect}) => {
30+
// Test implementation here
31+
// Test implementation here
32+
const imagePath = __dirname + '/578x478.jpg';
33+
34+
const imageStream = fs.createReadStream(imagePath);
35+
36+
//assert error is thrown for invalid image
37+
const { data, format, md5, sha1 } = await computeHashesFromStream(imageStream,
38+
RequestEncoding.UNCOMPRESSED,
39+
ImageFormat.JPEG,
40+
true,
41+
[HashType.MD5, HashType.SHA1]
42+
);
43+
44+
expect(data).toBeDefined();
45+
expect(format).toBe(ImageFormat.RAW_UINT8);
46+
47+
expect(md5).toEqual('d390b6cd7436fd41d1bcd005e7e3e652');
48+
expect(sha1).toEqual('7f979ca35cfe390bd92f5dfa4a05919da76f0e43');
49+
});
50+
51+
it('should throw an error for an invalid image', async ({expect}) => {
52+
// Test implementation here
53+
const imagePath = __dirname + '/578x478.jpg';
54+
55+
const imageStream = fs.createReadStream(imagePath);
56+
57+
//assert error is thrown for invalid image
58+
await expect(computeHashesFromStream(imageStream)).rejects.toThrow('Image must be 448x448 pixels');
59+
});
5860
});
5961
});

0 commit comments

Comments
 (0)