migration

This commit is contained in:
Daniel Arantes Loverde
2026-08-11 13:22:02 -03:00
parent c4d6998595
commit 7702836fe7
231 changed files with 4329 additions and 1958 deletions

View File

@@ -0,0 +1,161 @@
import Foundation
import Testing
@testable import PediFoods
private func decode<T: Decodable>(_ type: T.Type, _ json: String) throws -> T {
try JSONDecoder().decode(T.self, from: Data(json.utf8))
}
@Test("StoreSummary falls back to placeholder id/name when missing")
func storeSummaryDefaultsWhenFieldsMissing() throws {
let store = try decode(StoreSummary.self, "{}")
#expect(store.name == "Loja")
#expect(store.id.isEmpty == false)
#expect(store.rating == nil)
}
@Test("StoreSummary reads reviewsCount from any of its historical key names")
func storeSummaryReviewsCountKeyFallback() throws {
let viaReviews = try decode(StoreSummary.self, #"{"id":"1","name":"A","reviews":7}"#)
#expect(viaReviews.reviewsCount == 7)
let viaTotalReviews = try decode(StoreSummary.self, #"{"id":"1","name":"A","totalReviews":9}"#)
#expect(viaTotalReviews.reviewsCount == 9)
let viaReviewsCount = try decode(StoreSummary.self, #"{"id":"1","name":"A","reviewsCount":3}"#)
#expect(viaReviewsCount.reviewsCount == 3)
}
@Test("StoreSummary coerces rating/deliveryFee/distance from string or int payloads")
func storeSummaryFlexibleNumberCoercion() throws {
let asString = try decode(StoreSummary.self, #"{"id":"1","name":"A","rating":"4,5","deliveryFee":"7,90","distance":"1.2"}"#)
#expect(asString.rating == 4.5)
#expect(asString.deliveryFee == 7.90)
#expect(asString.distance == 1.2)
let asInt = try decode(StoreSummary.self, #"{"id":"1","name":"A","rating":5,"deliveryFee":0}"#)
#expect(asInt.rating == 5.0)
#expect(asInt.deliveryFee == 0.0)
}
@Test("StoreAddressInfo accepts either zipCode or lowercase zipcode key")
func storeAddressInfoZipCodeKeyFallback() throws {
let viaZipCode = try decode(StoreAddressInfo.self, #"{"zipCode":"01000-000"}"#)
#expect(viaZipCode.zipCode == "01000-000")
let viaZipcode = try decode(StoreAddressInfo.self, #"{"zipcode":"02000-000"}"#)
#expect(viaZipcode.zipCode == "02000-000")
}
@Test("StorePaymentMethodsInfo computed hasAny* flags are false when nothing accepted")
func paymentMethodsHasAnyFlagsDefaultFalse() throws {
let none = try decode(StorePaymentMethodsInfo.self, "{}")
#expect(none.hasAnyCreditCard == false)
#expect(none.hasAnyDebitCard == false)
#expect(none.hasAnyVoucher == false)
}
@Test("StorePaymentMethodsInfo computed hasAny* flags are true if any one brand is accepted")
func paymentMethodsHasAnyFlagsTrueOnSingleBrand() throws {
let visaOnly = try decode(StorePaymentMethodsInfo.self, #"{"acceptCreditVisa":true}"#)
#expect(visaOnly.hasAnyCreditCard == true)
#expect(visaOnly.hasAnyDebitCard == false)
let voucherOnly = try decode(StorePaymentMethodsInfo.self, #"{"acceptVoucherSodexo":true}"#)
#expect(voucherOnly.hasAnyVoucher == true)
}
@Test("StoreCatalogProduct decodes pizzaPrices as Double, Int, or comma-decimal String")
func storeCatalogProductPizzaPricesMultiType() throws {
let asDouble = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","pizzaPrices":{"M":29.9}}"#)
#expect(asDouble.pizzaPrices["M"] == 29.9)
let asInt = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","pizzaPrices":{"M":30}}"#)
#expect(asInt.pizzaPrices["M"] == 30.0)
let asString = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","pizzaPrices":{"M":"29,90"}}"#)
#expect(asString.pizzaPrices["M"] == 29.90)
let malformedString = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","pizzaPrices":{"M":"not-a-number"}}"#)
#expect(malformedString.pizzaPrices.isEmpty)
}
@Test("StoreCatalogProduct falls back through image/cover/photo and description/desc key names")
func storeCatalogProductImageAndDescriptionFallback() throws {
let viaCover = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","cover":"cover.png"}"#)
#expect(viaCover.image == "cover.png")
let viaPhoto = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","photo":"photo.png"}"#)
#expect(viaPhoto.image == "photo.png")
let viaDesc = try decode(StoreCatalogProduct.self, #"{"id":"1","name":"P","desc":"short desc"}"#)
#expect(viaDesc.description == "short desc")
}
@Test("StoreCatalogProduct falls back from addonGroups to legacy addons key")
func storeCatalogProductAddonGroupsFallback() throws {
let json = #"{"id":"1","name":"P","addons":[{"id":"g1","name":"Extras","items":[{"id":"i1","name":"Cheese","price":2.5}]}]}"#
let product = try decode(StoreCatalogProduct.self, json)
#expect(product.addonGroups.count == 1)
#expect(product.addonGroups[0].items[0].price == 2.5)
}
@Test("StoreAddonGroup and StoreAddonItem fall back to placeholder id/name when missing")
func addonGroupAndItemDefaults() throws {
let group = try decode(StoreAddonGroup.self, "{}")
#expect(group.name == "Adicionais")
#expect(group.items.isEmpty)
let item = try decode(StoreAddonItem.self, "{}")
#expect(item.name == "Item")
#expect(item.price == nil)
}
@Test("CepLookupResult prefers direct keys over normalized over raw")
func cepLookupResultPrefersDirectOverNormalizedOverRaw() throws {
let json = """
{
"zipCode": "01000-000",
"normalized": { "cep": "02000-000", "logradouro": "Normalized St" },
"raw": { "cep": "03000-000", "address": "Raw St" }
}
"""
let result = try decode(CepLookupResult.self, json)
#expect(result.zipCode == "01000-000")
#expect(result.street == "Normalized St")
}
@Test("CepLookupResult falls back to raw block when direct and normalized are absent")
func cepLookupResultFallsBackToRawBlock() throws {
let json = #"{"raw":{"cep":"03000-000","address":"Raw St","lat":"-23,55","lng":-46.6}}"#
let result = try decode(CepLookupResult.self, json)
#expect(result.zipCode == "03000-000")
#expect(result.street == "Raw St")
#expect(result.latitude == -23.55)
#expect(result.longitude == -46.6)
}
@Test("CepLookupResult resolves via alternate Portuguese field names (bairro/cidade/uf)")
func cepLookupResultPortugueseFieldNames() throws {
let json = #"{"bairro":"Centro","cidade":"São Paulo","uf":"SP"}"#
let result = try decode(CepLookupResult.self, json)
#expect(result.neighborhood == "Centro")
#expect(result.city == "São Paulo")
#expect(result.state == "SP")
}
@Test("StoreCatalogCategory falls back to empty products array and placeholder id/name")
func storeCatalogCategoryDefaults() throws {
let category = try decode(StoreCatalogCategory.self, "{}")
#expect(category.name == "Categoria")
#expect(category.isPizzaCategory == false)
#expect(category.products.isEmpty)
}
@Test("CustomerProfile decodes addressBook from the snake_case address_book key")
func customerProfileAddressBookSnakeCaseKey() throws {
let json = #"{"id":"1","name":"A","email":"a@a.com","address_book":[{"id":"addr1","city":"SP"}]}"#
let profile = try decode(CustomerProfile.self, json)
#expect(profile.addressBook?.count == 1)
#expect(profile.addressBook?.first?.city == "SP")
}