mirror of
https://github.com/acamarata/moon-sighting.git
synced 2026-06-30 19:04:24 +00:00
Fix critical bug: arcvMinimum polynomial constant was 7.1651 (wrong) instead of 11.8371 (Odeh 2006) in getMoonVisibilityEstimate. Now imports the canonical arcvMinimum() from visibility module. Deduplicate shared code across modules: - arcvMinimum polynomial: single source in visibility/index.ts - dot/norm vector helpers: use vdot/vnorm from math/index.ts - DEG constant: use DEG2RAD from math/index.ts - jdToJSDate: use jdToDate from time/index.ts Add input validation to all public API functions (lat/lon range, valid Date instances). Add ESLint + Prettier with TypeScript support. Convert tests to node:test runner. Fix package.json exports to use nested types-first format. Pin devDependencies to caret ranges. Add noImplicitReturns and noFallthroughCasesInSwitch to tsconfig. Replace .markdownlint.json with .vscode/settings.json. Update CI workflow with lint job. Expand .gitignore coverage.
147 lines
5.4 KiB
JavaScript
147 lines
5.4 KiB
JavaScript
'use strict'
|
|
|
|
/**
|
|
* moon-sighting CJS test suite
|
|
* Runs with: node --test test-cjs.cjs
|
|
* Verifies the CommonJS build is functional.
|
|
*/
|
|
|
|
const { describe, it } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const {
|
|
YALLOP_THRESHOLDS,
|
|
YALLOP_DESCRIPTIONS,
|
|
ODEH_THRESHOLDS,
|
|
ODEH_DESCRIPTIONS,
|
|
WGS84,
|
|
getMoonPhase,
|
|
getMoonPosition,
|
|
getMoonIllumination,
|
|
getMoonVisibilityEstimate,
|
|
getMoon,
|
|
initKernels,
|
|
downloadKernels,
|
|
verifyKernels,
|
|
getMoonSightingReport,
|
|
getSunMoonEvents,
|
|
} = require('./dist/index.cjs')
|
|
|
|
describe('CJS compatibility', () => {
|
|
it('require() works', () => {
|
|
assert.ok(YALLOP_THRESHOLDS !== undefined)
|
|
})
|
|
it('YALLOP_THRESHOLDS.A is 0.216', () => {
|
|
assert.equal(YALLOP_THRESHOLDS.A, 0.216)
|
|
})
|
|
it('ODEH_THRESHOLDS.A is 5.65', () => {
|
|
assert.equal(ODEH_THRESHOLDS.A, 5.65)
|
|
})
|
|
it('WGS84.a is 6378137.0', () => {
|
|
assert.equal(WGS84.a, 6378137.0)
|
|
})
|
|
it('All API functions are exported', () => {
|
|
assert.equal(typeof getMoonPhase, 'function')
|
|
assert.equal(typeof getMoonPosition, 'function')
|
|
assert.equal(typeof getMoonIllumination, 'function')
|
|
assert.equal(typeof getMoonVisibilityEstimate, 'function')
|
|
assert.equal(typeof getMoon, 'function')
|
|
assert.equal(typeof initKernels, 'function')
|
|
assert.equal(typeof downloadKernels, 'function')
|
|
assert.equal(typeof verifyKernels, 'function')
|
|
assert.equal(typeof getMoonSightingReport, 'function')
|
|
assert.equal(typeof getSunMoonEvents, 'function')
|
|
})
|
|
})
|
|
|
|
describe('CJS getMoonPhase', () => {
|
|
it('returns valid phase', () => {
|
|
const valid = new Set([
|
|
'new-moon', 'waxing-crescent', 'first-quarter', 'waxing-gibbous',
|
|
'full-moon', 'waning-gibbous', 'last-quarter', 'waning-crescent',
|
|
])
|
|
const p = getMoonPhase(new Date('2025-03-14T12:00:00Z'))
|
|
assert.ok(valid.has(p.phase), `got: ${p.phase}`)
|
|
})
|
|
it('illumination in [0, 100]', () => {
|
|
const p = getMoonPhase(new Date('2025-03-01T12:00:00Z'))
|
|
assert.ok(p.illumination >= 0 && p.illumination <= 100)
|
|
})
|
|
it('near full moon has high illumination', () => {
|
|
const p = getMoonPhase(new Date('2025-03-14T12:00:00Z'))
|
|
assert.ok(p.illumination > 85, `illumination=${p.illumination.toFixed(1)}%`)
|
|
})
|
|
it('Dates are Date objects', () => {
|
|
const p = getMoonPhase(new Date('2025-03-01T12:00:00Z'))
|
|
assert.ok(p.nextNewMoon instanceof Date)
|
|
assert.ok(p.prevNewMoon instanceof Date)
|
|
assert.ok(p.nextFullMoon instanceof Date)
|
|
})
|
|
})
|
|
|
|
describe('CJS getMoonPosition and getMoonIllumination', () => {
|
|
it('getMoonPosition returns valid azimuth/altitude', () => {
|
|
const pos = getMoonPosition(new Date('2025-03-14T20:00:00Z'), 51.5074, -0.1278, 10)
|
|
assert.ok(pos.azimuth >= 0 && pos.azimuth < 360, `azimuth=${pos.azimuth}`)
|
|
assert.ok(pos.altitude >= -90 && pos.altitude <= 90, `altitude=${pos.altitude}`)
|
|
assert.ok(pos.distance > 356000 && pos.distance < 407000, `distance=${pos.distance}`)
|
|
assert.ok(isFinite(pos.parallacticAngle))
|
|
})
|
|
it('getMoonIllumination near full moon: fraction > 0.85', () => {
|
|
const illum = getMoonIllumination(new Date('2025-03-14T12:00:00Z'))
|
|
assert.ok(illum.fraction > 0.85, `fraction=${illum.fraction.toFixed(3)}`)
|
|
assert.ok(illum.phase > 0.4 && illum.phase < 0.6, `phase=${illum.phase.toFixed(3)}`)
|
|
assert.ok(isFinite(illum.angle))
|
|
})
|
|
it('getMoonIllumination waxing: isWaxing = true', () => {
|
|
const illum = getMoonIllumination(new Date('2025-03-05T12:00:00Z'))
|
|
assert.equal(illum.isWaxing, true)
|
|
})
|
|
})
|
|
|
|
describe('CJS getMoonPhase phaseName/phaseSymbol', () => {
|
|
it('phaseName is a non-empty string', () => {
|
|
const p = getMoonPhase(new Date('2025-03-05T12:00:00Z'))
|
|
assert.ok(typeof p.phaseName === 'string' && p.phaseName.length > 0)
|
|
})
|
|
it('phaseSymbol is a moon emoji', () => {
|
|
const SYMBOLS = new Set(['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'])
|
|
const p = getMoonPhase(new Date('2025-03-05T12:00:00Z'))
|
|
assert.ok(SYMBOLS.has(p.phaseSymbol), `got: ${p.phaseSymbol}`)
|
|
})
|
|
it('Waxing crescent: correct phaseName and phaseSymbol', () => {
|
|
const p = getMoonPhase(new Date('2025-03-05T12:00:00Z'))
|
|
assert.equal(p.phaseName, 'Waxing Crescent')
|
|
assert.equal(p.phaseSymbol, '🌒')
|
|
})
|
|
})
|
|
|
|
describe('CJS getMoonVisibilityEstimate', () => {
|
|
it('returns valid zone', () => {
|
|
const v = getMoonVisibilityEstimate(new Date('2025-03-02T18:30:00Z'), 51.5074, -0.1278, 10)
|
|
assert.ok(['A', 'B', 'C', 'D'].includes(v.zone), `zone=${v.zone}`)
|
|
assert.ok(isFinite(v.V))
|
|
assert.equal(v.isApproximate, true)
|
|
})
|
|
it('near new moon: zone C or D', () => {
|
|
const v = getMoonVisibilityEstimate(new Date('2025-03-29T18:00:00Z'), 21.4225, 39.8262)
|
|
assert.ok(['C', 'D'].includes(v.zone), `zone=${v.zone}`)
|
|
})
|
|
})
|
|
|
|
describe('CJS getMoon', () => {
|
|
it('returns all four sub-results', () => {
|
|
const m = getMoon(new Date('2025-03-05T20:00:00Z'), 51.5074, -0.1278, 10)
|
|
assert.ok(typeof m.phase === 'object')
|
|
assert.ok(typeof m.position === 'object')
|
|
assert.ok(typeof m.illumination === 'object')
|
|
assert.ok(typeof m.visibility === 'object')
|
|
})
|
|
it('phase.phaseName is non-empty', () => {
|
|
const m = getMoon(new Date('2025-03-05T20:00:00Z'), 51.5074, -0.1278)
|
|
assert.ok(typeof m.phase.phaseName === 'string' && m.phase.phaseName.length > 0)
|
|
})
|
|
it('visibility.isApproximate is true', () => {
|
|
const m = getMoon(new Date(), 51.5074, -0.1278)
|
|
assert.equal(m.visibility.isApproximate, true)
|
|
})
|
|
})
|