Add comprehensive error handling for API device attachment
- Add explicit 429 rate limit handling with RATE_LIMITED error code - Add 6 new test cases for edge scenarios: - 403 Forbidden handling - 429 Rate Limit handling - JSON parse error handling - WellNuo API failure when fetching beneficiary - Timeout error detection - MAC address uppercase normalization verification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
71f194cc4d
commit
0c801c3b19
@ -382,5 +382,175 @@ describe('API - Sensor Attachment Error Handling', () => {
|
||||
// Token will be URL encoded in the body, so just check it contains the token
|
||||
expect(bodyString).toContain('token=');
|
||||
});
|
||||
|
||||
it('should handle 403 Forbidden from Legacy API', async () => {
|
||||
// Mock successful beneficiary lookup
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 123,
|
||||
firstName: 'Maria',
|
||||
deploymentId: mockDeploymentId
|
||||
})
|
||||
})
|
||||
// Mock 403 response from Legacy API
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 403,
|
||||
json: async () => ({ message: 'Access denied' })
|
||||
});
|
||||
|
||||
const result = await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
mockDeviceMac
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('UNAUTHORIZED');
|
||||
expect(result.error?.message).toMatch(/Authentication expired/);
|
||||
expect(result.error?.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should handle 429 Rate Limit from Legacy API', async () => {
|
||||
// Mock successful beneficiary lookup
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 123,
|
||||
firstName: 'Maria',
|
||||
deploymentId: mockDeploymentId
|
||||
})
|
||||
})
|
||||
// Mock 429 response from Legacy API
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 429,
|
||||
json: async () => ({ message: 'Too many requests' })
|
||||
});
|
||||
|
||||
const result = await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
mockDeviceMac
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('RATE_LIMITED');
|
||||
expect(result.error?.message).toContain('Too many requests');
|
||||
expect(result.error?.status).toBe(429);
|
||||
});
|
||||
|
||||
it('should handle JSON parse errors gracefully', async () => {
|
||||
// Mock successful beneficiary lookup
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 123,
|
||||
firstName: 'Maria',
|
||||
deploymentId: mockDeploymentId
|
||||
})
|
||||
})
|
||||
// Mock response with invalid JSON
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => { throw new SyntaxError('Unexpected token'); }
|
||||
});
|
||||
|
||||
const result = await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
mockDeviceMac
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('EXCEPTION');
|
||||
});
|
||||
|
||||
it('should handle WellNuo API failure when getting beneficiary', async () => {
|
||||
// Mock failed beneficiary lookup from WellNuo API
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 500,
|
||||
json: async () => ({ message: 'Internal error' })
|
||||
});
|
||||
|
||||
const result = await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
mockDeviceMac
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('DEPLOYMENT_NOT_FOUND');
|
||||
expect(result.error?.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should handle timeout errors', async () => {
|
||||
// Mock successful beneficiary lookup
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 123,
|
||||
firstName: 'Maria',
|
||||
deploymentId: mockDeploymentId
|
||||
})
|
||||
})
|
||||
// Mock timeout error
|
||||
.mockRejectedValueOnce(
|
||||
new Error('network request failed: timeout')
|
||||
);
|
||||
|
||||
const result = await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
mockDeviceMac
|
||||
);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('NETWORK_ERROR');
|
||||
expect(result.error?.message).toContain('No internet connection');
|
||||
});
|
||||
|
||||
it('should uppercase device MAC address in request', async () => {
|
||||
const lowercaseMac = '142b2f81a14c';
|
||||
|
||||
// Mock successful beneficiary lookup
|
||||
(global.fetch as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
id: 123,
|
||||
firstName: 'Maria',
|
||||
deploymentId: mockDeploymentId
|
||||
})
|
||||
})
|
||||
// Mock successful device_form response
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
status: '200 OK'
|
||||
})
|
||||
});
|
||||
|
||||
await api.attachDeviceToBeneficiary(
|
||||
mockBeneficiaryId,
|
||||
mockWellId,
|
||||
lowercaseMac
|
||||
);
|
||||
|
||||
// Verify MAC is uppercased
|
||||
const secondCall = (global.fetch as jest.Mock).mock.calls[1];
|
||||
const bodyString = secondCall[1].body;
|
||||
expect(bodyString).toContain(`device_mac=${lowercaseMac.toUpperCase()}`);
|
||||
expect(bodyString).not.toContain(`device_mac=${lowercaseMac}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -2048,6 +2048,9 @@ class ApiService {
|
||||
} else if (attachResponse.status === 404) {
|
||||
errorMessage = 'Sensor or deployment not found.';
|
||||
errorCode = 'NOT_FOUND';
|
||||
} else if (attachResponse.status === 429) {
|
||||
errorMessage = 'Too many requests. Please wait a moment and try again.';
|
||||
errorCode = 'RATE_LIMITED';
|
||||
} else if (attachResponse.status === 500) {
|
||||
errorMessage = 'Server error. Please try again later.';
|
||||
errorCode = 'SERVER_ERROR';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user