Separate parser and helpers from Password class for better testability

This commit is contained in:
Danny Moesch 2018-11-11 18:09:52 +01:00 committed by Bob Sun
parent 2abbceb2e9
commit 7c12263458
17 changed files with 913 additions and 537 deletions

View file

@ -0,0 +1,29 @@
//
// PasswordHelpersTest.swift
// passKitTests
//
// Created by Danny Moesch on 30.09.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import OneTimePassword
import XCTest
@testable import passKit
class PasswordHelpersTest: XCTestCase {
func testOtpType() {
let secret = "secret".data(using: .utf8)!
let totpGenerator = Generator(factor: .timer(period: 30.0), secret: secret, algorithm: .sha1, digits: 6)!
let totpToken = Token(name: "", issuer: "", generator: totpGenerator)
XCTAssertEqual(OtpType(token: totpToken), .totp)
let hotpGenerator = Generator(factor: .counter(4), secret: secret, algorithm: .sha1, digits: 6)!
let hotpToken = Token(name: "", issuer: "", generator: hotpGenerator)
XCTAssertEqual(OtpType(token: hotpToken), .hotp)
XCTAssertEqual(OtpType(token: nil), .none)
}
}

View file

@ -0,0 +1,35 @@
//
// StringExtensionTest.swift
// passKitTests
//
// Created by Danny Moesch on 30.09.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import XCTest
@testable import passKit
class StringExtensionTest: XCTestCase {
func testStringByAddingPercentEncodingForRFC3986() {
[
("!#$&'()*+,/:;=?@[]^", "%21%23%24%26%27%28%29%2A%2B%2C/%3A%3B%3D?%40%5B%5D%5E"),
("-._~/?", "-._~/?"),
("A*b!c", "A%2Ab%21c"),
].forEach { unencoded, encoded in
XCTAssertEqual(unencoded.stringByAddingPercentEncodingForRFC3986(), encoded)
}
}
func testConcatenateAsLines() {
[
("a" | "b", "a\nb"),
("" | "b", "\nb"),
("a" | "", "a"),
("" | "", ""),
].forEach { concatenated, result in
XCTAssertEqual(concatenated, result)
}
}
}

View file

@ -0,0 +1,276 @@
//
// PasswordTests.swift
// passKitTests
//
// Created by Danny Moesch on 02.05.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import XCTest
@testable import passKit
class PasswordTest: XCTestCase {
private let PASSWORD_PATH = "/path/to/password"
private let PASSWORD_URL = URL(fileURLWithPath: "/path/to/password")
private let PASSWORD_STRING = "abcd1234"
private let OTP_TOKEN = "otpauth://totp/email@email.com?secret=abcd1234"
private let SECURE_URL_FIELD = "url" => "https://secure.com"
private let INSECURE_URL_FIELD = "url" => "http://insecure.com"
private let LOGIN_FIELD = "login" => "login name"
private let USERNAME_FIELD = "username" => "some username"
private let NOTE_FIELD = "note" => "A NOTE"
private let HINT_FIELD = "some hints" => "äöüß // €³ %% −° && @²` | [{\\}],.<>"
func testUrl() {
let password = getPasswordObjectWith(content: "")
XCTAssertEqual(password.url, PASSWORD_URL)
XCTAssertEqual(password.namePath, PASSWORD_PATH)
}
func testEmptyFile() {
[
"",
"\n",
].forEach { fileContent in
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, "")
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, "")
XCTAssertTrue(password.getFilteredAdditions().isEmpty)
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
}
}
func testEmptyPassword() {
let fileContent = "\n\(LOGIN_FIELD.asString)"
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, "")
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, LOGIN_FIELD.asString)
XCTAssertFalse(does(password, contain: LOGIN_FIELD))
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertEqual(password.login, LOGIN_FIELD.content)
}
func testSimplePasswordFile() {
let additions = SECURE_URL_FIELD | LOGIN_FIELD | USERNAME_FIELD | NOTE_FIELD
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: SECURE_URL_FIELD))
XCTAssertFalse(does(password, contain: LOGIN_FIELD))
XCTAssertFalse(does(password, contain: USERNAME_FIELD))
XCTAssertTrue(does(password, contain: NOTE_FIELD))
XCTAssertEqual(password.urlString, SECURE_URL_FIELD.content)
XCTAssertEqual(password.login, LOGIN_FIELD.content)
XCTAssertEqual(password.username, USERNAME_FIELD.content)
}
func testTwoPasswords() {
let additions = "efgh5678" | INSECURE_URL_FIELD
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: INSECURE_URL_FIELD))
XCTAssertTrue(does(password, contain: Constants.unknown(1) => "efgh5678"))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, INSECURE_URL_FIELD.content)
XCTAssertNil(password.login)
}
func testNoPassword() {
let fileContent = SECURE_URL_FIELD | NOTE_FIELD
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, SECURE_URL_FIELD.asString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, NOTE_FIELD.asString)
XCTAssertTrue(does(password, contain: NOTE_FIELD))
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
}
func testDuplicateKeys() {
let additions = SECURE_URL_FIELD | INSECURE_URL_FIELD
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: SECURE_URL_FIELD))
XCTAssertTrue(does(password, contain: INSECURE_URL_FIELD))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, SECURE_URL_FIELD.content)
XCTAssertNil(password.login)
}
func testUnknownKeys() {
let value1 = "value 1"
let value2 = "value 2"
let value3 = "value 3"
let value4 = "value 4"
let additions = value1 | NOTE_FIELD | value2 | value3 | SECURE_URL_FIELD | value4
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: Constants.unknown(1) => value1))
XCTAssertTrue(does(password, contain: NOTE_FIELD))
XCTAssertTrue(does(password, contain: Constants.unknown(2) => value2))
XCTAssertTrue(does(password, contain: Constants.unknown(3) => value3))
XCTAssertTrue(does(password, contain: SECURE_URL_FIELD))
XCTAssertTrue(does(password, contain: Constants.unknown(4) => value4))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, SECURE_URL_FIELD.content)
XCTAssertNil(password.login)
}
func testPasswordFileWithOtpToken() {
let additions = NOTE_FIELD | OTP_TOKEN
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertNotNil(password.getOtp())
}
func testFirstLineIsOtpToken() {
let password = getPasswordObjectWith(content: OTP_TOKEN)
XCTAssertEqual(password.password, OTP_TOKEN)
XCTAssertEqual(password.plainData, OTP_TOKEN.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, "")
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertNotNil(password.getOtp())
}
func testWrongOtpToken() {
let fileContent = "otpauth://htop/blabla"
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, fileContent)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertTrue(password.additionsPlainText.isEmpty)
XCTAssertEqual(password.otpType, OtpType.none)
XCTAssertNil(password.getOtp())
}
func testEmptyMultilineValues() {
let lineBreakField1 = "with line breaks" => "| \n"
let lineBreakField2 = "with line breaks" => "| \n "
let noLineBreakField = "without line breaks" => " > "
let additions = lineBreakField1 | lineBreakField2 | NOTE_FIELD | noLineBreakField
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: lineBreakField1.title => ""))
XCTAssertTrue(does(password, contain: lineBreakField2.title => ""))
XCTAssertTrue(does(password, contain: NOTE_FIELD))
XCTAssertTrue(does(password, contain: noLineBreakField.title => ""))
}
func testMultilineValues() {
let lineBreakField = "with line breaks" => "|\n This is \n text spread over \n multiple lines! "
let noLineBreakField = "without line breaks" => " > \n This is \n text spread over\n multiple lines!"
let additions = lineBreakField | NOTE_FIELD | noLineBreakField
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: lineBreakField.title => "This is \n text spread over \nmultiple lines!"))
XCTAssertTrue(does(password, contain: NOTE_FIELD))
XCTAssertTrue(does(password, contain: noLineBreakField.title => "This is text spread over multiple lines!"))
}
func testMultilineValuesMixed() {
let lineBreakField = "with line breaks" => "|\n This is \n \(HINT_FIELD.asString) spread over\n multiple lines!"
let noLineBreakField = "without line breaks" => " > \n This is \n | \n text spread over\nmultiple lines!"
let additions = lineBreakField | noLineBreakField | NOTE_FIELD
let fileContent = PASSWORD_STRING | additions
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PASSWORD_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.additionsPlainText, additions)
XCTAssertTrue(does(password, contain: lineBreakField.title => "This is \n\(HINT_FIELD.asString) spread over"))
XCTAssertTrue(does(password, contain: Constants.unknown(1) => " multiple lines!"))
XCTAssertTrue(does(password, contain: noLineBreakField.title => "This is | text spread over"))
XCTAssertTrue(does(password, contain: Constants.unknown(2) => "multiple lines!"))
XCTAssertTrue(does(password, contain: NOTE_FIELD))
}
func testUpdatePassword() {
let password = getPasswordObjectWith(content: "")
XCTAssertEqual(password.changed, 0)
password.updatePassword(name: "password", url: PASSWORD_URL, plainText: "")
XCTAssertEqual(password.changed, 0)
password.updatePassword(name: "", url: PASSWORD_URL, plainText: "a")
XCTAssertEqual(password.changed, 2)
password.updatePassword(name: "", url: URL(fileURLWithPath: "/some/path/"), plainText: "a")
XCTAssertEqual(password.changed, 3)
password.updatePassword(name: "", url: PASSWORD_URL, plainText: "")
XCTAssertEqual(password.changed, 3)
}
private func getPasswordObjectWith(content: String, url: URL? = nil) -> Password {
return Password(name: "password", url: url ?? PASSWORD_URL, plainText: content)
}
private func does(_ password: Password, contain field: AdditionField) -> Bool {
return password.getFilteredAdditions().contains(field)
}
}

View file

@ -0,0 +1,52 @@
//
// AdditionFieldTest.swift
// passKitTests
//
// Created by Danny Moesch on 30.09.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import XCTest
@testable import passKit
class AdditionFieldTest: XCTestCase {
func testAdditionField() {
let field1 = "key" => "value"
let field2 = "some other key" => "some other value"
let field3 = "" => "no title"
XCTAssertEqual(field1.asString, "key: value")
XCTAssertEqual(field2.asString, "some other key: some other value")
XCTAssertEqual(field3.asString, "no title")
XCTAssertTrue(field1.asTuple == ("key", "value"))
XCTAssertTrue(field2.asTuple == ("some other key", "some other value"))
XCTAssertTrue(field3.asTuple == ("", "no title"))
}
func testAdditionFieldEquals() {
XCTAssertEqual("key" => "value", "key" => "value")
XCTAssertNotEqual("key" => "value", "key" => "some other value")
}
func testInfixAdditionFieldInitialization() {
XCTAssertEqual("key" => "value", AdditionField(title: "key", content: "value"))
}
func testAdditionFieldOperators() {
let field1 = "key" => "value"
let field2 = "some other key" => "some other value"
let field3 = "" => "no title"
XCTAssertEqual("start" | field1, "start\nkey: value")
XCTAssertEqual("" | field1, "\nkey: value")
XCTAssertEqual(field1 | "end", "key: value\nend")
XCTAssertEqual(field1 | "", "key: value")
XCTAssertEqual("start" | field1 | field2, "start\nkey: value\nsome other key: some other value")
XCTAssertEqual(field1 | field2 | "end", "key: value\nsome other key: some other value\nend")
XCTAssertEqual(field1 | field2 | field3, "key: value\nsome other key: some other value\nno title")
XCTAssertEqual("check" => "for right" | "operator" => "precedence", "check: for right\noperator: precedence")
}
}

View file

@ -0,0 +1,31 @@
//
// ConstantsTest.swift
// passKitTests
//
// Created by Danny Moesch on 30.09.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import XCTest
@testable import passKit
class ConstantsTest: XCTestCase {
func testIsOtpRelated() {
XCTAssertTrue(Constants.isOtpRelated(line: "otpauth://something"))
XCTAssertTrue(Constants.isOtpRelated(line: "otp_algorithm: algorithm"))
XCTAssertFalse(Constants.isOtpRelated(line: "otp: something"))
XCTAssertFalse(Constants.isOtpRelated(line: "otp"))
}
func testUnknown() {
XCTAssertEqual(Constants.unknown(0), "unknown 0")
XCTAssertEqual(Constants.unknown(10), "unknown 10")
}
func testGetSeparator() {
XCTAssertEqual(Constants.getSeparator(breakingLines: true), "\n")
XCTAssertEqual(Constants.getSeparator(breakingLines: false), " ")
}
}

View file

@ -0,0 +1,102 @@
//
// ParserTest.swift
// passKitTests
//
// Created by Danny Moesch on 18.08.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
@testable import passKit
import XCTest
class ParserTest: XCTestCase {
private let FIELD = "key" => "value"
private let LOGIN_FIELD = "login" => "login name"
private let SECURE_URL_FIELD = "url" => "https://secure.com"
private let INSECURE_URL_FIELD = "url" => "http://insecure.com"
private let USERNAME_FIELD = "username" => "微 分 方 程"
private let NOTE_FIELD = "note" => "A NOTE"
private let MULTILINE_BLOCK_START = "multiline block" => "|"
private let MULTILINE_LINE_START = "multiline line" => ">"
func testInit() {
[
("", "", "", []),
("a", "a", "", []),
("a\nb", "a", "b", ["b"]),
("a\n\nb", "a", "\nb", ["b"]),
("a\r\nb", "a", "b", ["b"]),
("a\nb\nc\n\nd", "a", "b\nc\n\nd", ["b", "c", "d"]),
].forEach { plainText, firstLine, additionsSection, purgedAdditionalLines in
let parser = Parser(plainText: plainText)
XCTAssertEqual(parser.firstLine, firstLine)
XCTAssertEqual(parser.additionsSection, additionsSection)
XCTAssertEqual(parser.purgedAdditionalLines, purgedAdditionalLines)
}
}
func testGetKeyValuePair() {
XCTAssertTrue(Parser.getKeyValuePair(from: "key: value") == ("key", "value"))
XCTAssertTrue(Parser.getKeyValuePair(from: "a key: a value") == ("a key", "a value"))
XCTAssertTrue(Parser.getKeyValuePair(from: "key:value") == (nil, "key:value"))
XCTAssertTrue(Parser.getKeyValuePair(from: ": value") == (nil, "value"))
XCTAssertTrue(Parser.getKeyValuePair(from: "key: ") == ("key", ""))
XCTAssertTrue(Parser.getKeyValuePair(from: "otpauth://value") == ("otpauth", "otpauth://value"))
}
func testEmptyFiles() {
XCTAssertEqual(Parser(plainText: "").additionFields, [])
XCTAssertEqual(Parser(plainText: "\n").additionFields, [])
}
func testSimpleKeyValueLines() {
let fields0 = Parser(plainText: "" | FIELD | LOGIN_FIELD | SECURE_URL_FIELD).additionFields
let fields1 = Parser(plainText: "" | FIELD | "" | SECURE_URL_FIELD).additionFields
XCTAssertEqual(fields0, [FIELD, LOGIN_FIELD, SECURE_URL_FIELD])
XCTAssertEqual(fields1, [FIELD, SECURE_URL_FIELD])
}
func testLinesWithoutKey() {
let fields0 = Parser(plainText: "" | "value").additionFields
let fields1 = Parser(plainText: "" | LOGIN_FIELD | "value only" | INSECURE_URL_FIELD).additionFields
let fields2 = Parser(plainText: "" | LOGIN_FIELD | USERNAME_FIELD | "value:only").additionFields
let fields3 = Parser(plainText: "" | LOGIN_FIELD | "value 1" | "value 2").additionFields
XCTAssertEqual(fields0, [Constants.unknown(1) => "value"])
XCTAssertEqual(fields1, [LOGIN_FIELD, Constants.unknown(1) => "value only", INSECURE_URL_FIELD])
XCTAssertEqual(fields2, [LOGIN_FIELD, USERNAME_FIELD, Constants.unknown(1) => "value:only"])
XCTAssertEqual(fields3, [LOGIN_FIELD, Constants.unknown(1) => "value 1", Constants.unknown(2) => "value 2"])
}
func testMultilineValues() {
[
// Normal with one leading space
(" a b" | " cd", "a b\ncd", []),
// Normal with two leading spaces
(" a b" | " cd", "a b\ncd", []),
// Changing leading space lenght
(" a b" | " cd", "a b\n cd", []),
// First leading space longer than others
(" a b" | " cd", "a b", [Constants.unknown(1) => " cd"]),
// Empty first line
(" " | " cd", "cd", []),
// No leading space
("a b" | "cd", "", [Constants.unknown(1) => "a b", Constants.unknown(2) => "cd"]),
// Characters with special meaning in value
(" a: b" | " c: |" | " d", "a: b\nc: |\nd", []),
// Empty value at end
("", "", []),
// Empty value in between
("" | NOTE_FIELD, "", [NOTE_FIELD]),
].forEach { wrappedMultilineValue, content, additionalFields in
let blockField = Parser(plainText: "" | MULTILINE_BLOCK_START | wrappedMultilineValue).additionFields
XCTAssertEqual(blockField, [MULTILINE_BLOCK_START.title => content] + additionalFields)
let lineField = Parser(plainText: "" | MULTILINE_LINE_START | wrappedMultilineValue).additionFields
let contentWithoutLineBreaks = content.replacingOccurrences(of: "\n", with: " ")
XCTAssertEqual(lineField, [MULTILINE_LINE_START.title => contentWithoutLineBreaks] + additionalFields)
}
}
}

View file

@ -1,339 +0,0 @@
//
// PasswordTests.swift
// passKitTests
//
// Created by Danny Mösch on 02.05.18.
// Copyright © 2018 Bob Sun. All rights reserved.
//
import XCTest
@testable import passKit
class PasswordTest: XCTestCase {
static let EMPTY_STRING = ""
static let PASSWORD_NAME = "password"
static let PASSWORD_PATH = "/path/to/\(PASSWORD_NAME)"
static let PASSWORD_URL = URL(fileURLWithPath: PASSWORD_PATH)
static let PASSWORD_STRING = "abcd1234"
static let OTP_TOKEN = "otpauth://totp/email@email.com?secret=abcd1234"
static let SECURE_URL_FIELD = AdditionField(title: "url", content: "https://secure.com")
static let INSECURE_URL_FIELD = AdditionField(title: "url", content: "http://insecure.com")
static let LOGIN_FIELD = AdditionField(title: "login", content: "login name")
static let USERNAME_FIELD = AdditionField(title: "username", content: "some username")
static let NOTE_FIELD = AdditionField(title: "note", content: "A NOTE")
static let HINT_FIELD = AdditionField(title: "some hints", content: "äöüß // €³ %% −° && @²` | [{\\}],.<>")
func testUrl() {
let password1 = getPasswordObjectWith(content: PasswordTest.EMPTY_STRING)
XCTAssertEqual(password1.namePath, PasswordTest.PASSWORD_PATH)
}
func testLooksLikeOTP() {
XCTAssertTrue(Password.LooksLikeOTP(line: PasswordTest.OTP_TOKEN))
XCTAssertFalse(Password.LooksLikeOTP(line: "no_auth://totp/blabla"))
}
func testEmptyFile() {
let fileContent = PasswordTest.EMPTY_STRING
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PasswordTest.EMPTY_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), PasswordTest.EMPTY_STRING)
XCTAssertTrue(password.getFilteredAdditions().isEmpty)
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
}
func testOneEmptyLine() {
let fileContent = """
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, PasswordTest.EMPTY_STRING)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), PasswordTest.EMPTY_STRING)
XCTAssertTrue(password.getFilteredAdditions().isEmpty)
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
}
func testSimplePasswordFile() {
let passwordString = PasswordTest.PASSWORD_STRING
let urlField = PasswordTest.SECURE_URL_FIELD
let loginField = PasswordTest.LOGIN_FIELD
let usernameField = PasswordTest.USERNAME_FIELD
let noteField = PasswordTest.NOTE_FIELD
let fileContent = """
\(passwordString)
\(urlField.asString)
\(loginField.asString)
\(usernameField.asString)
\(noteField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(urlField, loginField, usernameField, noteField))
XCTAssertTrue(does(password, contain: urlField))
XCTAssertFalse(does(password, contain: loginField))
XCTAssertFalse(does(password, contain: usernameField))
XCTAssertTrue(does(password, contain: noteField))
XCTAssertEqual(password.urlString, urlField.content)
XCTAssertEqual(password.login, loginField.content)
XCTAssertEqual(password.username, usernameField.content)
}
func testTwoPasswords() {
let firstPasswordString = PasswordTest.PASSWORD_STRING
let secondPasswordString = "efgh5678"
let urlField = PasswordTest.INSECURE_URL_FIELD
let fileContent = """
\(firstPasswordString)
\(secondPasswordString)
\(urlField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, firstPasswordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(secondPasswordString, urlField.asString))
XCTAssertTrue(does(password, contain: urlField))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 1", content: secondPasswordString)))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, urlField.content)
XCTAssertNil(password.login)
}
func testNoPassword() {
let urlField = PasswordTest.SECURE_URL_FIELD
let noteField = PasswordTest.NOTE_FIELD
let fileContent = """
\(urlField.asString)
\(noteField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, urlField.asString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(noteField))
XCTAssertTrue(does(password, contain: noteField))
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
}
func testDuplicateKeys() {
let passwordString = PasswordTest.PASSWORD_STRING
let urlField1 = PasswordTest.SECURE_URL_FIELD
let urlField2 = PasswordTest.INSECURE_URL_FIELD
let fileContent = """
\(passwordString)
\(urlField1.asString)
\(urlField2.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(urlField1, urlField2))
XCTAssertTrue(does(password, contain: urlField1))
XCTAssertTrue(does(password, contain: urlField2))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, urlField1.content)
XCTAssertNil(password.login)
}
func testUnknownKeys() {
let passwordString = PasswordTest.PASSWORD_STRING
let value1 = "value 1"
let value2 = "value 2"
let value3 = "value 3"
let value4 = "value 4"
let noteField = PasswordTest.NOTE_FIELD
let urlField = PasswordTest.SECURE_URL_FIELD
let fileContent = """
\(passwordString)
\(value1)
\(noteField.asString)
\(value2)
\(value3)
\(urlField.asString)
\(value4)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(value1, noteField.asString, value2, value3, urlField.asString, value4))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 1", content: value1)))
XCTAssertTrue(does(password, contain: noteField))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 2", content: value2)))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 3", content: value3)))
XCTAssertTrue(does(password, contain: urlField))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 4", content: value4)))
XCTAssertNil(password.username)
XCTAssertEqual(password.urlString, urlField.content)
XCTAssertNil(password.login)
}
func testPasswordFileWithOtpToken() {
let passwordString = PasswordTest.PASSWORD_STRING
let noteField = PasswordTest.NOTE_FIELD
let otpToken = PasswordTest.OTP_TOKEN
let fileContent = """
\(passwordString)
\(noteField.asString)
\(otpToken)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(noteField.asString, otpToken))
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertNotNil(password.getOtp())
}
func testFirstLineIsOtpToken() {
let otpToken = PasswordTest.OTP_TOKEN
let fileContent = """
\(otpToken)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, otpToken)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), PasswordTest.EMPTY_STRING)
XCTAssertNil(password.username)
XCTAssertNil(password.urlString)
XCTAssertNil(password.login)
XCTAssertEqual(password.otpType, OtpType.totp)
XCTAssertNotNil(password.getOtp())
}
func testWrongOtpToken() {
let otpToken = "otpauth://htop/blabla"
let fileContent = """
\(otpToken)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, otpToken)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.otpType, OtpType.none)
XCTAssertNil(password.getOtp())
}
func testEmptyMultilineValues() {
let passwordString = PasswordTest.PASSWORD_STRING
let lineBreakField1 = AdditionField(title: "with line breaks", content: "| \n")
let lineBreakField2 = AdditionField(title: "with line breaks", content: "| \n ")
let noteField = PasswordTest.NOTE_FIELD
let noLineBreakField = AdditionField(title: "without line breaks", content: " > ")
let fileContent = """
\(passwordString)
\(lineBreakField1.asString)
\(lineBreakField2.asString)
\(noteField.asString)
\(noLineBreakField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(lineBreakField1, lineBreakField2, noteField, noLineBreakField))
XCTAssertTrue(does(password, contain: AdditionField(title: lineBreakField1.title, content: "")))
XCTAssertTrue(does(password, contain: AdditionField(title: lineBreakField2.title, content: "")))
XCTAssertTrue(does(password, contain: noteField))
XCTAssertTrue(does(password, contain: AdditionField(title: noLineBreakField.title, content: "")))
}
func testMultilineValues() {
let passwordString = PasswordTest.PASSWORD_STRING
let noteField = PasswordTest.NOTE_FIELD
let lineBreakField = AdditionField(title: "with line breaks", content: "|\n This is \n text spread over \n multiple lines! ")
let noLineBreakField = AdditionField(title: "without line breaks", content: " > \n This is \n text spread over\n multiple lines!")
let fileContent = """
\(passwordString)
\(lineBreakField.asString)
\(noteField.asString)
\(noLineBreakField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(lineBreakField, noteField, noLineBreakField))
XCTAssertTrue(does(password, contain: AdditionField(title: lineBreakField.title, content: "This is \n text spread over \nmultiple lines!")))
XCTAssertTrue(does(password, contain: noteField))
XCTAssertTrue(does(password, contain: AdditionField(title: noLineBreakField.title, content: "This is text spread over multiple lines!")))
}
func testMultilineValuesMixed() {
let passwordString = PasswordTest.PASSWORD_STRING
let hintField = PasswordTest.HINT_FIELD
let noteField = PasswordTest.NOTE_FIELD
let lineBreakField = AdditionField(title: "with line breaks", content: "|\n This is \n \(hintField.asString) spread over\n multiple lines!")
let noLineBreakField = AdditionField(title: "without line breaks", content: " > \n This is \n | \n text spread over\nmultiple lines!")
let fileContent = """
\(passwordString)
\(lineBreakField.asString)
\(noLineBreakField.asString)
\(noteField.asString)
"""
let password = getPasswordObjectWith(content: fileContent)
XCTAssertEqual(password.password, passwordString)
XCTAssertEqual(password.plainData, fileContent.data(using: .utf8))
XCTAssertEqual(password.getAdditionsPlainText(), asPlainText(lineBreakField, noLineBreakField, noteField))
XCTAssertTrue(does(password, contain: AdditionField(title: lineBreakField.title, content: "This is \n\(hintField.asString) spread over")))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 1", content: " multiple lines!")))
XCTAssertTrue(does(password, contain: AdditionField(title: noLineBreakField.title, content: "This is | text spread over")))
XCTAssertTrue(does(password, contain: AdditionField(title: "unknown 2", content: "multiple lines!")))
XCTAssertTrue(does(password, contain: noteField))
}
private func getPasswordObjectWith(content: String, url: URL = PasswordTest.PASSWORD_URL) -> Password {
return Password(name: PasswordTest.PASSWORD_NAME, url: url, plainText: content)
}
private func does(_ password: Password, contain field: AdditionField) -> Bool {
return password.getFilteredAdditions().contains(field)
}
private func asPlainText(_ strings: String...) -> String {
return strings.joined(separator: "\n")
}
private func asPlainText(_ fields: AdditionField...) -> String {
return fields.map { $0.asString }.joined(separator: "\n")
}
}