import { describe, expect, it } from 'vitest' import { bboxesEqual, isMunicipalityAreaName, isSelectionBoundedDataset, normalizeBboxFromCorners, parseBboxInput, productCoversZones, resultMetricLabel, selectedAreaCoverageZones, selectionAnalysisScale, selectionAreaSquareMetres, selectionDimensions, splitSelectionBbox, } from './mapWorkspaceUtils' import type { VectorSelectionBBox, VectorSelectionResponse } from '../../types' describe('map workspace selection guards', () => { it('normalizes drag corners into an EPSG:4326 bbox', () => { expect(normalizeBboxFromCorners([5.2, 51.3], [4.8, 50.9])).toEqual({ min_x: 4.8, min_y: 50.9, max_x: 5.2, max_y: 51.3, crs: 'EPSG:4326', }) }) it('rejects empty, inverted and degenerate manual selections', () => { expect(parseBboxInput({ min_x: '', min_y: '', max_x: '', max_y: '' })).toBeNull() expect(parseBboxInput({ min_x: '5', min_y: '51', max_x: '4', max_y: '52' })).toBeNull() expect(parseBboxInput({ min_x: '4', min_y: '51', max_x: '4', max_y: '52' })).toBeNull() }) it('treats sub-nanodegree bbox drift as the same persisted selection', () => { const bbox = normalizeBboxFromCorners([4.98, 51.15], [5.02, 51.19]) expect(bboxesEqual(bbox, { ...bbox, max_x: bbox.max_x + 1e-10 })).toBe(true) expect(bboxesEqual(bbox, { ...bbox, max_x: bbox.max_x + 1e-5 })).toBe(false) }) it('computes a positive bounded selection area and renders governed metrics', () => { const bbox = normalizeBboxFromCorners([5.0, 51.0], [5.01, 51.01]) expect(selectionAreaSquareMetres(bbox)).toBeGreaterThan(700_000) expect(selectionAreaSquareMetres(bbox)).toBeLessThan(900_000) const result = { feature_count: 12, truncated: false, summary: { metric_key: 'forest_area', metric_value: 14.236, metric_unit: 'ha', }, } as unknown as VectorSelectionResponse expect(resultMetricLabel(result)).toBe('14,24 ha') }) it('maps national work areas to regional provider zones without merging sources', () => { expect(selectedAreaCoverageZones('Belgium land')).toEqual([ 'belgium', 'flanders', 'wallonia', 'brussels', ]) expect(selectedAreaCoverageZones('RC Ardennes inland')).toEqual(['wallonia']) expect(selectedAreaCoverageZones('Brussels-Capital Region')).toEqual(['brussels']) expect(selectedAreaCoverageZones('Language boundary')).toEqual(['flanders', 'wallonia']) expect(productCoversZones(['wallonia'], ['flanders', 'wallonia'])).toBe(true) expect(productCoversZones(['brussels'], ['wallonia'])).toBe(false) }) it('recognizes Dutch and release-golden municipality areas as local scope', () => { expect(isMunicipalityAreaName('Gemeente Mol')).toBe(true) expect(isMunicipalityAreaName('RC Golden - Mol municipality')).toBe(true) expect(isMunicipalityAreaName('Flanders')).toBe(false) expect(isMunicipalityAreaName('RC Golden - Kempen region')).toBe(false) }) it('distinguishes selection-bounded acquisitions from reusable regional datasets', () => { expect(isSelectionBoundedDataset({ geometry_clipped_to_selection: true, bbox_epsg4326: [4.55, 50.58, 4.56, 50.59], })).toBe(true) expect(isSelectionBoundedDataset({ coverage_scope: 'bounded_selection', bbox_epsg4326: [5.1, 51.17, 5.11, 51.18], })).toBe(true) expect(isSelectionBoundedDataset({ geometry_clipped_to_selection: false, bbox_epsg4326: [2.5, 49.5, 6.4, 51.6], })).toBe(false) expect(isSelectionBoundedDataset({ geometry_clipped_to_selection: true, })).toBe(false) }) it('classifies local, regional and overview selections before provider calls', () => { const bbox = (widthDegrees: number, heightDegrees: number): VectorSelectionBBox => ({ min_x: 5, min_y: 51, max_x: 5 + widthDegrees, max_y: 51 + heightDegrees, crs: 'EPSG:4326', }) expect(selectionAnalysisScale(bbox(0.1, 0.1))).toBe('detail') expect(selectionAnalysisScale(bbox(0.35, 0.25))).toBe('regional') expect(selectionAnalysisScale(bbox(1, 1))).toBe('overview') }) it('splits regional selections into provider-safe tiles without changing the outer bounds', () => { const selection: VectorSelectionBBox = { min_x: 5, min_y: 51, max_x: 5.5, max_y: 51.35, crs: 'EPSG:4326', } const tiles = splitSelectionBbox(selection) expect(tiles.length).toBeGreaterThan(1) expect(Math.min(...tiles.map((tile) => tile.min_x))).toBe(selection.min_x) expect(Math.min(...tiles.map((tile) => tile.min_y))).toBe(selection.min_y) expect(Math.max(...tiles.map((tile) => tile.max_x))).toBe(selection.max_x) expect(Math.max(...tiles.map((tile) => tile.max_y))).toBe(selection.max_y) for (const tile of tiles) { const dimensions = selectionDimensions(tile) expect(dimensions.widthMetres).toBeLessThanOrEqual(18_100) expect(dimensions.heightMetres).toBeLessThanOrEqual(18_100) } }) it('refuses an unbounded detail fan-out', () => { const selection: VectorSelectionBBox = { min_x: 4, min_y: 50, max_x: 6, max_y: 52, crs: 'EPSG:4326', } expect(() => splitSelectionBbox(selection)).toThrow('detailpartities') }) })