Packages/MoonriseKit/Tests/LibraryTests/LibraryTests.swift
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}