Packages/MoonriseKit/Tests/LibraryTests/LibraryTests.swift
115 lines · 5365 bytes
10 symbols in this file
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 searchFindsByTitleBeforeEssay() {
46 guard let first = Library.pages.first else { return }
47 #expect(Library.search(first.title).first?.id == first.id)
48 #expect(Library.search(" ").isEmpty)
49}
50
51@Test func scaleObjectsComeFromPages() {
52 let objects = Library.scaleObjects
53 #expect(objects.first?.name == "ISS")
54 #expect(objects.last?.name == "Observable universe")
55 #expect(objects.contains { $0.name == "Earth" && abs($0.meters - 12_742_000) < 1 })
56 #expect(objects.contains { $0.page == "arcturus" })
57 #expect(objects.contains { $0.page == "m31" })
58 #expect(Set(objects.map(\.name)).count == objects.count)
59 for o in objects where o.page != nil { #expect(Library.page(id: o.page!) != nil, "\(o.name)") }
60}
61
62@Test func scaleSizeFormats() {
63 #expect(Library.formatSize(109) == "109 m")
64 #expect(Library.formatSize(12_742_000) == "12,742 km")
65 #expect(Library.formatSize(2 * 30.07 * 1.495978707e11) == "60.1 AU")
66 #expect(Library.formatSize(100_000 * 9.4607e15) == "100,000 light-years")
67 #expect(Library.formatSize(93e9 * 9.4607e15) == "93 billion light-years")
68}
69
70@Test func toursPointAtKnownThings() {
71 #expect(Tour.all.count == 4)
72 let constellations = Set(Catalog.constellations.map(\.id))
73 let stars = Set(Catalog.stars.map(\.hr))
74 for tour in Tour.all {
75 #expect(!tour.steps.isEmpty)
76 for step in tour.steps {
77 let t = step.target
78 #expect([t.constellation != nil, t.hr != nil, t.body != nil].filter { $0 }.count == 1, "\(tour.id) \(step.title)")
79 if let c = t.constellation { #expect(constellations.contains(c), "\(c)") }
80 if let hr = t.hr { #expect(stars.contains(hr), "\(hr)") }
81 if let p = step.page { #expect(Library.page(id: p) != nil, "\(p)") }
82 }
83 }
84 #expect(Set(Tour.all.flatMap(\.months)) == Set(1...12))
85}
86
87@Test func toursStartWithTheSeason() {
88 #expect(Tour.ordered(month: 1).first?.id == "winter")
89 #expect(Tour.ordered(month: 7).first?.id == "summer")
90 #expect(Tour.ordered(month: 11).map(\.id) == ["autumn", "winter", "spring", "summer"])
91}
92
93@Test func storiesMatchSpacecraftAndPages() {
94 #expect(MissionStory.all.count == 5)
95 let craft = Set(Spacecraft.all.map(\.name))
96 for s in MissionStory.all {
97 #expect(craft.contains(s.spacecraft), "\(s.spacecraft)")
98 #expect(Library.page(id: s.page)?.link.spacecraft == s.spacecraft, "\(s.page)")
99 let days = s.chapters.map(\.day)
100 #expect(days == days.sorted(), "\(s.page) chapters out of order")
101 // After launch, every chapter falls within the trajectory data (which starts up to 30 days after launch).
102 let craftData = Spacecraft.all.first { $0.name == s.spacecraft }!
103 for c in s.chapters.dropFirst() { #expect(craftData.position(at: c.day) != nil, "\(s.page) \(c.date)") }
104 }
105}
106
107@Test func relatedPages() throws {
108 let saturn = try #require(Library.page(id: "saturn"))
109 let related = saturn.related().map(\.id)
110 #expect(related.contains("titan"))
111 #expect(related.contains("cassini-huygens"))
112 #expect(!related.contains("saturn"))
113 #expect(related.count <= 10)
114 #expect(Library.page(id: "europa")!.related().first?.id == "jupiter")
115}