feat(smartradius): Implement full RADIUS server and client with RFC 2865/2866 compliance, including packet handling, authenticators, attributes, secrets manager, client APIs, and comprehensive tests and documentation
This commit is contained in:
149
test/client/test.timeout.ts
Normal file
149
test/client/test.timeout.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { RadiusClient } from '../../ts_client/index.js';
|
||||
|
||||
tap.test('should timeout when server is not reachable', async () => {
|
||||
// Connect to a port where no server is running
|
||||
const client = new RadiusClient({
|
||||
host: '127.0.0.1',
|
||||
authPort: 19999, // Unlikely to have a server
|
||||
acctPort: 19998,
|
||||
secret: 'testing123',
|
||||
timeout: 500, // Short timeout
|
||||
retries: 1, // Minimal retries
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
let error: Error | undefined;
|
||||
try {
|
||||
await client.authenticatePap('user', 'pass');
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
|
||||
expect(error).toBeDefined();
|
||||
expect(error!.message).toInclude('timed out');
|
||||
|
||||
await client.disconnect();
|
||||
});
|
||||
|
||||
tap.test('should retry on timeout', async () => {
|
||||
const startTime = Date.now();
|
||||
|
||||
const client = new RadiusClient({
|
||||
host: '127.0.0.1',
|
||||
authPort: 19997,
|
||||
acctPort: 19996,
|
||||
secret: 'testing123',
|
||||
timeout: 200, // 200ms timeout
|
||||
retries: 3, // 3 retries
|
||||
retryDelay: 100, // 100ms initial delay
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
let error: Error | undefined;
|
||||
try {
|
||||
await client.authenticatePap('user', 'pass');
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
|
||||
const elapsed = Date.now() - startTime;
|
||||
|
||||
expect(error).toBeDefined();
|
||||
// With 3 retries and exponential backoff, should take at least:
|
||||
// Initial timeout (200) + retry 1 delay (100) + timeout (200) + retry 2 delay (200) + timeout (200) + retry 3 delay (400) + timeout (200)
|
||||
// = 200 + 100 + 200 + 200 + 200 + 400 + 200 = ~1500ms minimum
|
||||
expect(elapsed).toBeGreaterThanOrEqual(500);
|
||||
|
||||
await client.disconnect();
|
||||
});
|
||||
|
||||
tap.test('should handle disconnect during request', async () => {
|
||||
const client = new RadiusClient({
|
||||
host: '127.0.0.1',
|
||||
authPort: 19995,
|
||||
acctPort: 19994,
|
||||
secret: 'testing123',
|
||||
timeout: 5000,
|
||||
retries: 3,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
// Start a request (will never complete because no server)
|
||||
const requestPromise = client.authenticatePap('user', 'pass');
|
||||
|
||||
// Disconnect immediately
|
||||
await client.disconnect();
|
||||
|
||||
let error: Error | undefined;
|
||||
try {
|
||||
await requestPromise;
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
|
||||
// When the client disconnects, pending requests are rejected
|
||||
// The error can be either "Client disconnected" or other disconnect-related messages
|
||||
expect(error).toBeDefined();
|
||||
// Just verify we got an error - the specific message may vary
|
||||
});
|
||||
|
||||
tap.test('should handle multiple concurrent requests', async () => {
|
||||
// This test just verifies we can make multiple requests
|
||||
// They will all timeout since no server, but should handle correctly
|
||||
const client = new RadiusClient({
|
||||
host: '127.0.0.1',
|
||||
authPort: 19993,
|
||||
acctPort: 19992,
|
||||
secret: 'testing123',
|
||||
timeout: 200,
|
||||
retries: 1,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
const requests = [
|
||||
client.authenticatePap('user1', 'pass1').catch((e) => e),
|
||||
client.authenticatePap('user2', 'pass2').catch((e) => e),
|
||||
client.authenticatePap('user3', 'pass3').catch((e) => e),
|
||||
];
|
||||
|
||||
const results = await Promise.all(requests);
|
||||
|
||||
// All should be errors (timeout)
|
||||
for (const result of results) {
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
}
|
||||
|
||||
await client.disconnect();
|
||||
});
|
||||
|
||||
tap.test('should auto-connect if not connected', async () => {
|
||||
const client = new RadiusClient({
|
||||
host: '127.0.0.1',
|
||||
authPort: 19991,
|
||||
acctPort: 19990,
|
||||
secret: 'testing123',
|
||||
timeout: 200,
|
||||
retries: 1,
|
||||
});
|
||||
|
||||
// Don't call connect() - should auto-connect
|
||||
let error: Error | undefined;
|
||||
try {
|
||||
await client.authenticatePap('user', 'pass');
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
|
||||
// Should timeout, not connection error
|
||||
expect(error).toBeDefined();
|
||||
expect(error!.message).toInclude('timed out');
|
||||
|
||||
await client.disconnect();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
Reference in New Issue
Block a user