import 'dart:io'; import 'package:doormile_cx/data/map_config.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('tile provider', () { test('CARTO serves anonymously and takes a key when one is supplied', () { const anonymous = MapTileProvider.carto; expect(anonymous.usable, isTrue); expect(anonymous.hasKey, isFalse); expect(anonymous.urlFor(DmMapStyle.streets), isNot(contains('api_key'))); final keyed = anonymous.withKey('abc123'); expect(keyed.hasKey, isTrue); expect( keyed.urlFor(DmMapStyle.streets), endsWith('?api_key=abc123'), ); // Nothing else about the source changes when the key arrives. expect(keyed.attribution, anonymous.attribution); expect(keyed.subdomains, anonymous.subdomains); }); test('the two styles are distinct sources', () { expect( MapTileProvider.carto.urlFor(DmMapStyle.streets), isNot(MapTileProvider.carto.urlFor(DmMapStyle.muted)), ); }); test('a key-only provider reports itself unusable without one', () { expect(MapTileProvider.maptiler.usable, isFalse); expect(MapTileProvider.maptiler.withKey('k').usable, isTrue); expect( MapTileProvider.maptiler.withKey('k').urlFor(DmMapStyle.streets), endsWith('?key=k'), ); expect(MapTileProvider.stadia.usable, isFalse); }); test('every preset credits its data source', () { for (final provider in MapTileProvider.presets.values) { expect( provider.attribution, contains('OpenStreetMap'), reason: '${provider.id} must credit OpenStreetMap', ); expect(provider.attributionUrl, startsWith('https://')); expect(provider.templates[DmMapStyle.streets], isNotNull); } }); }); group('resolved configuration', () { test('defaults to OpenStreetMap and identifies the app to it', () { // ── The User-Agent is not decoration here ── // // OSM's tile usage policy asks a distributed app for a valid // identifying agent, and blocks traffic it cannot attribute. A block // looks like every tile turning into the ground colour at once, with no // other symptom — so this asserts the identification, not just the URL. final config = DmMapConfig.fromEnvironment(); expect(config.provider.id, 'osm'); expect(config.warning, isNull); expect(config.userAgent, contains('Doormile/')); expect(config.userAgent, contains(DmMapConfig.packageName)); expect(config.attribution, contains('OpenStreetMap')); }); test('is swappable without touching a screen', () { final original = DmMapConfig.instance; addTearDown(() => DmMapConfig.instance = original); DmMapConfig.instance = DmMapConfig( provider: MapTileProvider.osm, userAgent: 'test/1.0', ); expect(DmMapConfig.instance.attribution, '© OpenStreetMap contributors'); expect(DmMapConfig.instance.retina, isFalse); expect(DmMapConfig.instance.maxNativeZoom, 19); }); }); test('no tile URL, subdomain list or map key lives in the UI layer', () { // The whole point of the provider layer: swapping CARTO for a keyed plan or // another vendor must never mean editing a map screen. final offenders = []; for (final entry in Directory('lib/ui').listSync(recursive: true)) { if (entry is! File || !entry.path.endsWith('.dart')) continue; final source = entry.readAsStringSync(); if (source.contains('cartocdn') || source.contains('tile.openstreetmap.org') || source.contains('api.maptiler.com') || source.contains('tiles.stadiamaps.com') || source.contains('{z}/{x}/{y}')) { offenders.add(entry.path); } } expect(offenders, isEmpty, reason: 'tile URLs belong in map_config.dart'); }); }