mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +00:00
724c88c201
A mechanical prettier --write over server/src, server/tests, server/scripts and shared/src with the workspaces' .prettierrc; nothing but layout and import order changes, and every file keeps the same set of imports. shared is formatted throughout and its format baseline is empty. The server baseline keeps the 35 files that Prettier would grow past their lint:size limit (they need a split first, which is not a formatting change), and the generated entity files stay as their generator writes them.
88 lines
3.8 KiB
TypeScript
88 lines
3.8 KiB
TypeScript
/**
|
|
* MAPS-QUOTA-001..004 — the Google key reads as absent once the admin's daily
|
|
* ceiling is reached (#1582), so search and details fall back the way a keyless
|
|
* install does, and every call through the Places client is counted.
|
|
*/
|
|
import type { AppSettingsRepository } from '../../../src/db/repositories/AppSettings.repository';
|
|
import type { PlaceDetailsCacheRepository } from '../../../src/db/repositories/PlaceDetailsCache.repository';
|
|
import type { PlacesRepository } from '../../../src/db/repositories/Places.repository';
|
|
import type { UsersRepository } from '../../../src/db/repositories/Users.repository';
|
|
import type { GoogleQuotaService } from '../../../src/nest/google-quota/google-quota.service';
|
|
import { GooglePlacesClient } from '../../../src/nest/maps/providers/google-places.provider';
|
|
import type { PlacePhotoCacheService } from '../../../src/nest/place-photos/place-photo-cache.service';
|
|
import { GoogleTransitProvider } from '../../../src/nest/transit/google-transit.provider';
|
|
import { buildMapsService } from '../../helpers/maps-service';
|
|
|
|
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
|
|
|
|
vi.mock('../../../src/db/database', () => ({
|
|
db: { prepare: () => ({ get: () => undefined, run: () => undefined, all: () => [] }) },
|
|
}));
|
|
|
|
function quota(exhausted: boolean) {
|
|
return { exhausted: vi.fn(async () => exhausted), record: vi.fn(async () => {}) };
|
|
}
|
|
|
|
/** No instance or per-user key in the store, so the operator's env key is what resolves. */
|
|
const appSettingsStub = (settings: Record<string, string> = {}) =>
|
|
({ getValue: async (key: string) => settings[key] ?? null }) as unknown as AppSettingsRepository;
|
|
const usersStub = { getApiKeyColumn: async () => null } as unknown as UsersRepository;
|
|
|
|
const svcWith = (q: ReturnType<typeof quota>) =>
|
|
buildMapsService(
|
|
{} as PlacePhotoCacheService,
|
|
appSettingsStub(),
|
|
usersStub,
|
|
{} as PlaceDetailsCacheRepository,
|
|
{} as PlacesRepository,
|
|
q as unknown as GoogleQuotaService,
|
|
);
|
|
|
|
beforeEach(() => {
|
|
process.env.PLACES_API_KEY = 'operator-key';
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env.PLACES_API_KEY;
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('Google daily ceiling in MapsService', () => {
|
|
it('MAPS-QUOTA-001: below the ceiling the key is spent as before', async () => {
|
|
const svc = svcWith(quota(false));
|
|
expect(await svc.getMapsKey(1)).toBe('operator-key');
|
|
expect(await svc.keyedProvider(1)).toMatchObject({ id: 'google', key: 'operator-key' });
|
|
});
|
|
|
|
it('MAPS-QUOTA-002: past the ceiling there is no key and no keyed Google provider', async () => {
|
|
const svc = svcWith(quota(true));
|
|
expect(await svc.getMapsKey(1)).toBeNull();
|
|
expect(await svc.keyedProvider(1)).toBeNull();
|
|
// The settings view still learns that a key exists.
|
|
expect((await svc.resolveMapsKey(1)).key).toBe('operator-key');
|
|
});
|
|
|
|
it('MAPS-QUOTA-003: every Places call is counted against the day', async () => {
|
|
const q = quota(false);
|
|
const google = new GooglePlacesClient(q as unknown as GoogleQuotaService);
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => new Response(JSON.stringify({ editorialSummary: { text: 'Hi' } }), { status: 200 })),
|
|
);
|
|
await google.fetchEditorialSummary('ChIJ1', 'key');
|
|
expect(q.record).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('Google daily ceiling in the transit provider', () => {
|
|
it('MAPS-QUOTA-004: past the ceiling the Google transit backend is inactive', async () => {
|
|
const settings = appSettingsStub({ transit_provider: 'google' });
|
|
expect(
|
|
await new GoogleTransitProvider(settings, usersStub, quota(false) as unknown as GoogleQuotaService).isActive(1),
|
|
).toBe(true);
|
|
expect(
|
|
await new GoogleTransitProvider(settings, usersStub, quota(true) as unknown as GoogleQuotaService).isActive(1),
|
|
).toBe(false);
|
|
});
|
|
});
|