A Changed Exit IP Is Not a Failed Request: An Offline Node.js QA Check
Separate request health, observable address continuity, and missing measurements with a small tested analyzer.
A proxy QA check can complete successfully and report a different exit IP. A timed-out request can report no IP at all. Putting both events into a single failure percentage hides the difference between a changing connection and a request that did not complete as expected.
This walkthrough uses an offline analyzer: it reads observations already collected elsewhere, makes no network requests, and never purchases or reconfigures a proxy. The complete implementation, fixture, and tests are published on GitHub. All numbers below are synthetic examples, not measurements of a service.
Define three separate signals
Give each completed attempt a record shaped like { ok, exitIp, latencyMs }. Define ok in the collector according to your expected HTTP status and response checks. Do not redefine it as “the address stayed the same.” A successful request can have an invalid or missing IP measurement without becoming a transport failure.
Keep the records in observation order. Count IP transitions only between neighboring successful records that both contain valid addresses. A failure or missing address breaks continuity. Skipping that gap and comparing the surrounding records would imply knowledge you do not have.
This is a deliberately conservative measurement contract, not a requirement imposed by HTTP or SOCKS. It also describes sampled observations, not every packet or every unobserved moment between requests.
Normalize before comparing
A hostname is not an observed exit IP. Validate syntax with Node's net.isIP, then normalize supported IPv6 spellings with the WHATWG URL API:
import { isIP } from 'node:net';
function normalizedIp(value) {
if (typeof value !== 'string') return null;
const family = isIP(value);
if (family === 4) return value;
if (family !== 6) return null;
try {
return new URL(`http://[${value}]/`).hostname;
} catch {
return null;
}
}
The analyzer resets its previous address on a failed attempt or invalid measurement. It increments comparablePairs only when the current and previous observations can be compared. If there are no comparable pairs, changeFraction is null, not zero. Empty data must not masquerade as perfect stability.
Run a sequence with an intentional gap
After downloading the files from the linked revision, save this next to analyze-session.mjs or run the included session-fixture.mjs:
import { summarizeObservations } from './analyze-session.mjs';
console.log(summarizeObservations([
{ ok: true, exitIp: '192.0.2.10', latencyMs: 80 },
{ ok: true, exitIp: '192.0.2.10', latencyMs: 90 },
{ ok: false, latencyMs: 5000 },
{ ok: true, exitIp: '198.51.100.20', latencyMs: 110 },
{ ok: true, exitIp: '203.0.113.30', latencyMs: 120 },
{ ok: true, exitIp: '203.0.113.30', latencyMs: 130 },
]));
These addresses come from RFC 5737 documentation ranges, not working proxy endpoints. The result is six requests, five successes, one failure, three comparable pairs, and one observed change. The apparent change across the failed attempt is not counted. The successful-request latency median is 110 ms; the failed attempt's duration remains relevant to failure monitoring but is not part of that median.
The published analyzer accepts at most 10,000 observations, requires boolean ok values, ignores invalid latency values, and returns aggregates without raw IPs or credentials. It does not fetch targets, retry requests, or change connection settings.
Test the numerical and continuity edges
The revision includes 12 offline regression cases. They cover gaps, a single observation, missing addresses, equivalent IPv6 spellings, nonmutation, secret-free output, and latency validation. Use the built-in Node.js test runner:
node --test analyze-session.test.mjs
node session-fixture.mjs
One useful numerical edge is the median of two very large finite nonnegative values. Adding them first can overflow. With sorted validated latencies, calculate the even-length midpoint as:
const lower = latencies[middle - 1];
const upper = latencies[middle];
const median = lower + (upper - lower) / 2;
A regression test checks two Number.MAX_VALUE samples. The ordinary fixture alone would not expose that mistake.
Keep the interpretation narrow
An IP observation does not prove a city, carrier, residential classification, unique person, or persistent application session. Cookies, application tokens, and server policy are separate variables. Record the destination, interval, IP family, connection-reuse policy, and test window so results can be interpreted in context.
Use only proxies and endpoints you are authorized to test. Keep authenticated URLs, passwords, API tokens, and customer traffic out of fixtures and logs. Limit access and retention for real IP observations. Keep HTTP status, authentication errors, timeouts, and retry decisions in the collector's existing failure policy; this summary adds a continuity signal rather than replacing those controls.
Where an authorized QA plan requires a particular connection type, ProxySmart maintains its own commercial catalogues for mobile 4G connections and residential connections. Verify the particular package against the test plan; this provider-independent analyzer requires purchasing neither.
Disclosure: this article was prepared for ProxySmart with AI assistance. The companion code was checked with automated offline tests. No live performance benchmark or independent product endorsement is claimed.
Comments
Join the discussionNo comments yet. Be the first to comment.