mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-06-09 07:43:59 +02:00
668ce16dc7
Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery, killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption keys and chain state during updates. New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers, CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets, desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing). Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami, @chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const, @Elhard1, @ttulttul
88 lines
3.8 KiB
TypeScript
88 lines
3.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { computeNightPolygon } from '@/utils/solarTerminator';
|
|
|
|
/** Extract polygon ring from result (type-narrowing helper) */
|
|
function getRing(result: GeoJSON.FeatureCollection): number[][] {
|
|
const geom = result.features[0].geometry;
|
|
if (geom.type !== 'Polygon') throw new Error('Expected Polygon geometry');
|
|
return geom.coordinates[0];
|
|
}
|
|
|
|
describe('computeNightPolygon', () => {
|
|
// ─── Structure validation ────────────────────────────────────────────────
|
|
|
|
it('returns a valid GeoJSON FeatureCollection', () => {
|
|
const result = computeNightPolygon();
|
|
expect(result.type).toBe('FeatureCollection');
|
|
expect(result.features).toHaveLength(1);
|
|
expect(result.features[0].type).toBe('Feature');
|
|
expect(result.features[0].geometry.type).toBe('Polygon');
|
|
});
|
|
|
|
it('polygon has at least 360 vertices (one per degree of longitude)', () => {
|
|
const ring = getRing(computeNightPolygon());
|
|
// 361 terminator points + 2 closing corners + 1 ring-close = ≥364
|
|
expect(ring.length).toBeGreaterThanOrEqual(364);
|
|
});
|
|
|
|
it('polygon ring is closed (first and last points match)', () => {
|
|
const ring = getRing(computeNightPolygon());
|
|
expect(ring[ring.length - 1]).toEqual(ring[0]);
|
|
});
|
|
|
|
// ─── Coordinate bounds ───────────────────────────────────────────────────
|
|
|
|
it('all coordinates are within valid lat/lng bounds', () => {
|
|
const ring = getRing(computeNightPolygon());
|
|
for (const [lng, lat] of ring) {
|
|
expect(lng).toBeGreaterThanOrEqual(-180);
|
|
expect(lng).toBeLessThanOrEqual(180);
|
|
expect(lat).toBeGreaterThanOrEqual(-85);
|
|
expect(lat).toBeLessThanOrEqual(85);
|
|
}
|
|
});
|
|
|
|
// ─── Deterministic for same input ────────────────────────────────────────
|
|
|
|
it('returns identical result for the same date', () => {
|
|
const date = new Date('2024-06-21T12:00:00Z');
|
|
const result1 = computeNightPolygon(date);
|
|
const result2 = computeNightPolygon(date);
|
|
expect(result1).toEqual(result2);
|
|
});
|
|
|
|
// ─── Seasonal behavior ──────────────────────────────────────────────────
|
|
|
|
it('equinox produces roughly symmetric polygon', () => {
|
|
const equinox = new Date('2024-03-20T12:00:00Z');
|
|
const ring = getRing(computeNightPolygon(equinox));
|
|
const lats = ring.map(([, lat]: number[]) => lat);
|
|
const maxLat = Math.max(...lats);
|
|
const minLat = Math.min(...lats);
|
|
expect(maxLat).toBeGreaterThan(50);
|
|
expect(minLat).toBeLessThan(-50);
|
|
});
|
|
|
|
it('summer solstice shifts night polygon southward', () => {
|
|
const summer = new Date('2024-06-21T00:00:00Z');
|
|
const ring = getRing(computeNightPolygon(summer));
|
|
const terminatorLats = ring
|
|
.filter(([lng]: number[]) => lng >= -180 && lng <= 180)
|
|
.slice(0, 361)
|
|
.map(([, lat]: number[]) => lat);
|
|
const avgLat =
|
|
terminatorLats.reduce((a: number, b: number) => a + b, 0) / terminatorLats.length;
|
|
expect(avgLat).toBeLessThan(15);
|
|
});
|
|
|
|
// ─── Different times produce different results ──────────────────────────
|
|
|
|
it('produces different polygons for different times of day', () => {
|
|
const morning = new Date('2024-06-21T06:00:00Z');
|
|
const evening = new Date('2024-06-21T18:00:00Z');
|
|
const ringM = getRing(computeNightPolygon(morning));
|
|
const ringE = getRing(computeNightPolygon(evening));
|
|
expect(ringM[0]).not.toEqual(ringE[0]);
|
|
});
|
|
});
|