Files
smartradius/test/server/test.packet.ts

191 lines
6.2 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import {
RadiusPacket,
ERadiusCode,
ERadiusAttributeType,
} from '../../ts_server/index.js';
tap.test('should encode and decode a basic Access-Request packet', async () => {
const identifier = 42;
const secret = 'testing123';
const attributes = [
{ type: ERadiusAttributeType.UserName, value: 'testuser' },
{ type: ERadiusAttributeType.UserPassword, value: 'testpass' },
{ type: ERadiusAttributeType.NasIpAddress, value: '192.168.1.1' },
{ type: ERadiusAttributeType.NasPort, value: 1 },
];
const packet = RadiusPacket.createAccessRequest(identifier, secret, attributes);
// Verify minimum packet size (20 bytes header)
expect(packet.length).toBeGreaterThanOrEqual(RadiusPacket.MIN_PACKET_SIZE);
// Verify maximum packet size
expect(packet.length).toBeLessThanOrEqual(RadiusPacket.MAX_PACKET_SIZE);
// Verify header
expect(packet[0]).toEqual(ERadiusCode.AccessRequest);
expect(packet[1]).toEqual(identifier);
// Decode the packet
const decoded = RadiusPacket.decode(packet);
expect(decoded.code).toEqual(ERadiusCode.AccessRequest);
expect(decoded.identifier).toEqual(identifier);
expect(decoded.authenticator.length).toEqual(16);
expect(decoded.attributes.length).toBeGreaterThan(0);
});
tap.test('should handle packet length validation', async () => {
// Packet too short
const shortPacket = Buffer.alloc(19);
let error: Error | undefined;
try {
RadiusPacket.decode(shortPacket);
} catch (e) {
error = e as Error;
}
expect(error).toBeDefined();
expect(error!.message).toInclude('too short');
});
tap.test('should handle invalid length in header', async () => {
const packet = Buffer.alloc(20);
packet[0] = ERadiusCode.AccessRequest;
packet[1] = 1; // identifier
packet.writeUInt16BE(10, 2); // length too small
let error: Error | undefined;
try {
RadiusPacket.decode(packet);
} catch (e) {
error = e as Error;
}
expect(error).toBeDefined();
});
tap.test('should handle maximum packet size', async () => {
const secret = 'testing123';
const identifier = 1;
// Create a packet that would exceed max size
const hugeAttributes: Array<{ type: number; value: Buffer }> = [];
// Each attribute can be max 255 bytes. Create enough to exceed 4096
for (let i = 0; i < 20; i++) {
hugeAttributes.push({
type: ERadiusAttributeType.ReplyMessage,
value: Buffer.alloc(250, 65), // 250 bytes of 'A'
});
}
// This should throw because packet would be too large
let error: Error | undefined;
try {
// Manually build the packet to test size limit
const rawAttrs = hugeAttributes.map((a) => ({
type: a.type,
value: a.value,
}));
RadiusPacket.encode({
code: ERadiusCode.AccessRequest,
identifier,
authenticator: Buffer.alloc(16),
attributes: rawAttrs,
});
} catch (e) {
error = e as Error;
}
expect(error).toBeDefined();
expect(error!.message).toInclude('too large');
});
tap.test('should parse and encode attributes correctly', async () => {
const secret = 'testing123';
const identifier = 1;
// Create packet with various attribute types
const packet = RadiusPacket.createAccessRequest(identifier, secret, [
{ type: 'User-Name', value: 'john.doe' }, // text
{ type: 'NAS-IP-Address', value: '10.0.0.1' }, // address
{ type: 'NAS-Port', value: 5060 }, // integer
{ type: 'NAS-Identifier', value: 'nas01.example.com' }, // text
]);
const decoded = RadiusPacket.decodeAndParse(packet);
// Find username
const usernameAttr = decoded.parsedAttributes.find(
(a) => a.type === ERadiusAttributeType.UserName
);
expect(usernameAttr).toBeDefined();
expect(usernameAttr!.value).toEqual('john.doe');
// Find NAS-IP-Address
const nasIpAttr = decoded.parsedAttributes.find(
(a) => a.type === ERadiusAttributeType.NasIpAddress
);
expect(nasIpAttr).toBeDefined();
expect(nasIpAttr!.value).toEqual('10.0.0.1');
// Find NAS-Port
const nasPortAttr = decoded.parsedAttributes.find(
(a) => a.type === ERadiusAttributeType.NasPort
);
expect(nasPortAttr).toBeDefined();
expect(nasPortAttr!.value).toEqual(5060);
});
tap.test('should create Access-Accept packet', async () => {
const identifier = 1;
const requestAuth = Buffer.alloc(16);
const secret = 'testing123';
const packet = RadiusPacket.createAccessAccept(identifier, requestAuth, secret, [
{ type: ERadiusAttributeType.ReplyMessage, value: 'Welcome!' },
{ type: ERadiusAttributeType.SessionTimeout, value: 3600 },
]);
const decoded = RadiusPacket.decode(packet);
expect(decoded.code).toEqual(ERadiusCode.AccessAccept);
expect(decoded.identifier).toEqual(identifier);
});
tap.test('should create Access-Reject packet', async () => {
const identifier = 1;
const requestAuth = Buffer.alloc(16);
const secret = 'testing123';
const packet = RadiusPacket.createAccessReject(identifier, requestAuth, secret, [
{ type: ERadiusAttributeType.ReplyMessage, value: 'Invalid credentials' },
]);
const decoded = RadiusPacket.decode(packet);
expect(decoded.code).toEqual(ERadiusCode.AccessReject);
});
tap.test('should create Access-Challenge packet', async () => {
const identifier = 1;
const requestAuth = Buffer.alloc(16);
const secret = 'testing123';
const state = Buffer.from('challenge-state-123');
const packet = RadiusPacket.createAccessChallenge(identifier, requestAuth, secret, [
{ type: ERadiusAttributeType.ReplyMessage, value: 'Enter OTP' },
{ type: ERadiusAttributeType.State, value: state },
]);
const decoded = RadiusPacket.decode(packet);
expect(decoded.code).toEqual(ERadiusCode.AccessChallenge);
});
tap.test('should get code name', async () => {
expect(RadiusPacket.getCodeName(ERadiusCode.AccessRequest)).toEqual('Access-Request');
expect(RadiusPacket.getCodeName(ERadiusCode.AccessAccept)).toEqual('Access-Accept');
expect(RadiusPacket.getCodeName(ERadiusCode.AccessReject)).toEqual('Access-Reject');
expect(RadiusPacket.getCodeName(ERadiusCode.AccountingRequest)).toEqual('Accounting-Request');
expect(RadiusPacket.getCodeName(ERadiusCode.AccountingResponse)).toEqual('Accounting-Response');
expect(RadiusPacket.getCodeName(ERadiusCode.AccessChallenge)).toEqual('Access-Challenge');
});
export default tap.start();