Skip to content

Commit e08ea25

Browse files
committed
test(core): add coverage tests for uncovered utilities
1 parent 9627056 commit e08ea25

8 files changed

Lines changed: 321 additions & 1 deletion

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { ApiError } from '../../src/errors/APIError';
5+
6+
describe('ApiError', () => {
7+
it('creates error without response', () => {
8+
const error = new ApiError({
9+
name: 'TestError',
10+
message: 'Test message',
11+
});
12+
expect(error.name).toBe('TestError');
13+
expect(error.message).toBe('Test message');
14+
expect(error.response).toBeUndefined();
15+
});
16+
17+
it('creates error with response', () => {
18+
const response = {
19+
statusCode: 404,
20+
headers: { 'content-type': 'application/json' },
21+
body: '{"error":"Not found"}',
22+
};
23+
const error = new ApiError({
24+
name: 'NotFoundError',
25+
message: 'Resource not found',
26+
response,
27+
});
28+
expect(error.response).toEqual(response);
29+
expect(error.response).not.toBe(response);
30+
expect(error.response?.headers).not.toBe(response.headers);
31+
});
32+
33+
it('replicates response to prevent mutation', () => {
34+
const response = {
35+
statusCode: 500,
36+
headers: { 'x-custom': 'value' },
37+
};
38+
const error = new ApiError({
39+
name: 'ServerError',
40+
message: 'Server error',
41+
response,
42+
});
43+
const errorResponse = error.response;
44+
response.headers['x-custom'] = 'modified';
45+
expect(errorResponse?.headers['x-custom']).toBe('value');
46+
});
47+
});

packages/core/__tests__/singleton/Auth/utils/index.test.ts

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { decodeJWT } from '../../../../src/singleton/Auth/utils';
1+
import {
2+
assertIdentityPoolIdConfig,
3+
assertOAuthConfig,
4+
assertTokenProviderConfig,
5+
decodeJWT,
6+
} from '../../../../src/singleton/Auth/utils';
27

38
const testSamples = [
49
{
@@ -35,4 +40,111 @@ describe('decodeJWT', () => {
3540
expect(result.toString()).toEqual(token);
3641
},
3742
);
43+
44+
it('throws error for invalid token format', () => {
45+
expect(() => decodeJWT('invalid')).toThrow('Invalid token');
46+
});
47+
48+
it('throws error for malformed payload', () => {
49+
expect(() => decodeJWT('header.invalid-payload.signature')).toThrow(
50+
'Invalid token payload',
51+
);
52+
});
53+
});
54+
55+
describe('assertTokenProviderConfig', () => {
56+
it('passes with valid user pool config', () => {
57+
expect(() => {
58+
assertTokenProviderConfig({
59+
userPoolId: 'us-east-1_test',
60+
userPoolClientId: 'client123',
61+
});
62+
}).not.toThrow();
63+
});
64+
65+
it('throws when config is undefined', () => {
66+
expect(() => {
67+
assertTokenProviderConfig(undefined);
68+
}).toThrow();
69+
});
70+
71+
it('throws when userPoolId is missing', () => {
72+
expect(() => {
73+
assertTokenProviderConfig({
74+
userPoolClientId: 'client123',
75+
} as any);
76+
}).toThrow();
77+
});
78+
79+
it('throws when userPoolClientId is missing', () => {
80+
expect(() => {
81+
assertTokenProviderConfig({
82+
userPoolId: 'us-east-1_test',
83+
} as any);
84+
}).toThrow();
85+
});
86+
});
87+
88+
describe('assertOAuthConfig', () => {
89+
it('passes with valid oauth config', () => {
90+
expect(() => {
91+
assertOAuthConfig({
92+
userPoolId: 'us-east-1_test',
93+
userPoolClientId: 'client123',
94+
loginWith: {
95+
oauth: {
96+
domain: 'example.auth.us-east-1.amazoncognito.com',
97+
redirectSignIn: ['http://localhost:3000/'],
98+
redirectSignOut: ['http://localhost:3000/'],
99+
responseType: 'code',
100+
scopes: ['openid'],
101+
},
102+
},
103+
});
104+
}).not.toThrow();
105+
});
106+
107+
it('throws when oauth config is missing', () => {
108+
expect(() => {
109+
assertOAuthConfig(undefined);
110+
}).toThrow();
111+
});
112+
113+
it('throws when domain is missing', () => {
114+
expect(() => {
115+
assertOAuthConfig({
116+
userPoolId: 'us-east-1_test',
117+
userPoolClientId: 'client123',
118+
loginWith: {
119+
oauth: {
120+
redirectSignIn: ['http://localhost:3000/'],
121+
redirectSignOut: ['http://localhost:3000/'],
122+
responseType: 'code',
123+
} as any,
124+
},
125+
});
126+
}).toThrow();
127+
});
128+
});
129+
130+
describe('assertIdentityPoolIdConfig', () => {
131+
it('passes with valid identity pool config', () => {
132+
expect(() => {
133+
assertIdentityPoolIdConfig({
134+
identityPoolId: 'us-east-1:test-id',
135+
});
136+
}).not.toThrow();
137+
});
138+
139+
it('throws when identityPoolId is missing', () => {
140+
expect(() => {
141+
assertIdentityPoolIdConfig(undefined);
142+
}).toThrow();
143+
});
144+
145+
it('throws when identityPoolId is empty', () => {
146+
expect(() => {
147+
assertIdentityPoolIdConfig({} as any);
148+
}).toThrow();
149+
});
38150
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { AMPLIFY_CONTEXT_BRAND, isAmplifyContext } from '@aws-amplify/core';
5+
6+
describe('isAmplifyContext', () => {
7+
it('returns true for branded context', () => {
8+
const ctx = { [AMPLIFY_CONTEXT_BRAND]: true };
9+
expect(isAmplifyContext(ctx)).toBe(true);
10+
});
11+
12+
it('returns false for null', () => {
13+
expect(isAmplifyContext(null)).toBe(false);
14+
});
15+
16+
it('returns false for undefined', () => {
17+
expect(isAmplifyContext(undefined)).toBe(false);
18+
});
19+
20+
it('returns false for non-object', () => {
21+
expect(isAmplifyContext('string')).toBe(false);
22+
expect(isAmplifyContext(123)).toBe(false);
23+
});
24+
25+
it('returns false for object without brand', () => {
26+
expect(isAmplifyContext({})).toBe(false);
27+
});
28+
});

packages/core/__tests__/storage/InMemoryStorage.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ describe('InMemoryStorage', () => {
3737
expect(inMemoryStorage.key(1)).toEqual('2');
3838
});
3939

40+
it('should return null for out of bounds index', () => {
41+
inMemoryStorage.setItem('1', value);
42+
expect(inMemoryStorage.key(10)).toBeNull();
43+
});
44+
4045
it('should not throw if trying to delete a non existing key', () => {
4146
const badKey = 'nonExistingKey';
4247
expect(() => {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { SyncKeyValueStorage } from '../../src/storage/SyncKeyValueStorage';
5+
6+
describe('SyncKeyValueStorage', () => {
7+
it('throws when accessing storage without initialization', () => {
8+
const storage = new SyncKeyValueStorage();
9+
expect(() => {
10+
storage.setItem('key', 'value');
11+
}).toThrow();
12+
});
13+
14+
it('works with provided storage', () => {
15+
const mockStorage = {
16+
setItem: jest.fn(),
17+
getItem: jest.fn(() => 'value'),
18+
removeItem: jest.fn(),
19+
clear: jest.fn(),
20+
} as any;
21+
22+
const storage = new SyncKeyValueStorage(mockStorage);
23+
storage.setItem('key', 'value');
24+
expect(mockStorage.setItem).toHaveBeenCalledWith('key', 'value');
25+
26+
const value = storage.getItem('key');
27+
expect(value).toBe('value');
28+
29+
storage.removeItem('key');
30+
expect(mockStorage.removeItem).toHaveBeenCalledWith('key');
31+
32+
storage.clear();
33+
expect(mockStorage.clear).toHaveBeenCalled();
34+
});
35+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { WordArray } from '../../src/utils/WordArray';
5+
6+
describe('WordArray', () => {
7+
test('creates empty WordArray', () => {
8+
const wa = new WordArray();
9+
expect(wa.words).toEqual([]);
10+
expect(wa.sigBytes).toBe(0);
11+
});
12+
13+
test('creates WordArray with words', () => {
14+
const wa = new WordArray([0x12345678, 0x9abcdef0]);
15+
expect(wa.words).toEqual([0x12345678, 0x9abcdef0]);
16+
expect(wa.sigBytes).toBe(8);
17+
});
18+
19+
test('creates WordArray with custom sigBytes', () => {
20+
const wa = new WordArray([0x12345678], 3);
21+
expect(wa.sigBytes).toBe(3);
22+
});
23+
24+
test('random generates WordArray', () => {
25+
const wa = new WordArray();
26+
const random = wa.random(8);
27+
expect(random.words.length).toBe(2);
28+
expect(random.sigBytes).toBe(8);
29+
});
30+
31+
test('toString converts to hex', () => {
32+
const wa = new WordArray([0x12345678], 4);
33+
const hex = wa.toString();
34+
expect(hex).toBe('12345678');
35+
});
36+
37+
test('toString handles partial bytes', () => {
38+
const wa = new WordArray([0x12345678], 2);
39+
const hex = wa.toString();
40+
expect(hex).toBe('1234');
41+
});
42+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { deepFreeze } from '../../src/utils/deepFreeze';
5+
6+
describe('deepFreeze', () => {
7+
test('freezes simple object', () => {
8+
const obj = { a: 1, b: 2 };
9+
const frozen = deepFreeze(obj);
10+
expect(Object.isFrozen(frozen)).toBe(true);
11+
});
12+
13+
test('freezes nested objects', () => {
14+
const obj = { a: { b: { c: 1 } } };
15+
const frozen = deepFreeze(obj);
16+
expect(Object.isFrozen(frozen)).toBe(true);
17+
expect(Object.isFrozen(frozen.a)).toBe(true);
18+
expect(Object.isFrozen(frozen.a.b)).toBe(true);
19+
});
20+
21+
test('freezes functions', () => {
22+
const obj = { fn: () => 'test' };
23+
const frozen = deepFreeze(obj);
24+
expect(Object.isFrozen(frozen.fn)).toBe(true);
25+
});
26+
});

packages/core/__tests__/utils/getClientInfo/getClientInfo.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,29 @@ describe('getClientInfo', () => {
179179
expect(result).toEqual(expect.objectContaining(expectedResult));
180180
},
181181
);
182+
183+
test('returns empty object when window is undefined', () => {
184+
const originalWindow = (global as any).window;
185+
delete (global as any).window;
186+
const result = getClientInfo();
187+
expect(result).toEqual({});
188+
(global as any).window = originalWindow;
189+
});
190+
191+
test('returns empty object when navigator is undefined', () => {
192+
mockNavigator.mockReturnValueOnce(undefined as any);
193+
const result = getClientInfo();
194+
expect(result).toEqual({});
195+
});
196+
197+
test('handles unknown user agent', () => {
198+
mockNavigator.mockReturnValueOnce({
199+
userAgent: 'UnknownBrowser',
200+
platform: 'Unknown',
201+
language: 'en',
202+
} as any);
203+
const result = getClientInfo();
204+
expect(result.model).toBe('');
205+
expect(result.version).toBe('');
206+
});
182207
});

0 commit comments

Comments
 (0)