krz/hutch

an ios client for sourcehut

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

v2.8.2: 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}