Skip to content

Commit c635fc7

Browse files
authored
Merge pull request #55 from nativeapptemplate/substrate-v2--add-tests
Add unit tests for untested Models and Date extension
2 parents 54955d9 + 4e4ab6d commit c635fc7

7 files changed

Lines changed: 321 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// DateExtensionsTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
import Foundation
7+
@testable import NativeAppTemplate
8+
import Testing
9+
10+
struct DateExtensionsTest {
11+
private func date(_ iso: String) throws -> Date {
12+
try #require(iso.iso8601)
13+
}
14+
15+
@Test
16+
func dateByAddingNumberOfSecondsAddsPositiveSeconds() throws {
17+
let date = try date("2026-01-01T00:00:00.000Z")
18+
let later = date.dateByAddingNumberOfSeconds(3600)
19+
#expect(later.timeIntervalSince(date) == 3600)
20+
}
21+
22+
@Test
23+
func dateByAddingNumberOfSecondsAcceptsNegative() throws {
24+
let date = try date("2026-01-01T00:00:00.000Z")
25+
let earlier = date.dateByAddingNumberOfSeconds(-60)
26+
#expect(earlier.timeIntervalSince(date) == -60)
27+
}
28+
29+
@Test
30+
func cardDateStringFormatsAsYearMonthDay() throws {
31+
let date = try date("2026-04-29T12:34:56.000Z")
32+
let formatted = date.cardDateString
33+
// Output is locale-fixed (en_US_POSIX) but uses the device's current
34+
// time zone, so we just assert the format shape.
35+
#expect(formatted.count == 10)
36+
#expect(formatted.contains("/"))
37+
let parts = formatted.split(separator: "/")
38+
#expect(parts.count == 3)
39+
#expect(parts[0].count == 4) // year
40+
#expect(parts[1].count == 2) // month
41+
#expect(parts[2].count == 2) // day
42+
}
43+
44+
@Test
45+
func cardTimeStringFormatsAsHourMinute() throws {
46+
let date = try date("2026-04-29T12:34:56.000Z")
47+
let formatted = date.cardTimeString
48+
#expect(formatted.count == 5)
49+
#expect(formatted[formatted.index(formatted.startIndex, offsetBy: 2)] == ":")
50+
}
51+
52+
@Test
53+
func cardDateTimeStringConcatenatesDateAndTime() throws {
54+
let date = try date("2026-04-29T12:34:56.000Z")
55+
let combined = date.cardDateTimeString
56+
#expect(combined == "\(date.cardDateString) \(date.cardTimeString)")
57+
}
58+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// ItemTagTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct ItemTagTest {
10+
@Test
11+
func toJsonWrapsFieldsUnderItemTagKey() throws {
12+
var itemTag = ItemTag()
13+
itemTag.name = "Table 1"
14+
itemTag.description = "Window seat"
15+
16+
let json = itemTag.toJson()
17+
let inner = try #require(json["item_tag"] as? [String: Any])
18+
19+
#expect(inner["name"] as? String == "Table 1")
20+
#expect(inner["description"] as? String == "Window seat")
21+
}
22+
23+
@Test
24+
func toJsonOnlyIncludesNameAndDescription() throws {
25+
var itemTag = ItemTag()
26+
itemTag.id = "abc-123"
27+
itemTag.shopId = "shop-1"
28+
itemTag.name = "n"
29+
itemTag.description = "d"
30+
itemTag.position = 5
31+
itemTag.shopName = "Cafe"
32+
33+
let json = itemTag.toJson()
34+
let inner = try #require(json["item_tag"] as? [String: Any])
35+
36+
#expect(inner.keys.sorted() == ["description", "name"])
37+
}
38+
39+
@Test
40+
func defaultsAreEmptyAndIdled() {
41+
let itemTag = ItemTag()
42+
#expect(itemTag.id.isEmpty)
43+
#expect(itemTag.shopId.isEmpty)
44+
#expect(itemTag.name.isEmpty)
45+
#expect(itemTag.description.isEmpty)
46+
#expect(itemTag.position == 0)
47+
#expect(itemTag.state == .idled)
48+
#expect(itemTag.completedAt == nil)
49+
}
50+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// SendConfirmationTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct SendConfirmationTest {
10+
@Test
11+
func toJsonIncludesEmailAndRedirectUrl() {
12+
let send = SendConfirmation(email: "alice@example.com")
13+
14+
let json = send.toJson()
15+
16+
#expect(json["email"] as? String == "alice@example.com")
17+
#expect(json["redirect_url"] as? String == send.redirectUrl)
18+
#expect(json.keys.sorted() == ["email", "redirect_url"])
19+
}
20+
21+
@Test
22+
func defaultRedirectUrlPointsToConfirmationResult() {
23+
let send = SendConfirmation(email: "x")
24+
#expect(send.redirectUrl.hasSuffix("/shopkeeper_auth/confirmation_result"))
25+
}
26+
27+
@Test
28+
func redirectUrlCanBeOverridden() {
29+
let send = SendConfirmation(email: "x", redirectUrl: "https://custom.example/cb")
30+
#expect(send.toJson()["redirect_url"] as? String == "https://custom.example/cb")
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// SendResetPasswordTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct SendResetPasswordTest {
10+
@Test
11+
func toJsonIncludesEmailAndRedirectUrl() {
12+
let send = SendResetPassword(email: "alice@example.com")
13+
14+
let json = send.toJson()
15+
16+
#expect(json["email"] as? String == "alice@example.com")
17+
#expect(json["redirect_url"] as? String == send.redirectUrl)
18+
#expect(json.keys.sorted() == ["email", "redirect_url"])
19+
}
20+
21+
@Test
22+
func defaultRedirectUrlPointsToResetPasswordEdit() {
23+
let send = SendResetPassword(email: "x")
24+
#expect(send.redirectUrl.hasSuffix("/shopkeeper_auth/reset_password/edit"))
25+
}
26+
27+
@Test
28+
func redirectUrlCanBeOverridden() {
29+
let send = SendResetPassword(email: "x", redirectUrl: "https://custom.example/cb")
30+
#expect(send.toJson()["redirect_url"] as? String == "https://custom.example/cb")
31+
}
32+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// ShopTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct ShopTest {
10+
private func makeShop() -> Shop {
11+
Shop(
12+
id: "shop-1",
13+
name: "Cafe",
14+
description: "A nice cafe",
15+
timeZone: "Asia/Tokyo"
16+
)
17+
}
18+
19+
@Test
20+
func toJsonForCreateWrapsFieldsUnderShopKey() throws {
21+
let shop = makeShop()
22+
23+
let json = shop.toJsonForCreate()
24+
let inner = try #require(json["shop"] as? [String: Any])
25+
26+
#expect(inner["name"] as? String == "Cafe")
27+
#expect(inner["description"] as? String == "A nice cafe")
28+
#expect(inner["time_zone"] as? String == "Asia/Tokyo")
29+
}
30+
31+
@Test
32+
func toJsonForCreateExcludesIdAndCounts() throws {
33+
let shop = makeShop()
34+
let inner = try #require(shop.toJsonForCreate()["shop"] as? [String: Any])
35+
36+
#expect(inner["id"] == nil)
37+
#expect(inner["item_tags_count"] == nil)
38+
#expect(inner["completed_item_tags_count"] == nil)
39+
#expect(inner.keys.sorted() == ["description", "name", "time_zone"])
40+
}
41+
42+
@Test
43+
func toJsonForUpdateMatchesCreateShape() throws {
44+
let shop = makeShop()
45+
let create = try #require(shop.toJsonForCreate()["shop"] as? [String: Any])
46+
let update = try #require(shop.toJsonForUpdate()["shop"] as? [String: Any])
47+
48+
#expect(create.keys.sorted() == update.keys.sorted())
49+
#expect(create["name"] as? String == update["name"] as? String)
50+
#expect(create["description"] as? String == update["description"] as? String)
51+
#expect(create["time_zone"] as? String == update["time_zone"] as? String)
52+
}
53+
54+
@Test
55+
func defaultCountsAreZero() {
56+
let shop = makeShop()
57+
#expect(shop.itemTagsCount == 0)
58+
#expect(shop.completedItemTagsCount == 0)
59+
}
60+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// SignUpTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct SignUpTest {
10+
@Test
11+
func toJsonForCreateIncludesPasswordAndDefaultsPlatformToIos() {
12+
let signUp = SignUp(
13+
name: "Alice",
14+
email: "alice@example.com",
15+
timeZone: "Asia/Tokyo",
16+
password: "secret123"
17+
)
18+
19+
let json = signUp.toJsonForCreate()
20+
21+
#expect(json["name"] as? String == "Alice")
22+
#expect(json["email"] as? String == "alice@example.com")
23+
#expect(json["time_zone"] as? String == "Asia/Tokyo")
24+
#expect(json["current_platform"] as? String == "ios")
25+
#expect(json["password"] as? String == "secret123")
26+
}
27+
28+
@Test
29+
func toJsonForUpdateExcludesPasswordAndPlatform() {
30+
let signUp = SignUp(
31+
name: "Alice",
32+
email: "alice@example.com",
33+
timeZone: "Asia/Tokyo",
34+
password: "secret123"
35+
)
36+
37+
let json = signUp.toJsonForUpdate()
38+
39+
#expect(json["name"] as? String == "Alice")
40+
#expect(json["email"] as? String == "alice@example.com")
41+
#expect(json["time_zone"] as? String == "Asia/Tokyo")
42+
#expect(json["password"] == nil)
43+
#expect(json["current_platform"] == nil)
44+
#expect(json.keys.sorted() == ["email", "name", "time_zone"])
45+
}
46+
47+
@Test
48+
func currentPlatformDefaultsToIos() {
49+
let signUp = SignUp(name: "n", email: "e", timeZone: "tz")
50+
#expect(signUp.currentPlatform == "ios")
51+
}
52+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// UpdatePasswordTest.swift
3+
// NativeAppTemplate
4+
//
5+
6+
@testable import NativeAppTemplate
7+
import Testing
8+
9+
struct UpdatePasswordTest {
10+
@Test
11+
func toJsonWrapsFieldsUnderShopkeeperKey() throws {
12+
let update = UpdatePassword(
13+
currentPassword: "old-pw",
14+
password: "new-pw",
15+
passwordConfirmation: "new-pw"
16+
)
17+
18+
let json = update.toJson()
19+
let inner = try #require(json["shopkeeper"] as? [String: Any])
20+
21+
#expect(inner["current_password"] as? String == "old-pw")
22+
#expect(inner["password"] as? String == "new-pw")
23+
#expect(inner["password_confirmation"] as? String == "new-pw")
24+
}
25+
26+
@Test
27+
func toJsonOnlyIncludesPasswordFields() throws {
28+
let update = UpdatePassword(
29+
currentPassword: "a",
30+
password: "b",
31+
passwordConfirmation: "c"
32+
)
33+
let inner = try #require(update.toJson()["shopkeeper"] as? [String: Any])
34+
35+
#expect(inner.keys.sorted() == ["current_password", "password", "password_confirmation"])
36+
}
37+
}

0 commit comments

Comments
 (0)