1// S2: the system that asks the ground a question first.2//3// The vectors below are the Go library's own — the leaf under4// (49.703498679, 11.770681595) is 0x47a1cbd595522b39 and its level-10 parent5// is token 47a1cb — read through a declared equirectangular flattening. Nothing6// here re-derives spherical geometry; what these tests document is the bridge7// between s2js and world pixels, and the contract's own edges.89import assert from "node:assert/strict";10import test from "node:test";1112import { s2System } from "../cellsystems/s2.ts";13import { surfaceExtent } from "../cellsystems/ground.ts";14import { sphere, sphereWithoutMapping, square } from "./grounds.ts";1516const on = s2System.on(sphere);1718/** The library's own test point, in the sphere ground's world pixels. */19const known = [20 ((11.770681595 + 180) / 360) * 8192,21 -(((90 - 49.703498679) / 180) * 4096),22] as const;2324test("S2 divides only a ground that says what its picture is of", () => {25 assert.equal(s2System.appliesTo(sphere), true);26 assert.equal(s2System.appliesTo(square), false, "a plane declares no sphere");27 assert.equal(s2System.appliesTo(sphereWithoutMapping), false,28 "a sphere with no invertible flattening cannot be divided geodesically");29});3031test("tokens carry the library's vectors, one port level down", () => {32 assert.deepEqual(on.locate(known), { label: "S2", value: "47a1cb" });33 assert.equal(on.level("47a1cb"), 11, "port level = S2 level + 1");34 assert.equal(s2System.maxLevel(sphere), 11);35 assert.equal(s2System.inputLength(sphere), 6, "a level-10 token is six characters");36});3738test("ids do not refine by appending characters", () => {39 const children = on.children("47a1cb");40 assert.deepEqual(children, ["47a1ca4", "47a1cac", "47a1cb4", "47a1cbc"]);41 for (const child of children) {42 assert.equal(on.parent(child), "47a1cb", `${child} nests`);43 }44 assert.deepEqual(children.map((child) => on.childIndex(child)), [0, 1, 2, 3]);45 // The proof the contract holds nothing geohash-shaped: the first child's46 // token is not its parent's token plus a character.47 assert.ok(!children[0]?.startsWith("47a1cb"));48});4950test("the six faces are the root's children, in face order", () => {51 assert.deepEqual(on.children(""), ["1", "3", "5", "7", "9", "b"]);52 assert.deepEqual(on.children("").map((id) => on.childIndex(id)), [0, 1, 2, 3, 4, 5]);53 assert.equal(on.parent("1"), "", "a face's parent is the root");54});5556test("containment agrees with descent, all the way to the floor", () => {57 let id = "";58 for (let hop = 0; hop < 11; hop++) {59 id = on.descendTarget(id, known);60 assert.ok(id, `descent step ${hop} names a cell`);61 assert.equal(on.contains(id, known), true, `${id} holds its own point`);62 }63 assert.equal(id, "47a1cb", "eleven hops land on the level-10 cell");64 assert.equal(on.descendTarget(id, known), "", "the telescope has a floor");65});6667test("input parses whole or not at all", () => {68 assert.equal(on.parseInput(""), "");69 assert.equal(on.parseInput("47a1cb"), "47a1cb");70 assert.equal(on.parseInput("xyz"), null, "not hex, not a place");71 assert.equal(on.parseInput("47a1cbd595522b39"), "47a1cb",72 "a deeper token clamps to the floor, re-tokenized so the spelling is canonical");73 assert.equal(on.normalizeInput("47A1 CB!"), "47a1cb");74 assert.equal(on.normalizeInput("47a1cbd595522b39"), "47a1cb", "the field stops at six");75});7677test("rings close, stay continuous, and know the poles", () => {78 const ring = on.ring("47a1cb");79 assert.ok(ring.length > 8, "edges arrive tessellated");80 assert.deepEqual(ring[0], ring[ring.length - 1], "a cell away from a pole closes exactly");81 assert.equal(on.poleContained("5"), "north", "face 2 holds the north pole");82 assert.equal(on.poleContained("b"), "south", "face 5 holds the south pole");83 assert.equal(on.poleContained("47a1cb"), null);84 // A pole cell's walk accumulates a whole world of longitude, and its closure85 // joins in the frame the walk ended in — a world over from where it began.86 const polar = on.ring("5");87 const first = polar[0];88 const last = polar[polar.length - 1];89 assert.ok(first && last);90 assert.equal(last[1], first[1], "same row");91 assert.equal(Math.abs(last[0] - first[0]), 8192, "one whole world over");92});9394test("a ring may run off the picture rather than wrap", () => {95 const polar = on.ring("5");96 const [, , maximumX] = surfaceExtent(sphere);97 assert.ok(polar.some((point) => point[0] > maximumX),98 "continuity beats staying inside the frame; the adapters cut it");99});100101test("the root is the ground, in every method that can be handed it", () => {102 // The oracle answered these out of a token vocabulary that has no spelling103 // for the root, which produced numbers rather than errors. Nothing reaches104 // them — the root is never a plan cell — and the contract says "" is the105 // root of every system, so the clean lane keeps the contract instead.106 assert.equal(on.level(""), 0);107 assert.equal(on.parent(""), "");108 assert.equal(on.childIndex(""), 0);109 assert.equal(on.poleContained(""), null, "the whole sphere circles both poles; neither answer is honest");110 assert.deepEqual([...on.bbox("")], [...surfaceExtent(sphere)]);111 assert.deepEqual([...on.center("")], [4096, -2048]);112 assert.equal(on.contains("", known), true, "the root holds every point on the sphere");113 assert.equal(on.ring("").length, 5, "the root's ring is the picture's own rectangle");114});115116test("a ground S2 refuses is a ground S2 will not measure", () => {117 const refused = s2System.on(sphereWithoutMapping);118 assert.throws(() => refused.center("47a1cb"), /appliesTo/,119 "the failure names the question the caller should have asked");120 assert.equal(refused.contains("47a1cb", known), false, "with no mapping there is no leaf");121 assert.equal(refused.locate(known), null, "silence over plausibility");122});