Relax freshness limits and tolerate transient RCON failures
This commit is contained in:
@@ -12,6 +12,32 @@ function server(changes: Partial<ServerObservation> = {}): ServerObservation {
|
||||
}
|
||||
|
||||
describe('shared server observation', () => {
|
||||
it('tolerates only the first transient failure while retaining diagnostics and expiring evidence', () => {
|
||||
for (const reason of ['timeout', 'send-error']) {
|
||||
const raw = { ...server(), last_error: `${reason}:no reply`, rcon_failures: 1 };
|
||||
const selected = selectServerObservation(server({ rcon_status: raw }), undefined, now);
|
||||
expect(selected).toMatchObject({ health: 'online', last_error: null, rcon_status: raw });
|
||||
expect(selectServerObservation(selected, undefined, now + 90_000).health).toBe('online');
|
||||
expect(selectServerObservation(selected, undefined, now + 90_001).health).toBe('stale');
|
||||
expect(selectServerObservation(selected, undefined, now + 120_001).health).toBe('offline');
|
||||
expect(selectServerObservation(server({ rcon_status: { ...raw, rcon_failures: 2 } }), undefined, now))
|
||||
.toMatchObject({ health: 'stale', last_error: raw.last_error });
|
||||
}
|
||||
for (const reason of ['no-config', 'no-port', 'bad-reply', 'no-players']) {
|
||||
expect(selectServerObservation(server({ last_error: `${reason}:failed`, rcon_failures: 1 }), undefined, now).health).toBe('stale');
|
||||
}
|
||||
expect(selectServerObservation(server({ last_error: 'timeout:legacy failure' }), undefined, now).health).toBe('stale');
|
||||
});
|
||||
|
||||
it('accepts telemetry and container evidence through 90 seconds, then expires each independently', () => {
|
||||
expect(selectServerObservation(server(), report(), now + 90_000)).toMatchObject({ health: 'online', status_source: 'telemetry' });
|
||||
expect(selectServerObservation(server(), report(90_001), now + 90_001).health).toBe('stale');
|
||||
const current = server({ container_checked_at: at(90_001) });
|
||||
expect(selectServerObservation(current, report(), now + 90_001).health).toBe('stale');
|
||||
expect(freshTelemetry(report(-90_000), now)).toBe(true);
|
||||
expect(freshTelemetry(report(-90_001), now)).toBe(false);
|
||||
});
|
||||
|
||||
it('selects the telemetry count and roster together and keeps raw RCON evidence', () => {
|
||||
const raw = server({ last_error: 'timeout' });
|
||||
const selected = selectServerObservation(server({ rcon_status: raw }), report(), now);
|
||||
@@ -25,8 +51,8 @@ describe('shared server observation', () => {
|
||||
const original = server();
|
||||
const selected = selectServerObservation(server({ rcon_status: original, telemetry: report(0, 2) }), report(-1000), now);
|
||||
expect(selected.last_players).toBe(2);
|
||||
const fallback = selectServerObservation({ ...selected, container_checked_at: at(46_000),
|
||||
rcon_status: { ...original, last_seen_at: at(40_000) } }, undefined, now + 46_000);
|
||||
const fallback = selectServerObservation({ ...selected, container_checked_at: at(91_000),
|
||||
rcon_status: { ...original, last_seen_at: at(40_000) } }, undefined, now + 91_000);
|
||||
expect(fallback).toMatchObject({ status_source: 'rcon', health: 'online', last_players: 4, last_seen_at: at(40_000) });
|
||||
});
|
||||
|
||||
@@ -40,19 +66,19 @@ describe('shared server observation', () => {
|
||||
it('uses fresh RCON with unknown container state, but does not substitute telemetry or extend freshness', () => {
|
||||
const raw = server({ container_state: 'unknown', container_checked_at: at(-60_000) });
|
||||
expect(selectServerObservation(raw, report(), now)).toMatchObject({ health: 'online', last_players: 4, status_source: 'rcon' });
|
||||
expect(selectServerObservation(raw, report(46_000), now + 46_000).health).toBe('stale');
|
||||
expect(selectServerObservation(raw, report(91_000), now + 91_000).health).toBe('stale');
|
||||
expect(selectServerObservation(raw, report(121_000), now + 121_000).health).toBe('offline');
|
||||
expect(selectServerObservation({ ...raw, telemetry_after: at() }, report(), now).health).toBe('offline');
|
||||
expect(selectServerObservation({ ...raw, last_seen_at: null }, report(), now).health).toBe('offline');
|
||||
});
|
||||
|
||||
it('requires fresh container evidence and reports after the latest lifecycle submission', () => {
|
||||
expect(selectServerObservation(server({ container_checked_at: at(-46_000) }), report(), now).health).not.toBe('online');
|
||||
expect(selectServerObservation(server({ container_checked_at: at(-91_000) }), report(), now).health).not.toBe('online');
|
||||
expect(selectServerObservation(server({ health: 'provisioning' }), report(), now).health).toBe('provisioning');
|
||||
expect(selectServerObservation(server({ telemetry_after: at() }), report(-1), now).health).toBe('offline');
|
||||
expect(freshTelemetry(report(), now, 'invalid')).toBe(false);
|
||||
expect(freshTelemetry(report(2000), now)).toBe(false);
|
||||
expect(freshTelemetry(report(-46_000), now)).toBe(false);
|
||||
expect(freshTelemetry(report(-91_000), now)).toBe(false);
|
||||
expect(freshTelemetry({ ...report(), players: [{}] } as never, now)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user