krz/hutch

an ios client for sourcehut

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

main: HutchTests/PatchsetTests.swift · raw

  1import Foundation
  2import Testing
  3@testable import Hutch
  4
  5@MainActor
  6struct PatchsetStatusTests {
  7
  8    @Test
  9    func statusRawValuesMatchTheGraphQLEnum() {
 10        // lists.sr.ht's PatchsetStatus enum values, which are sent verbatim to
 11        // updatePatchset.
 12        #expect(PatchsetStatus.unknown.rawValue == "UNKNOWN")
 13        #expect(PatchsetStatus.proposed.rawValue == "PROPOSED")
 14        #expect(PatchsetStatus.needsRevision.rawValue == "NEEDS_REVISION")
 15        #expect(PatchsetStatus.superseded.rawValue == "SUPERSEDED")
 16        #expect(PatchsetStatus.approved.rawValue == "APPROVED")
 17        #expect(PatchsetStatus.rejected.rawValue == "REJECTED")
 18        #expect(PatchsetStatus.applied.rawValue == "APPLIED")
 19    }
 20
 21    @Test
 22    func assignableStatusesExcludeServerManagedOnes() {
 23        // UNKNOWN is a sentinel and SUPERSEDED is set by the server when a newer
 24        // version lands, so neither should be offered as a reviewer choice.
 25        #expect(!PatchsetStatus.assignable.contains(.unknown))
 26        #expect(!PatchsetStatus.assignable.contains(.superseded))
 27        #expect(PatchsetStatus.assignable.contains(.approved))
 28        #expect(PatchsetStatus.assignable.contains(.rejected))
 29        #expect(PatchsetStatus.assignable.contains(.applied))
 30        #expect(PatchsetStatus.assignable.contains(.needsRevision))
 31        #expect(PatchsetStatus.assignable.contains(.proposed))
 32    }
 33
 34    @Test
 35    func openStatusesAreThoseAwaitingADecision() {
 36        #expect(PatchsetStatus.proposed.isOpen)
 37        #expect(PatchsetStatus.needsRevision.isOpen)
 38        #expect(!PatchsetStatus.applied.isOpen)
 39        #expect(!PatchsetStatus.rejected.isOpen)
 40        #expect(!PatchsetStatus.superseded.isOpen)
 41    }
 42
 43    @Test
 44    func statusDecodesFromTheWireFormat() throws {
 45        let decoded = try JSONDecoder().decode(PatchsetStatus.self, from: Data("\"NEEDS_REVISION\"".utf8))
 46        #expect(decoded == .needsRevision)
 47    }
 48}
 49
 50@MainActor
 51struct PatchsetSummaryTests {
 52
 53    @Test
 54    func versionLabelIsHiddenForFirstVersion() {
 55        let summary = PatchsetSummary(
 56            id: 1,
 57            subject: "[PATCH] fix the thing",
 58            version: 1,
 59            prefix: nil,
 60            status: .proposed
 61        )
 62
 63        #expect(summary.versionLabel == nil)
 64    }
 65
 66    @Test
 67    func versionLabelIsShownForRevisions() {
 68        let summary = PatchsetSummary(
 69            id: 1,
 70            subject: "[PATCH v3] fix the thing",
 71            version: 3,
 72            prefix: nil,
 73            status: .proposed
 74        )
 75
 76        #expect(summary.versionLabel == "v3")
 77    }
 78}
 79
 80@MainActor
 81struct PatchsetOrderingTests {
 82
 83    private func makePatch(id: Int, index: Int?, count: Int?) -> PatchsetEmail {
 84        PatchsetEmail(
 85            id: id,
 86            subject: "patch \(id)",
 87            date: nil,
 88            sender: Entity(canonicalName: "~someone"),
 89            contentBlocks: [],
 90            index: index,
 91            count: count
 92        )
 93    }
 94
 95    @Test
 96    func patchesAreOrderedBySeriesIndexNotReceiptOrder() {
 97        let patches = [
 98            makePatch(id: 30, index: 3, count: 3),
 99            makePatch(id: 10, index: 1, count: 3),
100            makePatch(id: 20, index: 2, count: 3)
101        ]
102
103        let ordered = PatchsetDetailViewModel.orderPatches(patches)
104
105        #expect(ordered.map(\.index) == [1, 2, 3])
106    }
107
108    @Test
109    func unindexedPatchesAreKeptAtTheEndRatherThanDropped() {
110        let patches = [
111            makePatch(id: 99, index: nil, count: nil),
112            makePatch(id: 20, index: 2, count: 2),
113            makePatch(id: 10, index: 1, count: 2)
114        ]
115
116        let ordered = PatchsetDetailViewModel.orderPatches(patches)
117
118        #expect(ordered.count == 3)
119        #expect(ordered.map(\.index) == [1, 2, nil])
120    }
121
122    @Test
123    func orderingIsStableForASingleUnindexedPatch() {
124        // A lone patch with no [PATCH n/m] prefix is the common one-off case.
125        let patches = [makePatch(id: 1, index: nil, count: nil)]
126
127        let ordered = PatchsetDetailViewModel.orderPatches(patches)
128
129        #expect(ordered.map(\.id) == [1])
130    }
131
132    @Test
133    func seriesLabelIsHiddenForSinglePatchSeries() {
134        let patch = makePatch(id: 1, index: 1, count: 1)
135        #expect(patch.seriesLabel == nil)
136    }
137
138    @Test
139    func seriesLabelShowsPositionForMultiPatchSeries() {
140        let patch = makePatch(id: 1, index: 2, count: 5)
141        #expect(patch.seriesLabel == "2/5")
142    }
143}