krz/octosentry

macOS menu bar app to monitor GitHub security alerts

clone: git clone https://gitbay.org/krz/octosentry.git

main: octosentryTests/UpdateStoreTests.swift · raw

 1//
 2//  UpdateStoreTests.swift
 3//  octosentryTests
 4//
 5//  isNewer decides whether anyone ever hears about a release: too strict and
 6//  updates go unnoticed, too loose and every user is nagged to install what
 7//  they already have.
 8//
 9
10import Foundation
11import Testing
12@testable import octosentry
13
14// isNewer is a pure function but inherits the target's MainActor isolation.
15@MainActor
16struct UpdateStoreTests {
17
18    // MARK: - Ordering
19
20    @Test func aNewerPatchMinorOrMajorIsOffered() {
21        #expect(UpdateStore.isNewer("1.1.1", than: "1.1.0"))
22        #expect(UpdateStore.isNewer("1.2.0", than: "1.1.9"))
23        #expect(UpdateStore.isNewer("2.0.0", than: "1.9.9"))
24    }
25
26    @Test func theSameVersionIsNotOffered() {
27        #expect(!UpdateStore.isNewer("1.1.1", than: "1.1.1"))
28        #expect(!UpdateStore.isNewer("1.0.0", than: "1.0.0"))
29    }
30
31    @Test func anOlderReleaseIsNotOffered() {
32        #expect(!UpdateStore.isNewer("1.1.0", than: "1.1.1"))
33        #expect(!UpdateStore.isNewer("1.0.1", than: "1.1.0"))
34        #expect(!UpdateStore.isNewer("0.7.0", than: "1.0.0"))
35    }
36
37    // A local build ahead of the latest release must not prompt for the
38    // release it already contains.
39    @Test func aBundleAheadOfTheLatestReleaseIsNotPrompted() {
40        #expect(!UpdateStore.isNewer("1.1", than: "1.1.1"))
41    }
42
43    // MARK: - Numeric, not lexical
44
45    // The comparison that a string compare gets wrong: "1.10" sorts before
46    // "1.9" as text, and after it as versions.
47    @Test func componentsCompareNumericallyNotAsText() {
48        #expect(UpdateStore.isNewer("1.10.0", than: "1.9.0"))
49        #expect(!UpdateStore.isNewer("1.9.0", than: "1.10.0"))
50        #expect(UpdateStore.isNewer("1.1.10", than: "1.1.9"))
51    }
52
53    // MARK: - Shapes the tag names actually take
54
55    @Test func missingTrailingComponentsCountAsZero() {
56        #expect(!UpdateStore.isNewer("1.1", than: "1.1.0"))
57        #expect(!UpdateStore.isNewer("1.1.0", than: "1.1"))
58        #expect(UpdateStore.isNewer("1.1.1", than: "1.1"))
59    }
60
61    @Test func aLeadingVIsIgnored() {
62        #expect(UpdateStore.isNewer("v1.1.1", than: "1.1.0"))
63        #expect(!UpdateStore.isNewer("v1.1.1", than: "1.1.1"))
64        #expect(UpdateStore.isNewer("v2.0.0", than: "v1.9.9"))
65    }
66
67    // Rather than crash or throw on a tag that isn't a version.
68    @Test func unparseableComponentsCountAsZero() {
69        #expect(!UpdateStore.isNewer("nightly", than: "1.1.1"))
70        #expect(UpdateStore.isNewer("1.1.1", than: "nightly"))
71    }
72
73    // MARK: - This repo's real tags
74
75    @Test func everyShippedTagIsNewerThanTheOneBefore() {
76        let shipped = ["0.1.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0", "0.7.0", "1.0.0", "1.0.1", "1.1", "1.1.1"]
77
78        for (earlier, later) in zip(shipped, shipped.dropFirst()) {
79            #expect(UpdateStore.isNewer(later, than: earlier), "\(later) should be newer than \(earlier)")
80            #expect(!UpdateStore.isNewer(earlier, than: later), "\(earlier) should not be newer than \(later)")
81        }
82    }
83}