krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

Packages/MoonriseKit/Tests/LibraryTests/LibraryTests.swift

6c4b28384d65e09486e0ad62f2ac9d10401cc80c
moonrise/Packages/MoonriseKit/Tests/LibraryTests/LibraryTests.swift history · blame · raw

121 lines · 5556 bytes

  1import Foundation
  2import SkyEngine
  3import Testing
  4@testable import Library
  5@testable import SkyRenderer
  6
  7// Checks every bundled page against the rules in docs/library-content.org.
  8
  9let allowedLicences: Set<String> = ["Public domain", "CC BY 4.0", "CC BY-SA 3.0 IGO"]
 10
 11@Test func idsAreUnique() {
 12    let ids = Library.pages.map(\.id)
 13    #expect(Set(ids).count == ids.count, "duplicates: \(Dictionary(grouping: ids, by: { $0 }).filter { $1.count > 1 }.keys.sorted())")
 14}
 15
 16@Test(arguments: Library.pages)
 17func pageFollowsTheBrief(_ p: LibraryPage) {
 18    #expect(!p.title.isEmpty && !p.subtitle.isEmpty, "\(p.id): title/subtitle")
 19    #expect((1...5).contains(p.sources.count), "\(p.id): \(p.sources.count) sources")
 20    #expect((3...9).contains(p.facts.count), "\(p.id): \(p.facts.count) facts")
 21    for f in p.facts {
 22        #expect(p.sources.indices.contains(f.source), "\(p.id): fact '\(f.label)' cites missing source \(f.source)")
 23    }
 24    #expect(!p.sources.contains { $0.url.host()?.contains("wikipedia.org") == true }, "\(p.id): Wikipedia as a source")
 25    #expect((120...300).contains(p.wordCount), "\(p.id): essay \(p.wordCount) words")
 26    if let image = p.image {
 27        #expect(allowedLicences.contains(image.licence), "\(p.id): licence \(image.licence)")
 28        #expect(image.url.scheme == "https", "\(p.id): image URL")
 29        #expect(!image.credit.isEmpty, "\(p.id): image credit")
 30    } else {
 31        #expect([.constellations, .stars, .missions, .rockets].contains(p.section), "\(p.id): missing image")
 32    }
 33}
 34
 35@Test(arguments: Library.pages)
 36func linksPointAtThingsTheAppKnows(_ p: LibraryPage) {
 37    let l = p.link
 38    if let body = l.body { #expect(Body(rawValue: body) != nil, "\(p.id): body \(body)") }
 39    if let c = l.constellation { #expect(constellationNames[c] != nil, "\(p.id): constellation \(c)") }
 40    if let hr = l.hr { #expect(Catalog.stars.contains { $0.hr == hr }, "\(p.id): HR \(hr)") }
 41    if let m = l.messier { #expect((1...110).contains(m), "\(p.id): M\(m)") }
 42    if let s = l.spacecraft { #expect(Spacecraft.all.contains { $0.name == s }, "\(p.id): spacecraft \(s)") }
 43}
 44
 45@Test func chartMessierObjectsOpenTheirPages() {
 46    for o in Catalog.deepSky {
 47        #expect(Library.page(id: o.page)?.link.messier == o.messier, "M\(o.messier): page \(o.page)")
 48    }
 49}
 50
 51@Test func searchFindsByTitleBeforeEssay() {
 52    guard let first = Library.pages.first else { return }
 53    #expect(Library.search(first.title).first?.id == first.id)
 54    #expect(Library.search("   ").isEmpty)
 55}
 56
 57@Test func scaleObjectsComeFromPages() {
 58    let objects = Library.scaleObjects
 59    #expect(objects.first?.name == "ISS")
 60    #expect(objects.last?.name == "Observable universe")
 61    #expect(objects.contains { $0.name == "Earth" && abs($0.meters - 12_742_000) < 1 })
 62    #expect(objects.contains { $0.page == "arcturus" })
 63    #expect(objects.contains { $0.page == "m31" })
 64    #expect(Set(objects.map(\.name)).count == objects.count)
 65    for o in objects where o.page != nil { #expect(Library.page(id: o.page!) != nil, "\(o.name)") }
 66}
 67
 68@Test func scaleSizeFormats() {
 69    #expect(Library.formatSize(109) == "109 m")
 70    #expect(Library.formatSize(12_742_000) == "12,742 km")
 71    #expect(Library.formatSize(2 * 30.07 * 1.495978707e11) == "60.1 AU")
 72    #expect(Library.formatSize(100_000 * 9.4607e15) == "100,000 light-years")
 73    #expect(Library.formatSize(93e9 * 9.4607e15) == "93 billion light-years")
 74}
 75
 76@Test func toursPointAtKnownThings() {
 77    #expect(Tour.all.count == 4)
 78    let constellations = Set(Catalog.constellations.map(\.id))
 79    let stars = Set(Catalog.stars.map(\.hr))
 80    for tour in Tour.all {
 81        #expect(!tour.steps.isEmpty)
 82        for step in tour.steps {
 83            let t = step.target
 84            #expect([t.constellation != nil, t.hr != nil, t.body != nil].filter { $0 }.count == 1, "\(tour.id) \(step.title)")
 85            if let c = t.constellation { #expect(constellations.contains(c), "\(c)") }
 86            if let hr = t.hr { #expect(stars.contains(hr), "\(hr)") }
 87            if let p = step.page { #expect(Library.page(id: p) != nil, "\(p)") }
 88        }
 89    }
 90    #expect(Set(Tour.all.flatMap(\.months)) == Set(1...12))
 91}
 92
 93@Test func toursStartWithTheSeason() {
 94    #expect(Tour.ordered(month: 1).first?.id == "winter")
 95    #expect(Tour.ordered(month: 7).first?.id == "summer")
 96    #expect(Tour.ordered(month: 11).map(\.id) == ["autumn", "winter", "spring", "summer"])
 97}
 98
 99@Test func storiesMatchSpacecraftAndPages() {
100    #expect(MissionStory.all.count == 5)
101    let craft = Set(Spacecraft.all.map(\.name))
102    for s in MissionStory.all {
103        #expect(craft.contains(s.spacecraft), "\(s.spacecraft)")
104        #expect(Library.page(id: s.page)?.link.spacecraft == s.spacecraft, "\(s.page)")
105        let days = s.chapters.map(\.day)
106        #expect(days == days.sorted(), "\(s.page) chapters out of order")
107        // After launch, every chapter falls within the trajectory data (which starts up to 30 days after launch).
108        let craftData = Spacecraft.all.first { $0.name == s.spacecraft }!
109        for c in s.chapters.dropFirst() { #expect(craftData.position(at: c.day) != nil, "\(s.page) \(c.date)") }
110    }
111}
112
113@Test func relatedPages() throws {
114    let saturn = try #require(Library.page(id: "saturn"))
115    let related = saturn.related().map(\.id)
116    #expect(related.contains("titan"))
117    #expect(related.contains("cassini-huygens"))
118    #expect(!related.contains("saturn"))
119    #expect(related.count <= 10)
120    #expect(Library.page(id: "europa")!.related().first?.id == "jupiter")
121}