mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Fire perimeters were placed at their average vertex, so a fire enclosing the whole requested area was missed. Polygons now count when a vertex is inside the area, an edge crosses it, or the polygon contains it outside any hole.
697 lines
19 KiB
JavaScript
697 lines
19 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
catalogForSurface,
|
|
composeCatalog,
|
|
coreTools,
|
|
defineTool,
|
|
ToolError,
|
|
} from '../index.js';
|
|
|
|
const conditions = {
|
|
observedAt: '2026-10-01T19:15:00.000Z',
|
|
temperatureC: 26.3,
|
|
apparentTemperatureC: 32,
|
|
precipitationMm: 0,
|
|
cloudCoverPct: 100,
|
|
windKph: 5.4,
|
|
windDirectionDeg: 318,
|
|
visibilityM: 15400,
|
|
weatherCode: 3,
|
|
};
|
|
const weather = {
|
|
calls: [],
|
|
async getConditions(latitude, longitude) {
|
|
this.calls.push([latitude, longitude]);
|
|
return { status: 'ready', weather: conditions };
|
|
},
|
|
};
|
|
const places = {
|
|
resolve: async (name) =>
|
|
name === 'Austin'
|
|
? {
|
|
name: 'Austin, Texas',
|
|
bounds: { west: -97.94, south: 30.1, east: -97.56, north: 30.52 },
|
|
}
|
|
: null,
|
|
};
|
|
const catalogWith = (services) =>
|
|
composeCatalog({ tools: coreTools, services });
|
|
|
|
test('weather describes current conditions at a point or place', async () => {
|
|
const catalog = catalogWith({ weather, places });
|
|
const result = await catalog.call('get_weather', {
|
|
location: { place: 'Austin' },
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'Weather at Austin, Texas: overcast, 26°C, wind 5 km/h.',
|
|
);
|
|
assert.deepEqual(result.data.weather, {
|
|
observed_at: '2026-10-01T19:15:00.000Z',
|
|
conditions: 'overcast',
|
|
weather_code: 3,
|
|
temperature_c: 26.3,
|
|
feels_like_c: 32,
|
|
precipitation_mm: 0,
|
|
cloud_cover_pct: 100,
|
|
wind_kph: 5.4,
|
|
wind_direction_deg: 318,
|
|
visibility_m: 15400,
|
|
});
|
|
const [latitude, longitude] = weather.calls.at(-1);
|
|
assert.ok(Math.abs(latitude - 30.31) < 1e-9 && longitude === -97.75);
|
|
});
|
|
|
|
test('the regional brief returns place, weather and at most ten headlines', async () => {
|
|
const regional = {
|
|
async getBrief() {
|
|
return {
|
|
place: {
|
|
label: 'Edwards Plateau',
|
|
locality: null,
|
|
region: 'Edwards Plateau',
|
|
country: null,
|
|
kind: 'natural',
|
|
},
|
|
weather: conditions,
|
|
newsSource: 'Google News RSS',
|
|
articles: Array.from({ length: 12 }, (_, index) => ({
|
|
title: `Story ${index}`,
|
|
url: `https://news.example/${index}`,
|
|
domain: 'news.example',
|
|
publishedAt: '2026-10-01T00:00:00Z',
|
|
})),
|
|
};
|
|
},
|
|
};
|
|
const result = await catalogWith({ regional }).call('get_regional_brief', {
|
|
location: { lat: 30.27, lon: -97.74 },
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'Edwards Plateau: overcast, 26°C, wind 5 km/h; 10 recent headlines.',
|
|
);
|
|
assert.equal(result.data.articles.length, 10);
|
|
assert.deepEqual(result.data.articles[0], {
|
|
title: 'Story 0',
|
|
url: 'https://news.example/0',
|
|
source: 'news.example',
|
|
published_at: '2026-10-01T00:00:00Z',
|
|
});
|
|
});
|
|
|
|
const storm = (id, name, latitude, longitude, windKt) => ({
|
|
id,
|
|
name,
|
|
classification: 'Hurricane',
|
|
basin: 'Atlantic',
|
|
position: { latitude, longitude },
|
|
positionAt: '2026-10-01T18:00:00.000Z',
|
|
advisoryNumber: '12',
|
|
issuedAt: '2026-10-01T15:00:00.000Z',
|
|
windKt,
|
|
pressureHpa: 980,
|
|
movement: { towardDeg: 300, speedKt: 10 },
|
|
advisoryUrl: 'https://www.nhc.noaa.gov/text/X.shtml',
|
|
});
|
|
|
|
test('cyclones are listed strongest first and can be limited to an area', async () => {
|
|
const cyclones = {
|
|
async getSnapshot() {
|
|
return {
|
|
source: 'NOAA NHC / CPHC',
|
|
coverage: 'Atlantic and eastern/central North Pacific',
|
|
stale: false,
|
|
unavailable: false,
|
|
storms: [
|
|
storm('a', 'Alpha', 25, -70, 65),
|
|
storm('b', 'Beta', 15, -110, 120),
|
|
],
|
|
};
|
|
},
|
|
};
|
|
const all = await catalogWith({ cyclones }).call('get_cyclones', {});
|
|
assert.equal(
|
|
all.summary,
|
|
'2 active tropical cyclones; strongest Beta at 120 kt.',
|
|
);
|
|
assert.deepEqual(
|
|
all.data.storms.map((row) => row.id),
|
|
['b', 'a'],
|
|
);
|
|
const atlantic = await catalogWith({ cyclones }).call('get_cyclones', {
|
|
area: { bbox: [-100, 0, -10, 50] },
|
|
});
|
|
assert.equal(
|
|
atlantic.summary,
|
|
'1 active tropical cyclone in the requested box; strongest Alpha at 65 kt.',
|
|
);
|
|
const down = { getSnapshot: async () => ({ unavailable: true, storms: [] }) };
|
|
await assert.rejects(
|
|
catalogWith({ cyclones: down }).call('get_cyclones', {}),
|
|
(error) => error.code === 'unavailable',
|
|
);
|
|
});
|
|
|
|
test('fire perimeters are placed by their outline and sorted by size', async () => {
|
|
const ring = (lon, lat) => [
|
|
[
|
|
[lon, lat],
|
|
[lon + 0.1, lat],
|
|
[lon + 0.1, lat + 0.1],
|
|
[lon, lat + 0.1],
|
|
],
|
|
];
|
|
const perimeters = {
|
|
async getSnapshot() {
|
|
return [
|
|
{
|
|
stableId: 'small',
|
|
name: 'Small Fire',
|
|
acres: 120.4,
|
|
containedPct: 80,
|
|
state: 'US-CA',
|
|
county: 'Kern',
|
|
category: 'WF',
|
|
cause: 'Human',
|
|
behavior: null,
|
|
personnel: 20,
|
|
discoveredTime: Date.UTC(2026, 8, 1),
|
|
updatedTime: null,
|
|
polygons: [ring(-118.5, 35.2)],
|
|
},
|
|
{
|
|
stableId: 'big',
|
|
name: 'Big Fire',
|
|
acres: 5000,
|
|
containedPct: 10,
|
|
polygons: [ring(-118.8, 35.4)],
|
|
},
|
|
{
|
|
stableId: 'far',
|
|
name: 'Far Fire',
|
|
acres: 9000,
|
|
polygons: [ring(-80, 30)],
|
|
},
|
|
{ stableId: 'empty', name: 'No Outline', acres: 1, polygons: [] },
|
|
];
|
|
},
|
|
};
|
|
const result = await catalogWith({ perimeters }).call('get_fire_perimeters', {
|
|
area: { bbox: [-120, 34, -117, 36] },
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'2 mapped wildfire perimeters in the requested box.',
|
|
);
|
|
assert.deepEqual(
|
|
result.data.rows.map((row) => row.id),
|
|
['big', 'small'],
|
|
);
|
|
assert.deepEqual(result.data.rows[1], {
|
|
id: 'small',
|
|
name: 'Small Fire',
|
|
acres: 120,
|
|
contained_pct: 80,
|
|
state: 'US-CA',
|
|
county: 'Kern',
|
|
category: 'WF',
|
|
cause: 'Human',
|
|
behavior: null,
|
|
personnel: 20,
|
|
discovered: '2026-09-01T00:00:00.000Z',
|
|
updated: null,
|
|
lat: 35.25,
|
|
lon: -118.45,
|
|
});
|
|
});
|
|
|
|
test('terrain heights are returned in request order', async () => {
|
|
const terrain = {
|
|
async getHeights(points) {
|
|
return points.map((point) => ({
|
|
...point,
|
|
elevation: point.lat * 100,
|
|
geoid: -30.04,
|
|
ellipsoid: point.lat * 100 - 30.04,
|
|
}));
|
|
},
|
|
};
|
|
const one = await catalogWith({ terrain }).call('get_terrain_height', {
|
|
points: [{ lat: 27.9881, lon: 86.925 }],
|
|
});
|
|
assert.equal(one.summary, 'Ground elevation is 2798.8 m at 27.9881, 86.925.');
|
|
const many = await catalogWith({ terrain }).call('get_terrain_height', {
|
|
points: [
|
|
{ lat: 1, lon: 1 },
|
|
{ lat: 2, lon: 2 },
|
|
],
|
|
});
|
|
assert.deepEqual(
|
|
many.data.points.map((point) => point.elevation_m),
|
|
[100, 200],
|
|
);
|
|
const short = { getHeights: async () => [] };
|
|
await assert.rejects(
|
|
catalogWith({ terrain: short }).call('get_terrain_height', {
|
|
points: [{ lat: 1, lon: 1 }],
|
|
}),
|
|
(error) => error.code === 'malformed',
|
|
);
|
|
await assert.rejects(
|
|
catalogWith({ terrain }).call('get_terrain_height', {
|
|
points: Array(21).fill({ lat: 0, lon: 0 }),
|
|
}),
|
|
/at most 20 items/,
|
|
);
|
|
});
|
|
|
|
test('military installations need a bounded area and are nearest first', async () => {
|
|
const requested = [];
|
|
const options = [];
|
|
let saturated = false;
|
|
const installations = {
|
|
async getMappedSites(box, { signal, ...rest } = {}) {
|
|
requested.push(box);
|
|
options.push(rest);
|
|
return {
|
|
saturated,
|
|
source: 'OpenStreetMap',
|
|
records: [
|
|
{
|
|
id: 'osm:1',
|
|
kind: 'installation',
|
|
class: 'base',
|
|
name: 'Naval Base Point Loma',
|
|
latitude: 32.6941,
|
|
longitude: -117.2494,
|
|
},
|
|
{
|
|
id: 'osm:2',
|
|
kind: 'installation',
|
|
class: 'airfield',
|
|
name: 'North Island',
|
|
latitude: 32.7,
|
|
longitude: -117.2,
|
|
},
|
|
],
|
|
};
|
|
},
|
|
};
|
|
const catalog = catalogWith({ installations });
|
|
const result = await catalog.call('find_military_installations', {
|
|
area: { lat: 32.7, lon: -117.2, radius_km: 20 },
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'2 mapped military installations in 20 km around 32.700, -117.200.',
|
|
);
|
|
assert.deepEqual(
|
|
result.data.rows.map((row) => row.id),
|
|
['osm:2', 'osm:1'],
|
|
);
|
|
assert.equal(result.data.source, 'OpenStreetMap');
|
|
assert.equal(result.data.complete, true);
|
|
assert.ok(requested[0].north - requested[0].south < 1);
|
|
assert.deepEqual(options[0], { exact: true, thinned: false });
|
|
saturated = true;
|
|
const partial = await catalog.call('find_military_installations', {
|
|
area: { lat: 32.7, lon: -117.2, radius_km: 20 },
|
|
});
|
|
assert.equal(partial.data.complete, false);
|
|
assert.match(
|
|
partial.summary,
|
|
/\(partial: the source returned only some sites\)\.$/,
|
|
);
|
|
await assert.rejects(
|
|
catalog.call('find_military_installations', {
|
|
area: { bbox: [-130, 20, -100, 50] },
|
|
}),
|
|
/at most 10° on each side/,
|
|
);
|
|
});
|
|
|
|
test('map features name what is at a location and report an unconfigured server', async () => {
|
|
const features = {
|
|
async getAdministrativeAreas() {
|
|
return [
|
|
{
|
|
id: 1,
|
|
category: 'administrative',
|
|
names: { primary: 'Texas' },
|
|
level: 4,
|
|
point: null,
|
|
},
|
|
{
|
|
id: 2,
|
|
category: 'administrative',
|
|
names: { primary: 'Travis County', english: 'Travis County' },
|
|
level: 6,
|
|
point: null,
|
|
},
|
|
{ id: 3, category: 'administrative', names: {}, level: 8, point: null },
|
|
];
|
|
},
|
|
getEnclosingAreas: async () => null,
|
|
getMonuments: async () => ({
|
|
unavailable: true,
|
|
code: 'OVERPASS_NOT_CONFIGURED',
|
|
}),
|
|
};
|
|
const catalog = catalogWith({ features });
|
|
const result = await catalog.call('get_map_features', {
|
|
location: { lat: 30.27, lon: -97.74 },
|
|
kind: 'administrative_areas',
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'2 named features (administrative areas) at 30.2700, -97.7400.',
|
|
);
|
|
assert.deepEqual(
|
|
result.data.rows.map((row) => [row.name, row.admin_level]),
|
|
[
|
|
['Texas', 4],
|
|
['Travis County', 6],
|
|
],
|
|
);
|
|
await assert.rejects(
|
|
catalog.call('get_map_features', {
|
|
location: { lat: 30.27, lon: -97.74 },
|
|
kind: 'monuments',
|
|
}),
|
|
(error) => error.code === 'unavailable' && /Overpass/.test(error.message),
|
|
);
|
|
await assert.rejects(
|
|
catalog.call('get_map_features', {
|
|
location: { lat: 30.27, lon: -97.74 },
|
|
kind: 'named_places',
|
|
}),
|
|
(error) => error.code === 'retry_later',
|
|
);
|
|
});
|
|
|
|
test('the situation brief combines available sections and marks failures', async () => {
|
|
const services = {
|
|
weather,
|
|
earthquakes: {
|
|
getSnapshot: async () => [
|
|
{
|
|
stableId: 'q',
|
|
usgsId: 'us1',
|
|
lat: 30.3,
|
|
lon: -97.7,
|
|
mag: 3.1,
|
|
place: 'near Austin',
|
|
time: 0,
|
|
depthKm: 5,
|
|
},
|
|
],
|
|
},
|
|
fires: { getSnapshot: async () => ({ keyRequired: true }) },
|
|
cyclones: {
|
|
getSnapshot: async () => {
|
|
throw new Error('upstream down');
|
|
},
|
|
},
|
|
places,
|
|
};
|
|
const result = await catalogWith(services).call('situation_brief', {
|
|
area: { place: 'Austin' },
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'Situation in Austin, Texas: Weather at Austin, Texas: overcast, 26°C, wind 5 km/h. ' +
|
|
'1 earthquake of M2.5+ in the last 24 hours in Austin, Texas; strongest M3.1 near Austin.',
|
|
);
|
|
assert.deepEqual(Object.keys(result.data.sections), [
|
|
'weather',
|
|
'earthquakes',
|
|
'fires',
|
|
'cyclones',
|
|
]);
|
|
assert.deepEqual(result.data.sections.fires, {
|
|
unavailable: true,
|
|
reason: 'Fire detections need a NASA FIRMS key configured for this server',
|
|
});
|
|
assert.deepEqual(result.data.sections.cyclones, {
|
|
unavailable: true,
|
|
reason: 'unavailable right now',
|
|
});
|
|
assert.equal(result.data.sections.aircraft, undefined);
|
|
});
|
|
|
|
test('the HUD caption summarizes the brief through the summary service', async () => {
|
|
const sent = [];
|
|
const summary = {
|
|
async summarize(context) {
|
|
sent.push(context);
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
data: { summary: 'Overcast skies over Austin', error: null },
|
|
};
|
|
},
|
|
};
|
|
const result = await catalogWith({ weather, summary, places }).call(
|
|
'get_hud_caption',
|
|
{ area: { place: 'Austin' } },
|
|
);
|
|
assert.equal(result.summary, 'Overcast skies over Austin');
|
|
assert.equal(sent[0].location, 'Austin, Texas');
|
|
assert.deepEqual(sent[0].placeLabels, ['Austin, Texas']);
|
|
assert.deepEqual(sent[0].enabledLayerLabels, ['Weather']);
|
|
assert.equal(sent[0].feedProvenance.overall, 'nominal');
|
|
assert.match(sent[0].observations[0], /^Weather at /);
|
|
const failing = {
|
|
summarize: async () => ({
|
|
ok: false,
|
|
status: 502,
|
|
data: { summary: null },
|
|
}),
|
|
};
|
|
await assert.rejects(
|
|
catalogWith({ weather, summary: failing, places }).call('get_hud_caption', {
|
|
area: { place: 'Austin' },
|
|
}),
|
|
(error) => error instanceof ToolError && error.code === 'unavailable',
|
|
);
|
|
});
|
|
|
|
test('military awareness gathers contacts around a point and marks failures', async () => {
|
|
const contact = (id, latitude, longitude) => ({
|
|
id,
|
|
reference: id,
|
|
latitude,
|
|
longitude,
|
|
callsign: 'RCH123',
|
|
baroAltitudeM: 9000,
|
|
onGround: false,
|
|
positionTimeMs: Date.UTC(2026, 0, 1),
|
|
});
|
|
const military = {
|
|
getSnapshot: async () => ({
|
|
records: [contact('ae1234', 32.8, -117.1), contact('ae9999', 40, -100)],
|
|
complete: true,
|
|
source: 'Military feed',
|
|
freshness: 'current',
|
|
}),
|
|
};
|
|
const aircraft = {
|
|
getSnapshot: async () => {
|
|
throw new Error('upstream down');
|
|
},
|
|
};
|
|
const installations = {
|
|
getMappedSites: async () => ({
|
|
source: 'OpenStreetMap',
|
|
records: [
|
|
{
|
|
id: 'osm:1',
|
|
kind: 'installation',
|
|
class: 'base',
|
|
name: 'Naval Base Point Loma',
|
|
latitude: 32.6941,
|
|
longitude: -117.2494,
|
|
},
|
|
],
|
|
}),
|
|
};
|
|
const catalog = catalogWith({ military, aircraft, installations });
|
|
const result = await catalog.call('military_awareness', {
|
|
location: { lat: 32.7, lon: -117.2 },
|
|
radius_km: 100,
|
|
});
|
|
assert.equal(
|
|
result.summary,
|
|
'Military awareness within 100 km of 32.7000, -117.2000: ' +
|
|
'1 military aircraft in 100 km around 32.700, -117.200. ' +
|
|
'1 mapped military installation in 100 km around 32.700, -117.200.',
|
|
);
|
|
assert.deepEqual(Object.keys(result.data.sections), [
|
|
'military_aircraft',
|
|
'aircraft',
|
|
'installations',
|
|
]);
|
|
assert.deepEqual(
|
|
result.data.sections.military_aircraft.data.rows.map((row) => row.id),
|
|
['ae1234'],
|
|
);
|
|
assert.deepEqual(result.data.sections.aircraft, {
|
|
unavailable: true,
|
|
reason: 'unavailable right now',
|
|
});
|
|
assert.equal(result.data.radius_km, 100);
|
|
const voice = catalogForSurface(catalog, 'voice');
|
|
assert.equal(voice.get('aircraft_in_area'), undefined);
|
|
assert.deepEqual(
|
|
Object.keys(
|
|
(
|
|
await voice.call('military_awareness', {
|
|
location: { lat: 32.7, lon: -117.2 },
|
|
radius_km: 100,
|
|
})
|
|
).data.sections,
|
|
),
|
|
['military_aircraft', 'aircraft', 'installations'],
|
|
);
|
|
assert.equal(
|
|
(await catalog.call('military_awareness', { location: { lat: 0, lon: 0 } }))
|
|
.data.radius_km,
|
|
250,
|
|
);
|
|
await assert.rejects(
|
|
catalog.call('military_awareness', {
|
|
location: { lat: 0, lon: 0 },
|
|
radius_km: 500,
|
|
}),
|
|
/radius_km/,
|
|
);
|
|
});
|
|
|
|
test('composites reach their sections through the catalog', async () => {
|
|
const replacement = defineTool({
|
|
name: 'get_weather',
|
|
title: 'Weather',
|
|
description: 'Replaced weather.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: { location: { type: 'object' } },
|
|
},
|
|
run: async () => ({ summary: 'Replaced weather.', data: {} }),
|
|
});
|
|
const seen = [];
|
|
const catalog = composeCatalog({
|
|
tools: [...coreTools, replacement],
|
|
replace: ['get_weather'],
|
|
services: { weather, places },
|
|
interceptors: [
|
|
(call, next) => {
|
|
seen.push([call.tool.name, call.parent ?? null]);
|
|
return next(call);
|
|
},
|
|
],
|
|
});
|
|
const result = await catalog.call('situation_brief', {
|
|
area: { place: 'Austin' },
|
|
});
|
|
assert.equal(result.data.sections.weather.summary, 'Replaced weather.');
|
|
assert.deepEqual(seen, [
|
|
['situation_brief', null],
|
|
['get_weather', 'situation_brief'],
|
|
]);
|
|
});
|
|
|
|
test('the HUD caption context carries each section feed state', async () => {
|
|
const sent = [];
|
|
const summary = {
|
|
async summarize(context) {
|
|
sent.push(context);
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
data: { summary: 'Austin aircraft STALE' },
|
|
};
|
|
},
|
|
};
|
|
const aircraft = {
|
|
getSnapshot: async () => ({
|
|
records: [],
|
|
complete: true,
|
|
source: 'Test feed',
|
|
freshness: 'stale',
|
|
}),
|
|
};
|
|
const cyclones = {
|
|
getSnapshot: async () => {
|
|
throw new Error('upstream down');
|
|
},
|
|
};
|
|
await catalogWith({ weather, summary, places, aircraft, cyclones }).call(
|
|
'get_hud_caption',
|
|
{ area: { place: 'Austin' } },
|
|
);
|
|
assert.deepEqual(
|
|
sent[0].enabledLayers.map((layer) => [layer.id, layer.feedState]),
|
|
[
|
|
['weather', 'nominal'],
|
|
['aircraft', 'stale'],
|
|
['cyclones', 'unavailable'],
|
|
],
|
|
);
|
|
assert.equal(sent[0].feedProvenance.overall, 'unavailable');
|
|
assert.match(sent[0].feedProvenance.note, /Aircraft is STALE/);
|
|
});
|
|
|
|
test('brief sections use a named place at its own point', async () => {
|
|
const tokyo = {
|
|
resolve: async () => ({
|
|
name: 'Tokyo, Japan',
|
|
point: { lat: 35.6769, lon: 139.7639 },
|
|
bounds: { west: 135, south: 20, east: 155, north: 36 },
|
|
}),
|
|
};
|
|
const queries = [];
|
|
const aircraft = {
|
|
getSnapshot: async (query) => {
|
|
queries.push(query);
|
|
return { records: [], complete: true, source: 'Test feed' };
|
|
},
|
|
};
|
|
const result = await catalogWith({ weather, places: tokyo, aircraft }).call(
|
|
'situation_brief',
|
|
{ area: { place: 'Tokyo' } },
|
|
);
|
|
assert.deepEqual(queries[0], { latitude: 35.6769, longitude: 139.7639 });
|
|
assert.match(result.data.sections.aircraft.summary, /in Tokyo, Japan\.$/);
|
|
});
|
|
|
|
test('a fire perimeter enclosing the whole area is found', async () => {
|
|
const perimeters = {
|
|
getSnapshot: async () => [
|
|
{
|
|
stableId: 'big',
|
|
name: 'Big Fire',
|
|
acres: 250000,
|
|
polygons: [
|
|
[
|
|
[
|
|
[-122, 38],
|
|
[-120, 38],
|
|
[-120, 40],
|
|
[-122, 40],
|
|
],
|
|
],
|
|
],
|
|
},
|
|
],
|
|
};
|
|
const result = await catalogWith({ perimeters }).call('get_fire_perimeters', {
|
|
area: { bbox: [-121.2, 38.9, -120.8, 39.1] },
|
|
});
|
|
assert.deepEqual(
|
|
result.data.rows.map((row) => row.id),
|
|
['big'],
|
|
);
|
|
});
|