feat: add OAuth API endpoint unit test

This commit is contained in:
CMK 2021-01-29 19:38:11 +08:00
parent fe83c02e03
commit 71de1ed9be
15 changed files with 482 additions and 78 deletions

View File

@ -11,6 +11,12 @@
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */; };
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */; };
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */; };
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */; };
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */; };
DB0140BD25C40D7500F9F3CF /* CommonOSLog in Frameworks */ = {isa = PBXBuildFile; productRef = DB0140BC25C40D7500F9F3CF /* CommonOSLog */; };
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D102225BAA7B400EAA174 /* Assets.swift */; };
@ -108,6 +114,11 @@
A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.release.xcconfig"; sourceTree = "<group>"; };
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = "<group>"; };
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewController.swift; sourceTree = "<group>"; };
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPinBasedAuthenticationViewModel.swift; sourceTree = "<group>"; };
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
DB3D102225BAA7B400EAA174 /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
@ -160,6 +171,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DB0140BD25C40D7500F9F3CF /* CommonOSLog in Frameworks */,
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
@ -224,6 +236,25 @@
name = Frameworks;
sourceTree = "<group>";
};
DB01409B25C40BB600F9F3CF /* Authentication */ = {
isa = PBXGroup;
children = (
DB0140A625C40C0900F9F3CF /* PinBased */,
DB01409525C40B6700F9F3CF /* AuthenticationViewController.swift */,
);
path = Authentication;
sourceTree = "<group>";
};
DB0140A625C40C0900F9F3CF /* PinBased */ = {
isa = PBXGroup;
children = (
DB0140A025C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift */,
DB0140AD25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift */,
DB0140A725C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift */,
);
path = PinBased;
sourceTree = "<group>";
};
DB3D0FF725BAA68500EAA174 /* Supporting Files */ = {
isa = PBXGroup;
children = (
@ -400,6 +431,7 @@
children = (
DB8AF54E25C13703002E6C99 /* MainTab */,
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
DB01409B25C40BB600F9F3CF /* Authentication */,
);
path = Scene;
sourceTree = "<group>";
@ -407,6 +439,7 @@
DB8AF56225C138BC002E6C99 /* Extension */ = {
isa = PBXGroup;
children = (
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */,
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */,
);
path = Extension;
@ -447,6 +480,7 @@
packageProductDependencies = (
DB3D0FF225BAA61700EAA174 /* AlamofireImage */,
5D526FE125BE9AC400460CB9 /* MastodonSDK */,
DB0140BC25C40D7500F9F3CF /* CommonOSLog */,
);
productName = Mastodon;
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
@ -569,6 +603,7 @@
mainGroup = DB427DC925BAA00100D1B89D;
packageReferences = (
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */,
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */,
);
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
projectDirPath = "";
@ -753,15 +788,20 @@
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */,
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */,
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */,
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1263,6 +1303,14 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/MainasuK/CommonOSLog";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.1.1;
};
};
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Alamofire/AlamofireImage.git";
@ -1278,6 +1326,11 @@
isa = XCSwiftPackageProductDependency;
productName = MastodonSDK;
};
DB0140BC25C40D7500F9F3CF /* CommonOSLog */ = {
isa = XCSwiftPackageProductDependency;
package = DB0140BB25C40D7500F9F3CF /* XCRemoteSwiftPackageReference "CommonOSLog" */;
productName = CommonOSLog;
};
DB3D0FF225BAA61700EAA174 /* AlamofireImage */ = {
isa = XCSwiftPackageProductDependency;
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;

View File

@ -19,6 +19,15 @@
"version": "4.1.0"
}
},
{
"package": "CommonOSLog",
"repositoryURL": "https://github.com/MainasuK/CommonOSLog",
"state": {
"branch": null,
"revision": "c121624a30698e9886efe38aebb36ff51c01b6c2",
"version": "0.1.1"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",

View File

@ -0,0 +1,20 @@
//
// OSLog.swift
// Mastodon
//
// Created by Cirno MainasuK on 2021/1/29
//
import os
import Foundation
import CommonOSLog
extension OSLog {
static let api: OSLog = {
#if DEBUG
return OSLog(subsystem: OSLog.subsystem + ".api", category: "api")
#else
return OSLog.disabled
#endif
}()
}

View File

@ -0,0 +1,10 @@
//
// AuthenticationViewController.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021/1/29.
//
import UIKit

View File

@ -0,0 +1,25 @@
//
// MastodonPinBasedAuthenticationViewController.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021/1/29.
//
import os.log
import Foundation
import WebKit
final class MastodonPinBasedAuthenticationViewController: NSObject {
weak var viewModel: MastodonPinBasedAuthenticationViewModel?
init(viewModel: MastodonPinBasedAuthenticationViewModel) {
self.viewModel = viewModel
}
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
}

View File

@ -0,0 +1,12 @@
//
// MastodonPinBasedAuthenticationViewModel.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021/1/29.
//
import Foundation
final class MastodonPinBasedAuthenticationViewModel {
}

View File

@ -0,0 +1,34 @@
//
// MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift
// Mastodon
//
// Created by Cirno MainasuK on 2021/1/29.
//
import os.log
import Foundation
import WebKit
final class MastodonPinBasedAuthenticationViewModelNavigationDelegateShim: NSObject {
weak var viewModel: MastodonPinBasedAuthenticationViewModel?
init(viewModel: MastodonPinBasedAuthenticationViewModel) {
self.viewModel = viewModel
}
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
}
// MARK: - WKNavigationDelegate
extension MastodonPinBasedAuthenticationViewModelNavigationDelegateShim: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// TODO:
}
}

View File

@ -2,9 +2,26 @@
"configurations" : [
{
"id" : "5119353D-C795-4264-89FD-8376D9B144F8",
"name" : "Configuration 1",
"name" : "mstdn.jp",
"options" : {
"environmentVariableEntries" : [
{
"key" : "domain",
"value" : "mstdn.jp"
}
]
}
},
{
"id" : "C5184AF3-B83B-4A7E-949C-6B1AA3ABE7D1",
"name" : "pawoo.net",
"options" : {
"environmentVariableEntries" : [
{
"key" : "domain",
"value" : "pawoo.net"
}
]
}
}
],
@ -13,6 +30,10 @@
},
"testTargets" : [
{
"skippedTests" : [
"MastodonSDKTests\/testCreateAnAnpplication()",
"MastodonSDKTests\/testVerifyAppCredentials()"
],
"target" : {
"containerPath" : "container:MastodonSDK",
"identifier" : "MastodonSDKTests",

View File

@ -14,12 +14,31 @@ extension Mastodon.API.App {
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("apps")
}
static func verifyCredentialsEndpointURL(domain: String) -> URL {
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("apps/verify_credentials")
}
/// Create an application
///
/// Using this endpoint to obtain `client_id` and `client_secret` for later OAuth token exchange
///
/// - Since: 0.0.0
/// - Version: 3.3.0
/// # Last Update
/// 2021/1/29
/// # Reference
/// [Document](https://docs.joinmastodon.org/methods/apps/)
/// - Parameters:
/// - session: `URLSession`
/// - domain: Mastodon instance domain. e.g. "example.com"
/// - query: `CreateQuery`
/// - Returns: `AnyPublisher` contains `Application` nested in the response
public static func create(
session: URLSession,
domain: String,
query: CreateQuery
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Application>, Error> {
let request = Mastodon.API.request(
let request = Mastodon.API.post(
url: appEndpointURL(domain: domain),
query: query,
authorization: nil
@ -32,6 +51,39 @@ extension Mastodon.API.App {
.eraseToAnyPublisher()
}
/// Verify application token
///
/// Using this endpoint to verify App token
///
/// - Since: 2.0.0
/// - Version: 3.3.0
/// # Last Update
/// 2021/1/29
/// # Reference
/// [Document](https://docs.joinmastodon.org/methods/apps/)
/// - Parameters:
/// - session: `URLSession`
/// - domain: Mastodon instance domain. e.g. "example.com"
/// - authorization: App token
/// - Returns: `AnyPublisher` contains `Application` nested in the response
public static func verifyCredentials(
session: URLSession,
domain: String,
authorization: Mastodon.API.OAuth.Authorization
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Application>, Error> {
let request = Mastodon.API.get(
url: verifyCredentialsEndpointURL(domain: domain),
query: nil,
authorization: authorization
)
return session.dataTaskPublisher(for: request)
.tryMap { data, response in
let value = try Mastodon.API.decode(type: Mastodon.Entity.Application.self, from: data, response: response)
return Mastodon.Response.Content(value: value, response: response)
}
.eraseToAnyPublisher()
}
}
extension Mastodon.API.App {

View File

@ -6,6 +6,7 @@
//
import Foundation
import Combine
extension Mastodon.API.OAuth {
@ -16,3 +17,101 @@ extension Mastodon.API.OAuth {
}
}
extension Mastodon.API.OAuth {
static func authorizeEndpointURL(domain: String) -> URL {
return Mastodon.API.oauthEndpointURL(domain: domain).appendingPathComponent("authorize")
}
/// Construct user authorize endpoint URL
///
/// This method construct a URL for user authorize
///
/// - Since: 0.1.0
/// - Version: 3.3.0
/// # Last Update
/// 2021/1/29
/// # Reference
/// [Document](https://docs.joinmastodon.org/methods/apps/oauth/)
/// - Parameters:
/// - session: `URLSession`
/// - domain: Mastodon instance domain. e.g. "example.com"
/// - query: `AuthorizeQuery`
static func authorizeURL(
domain: String,
query: AuthorizeQuery
) -> URL {
let request = Mastodon.API.get(
url: authorizeEndpointURL(domain: domain),
query: query,
authorization: nil
)
let url = request.url!
return url
}
// static func authorize(
// session: URLSession,
// domain: String,
// query: AuthorizeQuery
// ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
// let request = Mastodon.API.post(
// url: authorizeEndpointURL(domain: domain),
// query: query,
// authorization: nil
// )
// return session.dataTaskPublisher(for: request)
// .tryMap { data, response in
// let value = try Mastodon.API.decode(type: Mastodon.Entity.Token.self, from: data, response: response)
// return Mastodon.Response.Content(value: value, response: response)
// }
// .eraseToAnyPublisher()
// }
}
extension Mastodon.API.OAuth {
public struct AuthorizeQuery: GetQuery {
public let forceLogin: String?
public let responseType: String
public let clientID: String
public let redirectURI: String
public let scope: String?
public init(
forceLogin: String? = nil,
responseType: String = "code",
clientID: String,
redirectURI: String = "urn:ietf:wg:oauth:2.0:oob",
scope: String? = "read write follow push"
) {
self.forceLogin = forceLogin
self.responseType = responseType
self.clientID = clientID
self.redirectURI = redirectURI
self.scope = scope
}
enum CodingKeys: String, CodingKey {
case forceLogin = "force_login"
case responseType = "response_type"
case clientID
case redirectURI = "redirect_uri"
case scope
}
var queryItems: [URLQueryItem]? {
var items: [URLQueryItem] = []
forceLogin.flatMap { items.append(URLQueryItem(name: "force_login", value: $0)) }
items.append(URLQueryItem(name: "response_type", value: responseType))
items.append(URLQueryItem(name: "clientID", value: clientID))
items.append(URLQueryItem(name: "redirect_uri", value: redirectURI))
scope.flatMap { items.append(URLQueryItem(name: "scope", value: $0)) }
guard !items.isEmpty else { return nil }
return items
}
}
}

View File

@ -19,7 +19,7 @@ extension Mastodon.API.Timeline {
domain: String,
query: PublicTimelineQuery
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>, Error> {
let request = Mastodon.API.request(
let request = Mastodon.API.get(
url: publicTimelineEndpointURL(domain: domain),
query: query,
authorization: nil
@ -65,27 +65,13 @@ extension Mastodon.API.Timeline {
var queryItems: [URLQueryItem]? {
var items: [URLQueryItem] = []
local.flatMap {
items.append(URLQueryItem(name: "local", value: $0.queryItemValue))
}
remote.flatMap {
items.append(URLQueryItem(name: "remote", value: $0.queryItemValue))
}
onlyMedia.flatMap {
items.append(URLQueryItem(name: "only_media", value: $0.queryItemValue))
}
maxID.flatMap {
items.append(URLQueryItem(name: "max_id", value: $0))
}
sinceID.flatMap {
items.append(URLQueryItem(name: "since_id", value: $0))
}
minID.flatMap {
items.append(URLQueryItem(name: "min_id", value: $0))
}
limit.flatMap {
items.append(URLQueryItem(name: "limit", value: String($0)))
}
local.flatMap { items.append(URLQueryItem(name: "local", value: $0.queryItemValue)) }
remote.flatMap { items.append(URLQueryItem(name: "remote", value: $0.queryItemValue)) }
onlyMedia.flatMap { items.append(URLQueryItem(name: "only_media", value: $0.queryItemValue)) }
maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) }
sinceID.flatMap { items.append(URLQueryItem(name: "since_id", value: $0)) }
minID.flatMap { items.append(URLQueryItem(name: "min_id", value: $0)) }
limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) }
guard !items.isEmpty else { return nil }
return items
}

View File

@ -53,9 +53,15 @@ extension Mastodon.API {
return decoder
}()
static func oauthEndpointURL(domain: String) -> URL {
return URL(string: "https://" + domain + "/oauth/")!
}
static func endpointURL(domain: String) -> URL {
return URL(string: "https://" + domain + "/api/v1/")!
}
static func endpointV2URL(domain: String) -> URL {
return URL(string: "https://" + domain + "/api/v2/")!
}
}
@ -67,13 +73,15 @@ extension Mastodon.API {
extension Mastodon.API {
static func request(
static func get(
url: URL,
query: GetQuery,
query: GetQuery?,
authorization: OAuth.Authorization?
) -> URLRequest {
var components = URLComponents(string: url.absoluteString)!
if let query = query {
components.queryItems = query.queryItems
}
let requestURL = components.url!
var request = URLRequest(
@ -91,9 +99,9 @@ extension Mastodon.API {
return request
}
static func request(
static func post(
url: URL,
query: PostQuery,
query: PostQuery?,
authorization: OAuth.Authorization?
) -> URLRequest {
let components = URLComponents(string: url.absoluteString)!
@ -104,7 +112,9 @@ extension Mastodon.API {
timeoutInterval: Mastodon.API.timeoutInterval
)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
if let query = query {
request.httpBody = query.body
}
if let authorization = authorization {
request.setValue(
"Bearer \(authorization.accessToken)",

View File

@ -0,0 +1,83 @@
//
// MastodonSDK+API+AppTests.swift
//
//
// Created by MainasuK Cirno on 2021/1/29.
//
import os.log
import XCTest
import Combine
@testable import MastodonSDK
extension MastodonSDKTests {
func testCreateAnAnpplication() throws {
try _testCreateAnAnpplication(domain: domain)
}
func _testCreateAnAnpplication(domain: String) throws {
let theExpectation = expectation(description: "Create An Application")
let query = Mastodon.API.App.CreateQuery(
clientName: "XCTest",
website: nil
)
Mastodon.API.App.create(session: session, domain: domain, query: query)
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .failure(let error):
XCTFail(error.localizedDescription)
case .finished:
break
}
} receiveValue: { response in
XCTAssertEqual(response.value.name, "XCTest")
XCTAssertEqual(response.value.website, nil)
XCTAssertEqual(response.value.redirectURI, "urn:ietf:wg:oauth:2.0:oob")
os_log("%{public}s[%{public}ld], %{public}s: (%s) clientID %s", ((#file as NSString).lastPathComponent), #line, #function, domain, response.value.clientID ?? "nil")
os_log("%{public}s[%{public}ld], %{public}s: (%s) clientSecret %s", ((#file as NSString).lastPathComponent), #line, #function, domain, response.value.clientSecret ?? "nil")
theExpectation.fulfill()
}
.store(in: &disposeBag)
wait(for: [theExpectation], timeout: 5.0)
}
}
extension MastodonSDKTests {
func testVerifyAppCredentials() throws {
try _testVerifyAppCredentials(domain: domain, accessToken: "")
}
func _testVerifyAppCredentials(domain: String, accessToken: String) throws {
let theExpectation = expectation(description: "Verify App Credentials")
let authorization = Mastodon.API.OAuth.Authorization(accessToken: accessToken)
Mastodon.API.App.verifyCredentials(
session: session,
domain: domain,
authorization: authorization
)
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .failure(let error):
XCTFail(error.localizedDescription)
case .finished:
break
}
} receiveValue: { response in
XCTAssertEqual(response.value.name, "XCTest")
XCTAssertEqual(response.value.website, nil)
theExpectation.fulfill()
}
.store(in: &disposeBag)
wait(for: [theExpectation], timeout: 5.0)
}
}

View File

@ -0,0 +1,29 @@
//
// MastodonSDK+API+OAuthTests.swift
//
//
// Created by MainasuK Cirno on 2021/1/29.
//
import os.log
import XCTest
import Combine
@testable import MastodonSDK
extension MastodonSDKTests {
func testOAuthAuthorize() throws {
try _testOAuthAuthorize(domain: domain)
}
func _testOAuthAuthorize(domain: String) throws {
let query = Mastodon.API.OAuth.AuthorizeQuery(clientID: "StubClientID")
let authorizeURL = Mastodon.API.OAuth.authorizeURL(domain: domain, query: query)
os_log("%{public}s[%{public}ld], %{public}s: (%s) authorizeURL %s", ((#file as NSString).lastPathComponent), #line, #function, domain, authorizeURL.absoluteString)
XCTAssertEqual(
authorizeURL.absoluteString,
"https://\(domain)/oauth/authorize?response_type=code&clientID=StubClientID&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=read%20write%20follow%20push"
)
}
}

View File

@ -6,58 +6,19 @@ final class MastodonSDKTests: XCTestCase {
var disposeBag = Set<AnyCancellable>()
let mstdnDomain = "mstdn.jp"
let pawooDomain = "pawoo.net"
let session = URLSession(configuration: .ephemeral)
var domain: String { MastodonSDKTests.environmentVariable(key: "domain") }
static func environmentVariable(key: String) -> String {
return ProcessInfo.processInfo.environment[key]!
}
}
extension MastodonSDKTests {
func testCreateAnAnpplication_mstdn() throws {
try _testCreateAnAnpplication(domain: pawooDomain)
}
func testCreateAnAnpplication_pawoo() throws {
try _testCreateAnAnpplication(domain: pawooDomain)
}
func _testCreateAnAnpplication(domain: String) throws {
let theExpectation = expectation(description: "Create An Application")
let query = Mastodon.API.App.CreateQuery(
clientName: "XCTest",
website: nil
)
Mastodon.API.App.create(session: session, domain: domain, query: query)
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .failure(let error):
XCTFail(error.localizedDescription)
case .finished:
break
}
} receiveValue: { response in
XCTAssertEqual(response.value.name, "XCTest")
XCTAssertEqual(response.value.website, nil)
XCTAssertEqual(response.value.redirectURI, "urn:ietf:wg:oauth:2.0:oob")
theExpectation.fulfill()
}
.store(in: &disposeBag)
wait(for: [theExpectation], timeout: 10.0)
}
}
extension MastodonSDKTests {
func testPublicTimeline_mstdn() throws {
try _testPublicTimeline(domain: mstdnDomain)
}
func testPublicTimeline_pawoo() throws {
try _testPublicTimeline(domain: pawooDomain)
func testPublicTimeline() throws {
try _testPublicTimeline(domain: domain)
}
private func _testPublicTimeline(domain: String) throws {