krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.16.0: HutchTests/TicketListViewModelTests.swift · raw
1import Foundation
2import Testing
3@testable import Hutch
4
5struct TicketListViewModelTests {
6
7 @Test
8 func filteredTicketsReturnsStatusFilteredTicketsWhenSearchTextIsEmpty() {
9 let tickets = [
10 makeTicket(id: 1, title: "Crash on launch", status: .reported, submitter: "~owner", labels: []),
11 makeTicket(id: 2, title: "Already fixed", status: .resolved, submitter: "~owner", labels: [])
12 ]
13
14 let filtered = filterTickets(tickets, filter: .open, query: "")
15
16 #expect(filtered.map(\.id) == [1])
17 }
18
19 @Test
20 func filteredTicketsMatchesTitleAndTicketId() {
21 let tickets = [
22 makeTicket(id: 42, title: "Crash on launch", status: .reported, submitter: "~owner", labels: []),
23 makeTicket(id: 99, title: "Settings polish", status: .reported, submitter: "~owner", labels: [])
24 ]
25
26 let titleMatches = filterTickets(tickets, filter: .all, query: "settings")
27 let idMatches = filterTickets(tickets, filter: .all, query: "42")
28
29 #expect(titleMatches.map(\.id) == [99])
30 #expect(idMatches.map(\.id) == [42])
31 }
32
33 @Test
34 func filteredTicketsMatchesSubmitterAndLabels() {
35 let tickets = [
36 makeTicket(id: 1, title: "Crash on launch", status: .reported, submitter: "~owner", labels: [makeLabel(id: 1, name: "bug")]),
37 makeTicket(id: 2, title: "Needs triage", status: .reported, submitter: "~triage", labels: [makeLabel(id: 2, name: "needs-info")])
38 ]
39
40 let submitterMatches = filterTickets(tickets, filter: .all, query: "~triage")
41 let labelMatches = filterTickets(tickets, filter: .all, query: "bug")
42
43 #expect(submitterMatches.map(\.id) == [2])
44 #expect(labelMatches.map(\.id) == [1])
45 }
46
47 @Test
48 @MainActor
49 func resolveTicketInputHasCorrectStatusAndDefaultResolution() {
50 let input: [String: any Sendable] = [
51 "status": TicketStatus.resolved.rawValue,
52 "resolution": TicketResolution.fixed.rawValue
53 ]
54 #expect(input["status"] as? String == "resolved")
55 #expect(input["resolution"] as? String == "fixed")
56 }
57
58 @Test
59 @MainActor
60 func reopenTicketInputOmitsResolution() {
61 let input: [String: any Sendable] = [
62 "status": TicketStatus.reported.rawValue
63 ]
64 #expect(input["status"] as? String == "reported")
65 #expect(input["resolution"] == nil)
66 }
67
68 private func filterTickets(_ tickets: [TicketSummary], filter: TicketFilter, query: String) -> [TicketSummary] {
69 let statusFiltered: [TicketSummary]
70 switch filter {
71 case .open:
72 statusFiltered = tickets.filter { $0.status.isOpen }
73 case .resolved:
74 statusFiltered = tickets.filter { !$0.status.isOpen }
75 case .all:
76 statusFiltered = tickets
77 }
78
79 let q = query.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
80 guard !q.isEmpty else { return statusFiltered }
81 return statusFiltered.filter {
82 String($0.id).contains(q) ||
83 $0.title.lowercased().contains(q) ||
84 $0.submitter.canonicalName.lowercased().contains(q) ||
85 $0.labels.contains { $0.name.lowercased().contains(q) }
86 }
87 }
88
89 private func makeTicket(
90 id: Int,
91 title: String,
92 status: TicketStatus,
93 submitter: String,
94 labels: [TicketLabel]
95 ) -> TicketSummary {
96 TicketSummary(
97 id: id,
98 title: title,
99 status: status,
100 resolution: status == .resolved ? .fixed : nil,
101 created: Date(),
102 submitter: Entity(canonicalName: submitter),
103 labels: labels,
104 assignees: []
105 )
106 }
107
108 private func makeLabel(id: Int, name: String) -> TicketLabel {
109 TicketLabel(
110 id: id,
111 name: name,
112 backgroundColor: "#000000",
113 foregroundColor: "#ffffff"
114 )
115 }
116}