From 671c1e94138e56a4a2e0eef51fb9ea3ea0e14167 Mon Sep 17 00:00:00 2001 From: Jack Steam Date: Mon, 19 Oct 2026 18:18:03 -0500 Subject: [PATCH] test: own website numerical regressions in the engine --- .github/workflows/full-data.yml | 18 + docs/website-regressions.md | 35 + .../verify-fagan-bradley.ts | 37 + tests/fagan_bradley_legacy.rs | 103 ++ tests/fixtures/fagan-bradley-71350f0.json | 1143 +++++++++++++++++ tests/fixtures/true-sky-hd-chat-app.json | 1097 ++++++++++++++++ tests/true_sky_source.rs | 60 + 7 files changed, 2493 insertions(+) create mode 100644 docs/website-regressions.md create mode 100644 examples/configurable-chart/verify-fagan-bradley.ts create mode 100644 tests/fagan_bradley_legacy.rs create mode 100644 tests/fixtures/fagan-bradley-71350f0.json create mode 100644 tests/fixtures/true-sky-hd-chat-app.json create mode 100644 tests/true_sky_source.rs diff --git a/.github/workflows/full-data.yml b/.github/workflows/full-data.yml index c4571e0..ac78638 100644 --- a/.github/workflows/full-data.yml +++ b/.github/workflows/full-data.yml @@ -6,6 +6,10 @@ on: description: Authorized URL for the SHA-pinned production dataset required: false type: string + fagan_bradley_dataset_url: + description: Authorized URL for the historical website dataset (SHA 06d4c26e...) + required: false + type: string regenerate: description: Acquire official JPL inputs and regenerate twice required: true @@ -42,6 +46,20 @@ jobs: bun install --frozen-lockfile bun run build:wasm bun run verify "$RUNNER_TEMP/production.bin" "$RUNNER_TEMP/conventions-wasm.json" + cargo build --locked --release --manifest-path ../../Cargo.toml -p dataset-builder + bun prepare-segments.ts "$RUNNER_TEMP/production.bin" "$RUNNER_TEMP/segments" + bun verify-segments.ts "$RUNNER_TEMP/production.bin" "$RUNNER_TEMP/segments" + - if: inputs.fagan_bradley_dataset_url != '' + env: + DATASET_URL: ${{ inputs.fagan_bradley_dataset_url }} + run: | + curl --fail --location --proto '=https' "$DATASET_URL" --output "$RUNNER_TEMP/fagan-bradley.bin" + FAGAN_BRADLEY_DATASET="$RUNNER_TEMP/fagan-bradley.bin" cargo test --locked --test fagan_bradley_legacy -- --include-ignored + cargo install wasm-bindgen-cli --version 0.2.122 --locked + cd examples/configurable-chart + bun install --frozen-lockfile + bun run build:wasm + bun verify-fagan-bradley.ts "$RUNNER_TEMP/fagan-bradley.bin" - if: inputs.regenerate run: | python -m pip install --require-hashes -r tools/jpl/requirements.lock diff --git a/docs/website-regressions.md b/docs/website-regressions.md new file mode 100644 index 0000000..d60350c --- /dev/null +++ b/docs/website-regressions.md @@ -0,0 +1,35 @@ +# Regression ownership after the website migration + +The library owns numerical calculations, native/Wasm consistency and extraction +parity. Website CI owns its adapter presets and state, segment selection/loading, +course/UI code and packaged asset delivery. Website releases do not run this +repository's verification workflows. + +`tests/true_sky_source.rs` preserves the original True Sky source fixture from the +website. It runs with `cargo test --locked --workspace` without external data. + +The eight historical Fagan-Bradley charts are preserved byte-for-byte in +`tests/fixtures/fagan-bradley-71350f0.json`. They were captured from engine revision +`71350f0889c3b5e1585935d2017c97efe0bb5848`, before the chart API migration. +Their dataset SHA-256 is +`06d4c26e589207bf479dbd217fafb3e4d6c559626425b156912e39a4d859487b`. +This differs from the dataset used by `tests/fixtures/conventions.json`. +Use authorized local bytes or the explicit full-data workflow's separate +`fagan_bradley_dataset_url` input; no private website credential belongs here. + +```sh +FAGAN_BRADLEY_DATASET=/absolute/path/cheb.bin cargo test --locked --test fagan_bradley_legacy -- --include-ignored +cd examples/configurable-chart +bun install --frozen-lockfile +bun run build:wasm +bun verify-fagan-bradley.ts /absolute/path/cheb.bin +``` + +Both runtimes compare numbers within an absolute tolerance of 1e-12 and compare +all other captured values and ordering exactly. Tiny arithmetic rounding is +allowed; fixtures must never be regenerated to hide an implementation change. +Comparator negative controls reject changes above tolerance and reordered bodies. + +The explicit full-data workflow also prepares segments and runs +`verify-segments.ts` against its production dataset. Full-data checks remain +manual, separate from normal source CI and from independent astronomical accuracy. diff --git a/examples/configurable-chart/verify-fagan-bradley.ts b/examples/configurable-chart/verify-fagan-bradley.ts new file mode 100644 index 0000000..fb18041 --- /dev/null +++ b/examples/configurable-chart/verify-fagan-bradley.ts @@ -0,0 +1,37 @@ +// Copyright (c) Jack Asher +// SPDX-License-Identifier: MPL-2.0 +import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; +import { readFile } from 'node:fs/promises'; +import init, { calculate } from './engine/chart.js'; + +const hash = (bytes: Uint8Array) => createHash('sha256').update(bytes).digest('hex'); +const compare = (actual: unknown, expected: unknown): void => { + if (typeof actual === 'number' && typeof expected === 'number') { + assert.ok(Number.isFinite(actual) && Math.abs(actual - expected) <= 1e-12, `${actual} != ${expected}`); + } else if (actual !== null && expected !== null && typeof actual === 'object' && typeof expected === 'object') { + assert.equal(Array.isArray(actual), Array.isArray(expected)); + if (Array.isArray(actual) && Array.isArray(expected)) assert.equal(actual.length, expected.length); + assert.deepEqual(Object.keys(actual).sort(), Object.keys(expected).sort()); + for (const key of Object.keys(expected)) compare((actual as Record)[key], (expected as Record)[key]); + } else assert.equal(actual, expected); +}; +compare(29.288207831506444, 29.288207831506437); +assert.throws(() => compare(1, 1.00000001)); +assert.throws(() => compare({'0': 1}, [1])); +assert.throws(() => compare(['SUN', 'MOON'], ['MOON', 'SUN'])); +const path = process.argv[2]; +if (!path) throw new Error('Usage: bun verify-fagan-bradley.ts /path/to/historical-cheb.bin'); +const fixture = await readFile(new URL('../../tests/fixtures/fagan-bradley-71350f0.json', import.meta.url)); +assert.equal(hash(fixture), 'd70c88d0452efce0f0725a8baf8109992915b4221beed2ec1d75840bf3eb3c8a'); +const source = JSON.parse(fixture.toString()) as {engineRevision: string; datasetSha256: string; cases: {input: [string, number, number, string]; chart: unknown}[]}; +assert.equal(source.engineRevision, '71350f0889c3b5e1585935d2017c97efe0bb5848'); +assert.equal(source.cases.length, 8); +const bytes = await readFile(path); +assert.equal(hash(bytes), source.datasetSha256, 'Use the historical dataset, not the conventions dataset'); +await init({module_or_path: await readFile(new URL('./engine/chart_bg.wasm', import.meta.url))}); +for (const {input: [utc, latitude, longitude, house_system], chart: expected} of source.cases) { + const chart = JSON.parse(calculate(bytes, JSON.stringify({instant: {scale:'utc', value:utc}, latitude, longitude, house_system, configuration: {reference:'fagan-bradley', divisions:'equal', ophiuchus:null}}))) as {bodies: {name: string; longitude: number; speed: number; declination: number}[]; cusps: number[]; reference_offset_degrees: number; house_system: string}; + compare({bodies: chart.bodies.map(({name, longitude, speed, declination}) => ({name, longitude, speed, declination})), cusps: chart.cusps, ayanamsa_degrees: chart.reference_offset_degrees, house_system: chart.house_system}, expected); +} +console.log(JSON.stringify({runtime:'Wasm', cases:8, datasetSha256:source.datasetSha256, tolerance:1e-12})); diff --git a/tests/fagan_bradley_legacy.rs b/tests/fagan_bradley_legacy.rs new file mode 100644 index 0000000..e229b55 --- /dev/null +++ b/tests/fagan_bradley_legacy.rs @@ -0,0 +1,103 @@ +// Copyright (c) Jack Asher +// SPDX-License-Identifier: MPL-2.0 + +use astrology_engine::{calculate_chart, ChartInput, Ephemeris, Location, ZodiacConfiguration}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; + +fn compare(actual: &Value, expected: &Value) { + match (actual, expected) { + (Value::Number(a), Value::Number(b)) => { + let delta = (a.as_f64().unwrap() - b.as_f64().unwrap()).abs(); + assert!( + delta <= 1e-12, + "numeric regression: {a} != {b} (delta {delta})" + ); + } + (Value::Array(a), Value::Array(b)) => { + assert_eq!(a.len(), b.len()); + for (a, b) in a.iter().zip(b) { + compare(a, b); + } + } + (Value::Object(a), Value::Object(b)) => { + assert_eq!(a.keys().collect::>(), b.keys().collect::>()); + for (key, b) in b { + compare(&a[key], b); + } + } + _ => assert_eq!(actual, expected), + } +} + +#[test] +fn comparison_bounds_rounding_without_hiding_regressions() { + compare( + &json!({"cusps": [29.288207831506444]}), + &json!({"cusps": [29.288207831506437]}), + ); + assert!(std::panic::catch_unwind(|| compare(&json!(1.0), &json!(1.00000001))).is_err()); + assert!( + std::panic::catch_unwind(|| compare(&json!(["SUN", "MOON"]), &json!(["MOON", "SUN"]))) + .is_err() + ); +} + +#[test] +#[ignore = "requires FAGAN_BRADLEY_DATASET with the historical website dataset"] +fn historical_fagan_bradley_charts() { + let fixture = include_bytes!("fixtures/fagan-bradley-71350f0.json"); + assert_eq!( + format!("{:x}", Sha256::digest(fixture)), + "d70c88d0452efce0f0725a8baf8109992915b4221beed2ec1d75840bf3eb3c8a" + ); + let source: Value = serde_json::from_slice(fixture).unwrap(); + assert_eq!( + source["engineRevision"], + "71350f0889c3b5e1585935d2017c97efe0bb5848" + ); + let bytes = + std::fs::read(std::env::var("FAGAN_BRADLEY_DATASET").expect("set FAGAN_BRADLEY_DATASET")) + .unwrap(); + assert_eq!( + format!("{:x}", Sha256::digest(&bytes)), + source["datasetSha256"].as_str().unwrap() + ); + let ephemeris = Ephemeris::parse(bytes).unwrap(); + let cases = source["cases"].as_array().unwrap(); + assert_eq!(cases.len(), 8); + for case in cases { + let input = &case["input"]; + let chart = calculate_chart( + &ephemeris, + ChartInput { + epoch: input[0].as_str().unwrap().parse().unwrap(), + location: Location { + latitude: input[1].as_f64().unwrap(), + longitude: input[2].as_f64().unwrap(), + }, + house_system: serde_json::from_value(input[3].clone()).unwrap(), + zodiac: serde_json::from_value::( + json!({"reference":"fagan-bradley", "divisions":"equal", "ophiuchus":null}), + ) + .unwrap(), + }, + ) + .unwrap(); + let bodies: Vec<_> = chart + .bodies + .iter() + .map(|body| { + json!({ + "name": body.values.name, "longitude": body.values.longitude, + "speed": body.values.speed, "declination": body.values.declination, + }) + }) + .collect(); + compare( + &json!({"bodies": bodies, "cusps": chart.cusps, "ayanamsa_degrees": chart.reference_offset_degrees, "house_system": chart.house_system}), + &case["chart"], + ); + } + println!("Compared 8 historical Fagan-Bradley charts at 1e-12 degree tolerance"); +} diff --git a/tests/fixtures/fagan-bradley-71350f0.json b/tests/fixtures/fagan-bradley-71350f0.json new file mode 100644 index 0000000..9c30cf9 --- /dev/null +++ b/tests/fixtures/fagan-bradley-71350f0.json @@ -0,0 +1,1143 @@ +{ + "engineRevision": "71350f0889c3b5e1585935d2017c97efe0bb5848", + "websiteRevision": "6edcade", + "datasetSha256": "06d4c26e589207bf479dbd217fafb3e4d6c559626425b156912e39a4d859487b", + "cases": [ + { + "input": [ + "2000-01-01T12:00:00Z", + 51.5, + -0.12, + "equal" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 255.6324900954022, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "EARTH", + "longitude": 75.63249009540222, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "MOON", + "longitude": 198.58736838029816, + "speed": 12.023829257437939, + "declination": -10.90065177733138 + }, + { + "name": "NORTH_NODE", + "longitude": 99.21760928277554, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "SOUTH_NODE", + "longitude": 279.2176092827755, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "MERCURY", + "longitude": 247.15285601661702, + "speed": 1.5562218147991018, + "declination": -24.418906305364477 + }, + { + "name": "VENUS", + "longitude": 216.82936151702845, + "speed": 1.2090000618863144, + "declination": -18.448919961250713 + }, + { + "name": "MARS", + "longitude": 303.2268732694325, + "speed": 0.7756327582912093, + "declination": -13.182490120329785 + }, + { + "name": "JUPITER", + "longitude": 0.5166556444374066, + "speed": 0.04072084743327764, + "declination": 8.5942586052536 + }, + { + "name": "SATURN", + "longitude": 15.659232329594513, + "speed": -0.019984242525737272, + "declination": 12.614760263190702 + }, + { + "name": "URANUS", + "longitude": 290.0727578145429, + "speed": 0.050305979218052244, + "declination": -17.020323740553817 + }, + { + "name": "NEPTUNE", + "longitude": 278.4565812068426, + "speed": 0.035532538829471605, + "declination": -19.21323528314324 + }, + { + "name": "PLUTO", + "longitude": 226.7183473586659, + "speed": 0.03511581846910872, + "declination": -11.394288095177117 + }, + { + "name": "CHIRON", + "longitude": 226.88119300300923, + "speed": 0.1142659203881351, + "declination": -18.14098238004671 + }, + { + "name": "CERES", + "longitude": 159.7166472057482, + "speed": 0.21961874441925033, + "declination": 9.09010225321794 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 359.28820783150644, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 254.7639921118028, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 179.28820783150644, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 74.7639921118028, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 359.28820783150644, + 29.288207831506437, + 59.28820783150644, + 89.28820783150644, + 119.28820783150645, + 149.28820783150644, + 179.28820783150644, + 209.28820783150644, + 239.28820783150644, + 269.28820783150644, + 299.28820783150644, + 329.28820783150644 + ], + "ayanamsa_degrees": 24.736400313401692, + "house_system": "equal" + } + }, + { + "input": [ + "1990-06-15T03:45:00Z", + -33.86, + 151.21, + "whole-sign" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 59.190731772468695, + "speed": 0.9551099877830325, + "declination": 23.296965608653686 + }, + { + "name": "EARTH", + "longitude": 239.19073177246867, + "speed": 0.9551099877830325, + "declination": 23.296965608653686 + }, + { + "name": "MOON", + "longitude": 316.17524029489766, + "speed": 13.243377722356797, + "declination": -4.937640580934783 + }, + { + "name": "NORTH_NODE", + "longitude": 283.50595079376575, + "speed": 0.017377453378287555, + "declination": -18.239438986491088 + }, + { + "name": "SOUTH_NODE", + "longitude": 103.50595079376576, + "speed": 0.017377453378287555, + "declination": -18.239438986491088 + }, + { + "name": "MERCURY", + "longitude": 40.493059231763496, + "speed": 1.7001173851711542, + "declination": 19.455915855713393 + }, + { + "name": "VENUS", + "longitude": 23.762908765332263, + "speed": 1.1756452323499502, + "declination": 15.42293139679627 + }, + { + "name": "MARS", + "longitude": 346.18360698119034, + "speed": 0.7193048896062351, + "declination": 2.447711276296617 + }, + { + "name": "JUPITER", + "longitude": 81.20454494484498, + "speed": 0.21642121816199733, + "declination": 22.688746183065433 + }, + { + "name": "SATURN", + "longitude": 269.44152998418485, + "speed": -0.058308924742902946, + "declination": -21.186538857702708 + }, + { + "name": "URANUS", + "longitude": 253.56806730263867, + "speed": -0.03886633657322136, + "declination": -23.512679474122066 + }, + { + "name": "NEPTUNE", + "longitude": 259.1151554790776, + "speed": -0.02497844707721697, + "declination": -21.86277984838781 + }, + { + "name": "PLUTO", + "longitude": 200.797958786219, + "speed": -0.020331348500519653, + "declination": -1.2520730212065203 + }, + { + "name": "CHIRON", + "longitude": 81.41523969083813, + "speed": 0.10106120174822308, + "declination": 16.255113440266562 + }, + { + "name": "CERES", + "longitude": 93.1204960025074, + "speed": 0.42566949426856127, + "declination": 26.757096074438877 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 186.164910843906, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 84.44696548837148, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 6.164910843906014, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 264.44696548837146, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 180.0, + 210.0, + 240.0, + 270.0, + 300.0, + 330.0, + 0.0, + 30.0, + 60.0, + 90.0, + 120.0, + 150.0 + ], + "ayanamsa_degrees": 24.610504497388646, + "house_system": "whole-sign" + } + }, + { + "input": [ + "2000-01-01T12:00:00Z", + 51.5, + -0.12, + "placidus" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 255.6324900954022, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "EARTH", + "longitude": 75.63249009540222, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "MOON", + "longitude": 198.58736838029816, + "speed": 12.023829257437939, + "declination": -10.90065177733138 + }, + { + "name": "NORTH_NODE", + "longitude": 99.21760928277554, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "SOUTH_NODE", + "longitude": 279.2176092827755, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "MERCURY", + "longitude": 247.15285601661702, + "speed": 1.5562218147991018, + "declination": -24.418906305364477 + }, + { + "name": "VENUS", + "longitude": 216.82936151702845, + "speed": 1.2090000618863144, + "declination": -18.448919961250713 + }, + { + "name": "MARS", + "longitude": 303.2268732694325, + "speed": 0.7756327582912093, + "declination": -13.182490120329785 + }, + { + "name": "JUPITER", + "longitude": 0.5166556444374066, + "speed": 0.04072084743327764, + "declination": 8.5942586052536 + }, + { + "name": "SATURN", + "longitude": 15.659232329594513, + "speed": -0.019984242525737272, + "declination": 12.614760263190702 + }, + { + "name": "URANUS", + "longitude": 290.0727578145429, + "speed": 0.050305979218052244, + "declination": -17.020323740553817 + }, + { + "name": "NEPTUNE", + "longitude": 278.4565812068426, + "speed": 0.035532538829471605, + "declination": -19.21323528314324 + }, + { + "name": "PLUTO", + "longitude": 226.7183473586659, + "speed": 0.03511581846910872, + "declination": -11.394288095177117 + }, + { + "name": "CHIRON", + "longitude": 226.88119300300923, + "speed": 0.1142659203881351, + "declination": -18.14098238004671 + }, + { + "name": "CERES", + "longitude": 159.7166472057482, + "speed": 0.21961874441925033, + "declination": 9.09010225321794 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 359.28820783150644, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 254.7639921118028, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 179.28820783150644, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 74.7639921118028, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 359.28820783150644, + 36.2795305754438, + 57.17934200371593, + 74.7639921118028, + 94.18832410884029, + 122.765557033848, + 179.28820783150644, + 216.2795305754438, + 237.17934200371593, + 254.7639921118028, + 274.1883241088403, + 302.765557033848 + ], + "ayanamsa_degrees": 24.736400313401692, + "house_system": "placidus" + } + }, + { + "input": [ + "2000-01-01T12:00:00Z", + 80, + 90, + "placidus" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 255.6324900954022, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "EARTH", + "longitude": 75.63249009540222, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "MOON", + "longitude": 198.58736838029816, + "speed": 12.023829257437939, + "declination": -10.90065177733138 + }, + { + "name": "NORTH_NODE", + "longitude": 99.21760928277554, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "SOUTH_NODE", + "longitude": 279.2176092827755, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "MERCURY", + "longitude": 247.15285601661702, + "speed": 1.5562218147991018, + "declination": -24.418906305364477 + }, + { + "name": "VENUS", + "longitude": 216.82936151702845, + "speed": 1.2090000618863144, + "declination": -18.448919961250713 + }, + { + "name": "MARS", + "longitude": 303.2268732694325, + "speed": 0.7756327582912093, + "declination": -13.182490120329785 + }, + { + "name": "JUPITER", + "longitude": 0.5166556444374066, + "speed": 0.04072084743327764, + "declination": 8.5942586052536 + }, + { + "name": "SATURN", + "longitude": 15.659232329594513, + "speed": -0.019984242525737272, + "declination": 12.614760263190702 + }, + { + "name": "URANUS", + "longitude": 290.0727578145429, + "speed": 0.050305979218052244, + "declination": -17.020323740553817 + }, + { + "name": "NEPTUNE", + "longitude": 278.4565812068426, + "speed": 0.035532538829471605, + "declination": -19.21323528314324 + }, + { + "name": "PLUTO", + "longitude": 226.7183473586659, + "speed": 0.03511581846910872, + "declination": -11.394288095177117 + }, + { + "name": "CHIRON", + "longitude": 226.88119300300923, + "speed": 0.1142659203881351, + "declination": -18.14098238004671 + }, + { + "name": "CERES", + "longitude": 159.7166472057482, + "speed": 0.21961874441925033, + "declination": 9.09010225321794 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 133.16758130514813, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 346.63746934875593, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 313.16758130514813, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 166.63746934875593, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 133.16758130514813, + 144.3242106530174, + 155.48084000088667, + 166.63746934875593, + 215.48084000088667, + 264.3242106530174, + 313.16758130514813, + 324.3242106530174, + 335.48084000088664, + 346.63746934875593, + 35.48084000088669, + 84.32421065301743 + ], + "ayanamsa_degrees": 24.736400313401692, + "house_system": "porphyry" + } + }, + { + "input": [ + "2000-01-01T12:00:00Z", + 80, + 20, + "equal" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 255.6324900954022, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "EARTH", + "longitude": 75.63249009540222, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "MOON", + "longitude": 198.58736838029816, + "speed": 12.023829257437939, + "declination": -10.90065177733138 + }, + { + "name": "NORTH_NODE", + "longitude": 99.21760928277554, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "SOUTH_NODE", + "longitude": 279.2176092827755, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "MERCURY", + "longitude": 247.15285601661702, + "speed": 1.5562218147991018, + "declination": -24.418906305364477 + }, + { + "name": "VENUS", + "longitude": 216.82936151702845, + "speed": 1.2090000618863144, + "declination": -18.448919961250713 + }, + { + "name": "MARS", + "longitude": 303.2268732694325, + "speed": 0.7756327582912093, + "declination": -13.182490120329785 + }, + { + "name": "JUPITER", + "longitude": 0.5166556444374066, + "speed": 0.04072084743327764, + "declination": 8.5942586052536 + }, + { + "name": "SATURN", + "longitude": 15.659232329594513, + "speed": -0.019984242525737272, + "declination": 12.614760263190702 + }, + { + "name": "URANUS", + "longitude": 290.0727578145429, + "speed": 0.050305979218052244, + "declination": -17.020323740553817 + }, + { + "name": "NEPTUNE", + "longitude": 278.4565812068426, + "speed": 0.035532538829471605, + "declination": -19.21323528314324 + }, + { + "name": "PLUTO", + "longitude": 226.7183473586659, + "speed": 0.03511581846910872, + "declination": -11.394288095177117 + }, + { + "name": "CHIRON", + "longitude": 226.88119300300923, + "speed": 0.1142659203881351, + "declination": -18.14098238004671 + }, + { + "name": "CERES", + "longitude": 159.7166472057482, + "speed": 0.21961874441925033, + "declination": 9.09010225321794 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 136.17635657178334, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 273.6112914100689, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 316.17635657178334, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 93.61129141006889, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 136.17635657178334, + 166.17635657178334, + 196.17635657178334, + 226.17635657178334, + 256.17635657178334, + 286.17635657178334, + 316.17635657178334, + 346.17635657178334, + 16.176356571783355, + 46.17635657178336, + 76.17635657178336, + 106.17635657178336 + ], + "ayanamsa_degrees": 24.736400313401692, + "house_system": "equal" + } + }, + { + "input": [ + "2000-01-01T12:00:00Z", + 80, + 20, + "whole-sign" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 255.6324900954022, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "EARTH", + "longitude": 75.63249009540222, + "speed": 1.0193935909841514, + "declination": -23.03243258073263 + }, + { + "name": "MOON", + "longitude": 198.58736838029816, + "speed": 12.023829257437939, + "declination": -10.90065177733138 + }, + { + "name": "NORTH_NODE", + "longitude": 99.21760928277554, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "SOUTH_NODE", + "longitude": 279.2176092827755, + "speed": -0.054484991663542814, + "declination": 19.2644809627616 + }, + { + "name": "MERCURY", + "longitude": 247.15285601661702, + "speed": 1.5562218147991018, + "declination": -24.418906305364477 + }, + { + "name": "VENUS", + "longitude": 216.82936151702845, + "speed": 1.2090000618863144, + "declination": -18.448919961250713 + }, + { + "name": "MARS", + "longitude": 303.2268732694325, + "speed": 0.7756327582912093, + "declination": -13.182490120329785 + }, + { + "name": "JUPITER", + "longitude": 0.5166556444374066, + "speed": 0.04072084743327764, + "declination": 8.5942586052536 + }, + { + "name": "SATURN", + "longitude": 15.659232329594513, + "speed": -0.019984242525737272, + "declination": 12.614760263190702 + }, + { + "name": "URANUS", + "longitude": 290.0727578145429, + "speed": 0.050305979218052244, + "declination": -17.020323740553817 + }, + { + "name": "NEPTUNE", + "longitude": 278.4565812068426, + "speed": 0.035532538829471605, + "declination": -19.21323528314324 + }, + { + "name": "PLUTO", + "longitude": 226.7183473586659, + "speed": 0.03511581846910872, + "declination": -11.394288095177117 + }, + { + "name": "CHIRON", + "longitude": 226.88119300300923, + "speed": 0.1142659203881351, + "declination": -18.14098238004671 + }, + { + "name": "CERES", + "longitude": 159.7166472057482, + "speed": 0.21961874441925033, + "declination": 9.09010225321794 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 136.17635657178334, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 273.6112914100689, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 316.17635657178334, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 93.61129141006889, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 120.0, + 150.0, + 180.0, + 210.0, + 240.0, + 270.0, + 300.0, + 330.0, + 0.0, + 30.0, + 60.0, + 90.0 + ], + "ayanamsa_degrees": 24.736400313401692, + "house_system": "whole-sign" + } + }, + { + "input": [ + "1850-01-01T12:00:00Z", + 51.5, + 0, + "equal" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 258.15436088411934, + "speed": 1.018988389059988, + "declination": -23.017088258761113 + }, + { + "name": "EARTH", + "longitude": 78.15436088411934, + "speed": 1.018988389059988, + "declination": -23.017088258761113 + }, + { + "name": "MOON", + "longitude": 119.11643921609598, + "speed": 14.587032481172574, + "declination": 14.029083139273508 + }, + { + "name": "NORTH_NODE", + "longitude": 121.85519718325739, + "speed": -0.005681272474529209, + "declination": 13.365349433843784 + }, + { + "name": "SOUTH_NODE", + "longitude": 301.85519718325736, + "speed": -0.005681272474529209, + "declination": 13.365349433843784 + }, + { + "name": "MERCURY", + "longitude": 265.8891929792167, + "speed": 1.6377719606143195, + "declination": -24.279954236295584 + }, + { + "name": "VENUS", + "longitude": 243.5716863052989, + "speed": 1.2556803113020045, + "declination": -23.16490336952721 + }, + { + "name": "MARS", + "longitude": 58.639095253878025, + "speed": -0.29446942308828383, + "declination": 26.479977865662125 + }, + { + "name": "JUPITER", + "longitude": 150.37302182607314, + "speed": 0.02012418897101398, + "declination": 3.940790937083452 + }, + { + "name": "SATURN", + "longitude": 339.2472652886995, + "speed": 0.04947900034727937, + "declination": -1.4385467901601898 + }, + { + "name": "URANUS", + "longitude": 359.6834852827186, + "speed": 0.0006827692812443331, + "declination": 8.156949980703256 + }, + { + "name": "NEPTUNE", + "longitude": 310.0555754473465, + "speed": 0.026013402746176695, + "declination": -11.1851224218713 + }, + { + "name": "PLUTO", + "longitude": 4.269139919403674, + "speed": -0.0034592513746325437, + "declination": -5.51439273768439 + }, + { + "name": "CHIRON", + "longitude": 223.19302126069343, + "speed": 0.11218087626543038, + "declination": -17.72007591816441 + }, + { + "name": "CERES", + "longitude": 281.4784745125677, + "speed": 0.3828996566581431, + "declination": -25.447910379239666 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 2.38198164350856, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 257.2668671261577, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 182.38198164350857, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 77.2668671261577, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 2.38198164350856, + 32.38198164350855, + 62.38198164350855, + 92.38198164350855, + 122.38198164350857, + 152.38198164350857, + 182.38198164350857, + 212.38198164350857, + 242.38198164350854, + 272.38198164350854, + 302.38198164350854, + 332.38198164350854 + ], + "ayanamsa_degrees": 22.643204156921794, + "house_system": "equal" + } + }, + { + "input": [ + "2149-12-20T11:58:00Z", + 51.5, + 0, + "equal" + ], + "chart": { + "bodies": [ + { + "name": "SUN", + "longitude": 241.91385917227257, + "speed": 1.0176000513789667, + "declination": -23.412876225200307 + }, + { + "name": "EARTH", + "longitude": 61.91385917227257, + "speed": 1.0176000513789667, + "declination": -23.412876225200307 + }, + { + "name": "MOON", + "longitude": 136.17418475346466, + "speed": 13.0492948075638, + "declination": 10.862768297500592 + }, + { + "name": "NORTH_NODE", + "longitude": 76.92571839377226, + "speed": 0.014029773230166853, + "declination": 22.7089922767156 + }, + { + "name": "SOUTH_NODE", + "longitude": 256.92571839377223, + "speed": 0.014029773230166853, + "declination": 22.7089922767156 + }, + { + "name": "MERCURY", + "longitude": 221.24170280369586, + "speed": 0.5433879685916772, + "declination": -19.00851849723037 + }, + { + "name": "VENUS", + "longitude": 214.64545529379473, + "speed": -0.047354340555212815, + "declination": -17.55299969093807 + }, + { + "name": "MARS", + "longitude": 231.0148831591618, + "speed": 0.7274936148231781, + "declination": -23.250003140901125 + }, + { + "name": "JUPITER", + "longitude": 236.5671669200193, + "speed": 0.22653365833742, + "declination": -22.888054347735 + }, + { + "name": "SATURN", + "longitude": 57.044046073195524, + "speed": -0.0822491310535689, + "declination": 21.862189367993157 + }, + { + "name": "URANUS", + "longitude": 217.45952076323965, + "speed": 0.05773538762912622, + "declination": -20.83734233439444 + }, + { + "name": "NEPTUNE", + "longitude": 246.8338185854156, + "speed": 0.03767736680618228, + "declination": -22.284218188166538 + }, + { + "name": "PLUTO", + "longitude": 54.18432812762084, + "speed": -0.019242432048255154, + "declination": 13.88038759916828 + }, + { + "name": "CHIRON", + "longitude": 220.14148013852537, + "speed": 0.12303347914201268, + "declination": -17.949733638407125 + }, + { + "name": "CERES", + "longitude": 319.2086762666908, + "speed": 0.24976405318000516, + "declination": -14.735456126628508 + }, + { + "name": "ASCENDANT_SYMBOL", + "longitude": 330.27559528587165, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "MIDHEAVEN", + "longitude": 242.0586637432146, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "DESCENDANT", + "longitude": 150.2755952858716, + "speed": 0.0, + "declination": 0.0 + }, + { + "name": "IMUM_COELI", + "longitude": 62.0586637432146, + "speed": 0.0, + "declination": 0.0 + } + ], + "cusps": [ + 330.27559528587165, + 0.2755952858716526, + 30.275595285871653, + 60.27559528587165, + 90.27559528587165, + 120.27559528587165, + 150.2755952858716, + 180.2755952858716, + 210.2755952858716, + 240.2755952858716, + 270.2755952858716, + 300.2755952858716 + ], + "ayanamsa_degrees": 26.831210373350068, + "house_system": "equal" + } + } + ] +} diff --git a/tests/fixtures/true-sky-hd-chat-app.json b/tests/fixtures/true-sky-hd-chat-app.json new file mode 100644 index 0000000..16c2933 --- /dev/null +++ b/tests/fixtures/true-sky-hd-chat-app.json @@ -0,0 +1,1097 @@ +{ + "sourceRevision": "dd2e03d9faf256342c84380ccb6fac83c06771ac", + "sourceFiles": { + "packages/hd-core/src/chart/longitudeToSign.ts": "8187829a8645fdabeb8734ea85ebd839c7113a8dbec1aaff397199ed8a50d61e", + "packages/hd-core/src/chart/wholeSignCusps.ts": "09d21648944f40cd1b7fb047997c1ba8fabb47bad57c6a6870ba3cef7c50b194", + "packages/hd-core/src/constants/SIGNS.ts": "b94f575071a870d679acc7bf42b7fb1530459b0a1fc892939f8e219b7fa9283b", + "packages/hd-readings/src/internal/ayanamsa.ts": "07877955671ad70d293ff6111597226dff4a3982c3f7b6550a7ca477f77870d7" + }, + "normalization": "remainder = longitude % 360; add 360 only when remainder < 0; canonicalize signed zero before invoking source", + "sectors": [ + { + "sign": "Aries", + "start": 0 + }, + { + "sign": "Taurus", + "start": 19.7286 + }, + { + "sign": "Gemini", + "start": 56.5875 + }, + { + "sign": "Cancer", + "start": 86.0412 + }, + { + "sign": "Leo", + "start": 103.19 + }, + { + "sign": "Virgo", + "start": 141.6065 + }, + { + "sign": "Libra", + "start": 191.32 + }, + { + "sign": "Scorpio", + "start": 210.1972 + }, + { + "sign": "Ophiuchus", + "start": 223.4245 + }, + { + "sign": "Sagittarius", + "start": 235.7818 + }, + { + "sign": "Capricorn", + "start": 269.2677 + }, + { + "sign": "Aquarius", + "start": 294.8435 + }, + { + "sign": "Pisces", + "start": 318.0103 + } + ], + "positions": [ + { + "input": -0.000001, + "normalized": 359.999999, + "result": { + "sign": "Pisces", + "degreesInSign": 41.98969900000003 + } + }, + { + "input": 0, + "normalized": 0, + "result": { + "sign": "Aries", + "degreesInSign": 0 + } + }, + { + "input": 0.000001, + "normalized": 0.000001, + "result": { + "sign": "Aries", + "degreesInSign": 0.000001 + } + }, + { + "input": 19.728599, + "normalized": 19.728599, + "result": { + "sign": "Aries", + "degreesInSign": 19.728599 + } + }, + { + "input": 19.7286, + "normalized": 19.7286, + "result": { + "sign": "Taurus", + "degreesInSign": 0 + } + }, + { + "input": 19.728601, + "normalized": 19.728601, + "result": { + "sign": "Taurus", + "degreesInSign": 0.0000010000000010279564 + } + }, + { + "input": 56.587499, + "normalized": 56.587499, + "result": { + "sign": "Taurus", + "degreesInSign": 36.858899 + } + }, + { + "input": 56.5875, + "normalized": 56.5875, + "result": { + "sign": "Gemini", + "degreesInSign": 0 + } + }, + { + "input": 56.587500999999996, + "normalized": 56.587500999999996, + "result": { + "sign": "Gemini", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 86.041199, + "normalized": 86.041199, + "result": { + "sign": "Gemini", + "degreesInSign": 29.453699000000007 + } + }, + { + "input": 86.0412, + "normalized": 86.0412, + "result": { + "sign": "Cancer", + "degreesInSign": 0 + } + }, + { + "input": 86.041201, + "normalized": 86.041201, + "result": { + "sign": "Cancer", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 103.189999, + "normalized": 103.189999, + "result": { + "sign": "Cancer", + "degreesInSign": 17.148798999999997 + } + }, + { + "input": 103.19, + "normalized": 103.19, + "result": { + "sign": "Leo", + "degreesInSign": 0 + } + }, + { + "input": 103.190001, + "normalized": 103.190001, + "result": { + "sign": "Leo", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 141.606499, + "normalized": 141.606499, + "result": { + "sign": "Leo", + "degreesInSign": 38.416499000000016 + } + }, + { + "input": 141.6065, + "normalized": 141.6065, + "result": { + "sign": "Virgo", + "degreesInSign": 0 + } + }, + { + "input": 141.606501, + "normalized": 141.606501, + "result": { + "sign": "Virgo", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 191.319999, + "normalized": 191.319999, + "result": { + "sign": "Virgo", + "degreesInSign": 49.713498999999985 + } + }, + { + "input": 191.32, + "normalized": 191.32, + "result": { + "sign": "Libra", + "degreesInSign": 0 + } + }, + { + "input": 191.320001, + "normalized": 191.320001, + "result": { + "sign": "Libra", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 210.197199, + "normalized": 210.197199, + "result": { + "sign": "Libra", + "degreesInSign": 18.87719900000002 + } + }, + { + "input": 210.1972, + "normalized": 210.1972, + "result": { + "sign": "Scorpio", + "degreesInSign": 0 + } + }, + { + "input": 210.197201, + "normalized": 210.197201, + "result": { + "sign": "Scorpio", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 223.424499, + "normalized": 223.424499, + "result": { + "sign": "Scorpio", + "degreesInSign": 13.227298999999988 + } + }, + { + "input": 223.4245, + "normalized": 223.4245, + "result": { + "sign": "Ophiuchus", + "degreesInSign": 0 + } + }, + { + "input": 223.424501, + "normalized": 223.424501, + "result": { + "sign": "Ophiuchus", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 235.781799, + "normalized": 235.781799, + "result": { + "sign": "Ophiuchus", + "degreesInSign": 12.357299000000012 + } + }, + { + "input": 235.7818, + "normalized": 235.7818, + "result": { + "sign": "Sagittarius", + "degreesInSign": 0 + } + }, + { + "input": 235.781801, + "normalized": 235.781801, + "result": { + "sign": "Sagittarius", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 269.267699, + "normalized": 269.267699, + "result": { + "sign": "Sagittarius", + "degreesInSign": 33.48589899999999 + } + }, + { + "input": 269.2677, + "normalized": 269.2677, + "result": { + "sign": "Capricorn", + "degreesInSign": 0 + } + }, + { + "input": 269.267701, + "normalized": 269.267701, + "result": { + "sign": "Capricorn", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 294.843499, + "normalized": 294.843499, + "result": { + "sign": "Capricorn", + "degreesInSign": 25.575799000000018 + } + }, + { + "input": 294.8435, + "normalized": 294.8435, + "result": { + "sign": "Aquarius", + "degreesInSign": 0 + } + }, + { + "input": 294.843501, + "normalized": 294.843501, + "result": { + "sign": "Aquarius", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 318.010299, + "normalized": 318.010299, + "result": { + "sign": "Aquarius", + "degreesInSign": 23.16679899999997 + } + }, + { + "input": 318.0103, + "normalized": 318.0103, + "result": { + "sign": "Pisces", + "degreesInSign": 0 + } + }, + { + "input": 318.01030099999997, + "normalized": 318.01030099999997, + "result": { + "sign": "Pisces", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": -720, + "normalized": 0, + "result": { + "sign": "Aries", + "degreesInSign": 0 + } + }, + { + "input": -360, + "normalized": 0, + "result": { + "sign": "Aries", + "degreesInSign": 0 + } + }, + { + "input": -0.000001, + "normalized": 359.999999, + "result": { + "sign": "Pisces", + "degreesInSign": 41.98969900000003 + } + }, + { + "input": 360, + "normalized": 0, + "result": { + "sign": "Aries", + "degreesInSign": 0 + } + }, + { + "input": 360.000001, + "normalized": 9.999999974752427e-7, + "result": { + "sign": "Aries", + "degreesInSign": 9.999999974752427e-7 + } + }, + { + "input": 720, + "normalized": 0, + "result": { + "sign": "Aries", + "degreesInSign": 0 + } + }, + { + "input": 1080.5, + "normalized": 0.5, + "result": { + "sign": "Aries", + "degreesInSign": 0.5 + } + } + ], + "houses": [ + { + "ascendant": 210.1972, + "cusps": [ + { + "index": 1, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 220, + "cusps": [ + { + "index": 1, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 223.4245, + "cusps": [ + { + "index": 1, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 230, + "cusps": [ + { + "index": 1, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 235.781799, + "cusps": [ + { + "index": 1, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 235.7818, + "cusps": [ + { + "index": 1, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + } + ] + }, + { + "ascendant": 359.999999, + "cusps": [ + { + "index": 1, + "longitude": 318.0103, + "sign": "Pisces", + "degreesInSign": 0 + }, + { + "index": 2, + "longitude": 0, + "sign": "Aries", + "degreesInSign": 0 + }, + { + "index": 3, + "longitude": 19.7286, + "sign": "Taurus", + "degreesInSign": 0 + }, + { + "index": 4, + "longitude": 56.5875, + "sign": "Gemini", + "degreesInSign": 0 + }, + { + "index": 5, + "longitude": 86.0412, + "sign": "Cancer", + "degreesInSign": 0 + }, + { + "index": 6, + "longitude": 103.19, + "sign": "Leo", + "degreesInSign": 0 + }, + { + "index": 7, + "longitude": 141.6065, + "sign": "Virgo", + "degreesInSign": 0 + }, + { + "index": 8, + "longitude": 191.32, + "sign": "Libra", + "degreesInSign": 0 + }, + { + "index": 9, + "longitude": 210.1972, + "sign": "Scorpio", + "degreesInSign": 0 + }, + { + "index": 10, + "longitude": 235.7818, + "sign": "Sagittarius", + "degreesInSign": 0 + }, + { + "index": 11, + "longitude": 269.2677, + "sign": "Capricorn", + "degreesInSign": 0 + }, + { + "index": 12, + "longitude": 294.8435, + "sign": "Aquarius", + "degreesInSign": 0 + } + ] + } + ], + "projections": [ + { + "utc": "1850-01-01T12:00:00Z", + "longitude": -720, + "projected": 330.87984710328806 + }, + { + "utc": "1850-01-01T12:00:00Z", + "longitude": 0, + "projected": 330.879847103288 + }, + { + "utc": "1850-01-01T12:00:00Z", + "longitude": 31, + "projected": 1.8798471032880002 + }, + { + "utc": "1850-01-01T12:00:00Z", + "longitude": 180, + "projected": 150.879847103288 + }, + { + "utc": "1850-01-01T12:00:00Z", + "longitude": 359.999999, + "projected": 330.879846103288 + }, + { + "utc": "1850-01-01T12:00:00Z", + "longitude": 720, + "projected": 330.87984710328806 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": -720, + "projected": 328.78441912999995 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": 0, + "projected": 328.78441913 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": 31, + "projected": 359.78441913 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": 180, + "projected": 148.78441913 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": 359.999999, + "projected": 328.78441813 + }, + { + "utc": "2000-01-01T12:00:00Z", + "longitude": 720, + "projected": 328.78441912999995 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": -720, + "projected": 328.4101280888558 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": 0, + "projected": 328.4101280888558 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": 31, + "projected": 359.4101280888558 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": 180, + "projected": 148.41012808885577 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": 359.999999, + "projected": 328.4101270888558 + }, + { + "utc": "2026-10-17T12:34:56Z", + "longitude": 720, + "projected": 328.4101280888558 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": -720, + "projected": 326.68941193242154 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": 0, + "projected": 326.68941193242154 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": 31, + "projected": 357.68941193242154 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": 180, + "projected": 146.68941193242154 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": 359.999999, + "projected": 326.68941093242154 + }, + { + "utc": "2149-12-20T11:58:00Z", + "longitude": 720, + "projected": 326.68941193242154 + } + ] +} diff --git a/tests/true_sky_source.rs b/tests/true_sky_source.rs new file mode 100644 index 0000000..110e9a8 --- /dev/null +++ b/tests/true_sky_source.rs @@ -0,0 +1,60 @@ +//! Preserve the approved source capture moved from the website. +use astrology_engine::{ + assign_sign, true_sky_offset, whole_sign_cusps, zodiac_sectors, Epoch, Ophiuchus, + SignDivisions, ZodiacConfiguration, ZodiacReference, +}; +use serde_json::Value; +use std::str::FromStr; +#[test] +fn approved_source_constellation_and_projection_contract() { + let source: Value = + serde_json::from_str(include_str!("fixtures/true-sky-hd-chat-app.json")).unwrap(); + assert_eq!( + source["sourceRevision"], + "dd2e03d9faf256342c84380ccb6fac83c06771ac" + ); + let config = ZodiacConfiguration { + reference: ZodiacReference::TrueSky, + divisions: SignDivisions::Constellation, + ophiuchus: Some(Ophiuchus::Enabled), + }; + assert_eq!(source["positions"].as_array().unwrap().len(), 46); + assert_eq!(source["houses"].as_array().unwrap().len(), 7); + assert_eq!(source["projections"].as_array().unwrap().len(), 24); + let sectors = zodiac_sectors(config).unwrap(); + assert_eq!(sectors.len(), 13); + for (i, sector) in sectors.iter().enumerate() { + let expected = &source["sectors"][i]; + assert_eq!( + serde_json::to_value(sector.sign).unwrap().as_str().unwrap(), + expected["sign"].as_str().unwrap().to_lowercase() + ); + assert_eq!(sector.start_degrees, expected["start"].as_f64().unwrap()); + } + for case in source["positions"].as_array().unwrap() { + let actual = assign_sign(case["normalized"].as_f64().unwrap(), config).unwrap(); + assert_eq!( + serde_json::to_value(actual.sign).unwrap().as_str().unwrap(), + case["result"]["sign"].as_str().unwrap().to_lowercase() + ); + assert!( + (actual.degrees_in_sign - case["result"]["degreesInSign"].as_f64().unwrap()).abs() + < 1e-12 + ); + } + for case in source["houses"].as_array().unwrap() { + let actual = whole_sign_cusps(case["ascendant"].as_f64().unwrap(), config).unwrap(); + for (i, cusp) in case["cusps"].as_array().unwrap().iter().enumerate() { + assert_eq!(actual[i], cusp["longitude"].as_f64().unwrap()); + } + } + for case in source["projections"].as_array().unwrap() { + let epoch = Epoch::from_str(case["utc"].as_str().unwrap()).unwrap(); + let projected = (case["longitude"].as_f64().unwrap() - true_sky_offset(epoch).unwrap()) + .rem_euclid(360.0); + assert!( + (projected - case["projected"].as_f64().unwrap()).abs() < 1e-12, + "projection: {case}" + ); + } +}