From 0fe7f8f8c1abcbe8587969f2796fffe5f6ec7277 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Tue, 12 Nov 2024 13:28:35 +0100 Subject: [PATCH 1/8] fetch meal rss feed --- Package.resolved | 9 ++++ Package.swift | 3 +- Sources/EmealKit/Mensa/Meal.swift | 2 + Sources/EmealKit/Mensa/MealFeed.swift | 59 +++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Sources/EmealKit/Mensa/MealFeed.swift diff --git a/Package.resolved b/Package.resolved index 8c95649..61f809e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "FeedKit", + "repositoryURL": "https://github.com/nmdias/FeedKit.git", + "state": { + "branch": null, + "revision": "68493a33d862c33c9a9f67ec729b3b7df1b20ade", + "version": "9.1.2" + } + }, { "package": "HTMLString", "repositoryURL": "https://github.com/alexaubry/HTMLString.git", diff --git a/Package.swift b/Package.swift index 999cf69..de0c845 100644 --- a/Package.swift +++ b/Package.swift @@ -18,11 +18,12 @@ let package = Package( dependencies: [ .package(url: "https://github.com/alexaubry/HTMLString.git", from: "5.0.0"), .package(url: "https://github.com/sharplet/Regex.git", from: "2.1.0"), + .package(url: "https://github.com/nmdias/FeedKit.git", from: "9.1.2"), ], targets: [ .target( name: "EmealKit", - dependencies: ["HTMLString", "Regex"]), + dependencies: ["HTMLString", "Regex", "FeedKit"]), .testTarget( name: "EmealKitTests", dependencies: ["EmealKit"]), diff --git a/Sources/EmealKit/Mensa/Meal.swift b/Sources/EmealKit/Mensa/Meal.swift index 3b19b63..0642978 100644 --- a/Sources/EmealKit/Mensa/Meal.swift +++ b/Sources/EmealKit/Mensa/Meal.swift @@ -11,6 +11,8 @@ public struct Meal: Identifiable, Equatable, Decodable { public var image: URL public var url: URL + public var isSoldOut: Bool? + private enum CodingKeys: String, CodingKey { case id case name diff --git a/Sources/EmealKit/Mensa/MealFeed.swift b/Sources/EmealKit/Mensa/MealFeed.swift new file mode 100644 index 0000000..776a644 --- /dev/null +++ b/Sources/EmealKit/Mensa/MealFeed.swift @@ -0,0 +1,59 @@ +import Foundation +import FeedKit +import os.log + +extension Meal { + public struct RSSMeal { + public let title: String + public let description: String + public let guid: String + public let link: String + public let author: String + + func matches(meal: Meal) -> Bool { + return link.contains(String(meal.id)) + } + } + + public static func rssData(session: URLSession = .shared) async throws -> [RSSMeal] { + let feedURL = URL(string: "https://www.studentenwerk-dresden.de/feeds/speiseplan.rss")! + let parser = FeedParser(URL: feedURL) + return try await withCheckedThrowingContinuation { continuation in + parser.parseAsync { result in + switch result { + case .success(let feed): + guard (feed.rssFeed?.title?.contains("von heute") ?? false) else { + Logger.emealKit.error("Wrong feed?") + continuation.resume(returning: []) + return + } + guard let items = feed.rssFeed?.items else { + Logger.emealKit.error("No feed items found") + continuation.resume(returning: []) + return + } + let meals = items.compactMap { item -> RSSMeal? in + guard let title = item.title, + let description = item.description, + let guid = item.guid?.value, + let link = item.link, + let author = item.author + else { + return nil + } + return RSSMeal( + title: title, + description: description, + guid: guid, + link: link, + author: author + ) + } + continuation.resume(returning: meals) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } +} From acafcbf9ddbb12b16b685355e13df62805e3b2a4 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Wed, 13 Nov 2024 23:09:54 +0100 Subject: [PATCH 2/8] refactor networking code to only use async APIs --- Sources/EmealKit/Mensa/MensaAPI.swift | 144 ++++---------------------- 1 file changed, 21 insertions(+), 123 deletions(-) diff --git a/Sources/EmealKit/Mensa/MensaAPI.swift b/Sources/EmealKit/Mensa/MensaAPI.swift index 24d8182..f6fe503 100644 --- a/Sources/EmealKit/Mensa/MensaAPI.swift +++ b/Sources/EmealKit/Mensa/MensaAPI.swift @@ -1,10 +1,6 @@ import Foundation import os.log -#if canImport(Combine) -import Combine -#endif - internal extension URL { enum Mensa { static let baseUrl = URL(string: "https://api.studentenwerk-dresden.de/openmensa/v2/")! @@ -15,138 +11,40 @@ internal extension URL { } } -extension URLSession { - fileprivate func emealDataTask(with url: URL, completion: @escaping (Result) -> Void) { - let task = self.dataTask(with: url) { data, response, error in - guard - let data = data, - error == nil - else { - if let urlError = error { - completion(.failure(.other(urlError))) - return - } - completion(.failure(.unknown)) - return - } - completion(.success(data)) - } - task.resume() - } -} - // MARK: - Canteens extension Canteen { - public static func all(session: URLSession = .shared, - completion: @escaping (Result<[Canteen], EmealError>) -> Void) { - Logger.emealKit.debug("Creating data task for all canteens") - session.emealDataTask(with: URL.Mensa.canteens) { result in - switch result { - case .failure(let error): - Logger.emealKit.error("Failed to fetch canteen data: \(String(describing: error))") - completion(.failure(error)) - case .success(let data): - do { - let canteens = try JSONDecoder().decode([Canteen].self, from: data) - Logger.emealKit.debug("Successfully fetched \(canteens.count) canteens") - completion(.success(canteens)) - } catch let error { - Logger.emealKit.error("Failed to decode Canteen data: \(String(describing: error))") - completion(.failure(.other(error))) - } - } - } - } - - @available(macOS 12.0, iOS 15.0, *) - public static func all(session: URLSession = .shared) async throws -> [Canteen] { - try await withCheckedThrowingContinuation { continuation in - Self.all(session: session) { result in - continuation.resume(with: result) - } + public static func all(session: URLSession = .shared) async throws(EmealError) -> [Canteen] { + Logger.emealKit.debug("Fetching all canteens") + do { + let (data, _) = try await session.data(from: URL.Mensa.canteens) + let canteens = try JSONDecoder().decode([Canteen].self, from: data) + Logger.emealKit.debug("Successfully fetched \(canteens.count) canteens") + return canteens + } catch (let error) { + Logger.emealKit.error("Failed to fetch canteen data: \(String(describing: error))") + throw .other(error) } } } -#if canImport(Combine) -extension Canteen { - public static func allPublisher(session: URLSession = .shared) -> AnyPublisher<[Canteen], EmealError> { - session.dataTaskPublisher(for: URL.Mensa.canteens) - .map { $0.data } - .decode(type: [Canteen].self, decoder: JSONDecoder()) - .mapError { EmealError.other($0) } - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() - } -} -#endif - // MARK: - Meals extension Meal { - public static func `for`(canteen: CanteenId, - on date: Date, - session: URLSession = .shared, - completion: @escaping (Result<[Meal], EmealError>) -> Void) { - Self.for(canteen: canteen.rawValue, on: date, session: session, completion: completion) - } - - public static func `for`(canteen: Int, - on date: Date, - session: URLSession = .shared, - completion: @escaping (Result<[Meal], EmealError>) -> Void) { - Logger.emealKit.debug("Creating data task for canteen \(canteen) on \(date)") - session.emealDataTask(with: URL.Mensa.meals(canteen: canteen, date: date)) { result in - switch result { - case .failure(let error): - Logger.emealKit.error("Failed to fetch meal data: \(String(describing: error))") - completion(.failure(error)) - case .success(let data): - do { - let meals = try JSONDecoder().decode([Meal].self, from: data) - Logger.emealKit.debug("Successfully fetched \(meals.count) meals") - completion(.success(meals)) - } catch let error { - Logger.emealKit.error("Failed to decode meal data: \(String(describing: error))") - completion(.failure(.other(error))) - } - } + public static func `for`(canteen: Int, on date: Date, session: URLSession = .shared) async throws(EmealError) -> [Meal] { + Logger.emealKit.debug("Fetching meals for canteen \(canteen) on \(date)") + do { + let (data, _) = try await session.data(from: URL.Mensa.meals(canteen: canteen, date: date)) + let meals = try JSONDecoder().decode([Meal].self, from: data) + Logger.emealKit.debug("Successfully fetched \(meals.count) meals") + return meals + } catch (let error) { + Logger.emealKit.error("Failed to fetch meal data: \(String(describing: error))") + throw .other(error) } } - @available(macOS 12.0, iOS 15.0, *) - public static func `for`(canteen: CanteenId, on date: Date, session: URLSession = .shared) async throws -> [Meal] { + public static func `for`(canteen: CanteenId, on date: Date, session: URLSession = .shared) async throws(EmealError) -> [Meal] { try await Self.for(canteen: canteen.rawValue, on: date, session: session) } - - @available(macOS 12.0, iOS 15.0, *) - public static func `for`(canteen: Int, on date: Date, session: URLSession = .shared) async throws -> [Meal] { - try await withCheckedThrowingContinuation { continuation in - Self.for(canteen: canteen, on: date, session: session) { result in - continuation.resume(with: result) - } - } - } -} - -#if canImport(Combine) -extension Meal { - public static func publisherFor(canteen: Int, - on date: Date, - session: URLSession = .shared) -> AnyPublisher<[Meal], EmealError> { - session.dataTaskPublisher(for: URL.Mensa.meals(canteen: canteen, date: date)) - .map { $0.data } - .decode(type: [Meal].self, decoder: JSONDecoder()) - .mapError { EmealError.other($0) } - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() - } - - public static func publisherFor(canteen: CanteenId, - on date: Date, - session: URLSession = .shared) -> AnyPublisher<[Meal], EmealError> { - Self.publisherFor(canteen: canteen.rawValue, on: date, session: session) - } } -#endif From c6a33f23dded756392c8a546979695d86a3340a4 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Wed, 13 Nov 2024 23:10:07 +0100 Subject: [PATCH 3/8] update expected canteen count --- Tests/APIValidationTests/MenuAPITests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/APIValidationTests/MenuAPITests.swift b/Tests/APIValidationTests/MenuAPITests.swift index 996478c..fc9fd70 100644 --- a/Tests/APIValidationTests/MenuAPITests.swift +++ b/Tests/APIValidationTests/MenuAPITests.swift @@ -3,7 +3,7 @@ import EmealKit @available(macOS 12.0, iOS 15.0, *) class MenuAPITests: XCTestCase { - static let expectedCanteenCount = 21 + static let expectedCanteenCount = 16 /// Tests expect one of the following canteens to have meals for the current day, otherwise they fail. static let expectedOpenCanteens: [CanteenId] = [.alteMensa, .mensaSiedepunkt, .mensaReichenbachstraße] From 4754ce25fc1803d78a582beaae329b311fddd8c2 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Wed, 13 Nov 2024 23:33:00 +0100 Subject: [PATCH 4/8] fetch rss data to augment meal data --- Sources/EmealKit/Mensa/MealFeed.swift | 4 ++++ Sources/EmealKit/Mensa/MensaAPI.swift | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Sources/EmealKit/Mensa/MealFeed.swift b/Sources/EmealKit/Mensa/MealFeed.swift index 776a644..7707f99 100644 --- a/Sources/EmealKit/Mensa/MealFeed.swift +++ b/Sources/EmealKit/Mensa/MealFeed.swift @@ -13,6 +13,10 @@ extension Meal { func matches(meal: Meal) -> Bool { return link.contains(String(meal.id)) } + + var isSoldOut: Bool { + title.lowercased().contains("ausverkauft") + } } public static func rssData(session: URLSession = .shared) async throws -> [RSSMeal] { diff --git a/Sources/EmealKit/Mensa/MensaAPI.swift b/Sources/EmealKit/Mensa/MensaAPI.swift index f6fe503..a2c2e9a 100644 --- a/Sources/EmealKit/Mensa/MensaAPI.swift +++ b/Sources/EmealKit/Mensa/MensaAPI.swift @@ -37,7 +37,17 @@ extension Meal { let (data, _) = try await session.data(from: URL.Mensa.meals(canteen: canteen, date: date)) let meals = try JSONDecoder().decode([Meal].self, from: data) Logger.emealKit.debug("Successfully fetched \(meals.count) meals") - return meals + + let feedItems = try await Self.rssData() + return meals.map { meal in + var meal = meal + let matchingItem = feedItems.first { $0.matches(meal: meal) } + if let matchingItem { + Logger.emealKit.debug("Found matching feeditem for \(meal.id)") + meal.isSoldOut = matchingItem.isSoldOut + } + return meal + } } catch (let error) { Logger.emealKit.error("Failed to fetch meal data: \(String(describing: error))") throw .other(error) From a88e05d084016b6634a9b2272776c9b8483df543 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 15 Nov 2024 18:58:37 +0100 Subject: [PATCH 5/8] fix and update mock data tests --- Sources/EmealKit/Mensa/MealFeed.swift | 2 +- Sources/EmealKit/Mensa/MensaAPI.swift | 28 +- Sources/EmealKit/URLSessionProtocol.swift | 9 + Tests/EmealKitTests/CanteenTests.swift | 4 +- Tests/EmealKitTests/MealTests.swift | 26 +- Tests/EmealKitTests/MockData.swift | 743 ++++++++++++++++++++++ Tests/EmealKitTests/MockURLSession.swift | 134 +--- 7 files changed, 813 insertions(+), 133 deletions(-) create mode 100644 Sources/EmealKit/URLSessionProtocol.swift create mode 100644 Tests/EmealKitTests/MockData.swift diff --git a/Sources/EmealKit/Mensa/MealFeed.swift b/Sources/EmealKit/Mensa/MealFeed.swift index 7707f99..10f5939 100644 --- a/Sources/EmealKit/Mensa/MealFeed.swift +++ b/Sources/EmealKit/Mensa/MealFeed.swift @@ -19,7 +19,7 @@ extension Meal { } } - public static func rssData(session: URLSession = .shared) async throws -> [RSSMeal] { + public static func rssData() async throws -> [RSSMeal] { let feedURL = URL(string: "https://www.studentenwerk-dresden.de/feeds/speiseplan.rss")! let parser = FeedParser(URL: feedURL) return try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/EmealKit/Mensa/MensaAPI.swift b/Sources/EmealKit/Mensa/MensaAPI.swift index a2c2e9a..dfb7c5b 100644 --- a/Sources/EmealKit/Mensa/MensaAPI.swift +++ b/Sources/EmealKit/Mensa/MensaAPI.swift @@ -14,10 +14,11 @@ internal extension URL { // MARK: - Canteens extension Canteen { - public static func all(session: URLSession = .shared) async throws(EmealError) -> [Canteen] { + public static func all(session: URLSessionProtocol = URLSession.shared) async throws(EmealError) -> [Canteen] { Logger.emealKit.debug("Fetching all canteens") do { let (data, _) = try await session.data(from: URL.Mensa.canteens) + print(String(data: data, encoding: .utf8)!) let canteens = try JSONDecoder().decode([Canteen].self, from: data) Logger.emealKit.debug("Successfully fetched \(canteens.count) canteens") return canteens @@ -31,22 +32,27 @@ extension Canteen { // MARK: - Meals extension Meal { - public static func `for`(canteen: Int, on date: Date, session: URLSession = .shared) async throws(EmealError) -> [Meal] { + public static func `for`(canteen: Int, on date: Date, session: URLSessionProtocol = URLSession.shared) async throws(EmealError) -> [Meal] { Logger.emealKit.debug("Fetching meals for canteen \(canteen) on \(date)") do { let (data, _) = try await session.data(from: URL.Mensa.meals(canteen: canteen, date: date)) let meals = try JSONDecoder().decode([Meal].self, from: data) Logger.emealKit.debug("Successfully fetched \(meals.count) meals") - let feedItems = try await Self.rssData() - return meals.map { meal in - var meal = meal - let matchingItem = feedItems.first { $0.matches(meal: meal) } - if let matchingItem { - Logger.emealKit.debug("Found matching feeditem for \(meal.id)") - meal.isSoldOut = matchingItem.isSoldOut + do { + let feedItems = try await Self.rssData() + return meals.map { meal in + var meal = meal + let matchingItem = feedItems.first { $0.matches(meal: meal) } + if let matchingItem { + Logger.emealKit.debug("Found matching feeditem for \(meal.id)") + meal.isSoldOut = matchingItem.isSoldOut + } + return meal } - return meal + } catch (let error) { + Logger.emealKit.log("Failed to fetch rss data, continuing without: \(String(describing: error))") + return meals } } catch (let error) { Logger.emealKit.error("Failed to fetch meal data: \(String(describing: error))") @@ -54,7 +60,7 @@ extension Meal { } } - public static func `for`(canteen: CanteenId, on date: Date, session: URLSession = .shared) async throws(EmealError) -> [Meal] { + public static func `for`(canteen: CanteenId, on date: Date, session: URLSessionProtocol = URLSession.shared) async throws(EmealError) -> [Meal] { try await Self.for(canteen: canteen.rawValue, on: date, session: session) } } diff --git a/Sources/EmealKit/URLSessionProtocol.swift b/Sources/EmealKit/URLSessionProtocol.swift new file mode 100644 index 0000000..ca90626 --- /dev/null +++ b/Sources/EmealKit/URLSessionProtocol.swift @@ -0,0 +1,9 @@ +import Foundation + +public protocol URLSessionProtocol { + func data(from url: URL) async throws -> (Data, URLResponse) +} + +extension URLSession: URLSessionProtocol { + +} diff --git a/Tests/EmealKitTests/CanteenTests.swift b/Tests/EmealKitTests/CanteenTests.swift index ea33ec9..d192713 100644 --- a/Tests/EmealKitTests/CanteenTests.swift +++ b/Tests/EmealKitTests/CanteenTests.swift @@ -5,8 +5,8 @@ import CoreLocation class CanteenTests: XCTestCase { @available(macOS 12.0, iOS 15.0, *) func testMockFetchAndDecode() async throws { - let canteens = try await Canteen.all(session: MockURLSession(mockData: .canteens)) - XCTAssertEqual(canteens.count, 21) + let canteens = try await Canteen.all(session: MockURLSession(data: .canteens)) + XCTAssertEqual(canteens.count, 16) } func testLocation() { diff --git a/Tests/EmealKitTests/MealTests.swift b/Tests/EmealKitTests/MealTests.swift index 1559767..87608b1 100644 --- a/Tests/EmealKitTests/MealTests.swift +++ b/Tests/EmealKitTests/MealTests.swift @@ -4,14 +4,20 @@ import EmealKit class MealTests: XCTestCase { @available(macOS 12.0, iOS 15.0, *) func testMockFetchAndDecode() async throws { - let meals = try await Meal.for(canteen: .alteMensa, on: Date(), session: MockURLSession(mockData: .meals)) - XCTAssertEqual(meals.count, 5) + let meals = try await Meal.for(canteen: .alteMensa, on: Date(), session: MockURLSession(data: .meals)) + XCTAssertEqual(meals.count, 4) } func testPlaceholderImage() { - let meal = Meal(id: 0, name: "", notes: [], prices: nil, category: "", - image: URL(string: "https://static.studentenwerk-dresden.de/bilder/mensen/studentenwerk-dresden-lieber-mensen-gehen.jpg")!, - url: URL(string: "q")!) + let meal = Meal( + id: 0, + name: "", + notes: [], + prices: nil, + category: "", + image: URL(string: "https://static.studentenwerk-dresden.de/bilder/mensen/studentenwerk-dresden-lieber-mensen-gehen.jpg")!, + url: URL(string: "q")! + ) XCTAssert(meal.imageIsPlaceholder) } @@ -78,5 +84,15 @@ class MealTests: XCTestCase { XCTAssertEqual(prices2.students, 1.0) XCTAssertEqual(prices2.employees, 1.0) } + + func testFeedData() async throws { + // Unfortunately we can't really test this with mock data since there's no way to inject anything into FeedKit. + let feedItems = try await Meal.rssData() + XCTAssertGreaterThan(feedItems.count, 0) + } + + func testSoldOut() async throws { + // see above + } } diff --git a/Tests/EmealKitTests/MockData.swift b/Tests/EmealKitTests/MockData.swift new file mode 100644 index 0000000..cc94906 --- /dev/null +++ b/Tests/EmealKitTests/MockData.swift @@ -0,0 +1,743 @@ +enum MockData: String { + case canteens = #""" + [ + { + "id": 6, + "name": "Mensa Matrix", + "city": "Dresden", + "address": "Reichenbachstr. 1, 01069 Dresden", + "coordinates": [ + 51.034283226863565, + 13.734020590782166 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-matrix.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-matrix.html" + }, + { + "id": 35, + "name": "Zeltschlösschen", + "city": "Dresden", + "address": "Nürnberger Straße 55, 01187 Dresden", + "coordinates": [ + 51.031614756984816, + 13.728645443916323 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-zeltschloesschen.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/zeltschloesschen.html" + }, + { + "id": 4, + "name": "Alte Mensa", + "city": "Dresden", + "address": "Mommsenstr. 13, 01069 Dresden", + "coordinates": [ + 51.02696733929933, + 13.726491630077364 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-alte-mensa.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/alte-mensa.html" + }, + { + "id": 8, + "name": "Mensologie", + "city": "Dresden", + "address": "Blasewitzer Str. 84, 01307 Dresden", + "coordinates": [ + 51.052705307014044, + 13.784312009811401 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensologie.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensologie.html" + }, + { + "id": 9, + "name": "Mensa Siedepunkt", + "city": "Dresden", + "address": "Zellescher Weg 17, 01069 Dresden", + "coordinates": [ + 51.02946063983054, + 13.738727867603302 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-siedepunkt.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-siedepunkt.html" + }, + { + "id": 32, + "name": "Mensa Johanna", + "city": "Dresden", + "address": "Marschnerstraße 38, 01307 Dresden", + "coordinates": [ + 51.053120073616405, + 13.760884255170824 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-johanna.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-johanna.html" + }, + { + "id": 10, + "name": "Mensa TellerRandt", + "city": "Tharandt", + "address": "Pienner Straße 15, 01737 Tharandt", + "coordinates": [ + 50.98060093483648, + 13.581464588642122 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-tellerrandt.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-tellerrandt.html" + }, + { + "id": 29, + "name": "Mensa U-Boot", + "city": "Dresden", + "address": "Mensa U-Boot im Potthoffbau Untergeschoss, George-Bähr-Straße/Hettnerstraße 3, 01069 Dresden", + "coordinates": [ + 51.03030323712326, + 13.72934550046921 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-u-boot.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/u-boot.html" + }, + { + "id": 11, + "name": "Mensa Palucca Hochschule", + "city": "Dresden", + "address": "Basteiplatz 4, 01277 Dresden", + "coordinates": [ + 51.02895, + 13.770829 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-palucca-hochschule.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-palucca-hochschule.html" + }, + { + "id": 33, + "name": "Mensa WUeins", + "city": "Dresden", + "address": "Wundtstraße 1, 01217 Dresden", + "coordinates": [ + 51.02990429156573, + 13.748951107263567 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-wueins.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-wueins.html" + }, + { + "id": 34, + "name": "Mensa Brühl", + "city": "Dresden", + "address": "Brühlsche Terrasse 1, 01067 DresdenZugang über den Innenhof der HfBK", + "coordinates": [ + 51.052948, + 13.741935 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-bruehl.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-bruehl.html" + }, + { + "id": 13, + "name": "Mensa Stimm-Gabel", + "city": "Dresden", + "address": "Wettiner Platz 13, 01067 Dresden", + "coordinates": [ + 51.053722, + 13.724652 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-stimm-gabel.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-stimm-gabel.html" + }, + { + "id": 24, + "name": "Mensa Kraatschn", + "city": "Zittau", + "address": "Hochwaldstr. 12, 02763 Zittau", + "coordinates": [ + 50.89042611030397, + 14.804495573043825 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-kraatschn.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-kraatschn.html" + }, + { + "id": 25, + "name": "Mensa Mahlwerk", + "city": "Zittau", + "address": "Schwenninger Weg 1, 02763 Zittau", + "coordinates": [ + 50.88397255787832, + 14.801915287971497 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mensa-mahlwerk.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mensa-mahlwerk.html" + }, + { + "id": 28, + "name": "MiO - Mensa im Osten", + "city": "Görlitz", + "address": "Furtstraße 1a, 02826 Görlitz", + "coordinates": [ + 51.14924302208328, + 14.998609721660616 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-mio-mensa-im-osten.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/mio-mensa-im-osten.html" + }, + { + "id": 36, + "name": "Grill Cube", + "city": "Dresden", + "address": "George-Bähr-Straße 1A-E, 01069 Dresden", + "coordinates": [ + 51.0285205, + 13.7287076 + ], + "url": "https://www.studentenwerk-dresden.de/mensen/details-grill-cube.html", + "menu": "https://www.studentenwerk-dresden.de/mensen/speiseplan/grill-cube.html" + } + ] + """# + + case meals = #""" + [ + { + "id": 310965, + "name": "Hähnchenbrustfilet Piccata (A, A1, G) mit Zitrone, dazu Pommes frites und Salat (J)", + "notes": [ + "Glutenhaltiges Getreide (A)", + "Weizen (A1)", + "Milch/Milchzucker (Laktose) (G)", + "Senf (J)" + ], + "prices": { + "Studierende": 3.91, + "Bedienstete": 7.1 + }, + "category": "fertig 1", + "image": "//static.studentenwerk-dresden.de/bilder/mensen/studentenwerk-dresden-lieber-mensen-gehen.jpg", + "url": "https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310965.html" + }, + { + "id": 310969, + "name": "Nuggets aus Gemüse und Jackfrucht (A, A1), dazu Kräuter-Dip (F, J) und Gemüsebratkartoffeln und Salat (J)", + "notes": [ + "Menü ist vegan", + "mit Antioxydationsmittel (3)", + "Glutenhaltiges Getreide (A)", + "Weizen (A1)", + "Soja (F)", + "Senf (J)" + ], + "prices": { + "Studierende": 3.8, + "Bedienstete": 6.9 + }, + "category": "fertig 2", + "image": "//static.studentenwerk-dresden.de/bilder/mensen/studentenwerk-dresden-lieber-mensen-gehen.jpg", + "url": "https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310969.html" + }, + { + "id": 310958, + "name": "Bunter Gemüsetopf mit Pilzen und getrockneten Tomaten (A, A1,G)", + "notes": [ + "Menü ist vegan", + "mit Antioxydationsmittel (3)", + "Soja (F)" + ], + "prices": { + "Studierende": 2.18, + "Bedienstete": 3.97 + }, + "category": "Terrine", + "image": "//bilderspeiseplan.studentenwerk-dresden.de/m18/202411/310958.jpg", + "url": "https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310958.html" + }, + { + "id": 308943, + "name": "Hausgemachte Pasta (A, A1) mit Basilikumpesto (H, H1) und veganem Schmelz oder mit Carbonara (C, G) und geraspeltem Hartkäse (G) - dann weder vegan noch vegetarisch -", + "notes": [ + "Menü ist vegan", + "mit tierischem Lab", + "enthält Schweinefleisch", + "enthält Knoblauch", + "mit Farbstoff (1)", + "mit Konservierungsstoff (2)", + "mit Antioxydationsmittel (3)", + "mit Phosphat (8)", + "Glutenhaltiges Getreide (A)", + "Weizen (A1)", + "Eier (C)", + "Milch/Milchzucker (Laktose) (G)", + "Schalenfrüchte (Nüsse) (H)", + "Mandeln (H1)" + ], + "prices": { + "Studierende": 2.35, + "Bedienstete": 4.9 + }, + "category": "Pastaria & Co.", + "image": "//bilderspeiseplan.studentenwerk-dresden.de/m18/202411/308943.jpg", + "url": "https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308943.html" + } + ] + """# + + case rssFeed = #""" + + + + Studentenwerk Dresden - Mensa Speiseplan von heute + https://www.studentenwerk-dresden.de/mensen/speiseplan/ + Der Speiseplan der Mensen des Studentenwerks Dresden + de-de + Fri, 15 Nov 2024 18:16:37 +0100 + + Milchgrieß (A, A1, G) mit Kirschkompott, zerlassende Butter und Zucker / Zimt, auch als Dessertportion möglich (2.80 EUR / 5.10 EUR) + Milchgrieß (A, A1, G) mit Kirschkompott, zerlassende Butter und Zucker / Zimt, auch als Dessertportion möglich (Studierende: 2.80 EUR / Bedienstete: 5.10 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309125.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309125.html + Mensa Matrix + + + Suppenstation: Feine Reissuppe mit Gemüse (A, A1, F) (2.18 EUR / 3.97 EUR) + Suppenstation: Feine Reissuppe mit Gemüse (A, A1, F) (Studierende: 2.18 EUR / Bedienstete: 3.97 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310365.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310365.html + Mensa Matrix + + + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu Nudeln gebraten mediterran (2.35 EUR / 4.90 EUR) + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu Nudeln gebraten mediterran (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310916.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310916.html + Mensa Matrix + + + Dessertportion: Milchgrieß (A, A1, G) mit Kirschkompott, zerlassende Butter und Zucker/Zimt (1.68 EUR / 3.06 EUR) + Dessertportion: Milchgrieß (A, A1, G) mit Kirschkompott, zerlassende Butter und Zucker/Zimt (Studierende: 1.68 EUR / Bedienstete: 3.06 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310952.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310952.html + Mensa Matrix + + + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelkroketten (A, A1) (4.43 EUR / 8.05 EUR) + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelkroketten (A, A1) (Studierende: 4.43 EUR / Bedienstete: 8.05 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310970.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310970.html + Mensa Matrix + + + Kohlroulade mit deftiger Schmorkrautsoße (A, A1, I) und Petersilienkartoffeln (3.65 EUR / 6.64 EUR) + Kohlroulade mit deftiger Schmorkrautsoße (A, A1, I) und Petersilienkartoffeln (Studierende: 3.65 EUR / Bedienstete: 6.64 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310539.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310539.html + Zeltschlösschen + + + Green Oats Burger mit Pesto-Dip (F), Eisbergsalat, Bruschetta und Pommes frites (4.45 EUR / 8.10 EUR) + Green Oats Burger mit Pesto-Dip (F), Eisbergsalat, Bruschetta und Pommes frites (Studierende: 4.45 EUR / Bedienstete: 8.10 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310540.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310540.html + Zeltschlösschen + + + Chicken Piccata Burger (A, A1, G) mit Pesto-Dip (G, H, H4), Eisbergsalat, Bruschetta und Pommes frites (ausverkauft) + Chicken Piccata Burger (A, A1, G) mit Pesto-Dip (G, H, H4), Eisbergsalat, Bruschetta und Pommes frites (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310541.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310541.html + Zeltschlösschen + + + Blumenkohl-Broccoli-Kartoffelauflauf in Sahnesoße mit Gouda gratiniert (A, A1, G), dazu Salat (J) (ausverkauft) + Blumenkohl-Broccoli-Kartoffelauflauf in Sahnesoße mit Gouda gratiniert (A, A1, G), dazu Salat (J) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310542.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310542.html + Zeltschlösschen + + + Nudelauflauf mit Blumenkohl in Käsesoße und Röstzwiebeln (A, A1, C, G), dazu Salat (J) (ausverkauft) + Nudelauflauf mit Blumenkohl in Käsesoße und Röstzwiebeln (A, A1, C, G), dazu Salat (J) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310959.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310959.html + Zeltschlösschen + + + Hamburger oder Cheeseburger (4.10 EUR / 4.41 EUR) + Hamburger oder Cheeseburger (Studierende: 4.10 EUR / Bedienstete: 4.41 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285627.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285627.html + Grill Cube + + + Valess Burger (4.50 EUR / 4.84 EUR) + Valess Burger (Studierende: 4.50 EUR / Bedienstete: 4.84 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285628.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285628.html + Grill Cube + + + Italian Burger mit Rind, Tomate, Mozzarella und Rucola (4.50 EUR / 4.84 EUR) + Italian Burger mit Rind, Tomate, Mozzarella und Rucola (Studierende: 4.50 EUR / Bedienstete: 4.84 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285629.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285629.html + Grill Cube + + + Currywurst mit milder oder scharfer Soße, dazu Pommes frites (Currywurst mit Brötchen 3,26€/3,51€) (4.20 EUR / 4.52 EUR) + Currywurst mit milder oder scharfer Soße, dazu Pommes frites (Currywurst mit Brötchen 3,26€/3,51€) (Studierende: 4.20 EUR / Bedienstete: 4.52 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285630.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285630.html + Grill Cube + + + Double-Hamburger oder Double-Cheeseburger (6.70 EUR / 7.20 EUR) + Double-Hamburger oder Double-Cheeseburger (Studierende: 6.70 EUR / Bedienstete: 7.20 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285631.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285631.html + Grill Cube + + + Unser veganes Angebot: Falafel Burger (4.10 EUR / 4.41 EUR) + Unser veganes Angebot: Falafel Burger (Studierende: 4.10 EUR / Bedienstete: 4.41 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285632.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-285632.html + Grill Cube + + + Vegane Currywurst mit milder oder scharfer Soße, dazu Pommes frites (mit Brötchen 3,26€/3,51€) (4.20 EUR / 4.52 EUR) + Vegane Currywurst mit milder oder scharfer Soße, dazu Pommes frites (mit Brötchen 3,26€/3,51€) (Studierende: 4.20 EUR / Bedienstete: 4.52 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-301772.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-301772.html + Grill Cube + + + Hausgemachte Pasta (A, A1) mit Basilikumpesto (H, H1) und veganem Schmelz oder mit Carbonara (C, G) und geraspeltem Hartkäse (G) - dann weder vegan noch vegetarisch - (2.35 EUR / 4.90 EUR) + Hausgemachte Pasta (A, A1) mit Basilikumpesto (H, H1) und veganem Schmelz oder mit Carbonara (C, G) und geraspeltem Hartkäse (G) - dann weder vegan noch vegetarisch - (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308943.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308943.html + Alte Mensa + + + Terrine: Bunter Gemüsetopf mit Pilzen und getrockneten Tomaten (A, A1,G) (ausverkauft) + Terrine: Bunter Gemüsetopf mit Pilzen und getrockneten Tomaten (A, A1,G) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310958.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310958.html + Alte Mensa + + + Hähnchenbrustfilet Piccata (A, A1, G) mit Zitrone, dazu Pommes frites und Salat (J) (ausverkauft) + Hähnchenbrustfilet Piccata (A, A1, G) mit Zitrone, dazu Pommes frites und Salat (J) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310965.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310965.html + Alte Mensa + + + Nuggets aus Gemüse und Jackfrucht (A, A1), dazu Kräuter-Dip (F, J) und Gemüsebratkartoffeln und Salat (J) (3.80 EUR / 6.90 EUR) + Nuggets aus Gemüse und Jackfrucht (A, A1), dazu Kräuter-Dip (F, J) und Gemüsebratkartoffeln und Salat (J) (Studierende: 3.80 EUR / Bedienstete: 6.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310969.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310969.html + Alte Mensa + + + Hausgemachte Kartoffelpuffer mit Wurzelgemüse (A, A1, C, I), dazu Kräuter-Quark-Dip (A, A1, C, G, I) (2.35 EUR / 4.90 EUR) + Hausgemachte Kartoffelpuffer mit Wurzelgemüse (A, A1, C, I), dazu Kräuter-Quark-Dip (A, A1, C, G, I) (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310280.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310280.html + Mensologie + + + Jägerschnitzel (panierte Jagdwurst) (A, A1, C, G, J), dazu Tomatensoße und Makkaroni (A, A1) (ausverkauft) + Jägerschnitzel (panierte Jagdwurst) (A, A1, C, G, J), dazu Tomatensoße und Makkaroni (A, A1) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310281.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310281.html + Mensologie + + + Kichererbsen-Bohnen Burger (A, A1, A4, F, J), dazu Pommes frites (I) und Ketchup (4.26 EUR / 7.75 EUR) + Kichererbsen-Bohnen Burger (A, A1, A4, F, J), dazu Pommes frites (I) und Ketchup (Studierende: 4.26 EUR / Bedienstete: 7.75 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310282.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310282.html + Mensologie + + + Suppenstation: Mexikanischer Feuertopf mit Schweinefleisch und Kidneybohnen (2.18 EUR / 3.97 EUR) + Suppenstation: Mexikanischer Feuertopf mit Schweinefleisch und Kidneybohnen (Studierende: 2.18 EUR / Bedienstete: 3.97 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310951.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310951.html + Mensologie + + + Med by you: Reis, veganer Planted-Krautgulasch (I, A, A4, J), Couscous (A, A1) (4.01 EUR / 7.30 EUR) + Med by you: Reis, veganer Planted-Krautgulasch (I, A, A4, J), Couscous (A, A1) (Studierende: 4.01 EUR / Bedienstete: 7.30 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310953.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310953.html + Mensologie + + + Rote Linsen-Lasagne (A, A1, F, I) dazu Salat (ausverkauft) + Rote Linsen-Lasagne (A, A1, F, I) dazu Salat (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308835.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308835.html + Mensa Siedepunkt + + + Gegrilltes Ofengemüse mit körniger Hüttenkäsecreme (G), dazu Pestokartoffeln (G, H, H4) (4.20 EUR / 7.64 EUR) + Gegrilltes Ofengemüse mit körniger Hüttenkäsecreme (G), dazu Pestokartoffeln (G, H, H4) (Studierende: 4.20 EUR / Bedienstete: 7.64 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308836.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308836.html + Mensa Siedepunkt + + + Abendangebot: Spaghetti (A, A1) Bolognese (I) mit geriebenem Gouda (G) (3.05 EUR / 5.55 EUR) + Abendangebot: Spaghetti (A, A1) Bolognese (I) mit geriebenem Gouda (G) (Studierende: 3.05 EUR / Bedienstete: 5.55 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308837.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308837.html + Mensa Siedepunkt + + + Abendangebot: Spaghetti (A, A1) mit Sojabolognese (F, I, J) und geriebenem veganen Schmelz (2.89 EUR / 5.25 EUR) + Abendangebot: Spaghetti (A, A1) mit Sojabolognese (F, I, J) und geriebenem veganen Schmelz (Studierende: 2.89 EUR / Bedienstete: 5.25 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308838.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-308838.html + Mensa Siedepunkt + + + Kürbis-Gemüsesuppe (A, A1, F) (2.18 EUR / 3.97 EUR) + Kürbis-Gemüsesuppe (A, A1, F) (Studierende: 2.18 EUR / Bedienstete: 3.97 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310910.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310910.html + Mensa Siedepunkt + + + Bunte Salatvariation aus Eisbergsalat, Chinakohl, Gurke, Tomaten, Paprika und Couscous-Linsenmix (A, A1), dazu kleine Frühlingsrollen (A, A1, F), inklusive einem Dressing nach Wahl (3.05 EUR / 5.54 EUR) + Bunte Salatvariation aus Eisbergsalat, Chinakohl, Gurke, Tomaten, Paprika und Couscous-Linsenmix (A, A1), dazu kleine Frühlingsrollen (A, A1, F), inklusive einem Dressing nach Wahl (Studierende: 3.05 EUR / Bedienstete: 5.54 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310955.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310955.html + Mensa Siedepunkt + + + Bunte Salatvariation aus Eisbergsalat, Chinakohl, Gurke, Tomaten, Paprika und Couscous-Linsenmix (A, A1), dazu Falafelbällchen (A, A1), inklusive einem Dressing nach Wahl (3.05 EUR / 5.54 EUR) + Bunte Salatvariation aus Eisbergsalat, Chinakohl, Gurke, Tomaten, Paprika und Couscous-Linsenmix (A, A1), dazu Falafelbällchen (A, A1), inklusive einem Dressing nach Wahl (Studierende: 3.05 EUR / Bedienstete: 5.54 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310956.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310956.html + Mensa Siedepunkt + + + Grillteller mit Biersteak (A, A3, J), Grillwürstchen und kleinen Hacksteaks (A, A1, C) auf Letscho (J), dazu Kartoffelkroketten (A, A1) (2.35 EUR / 4.90 EUR) + Grillteller mit Biersteak (A, A3, J), Grillwürstchen und kleinen Hacksteaks (A, A1, C) auf Letscho (J), dazu Kartoffelkroketten (A, A1) (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310964.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310964.html + Mensa Siedepunkt + + + Buntes Gemüse in Sojacreme (F) mit Reis (2.35 EUR / 4.90 EUR) + Buntes Gemüse in Sojacreme (F) mit Reis (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310401.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310401.html + Mensa Johanna + + + Schweinekeulenbraten in Schwarzbiersoße (A, A1, A3, I) mit Bayrischkraut und Petersilienkartoffeln oder Kartoffelklößen (L) (ausverkauft) + Schweinekeulenbraten in Schwarzbiersoße (A, A1, A3, I) mit Bayrischkraut und Petersilienkartoffeln oder Kartoffelklößen (L) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310402.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310402.html + Mensa Johanna + + + Spinatknödel (A, A1, C, G) mit Sahnepilzen (A, A1, G), geriebenem Gouda (G) und Salat (J) (ausverkauft) + Spinatknödel (A, A1, C, G) mit Sahnepilzen (A, A1, G), geriebenem Gouda (G) und Salat (J) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310403.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310403.html + Mensa Johanna + + + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (2.35 EUR / 4.90 EUR) + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310479.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310479.html + Mensa WUeins + + + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (ausverkauft) + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310483.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310483.html + Mensa WUeins + + + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (2.35 EUR / 4.90 EUR) + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310480.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310480.html + Mensa Brühl + + + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (ausverkauft) + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (ausverkauft) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310484.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310484.html + Mensa Brühl + + + COM RANG - Eierreis mit Karotten, roten Zwiebeln und Erbsen (A, A1, C, F, K) (3.00 EUR / 5.45 EUR) + COM RANG - Eierreis mit Karotten, roten Zwiebeln und Erbsen (A, A1, C, F, K) (Studierende: 3.00 EUR / Bedienstete: 5.45 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309252.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309252.html + Mensa U-Boot + + + GA NUONG - gegrillte Hähnchenbrust mit Kaiserschoten, Porree, Kokos und Bambussprossen (A, A1, F, K), dazu Thai Reis oder Mie Nudeln (A, A1, F, K) (4.50 EUR / 8.18 EUR) + GA NUONG - gegrillte Hähnchenbrust mit Kaiserschoten, Porree, Kokos und Bambussprossen (A, A1, F, K), dazu Thai Reis oder Mie Nudeln (A, A1, F, K) (Studierende: 4.50 EUR / Bedienstete: 8.18 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309266.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309266.html + Mensa U-Boot + + + COM/MIE CHAY - mit Kaiserschoten, Porree, Kokos und Bambussprossen (A, A1, F, K),dazu Thai Reis oder Mie Nudeln (A, A1, F, K) (3.65 EUR / 6.64 EUR) + COM/MIE CHAY - mit Kaiserschoten, Porree, Kokos und Bambussprossen (A, A1, F, K),dazu Thai Reis oder Mie Nudeln (A, A1, F, K) (Studierende: 3.65 EUR / Bedienstete: 6.64 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309267.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309267.html + Mensa U-Boot + + + COM RANG - Eierreis mit Schweinefleisch, Karotten, roten Zwiebeln und Erbsen (A, A1, C, F, K) (4.39 EUR / 7.99 EUR) + COM RANG - Eierreis mit Schweinefleisch, Karotten, roten Zwiebeln und Erbsen (A, A1, C, F, K) (Studierende: 4.39 EUR / Bedienstete: 7.99 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309269.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309269.html + Mensa U-Boot + + + gebratene Hähnchenkeule (F) mit Apfelrotkohl und Petersilienkartoffeln (3.44 EUR / 6.25 EUR) + gebratene Hähnchenkeule (F) mit Apfelrotkohl und Petersilienkartoffeln (Studierende: 3.44 EUR / Bedienstete: 6.25 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310661.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310661.html + Mensa TellerRandt + + + Soja-Geschnetzeltes mit Champignon und Paprika (F), dazu Gemüsebulgur (A, A1, I) (2.35 EUR / 4.90 EUR) + Soja-Geschnetzeltes mit Champignon und Paprika (F), dazu Gemüsebulgur (A, A1, I) (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310954.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310954.html + Mensa TellerRandt + + + Bratwurst mit Sauerkraut und Petersilinkartoffeln (3.44 EUR / 6.25 EUR) + Bratwurst mit Sauerkraut und Petersilinkartoffeln (Studierende: 3.44 EUR / Bedienstete: 6.25 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310957.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310957.html + Mensa TellerRandt + + + Deftiger Kasslerkammbraten (A, A1), dazu Fasssauerkraut und Hefeknödel (A, A1, C, G) (3.82 EUR / 6.95 EUR) + Deftiger Kasslerkammbraten (A, A1), dazu Fasssauerkraut und Hefeknödel (A, A1, C, G) (Studierende: 3.82 EUR / Bedienstete: 6.95 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310058.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310058.html + Mensa Kraatschn + + + Sojageschnetzeltes "Züricher Art" mit Champignons in Sahnesoße (F), dazu Tagliatelle (A, A1) und ein bunter Salat (2.35 EUR / 4.90 EUR) + Sojageschnetzeltes "Züricher Art" mit Champignons in Sahnesoße (F), dazu Tagliatelle (A, A1) und ein bunter Salat (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310059.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310059.html + Mensa Kraatschn + + + Levantinische Spezialitäten - "Shakshuka" Gemüse-Paprikapfanne mit Ei, (C, L), dazu gebackene Kartoffelecken und Knoblauch-Dip (A,A1,A4,H,J) (3.74 EUR / 6.80 EUR) + Levantinische Spezialitäten - "Shakshuka" Gemüse-Paprikapfanne mit Ei, (C, L), dazu gebackene Kartoffelecken und Knoblauch-Dip (A,A1,A4,H,J) (Studierende: 3.74 EUR / Bedienstete: 6.80 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310060.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310060.html + Mensa Kraatschn + + + Terrine: Heute: Vegane Blumenkohl-Cremesuppe (A,A1,F) oder deftiger Bohneneintopf mit Kassler (-), Preisangabe für kleine Schale (1.13 EUR / 2.06 EUR) + Terrine: Heute: Vegane Blumenkohl-Cremesuppe (A,A1,F) oder deftiger Bohneneintopf mit Kassler (-), Preisangabe für kleine Schale (Studierende: 1.13 EUR / Bedienstete: 2.06 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310674.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310674.html + Mensa Kraatschn + + + Heute: Kartoffelerbsenstampf (F), Currylinsenreis (I), Falafelstreifen (A, A1) und Champignonköpfe in Sojarahm (F), Preisangabe für kleine Schale (3.05 EUR / 5.54 EUR) + Heute: Kartoffelerbsenstampf (F), Currylinsenreis (I), Falafelstreifen (A, A1) und Champignonköpfe in Sojarahm (F), Preisangabe für kleine Schale (Studierende: 3.05 EUR / Bedienstete: 5.54 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310796.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310796.html + Mensa Kraatschn + + + Pastatheke heute: Zwei Sorten Pasta (A,A1), dazu vegane Gemüse-Rahmsoße (A,A1, F) oder Geflügelsoße (A,A1,G) und auf Wunsch geriebener Käse (G), Preisangabe für kleinen Teller (2.07 EUR / 3.76 EUR) + Pastatheke heute: Zwei Sorten Pasta (A,A1), dazu vegane Gemüse-Rahmsoße (A,A1, F) oder Geflügelsoße (A,A1,G) und auf Wunsch geriebener Käse (G), Preisangabe für kleinen Teller (Studierende: 2.07 EUR / Bedienstete: 3.76 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310961.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310961.html + Mensa Kraatschn + + + Deftiger Kasslerkammbraten (A, A1), dazu Fasssauerkraut und Hefeknödel (A, A1, C, G) (3.82 EUR / 6.95 EUR) + Deftiger Kasslerkammbraten (A, A1), dazu Fasssauerkraut und Hefeknödel (A, A1, C, G) (Studierende: 3.82 EUR / Bedienstete: 6.95 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310704.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310704.html + Mensa Mahlwerk + + + Sojageschnetzeltes "Züricher Art" mit Champignons in Sahnesoße (F), dazu Tagliatelle (A, A1) und ein bunter Salat (2.35 EUR / 4.90 EUR) + Sojageschnetzeltes "Züricher Art" mit Champignons in Sahnesoße (F), dazu Tagliatelle (A, A1) und ein bunter Salat (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310705.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310705.html + Mensa Mahlwerk + + + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (2.35 EUR / 4.90 EUR) + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310481.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310481.html + Mensa Stimm-Gabel + + + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (4.43 EUR / 8.05 EUR) + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (Studierende: 4.43 EUR / Bedienstete: 8.05 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310485.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310485.html + Mensa Stimm-Gabel + + + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (2.35 EUR / 4.90 EUR) + Räuchertofuwürfel mit Ananas, Pfirsich und Chili (A, A1, F), dazu mediterraner Gemüsereis (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310482.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310482.html + Mensa Palucca Hochschule + + + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (4.43 EUR / 8.05 EUR) + Hähnchenschnitzel Cordon bleu (A, A1, G) mit Geflügelsoße (I) und Karottengemüse, dazu Kartoffelbällchen (C) (Studierende: 4.43 EUR / Bedienstete: 8.05 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310486.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310486.html + Mensa Palucca Hochschule + + + Dürrröhrsdorfer Bratwurst (A, A1, J) mit Sauerkraut und Kartoffelpüree (G) (3.19 EUR / 5.80 EUR) + Dürrröhrsdorfer Bratwurst (A, A1, J) mit Sauerkraut und Kartoffelpüree (G) (Studierende: 3.19 EUR / Bedienstete: 5.80 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309755.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309755.html + MiO - Mensa im Osten + + + Mediterrane Tortelloni mit Basilikum-Käsekruste (A, A1) auf Spinat-Cremesoße (F) (2.35 EUR / 4.90 EUR) + Mediterrane Tortelloni mit Basilikum-Käsekruste (A, A1) auf Spinat-Cremesoße (F) (Studierende: 2.35 EUR / Bedienstete: 4.90 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309756.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-309756.html + MiO - Mensa im Osten + + + Tagessuppe - vegetarische Kartoffelsuppe (G, I), kleine Schale (1.13 EUR / 2.06 EUR) + Tagessuppe - vegetarische Kartoffelsuppe (G, I), kleine Schale (Studierende: 1.13 EUR / Bedienstete: 2.06 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310949.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310949.html + MiO - Mensa im Osten + + + Vegane Theke - Schwarzwurzel in Sojarahm (A, A1, F), Grünkohl, Kurkumareis und Spaghetti mit getrockneten Tomaten und Basilikum (A, A1), kleiner Teller (3.05 EUR / 5.54 EUR) + Vegane Theke - Schwarzwurzel in Sojarahm (A, A1, F), Grünkohl, Kurkumareis und Spaghetti mit getrockneten Tomaten und Basilikum (A, A1), kleiner Teller (Studierende: 3.05 EUR / Bedienstete: 5.54 EUR) + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310950.html + https://www.studentenwerk-dresden.de/mensen/speiseplan/details-310950.html + MiO - Mensa im Osten + + + + """# +} diff --git a/Tests/EmealKitTests/MockURLSession.swift b/Tests/EmealKitTests/MockURLSession.swift index 67695ae..053f58a 100644 --- a/Tests/EmealKitTests/MockURLSession.swift +++ b/Tests/EmealKitTests/MockURLSession.swift @@ -1,121 +1,27 @@ import Foundation -import Combine +import EmealKit -internal class MockURLSession: URLSession { - var mockData: Data? - var mockError: Error? +class MockURLSession: URLSessionProtocol { + var data: Data? + var response: URLResponse? + var error: Error? - init(mockData: Data? = nil, mockError: Error? = nil) { - self.mockData = mockData - self.mockError = mockError + init(data: MockData? = nil, response: URLResponse? = nil, error: Error? = nil) { + self.data = data?.rawValue.data(using: .utf8) + self.response = response + self.error = error } - init(mockData: MockData) { - self.mockData = mockData.rawValue.data(using: .utf8)! - self.mockError = nil + func data(from url: URL) async throws -> (Data, URLResponse) { + if let error { + throw error + } + let response = self.response ?? HTTPURLResponse( + url: url, + statusCode: 200, + httpVersion: nil, + headerFields: nil + )! + return (data ?? Data(), response) } - - override func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> MockURLSessionDataTask { - completionHandler(mockData, nil, mockError) - return MockURLSessionDataTask() - } -} - -internal class MockURLSessionDataTask: URLSessionDataTask { - override func resume() {} -} - -enum MockData: String { - case canteens = #"[{"id":29,"name":"BioMensa U-Boot","city":"Dresden","address":"BioMensa U-Boot im Potthoffbau Untergeschoss, George-B\u00e4hr-Stra\u00dfe\/Hettnerstra\u00dfe 3, 01069 Dresden","coordinates":[51.03030323712326,13.72934550046921],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-u-boot.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/u-boot.html"},{"id":6,"name":"Mensa Reichenbachstra\u00dfe","city":"Dresden","address":"Reichenbachstr. 1, 01069 Dresden","coordinates":[51.034283226863565,13.734020590782166],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-reichenbachstrasse.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-reichenbachstrasse.html"},{"id":35,"name":"Zeltschl\u00f6sschen","city":"Dresden","address":"N\u00fcrnberger Stra\u00dfe 55, 01187 Dresden","coordinates":[51.031614756984816,13.728645443916323],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-zeltschloesschen.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/zeltschloesschen.html"},{"id":4,"name":"Alte Mensa","city":"Dresden","address":"Mommsenstr. 13, 01069 Dresden","coordinates":[51.02696733929933,13.726491630077364],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-alte-mensa.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/alte-mensa.html"},{"id":8,"name":"Mensologie","city":"Dresden","address":"Blasewitzer Str. 84, 01307 Dresden","coordinates":[51.052705307014044,13.784312009811401],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensologie.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensologie.html"},{"id":9,"name":"Mensa Siedepunkt","city":"Dresden","address":"Zellescher Weg 17, 01069 Dresden","coordinates":[51.02946063983054,13.738727867603302],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-siedepunkt.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-siedepunkt.html"},{"id":32,"name":"Mensa Johannstadt","city":"Dresden","address":"Marschnerstra\u00dfe 38, 01307 Dresden","coordinates":[51.053120073616405,13.760884255170824],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-johannstadt.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-johannstadt.html"},{"id":10,"name":"Mensa TellerRandt","city":"Tharandt","address":"Pienner Stra\u00dfe 15, 01737 Tharandt","coordinates":[50.98060093483648,13.581464588642122],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-tellerrandt.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-tellerrandt.html"},{"id":11,"name":"Mensa Palucca Hochschule","city":"Dresden","address":"Basteiplatz 4, 01277 Dresden","coordinates":[51.02895,13.770829],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-palucca-hochschule.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-palucca-hochschule.html"},{"id":33,"name":"Mensa WUeins \/ Sportsbar","city":"Dresden","address":"Wundtstra\u00dfe 1, 01217 Dresden","coordinates":[51.02990429156573,13.748951107263567],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-wueins.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-wueins.html"},{"id":34,"name":"Mensa Br\u00fchl","city":"Dresden","address":"Br\u00fchlsche Terrasse 1, 01067 DresdenZugang \u00fcber den Innenhof der HfBK","coordinates":[51.052948,13.741935],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-bruehl.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-bruehl.html"},{"id":13,"name":"Mensa Stimm-Gabel","city":"Dresden","address":"Wettiner Platz 13, 01067 Dresden","coordinates":[51.053722,13.724652],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-stimm-gabel.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-stimm-gabel.html"},{"id":24,"name":"Mensa Zittau","city":"Zittau","address":"Hochwaldstr. 12, 02763 Zittau","coordinates":[50.89042611030397,14.804495573043825],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-zittau.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-zittau.html"},{"id":25,"name":"Mensa Mahlwerk","city":"Zittau","address":"Schwenninger Weg 1, 02763 Zittau","coordinates":[50.88397255787832,14.801915287971497],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-mahlwerk.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-mahlwerk.html"},{"id":28,"name":"Mensa G\u00f6rlitz","city":"G\u00f6rlitz","address":"Furtstra\u00dfe 1a, 02826 G\u00f6rlitz","coordinates":[51.14924302208328,14.998609721660616],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-goerlitz.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-goerlitz.html"},{"id":38,"name":"Mensa Rothenburg","city":"Rothenburg","address":"Friedensstra\u00dfe 120, 02929 Rothenburg \/O.L.","coordinates":[51.353944460945506,14.96465027332306],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-rothenburg.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-rothenburg.html"},{"id":39,"name":"Mensa Bautzen Polizeihochschule","city":"Bautzen","address":"Kantstra\u00dfe 25, 02625 Bautzen","coordinates":[51.18287722211444,14.445980787277223],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-bautzen.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-bautzen.html"},{"id":42,"name":"Mensa Bautzen Studienakademie","city":"Bautzen","address":"L\u00f6bauer Stra\u00dfe 1, 02625 Bautzen","coordinates":[51.1816753,14.4358941],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-ba-bautzen.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-ba-bautzen.html"},{"id":36,"name":"Grill Cube","city":"Dresden","address":"George-B\u00e4hr-Stra\u00dfe 1A-E, 01069 Dresden","coordinates":[51.0285205,13.7287076],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-grill-cube.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/grill-cube.html"},{"id":37,"name":"Pasta-Mobil","city":"Dresden","address":"George-B\u00e4hr-Stra\u00dfe 1A-E, 01069 Dresden","coordinates":[51.02853,13.72855],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-pasta-mobil.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/pasta-mobil.html"},{"id":30,"name":"Mensa Sport","city":"Dresden","address":"Messering 2a, 01067 Dresden","coordinates":[51.06903688508011,13.71892511844635],"url":"https:\/\/www.studentenwerk-dresden.de\/mensen\/details-mensa-sport.html","menu":"https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/mensa-sport.html"}]"# - case meals = #""" - [ - { - "id": 248434, - "name": "Kr\u00e4uterquark mit Kartoffeln, Leberwurst und Butter oder vegetarisch mit Gew\u00fcrzgurke", - "notes": [ - "enth\u00e4lt Schweinefleisch", - "mit Konservierungsstoff (2)", - "mit S\u00fc\u00dfungsmittel (9)", - "Milch\/Milchzucker (Laktose) (G)", - "Senf (J)" - ], - "prices": { - "Studierende": 2.35, - "Bedienstete": 4.27 - }, - "category": "fertig 2", - "image": "\/\/bilderspeiseplan.studentenwerk-dresden.de\/m18\/202009\/248434.jpg", - "url": "https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/details-248434.html" - }, - { - "id": 248435, - "name": "Pflaumen Quarkkn\u00f6del mit Semmelbutter, dazu Zimtso\u00dfe", - "notes": [ - "Men\u00fc ist vegetarisch", - "mit Farbstoff (1)", - "Glutenhaltiges Getreide (A)", - "Weizen (A1)", - "Eier (C)", - "Milch\/Milchzucker (Laktose) (G)", - "Ausgabe an der Pastatheke" - ], - "prices": { - "Studierende": 2.35, - "Bedienstete": 4.27 - }, - "category": "fertig 4", - "image": "\/\/bilderspeiseplan.studentenwerk-dresden.de\/m18\/202009\/248435.jpg", - "url": "https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/details-248435.html" - }, - { - "id": 248436, - "name": "Tiegelwurst mit Sauerkraut und Petersilienkartoffeln", - "notes": [ - "enth\u00e4lt Schweinefleisch", - "mit Konservierungsstoff (2)", - "Glutenhaltiges Getreide (A)", - "Weizen (A1)", - "Senf (J)" - ], - "prices": { - "Studierende": 2.55, - "Bedienstete": 4.64 - }, - "category": "fertig 1", - "image": "\/\/bilderspeiseplan.studentenwerk-dresden.de\/m18\/202009\/248436.jpg", - "url": "https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/details-248436.html" - }, - { - "id": 248914, - "name": "Asiatische Gem\u00fcsesuppe", - "notes": [ - "Men\u00fc ist vegan", - "Glutenhaltiges Getreide (A)", - "Weizen (A1)", - "Soja (F)" - ], - "prices": { - "Studierende": 1.97, - "Bedienstete": 3.59 - }, - "category": "Terrine", - "image": "\/\/static.studentenwerk-dresden.de\/bilder\/mensen\/studentenwerk-dresden-lieber-mensen-gehen.jpg", - "url": "https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/details-248914.html" - }, - { - "id": 246363, - "name": "Buntes aus unserer Salatwerkstatt, nach Wunsch zusammengestellt von unseren Mitarbeitern. W\u00e4hlbar gern auch in veganer oder vegetarischer Form.", - "notes": [ - "Men\u00fc ist vegan" - ], - "prices": { - "Studierende": 2.4, - "Bedienstete": 4.37 - }, - "category": "fertig 3", - "image": "\/\/bilderspeiseplan.studentenwerk-dresden.de\/m18\/202009\/246363.jpg", - "url": "https:\/\/www.studentenwerk-dresden.de\/mensen\/speiseplan\/details-246363.html" - } - ] - """# } From 7600373adba5d4a71d6470e6c423230dbc1ae644 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 15 Nov 2024 19:03:33 +0100 Subject: [PATCH 6/8] Update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fc71cb..d015fd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,9 @@ on: [push] jobs: build_and_test: - runs-on: macos-latest + runs-on: macos-15 # go back to macos-latest once 15 becomes the default steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build run: swift build -v - name: Tests From cf86d15cb38c6f2280d6fe98049c64e75679ed6b Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 15 Nov 2024 19:10:19 +0100 Subject: [PATCH 7/8] skip cardservice login in CI --- Tests/APIValidationTests/CardserviceAPITests.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/APIValidationTests/CardserviceAPITests.swift b/Tests/APIValidationTests/CardserviceAPITests.swift index fbcc7ff..9f8f939 100644 --- a/Tests/APIValidationTests/CardserviceAPITests.swift +++ b/Tests/APIValidationTests/CardserviceAPITests.swift @@ -50,6 +50,10 @@ class CardserviceAPITests: XCTestCase { } func testLoginSuccess() async throws { + if ProcessInfo.processInfo.environment["CI"] != nil { + print("Skipping successful login test in CI.") + return + } _ = try await Cardservice.login(username: username, password: password) } From d5b592d70e6ff565a6ce11ae9df93cec0f6941e5 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 15 Nov 2024 19:16:58 +0100 Subject: [PATCH 8/8] disable more tests in CI --- Tests/APIValidationTests/CardserviceAPITests.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/APIValidationTests/CardserviceAPITests.swift b/Tests/APIValidationTests/CardserviceAPITests.swift index 9f8f939..b20dd18 100644 --- a/Tests/APIValidationTests/CardserviceAPITests.swift +++ b/Tests/APIValidationTests/CardserviceAPITests.swift @@ -51,19 +51,27 @@ class CardserviceAPITests: XCTestCase { func testLoginSuccess() async throws { if ProcessInfo.processInfo.environment["CI"] != nil { - print("Skipping successful login test in CI.") + print("Skipping test in CI.") return } _ = try await Cardservice.login(username: username, password: password) } func testFetchCarddata() async throws { + if ProcessInfo.processInfo.environment["CI"] != nil { + print("Skipping test in CI.") + return + } let cardservice = try await Cardservice.login(username: username, password: password) let carddata = try await cardservice.carddata() XCTAssert(!carddata.isEmpty) } func testFetchTransactions() async throws { + if ProcessInfo.processInfo.environment["CI"] != nil { + print("Skipping test in CI.") + return + } let cardservice = try await Cardservice.login(username: username, password: password) let oneWeekAgo = Date().addingTimeInterval(-1 * 60 * 60 * 24 * 7) _ = try await cardservice.transactions(begin: oneWeekAgo)