feat(domain-intelligence): add domain intelligence lookups with RDAP and DNS enrichment
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as smartnetwork from '../ts/index.js';
|
||||
|
||||
let testSmartNetwork: smartnetwork.SmartNetwork;
|
||||
|
||||
tap.test('should create a SmartNetwork instance', async () => {
|
||||
testSmartNetwork = new smartnetwork.SmartNetwork();
|
||||
expect(testSmartNetwork).toBeInstanceOf(smartnetwork.SmartNetwork);
|
||||
});
|
||||
|
||||
tap.test('should get domain intelligence for google.com', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('google.com');
|
||||
console.log('Domain Intelligence for google.com:', JSON.stringify(result, null, 2));
|
||||
|
||||
// RDAP fields
|
||||
expect(result.domain).toEqual('google.com');
|
||||
expect(result.registrarName).toBeTruthy();
|
||||
expect(result.nameservers).toBeTruthy();
|
||||
expect(Array.isArray(result.nameservers)).toBeTrue();
|
||||
expect(result.nameservers!.length).toBeGreaterThan(0);
|
||||
expect(result.registrationDate).toBeTruthy();
|
||||
expect(result.expirationDate).toBeTruthy();
|
||||
expect(Array.isArray(result.status)).toBeTrue();
|
||||
expect(result.nameserversSource).toEqual('rdap');
|
||||
|
||||
// DNS enrichment fields
|
||||
expect(result.resolvedIpv4).toBeTruthy();
|
||||
expect(result.resolvedIpv4!.length).toBeGreaterThan(0);
|
||||
expect(result.mxRecords).toBeTruthy();
|
||||
expect(result.mxRecords!.length).toBeGreaterThan(0);
|
||||
expect(result.txtRecords).toBeTruthy();
|
||||
expect(result.txtRecords!.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('should get domain intelligence for cloudflare.com', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('cloudflare.com');
|
||||
console.log('Domain Intelligence for cloudflare.com:', JSON.stringify(result, null, 2));
|
||||
|
||||
expect(result.domain).toEqual('cloudflare.com');
|
||||
expect(result.registrarName).toBeTruthy();
|
||||
expect(result.nameservers).toBeTruthy();
|
||||
expect(result.nameservers!.length).toBeGreaterThan(0);
|
||||
expect(result.registrationDate).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('should get domain intelligence for wikipedia.org (.org TLD)', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('wikipedia.org');
|
||||
console.log('Domain Intelligence for wikipedia.org:', JSON.stringify(result, null, 2));
|
||||
|
||||
expect(result.domain).toEqual('wikipedia.org');
|
||||
expect(result.registrarName).toBeTruthy();
|
||||
expect(result.nameservers).toBeTruthy();
|
||||
expect(result.nameservers!.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('should normalize an FQDN with trailing dot', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('Google.Com.');
|
||||
expect(result.domain).toEqual('google.com');
|
||||
});
|
||||
|
||||
tap.test('should normalize an IDN to ASCII (punycode)', async () => {
|
||||
// The IDN "münchen.de" → "xn--mnchen-3ya.de"
|
||||
const result = await testSmartNetwork.getDomainIntelligence('münchen.de');
|
||||
console.log('IDN normalized to:', result.domain);
|
||||
// Even if the lookup fails (e.g. .de RDAP behavior), the normalization should still produce the ASCII form
|
||||
expect(result.domain).toEqual('xn--mnchen-3ya.de');
|
||||
});
|
||||
|
||||
tap.test('should handle unknown TLD gracefully', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('foo.invalidtld12345');
|
||||
console.log('Unknown TLD result:', JSON.stringify(result, null, 2));
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.registrarName).toBeNull();
|
||||
expect(result.nameservers).toBeNull();
|
||||
});
|
||||
|
||||
tap.test('should handle malformed input gracefully', async () => {
|
||||
const r1 = await testSmartNetwork.getDomainIntelligence('');
|
||||
expect(r1).toBeTruthy();
|
||||
expect(r1.domain).toBeNull();
|
||||
|
||||
const r2 = await testSmartNetwork.getDomainIntelligence('not a domain at all');
|
||||
expect(r2).toBeTruthy();
|
||||
expect(r2.domain).toBeNull();
|
||||
|
||||
const r3 = await testSmartNetwork.getDomainIntelligence('nodot');
|
||||
expect(r3).toBeTruthy();
|
||||
expect(r3.domain).toBeNull();
|
||||
});
|
||||
|
||||
tap.test('should parse MX records with priority and exchange', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('google.com');
|
||||
expect(result.mxRecords).toBeTruthy();
|
||||
for (const mx of result.mxRecords!) {
|
||||
expect(typeof mx.exchange).toEqual('string');
|
||||
expect(mx.exchange.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should populate nameservers via DNS for RDAP-less .de TLD', async () => {
|
||||
const result = await testSmartNetwork.getDomainIntelligence('bund.de');
|
||||
console.log('Domain Intelligence for bund.de:', JSON.stringify(result, null, 2));
|
||||
// .de has no RDAP in the IANA bootstrap, so RDAP fields are null
|
||||
expect(result.registrarName).toBeNull();
|
||||
// DNS layer should fill in nameservers + resolved IPs
|
||||
expect(result.nameservers).toBeTruthy();
|
||||
expect(result.nameservers!.length).toBeGreaterThan(0);
|
||||
expect(result.nameserversSource).toEqual('dns');
|
||||
expect(result.resolvedIpv4).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('should use cache when cacheTtl is set', async () => {
|
||||
const cached = new smartnetwork.SmartNetwork({ cacheTtl: 60000 });
|
||||
const r1 = await cached.getDomainIntelligence('google.com');
|
||||
const r2 = await cached.getDomainIntelligence('google.com');
|
||||
expect(r1.registrarName).toEqual(r2.registrarName);
|
||||
expect(r1.registrationDate).toEqual(r2.registrationDate);
|
||||
await cached.stop();
|
||||
});
|
||||
|
||||
tap.test('should stop cleanly (tears down shared smartdns client)', async () => {
|
||||
// If the Rust-backed smartdns bridge wasn't destroyed, this test process
|
||||
// would hang at exit instead of completing.
|
||||
await testSmartNetwork.stop();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
@@ -65,6 +65,13 @@ tap.test('should use cache when cacheTtl is set', async () => {
|
||||
// Second call should return the same cached result
|
||||
expect(r1.asn).toEqual(r2.asn);
|
||||
expect(r1.countryCode).toEqual(r2.countryCode);
|
||||
await cached.stop();
|
||||
});
|
||||
|
||||
tap.test('should stop cleanly (tears down shared smartdns client)', async () => {
|
||||
// If the Rust-backed smartdns bridge wasn't destroyed, this test process
|
||||
// would hang at exit instead of completing.
|
||||
await testSmartNetwork.stop();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user