mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
b3d69aa760
The corpus behind "is our own index good enough to replace Nominatim". When somebody searches and then picks a result, the client posts the query, the rank and the pick. Nothing else: no user, no trip, no session, and coordinates only to three decimals. Deliberately decoupled from the search endpoint. Correlating a response with a later pick would mean handing out a ticket on POST /api/maps/search and threading it through all fifteen call sites; the client already holds everything the row needs at the moment of the pick. Evaluation happens offline afterwards: export the corpus, run it against a candidate index, count how often the pick came back in the top five. That is why collection can start before such an index exists, which is the whole point, because the corpus needs weeks of real use and the index needs a server, and those two waits should not be serial. Off unless an admin switches it on, and it reads === 'true' rather than !== 'false'. The three older places switches fail open because a migration backfilled a row for installs already using them; nothing uses this one, so an instance that upgrades and never visits the admin panel logs nothing. Reading is admin only, retention is 180 days, and the switch state is on the unauthenticated app-config endpoint on purpose: whether an instance records what its users search for is something a visitor may know before logging in.
66 lines
2.8 KiB
TypeScript
66 lines
2.8 KiB
TypeScript
/**
|
|
* The shadow log's nightly retention provider: registration, the test gate, and
|
|
* that a failing purge cannot take the scheduler down with it.
|
|
*/
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
const logMock = vi.hoisted(() => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logError: vi.fn(), logWarn: vi.fn(), logDebug: vi.fn() }));
|
|
vi.mock('../../../src/nest/audit/audit-log.logger', () => logMock);
|
|
|
|
import { PlaceShadowRetentionJob } from '../../../src/nest/place-shadow/place-shadow.job';
|
|
import type { PlaceShadowService } from '../../../src/nest/place-shadow/place-shadow.service';
|
|
import type { CronRegistrarService } from '../../../src/nest/scheduling/cron-registrar.service';
|
|
|
|
function registrarStub(enabled = true) {
|
|
return { isEnabled: vi.fn(() => enabled), register: vi.fn(() => enabled), unregister: vi.fn() };
|
|
}
|
|
|
|
function makeJob(shadow: Partial<PlaceShadowService>, registrar = registrarStub()) {
|
|
return {
|
|
job: new PlaceShadowRetentionJob(shadow as PlaceShadowService, registrar as unknown as CronRegistrarService),
|
|
registrar,
|
|
};
|
|
}
|
|
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
describe('PlaceShadowRetentionJob', () => {
|
|
it('registers at 3:40, clear of the 3:00 idempotency purge', () => {
|
|
const { job, registrar } = makeJob({ purgeExpired: vi.fn(() => 0) });
|
|
job.onApplicationBootstrap();
|
|
expect(registrar.register).toHaveBeenCalledWith('place-shadow-retention', '40 3 * * *', expect.any(Function));
|
|
});
|
|
|
|
it('does not register under the test gate', () => {
|
|
const { job, registrar } = makeJob({ purgeExpired: vi.fn() }, registrarStub(false));
|
|
job.onApplicationBootstrap();
|
|
expect(registrar.register).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('logs only when it actually removed something', () => {
|
|
const { job } = makeJob({ purgeExpired: vi.fn(() => 0) });
|
|
job.tick();
|
|
expect(logMock.logInfo).not.toHaveBeenCalled();
|
|
|
|
const loud = makeJob({ purgeExpired: vi.fn(() => 12) });
|
|
loud.job.tick();
|
|
expect(logMock.logInfo).toHaveBeenCalledWith(expect.stringContaining('12'));
|
|
});
|
|
|
|
it('contains a failing purge instead of letting it escape into the scheduler', () => {
|
|
const { job } = makeJob({ purgeExpired: vi.fn(() => { throw new Error('database is locked'); }) });
|
|
expect(() => job.tick()).not.toThrow();
|
|
expect(logMock.logError).toHaveBeenCalledWith(expect.stringContaining('database is locked'));
|
|
});
|
|
|
|
it('runs the purge even while the log itself is switched off', () => {
|
|
// Switching collection off should let what it already gathered age out, so
|
|
// the job must not consult `enabled()` before purging.
|
|
const enabled = vi.fn(() => false);
|
|
const purgeExpired = vi.fn(() => 3);
|
|
makeJob({ enabled, purgeExpired }).job.tick();
|
|
expect(purgeExpired).toHaveBeenCalled();
|
|
expect(enabled).not.toHaveBeenCalled();
|
|
});
|
|
});
|