Add a contract for place enrichment

This commit is contained in:
Maurice
2026-08-09 22:30:59 +02:00
committed by Maurice
parent c674df72a2
commit 4cd50e6ed3
2 changed files with 160 additions and 6 deletions
+95
View File
@@ -3,6 +3,10 @@ import {
mapsAutocompleteRequestSchema,
mapsReverseQuerySchema,
mapsResolveUrlRequestSchema,
mapsPlaceEnrichmentRequestSchema,
mapsPlaceEnrichmentResultSchema,
placePhotoCandidateSchema,
placeDescriptionSchema,
} from './maps.schema';
import { describe, it, expect } from 'vitest';
@@ -60,3 +64,94 @@ describe('mapsResolveUrlRequestSchema', () => {
expect(mapsResolveUrlRequestSchema.safeParse({ url: '' }).success).toBe(false);
});
});
describe('mapsPlaceEnrichmentRequestSchema', () => {
const base = { lat: 52.5, lng: 13.4, name: 'Museum Ludwig' };
it('needs coordinates and a name, but not a place id', () => {
expect(mapsPlaceEnrichmentRequestSchema.safeParse(base).success).toBe(true);
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, placeId: 'ChIJx' }).success).toBe(true);
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ lat: 52.5, lng: 13.4 }).success).toBe(false);
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, name: '' }).success).toBe(false);
});
it('rejects coordinates that arrived as strings or null', () => {
// The client reads these off a provider blob, where lat can be a string.
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, lat: '52.5' }).success).toBe(false);
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, lng: null }).success).toBe(false);
});
it('caps the free-text fields so a pasted blob cannot reach the providers', () => {
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, name: 'x'.repeat(301) }).success).toBe(false);
expect(mapsPlaceEnrichmentRequestSchema.safeParse({ ...base, placeId: 'x'.repeat(301) }).success).toBe(false);
});
});
describe('placePhotoCandidateSchema', () => {
const candidate = {
key: 'ChIJx~p0',
url: '/api/maps/place-photo/ChIJx~p0/bytes',
attribution: 'Jane Doe',
license: 'CC BY-SA 4.0',
licenseUrl: 'https://creativecommons.org/licenses/by-sa/4.0/',
sourceUrl: 'https://commons.wikimedia.org/wiki/File:X.jpg',
source: 'wikimedia',
};
it('accepts a fully attributed candidate', () => {
expect(placePhotoCandidateSchema.safeParse(candidate).success).toBe(true);
});
it('allows every licence field to be null, because Google supplies none of them', () => {
expect(
placePhotoCandidateSchema.safeParse({
...candidate,
source: 'google',
attribution: null,
license: null,
licenseUrl: null,
sourceUrl: null,
}).success,
).toBe(true);
});
it('requires the fields to be present even when null, so the column cannot silently skip attribution', () => {
const { attribution, ...withoutAttribution } = candidate;
void attribution;
expect(placePhotoCandidateSchema.safeParse(withoutAttribution).success).toBe(false);
});
it('rejects an unknown source', () => {
expect(placePhotoCandidateSchema.safeParse({ ...candidate, source: 'unsplash' }).success).toBe(false);
});
});
describe('placeDescriptionSchema', () => {
it('keeps the source alongside the text', () => {
expect(
placeDescriptionSchema.safeParse({
text: 'A museum in Cologne.',
source: 'wikipedia',
sourceUrl: 'https://de.wikipedia.org/wiki/Museum_Ludwig',
license: 'CC BY-SA 4.0',
}).success,
).toBe(true);
expect(placeDescriptionSchema.safeParse({ text: 'A museum.' }).success).toBe(false);
});
});
describe('mapsPlaceEnrichmentResultSchema', () => {
it('accepts an empty result, which is what a keyless instance with no Commons hit returns', () => {
expect(mapsPlaceEnrichmentResultSchema.safeParse({ photos: [], description: null }).success).toBe(true);
});
it('accepts the kill-switch response', () => {
expect(
mapsPlaceEnrichmentResultSchema.safeParse({ photos: [], description: null, disabled: true }).success,
).toBe(true);
});
it('requires description to be explicitly null rather than absent', () => {
expect(mapsPlaceEnrichmentResultSchema.safeParse({ photos: [] }).success).toBe(false);
});
});