Files
sodalive-ios/work/tests/GiftDecodingContractCheck.swift
T

63 lines
5.7 KiB
Swift

import Foundation
@main
enum GiftDecodingContractCheck {
static var assertions = 0
static func check(_ condition: Bool, _ message: String) {
guard condition else { print("FAIL: \(message)"); exit(1) }
assertions += 1
}
static func decode<T: Decodable>(_ type: T.Type, _ json: String) throws -> T {
try JSONDecoder().decode(type, from: Data(json.utf8))
}
static func main() {
let form = #"{"success":true,"message":"Options loaded","data":{"sizes":[{"sizeCode":"S","name":"Small","basePriceCan":100,"salePriceCan":80}],"categories":[{"categoryId":7,"name":"Flowers","requiresDamageWaiver":true}]}}"#
let create = #"{"success":true,"message":"Gift created","data":{"applicationNo":"G-1001","status":"CREATED","statusName":"Created","priceCan":80,"trackingDeadlineAt":"2026-10-10T12:00:00Z"}}"#
let list = #"{"success":true,"message":"Gifts loaded","data":{"totalCount":1,"items":[{"applicationNo":"G-1001","direction":"SENT","status":"CREATED","statusName":"Created","priceCan":80,"categoryName":"Flowers","sizeName":"Small","counterpartMemberId":42,"counterpartNickname":"Mina","counterpartProfileImageUrl":null,"createdAt":"2026-10-07T12:00:00Z"}],"page":1,"size":20,"hasNext":false}}"#
let detail = #"{"success":true,"message":"Gift detail loaded","data":{"applicationNo":"G-1001","direction":"SENT","status":"CREATED","statusName":"Created","giftInfo":{"recipientCreatorNickname":"Mina","senderNickname":"Jae","sizeName":"Small","categoryName":"Flowers","applicationNo":null,"paidCan":80,"tracking":"123456","shippingRequestedAt":null},"senderInfo":{"name":"Jae","phoneNumber":"010-1234-5678","address":"Seoul"},"recipientAddress":null,"mailbox":{"name":"Mina","address":"Busan","phoneNumber":"010-9876-5432"},"trackingRequired":true,"recipientAddressRequired":false,"recipientAddressDeadlineAt":null,"delivery":{"canceledAt":null,"undeliverableAt":null,"undeliverableReason":null},"statusTimeline":[{"status":"CREATED","statusName":"Created","occurredAt":"2026-10-07T12:00:00Z"}]}}"#
let options = try! decode(ApiResponse<GiftFormOptionsResponse>.self, form)
check(options.success, "form success")
check(options.data?.sizes.first?.sizeCode == "S" && options.data?.sizes.first?.name == "Small", "size fields")
check(options.data?.categories.first?.categoryId == 7 && options.data?.categories.first?.name == "Flowers", "category fields")
check(options.data?.categories.first?.requiresDamageWaiver == true, "waiver")
let created = try! decode(ApiResponse<CreateGiftResponse>.self, create)
check(created.data?.applicationNo == "G-1001" && created.data?.statusName == "Created", "create fields")
check(created.data?.priceCan == 80 && created.data?.trackingDeadlineAt == "2026-10-10T12:00:00Z", "create values")
let gifts = try! decode(ApiResponse<GiftListResponse>.self, list)
check(gifts.data?.totalCount == 1 && gifts.data?.page == 1 && gifts.data?.size == 20 && gifts.data?.hasNext == false, "list paging")
check(gifts.data?.items.first?.categoryName == "Flowers" && gifts.data?.items.first?.counterpartNickname == "Mina", "list values")
check(gifts.data?.items.first?.counterpartProfileImageUrl == nil, "list nullable profile image")
let gift = try! decode(ApiResponse<GiftDetailResponse>.self, detail).data!
check(gift.applicationNo == "G-1001" && gift.giftInfo.categoryName == "Flowers", "detail values")
check(gift.applicationNo == "G-1001" && gift.giftInfo.applicationNo == nil, "detail application numbers")
check(gift.senderInfo?.name == "Jae" && gift.mailbox?.name == "Mina", "detail nested values")
check(gift.recipientAddress == nil && gift.recipientAddressDeadlineAt == nil && gift.giftInfo.shippingRequestedAt == nil, "detail nullable values")
check(gift.statusTimeline.first?.statusName == "Created", "timeline status name")
for json in [
#"{"success":true,"message":"Gift canceled","data":{"applicationNo":"G-1001","status":"CANCELED","statusName":"Canceled","priceCan":80,"canceledAt":"2026-10-07T13:00:00Z"}}"#,
#"{"success":true,"message":"Tracking registered","data":{"applicationNo":"G-1001","status":"TRACKING_REGISTERED","statusName":"Tracking registered","courierCompanyName":"Fast Express","trackingNumber":"123456","trackingRegisteredAt":"2026-10-07T13:00:00Z","recipientAddressDeadlineAt":"2026-10-10T12:00:00Z"}}"#,
#"{"success":true,"message":"Recipient address registered","data":{"applicationNo":"G-1001","status":"ADDRESS_REGISTERED","statusName":"Address registered","recipientAddressRegisteredAt":"2026-10-07T14:00:00Z"}}"#
] {
check(try! decode(ApiResponseWithoutData.self, json).success, "mutation success envelope")
}
check(try! decode(ApiResponseWithoutData.self, #"{"success":true}"#).success, "envelope without data")
check(!(try! decode(ApiResponseWithoutData.self, #"{"success":false,"data":{"applicationNo":"G-1001"}}"#).success), "false success")
let size = try! decode(GiftSizeOptionResponse.self, #"{"sizeCode":"S","name":"Small","basePriceCan":100,"salePriceCan":80}"#)
check(size.name == "Small", "direct size.name")
let category = try! decode(GiftCategoryOptionResponse.self, #"{"categoryId":7,"name":"Flowers","requiresDamageWaiver":true}"#)
check(category.name == "Flowers", "direct category.name")
let timeline = try! decode(GiftStatusTimelineResponse.self, #"{"status":"CREATED","statusName":"Created","occurredAt":"2026-10-07T12:00:00Z"}"#)
check(timeline.statusName == "Created", "direct timeline.statusName")
print("PASS: \(assertions) assertions")
}
}