diff --git a/internal/api/ui/login/mfa_init_sms.go b/internal/api/ui/login/mfa_init_sms.go index 048677f0f..0326d26a3 100644 --- a/internal/api/ui/login/mfa_init_sms.go +++ b/internal/api/ui/login/mfa_init_sms.go @@ -12,17 +12,20 @@ const ( type smsInitData struct { userData - Edit bool - MFAType domain.MFAType - Phone string + Edit bool + MFAType domain.MFAType + Phone string + CountryCode string + CountryCodes []domain.CountryCode } type smsInitFormData struct { - Edit bool `schema:"edit"` - Resend bool `schema:"resend"` - Phone string `schema:"phone"` - NewPhone string `schema:"newPhone"` - Code string `schema:"code"` + Edit bool `schema:"edit"` + Resend bool `schema:"resend"` + Phone string `schema:"phone"` + NewPhone string `schema:"newPhone"` + Code string `schema:"code"` + CountryCode string `schema:"countryCode"` } // handleRegisterOTPSMS checks if the user has a verified phone number and will directly add OTP SMS as 2FA. @@ -57,6 +60,10 @@ func (l *Login) renderRegisterSMS(w http.ResponseWriter, r *http.Request, authRe data.baseData = l.getBaseData(r, authReq, translator, "InitMFAOTP.Title", "InitMFAOTP.Description", err) data.profileData = l.getProfileData(authReq) data.MFAType = domain.MFATypeOTPSMS + data.CountryCodes = domain.GetCountryCodes(translator.Lang(r)) + if data.CountryCode == "" { + data.CountryCode = "CH" // default to Switzerland + } l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFASMSInit], data, nil) } @@ -75,7 +82,7 @@ func (l *Login) handleRegisterSMSCheck(w http.ResponseWriter, r *http.Request) { ctx := setUserContext(r.Context(), authReq.UserID, authReq.UserOrgID) // save the current state - data := &smsInitData{Phone: formData.Phone} + data := &smsInitData{Phone: formData.Phone, CountryCode: formData.CountryCode} if formData.Edit { data.Edit = true @@ -95,7 +102,15 @@ func (l *Login) handleRegisterSMSCheck(w http.ResponseWriter, r *http.Request) { if formData.Code == "" { data.Phone = formData.NewPhone if formData.NewPhone != formData.Phone { - _, err = l.command.ChangeUserPhone(ctx, authReq.UserID, formData.NewPhone, l.userCodeAlg) + // Normalize phone with selected country code before saving + normalizedPhone, normErr := domain.PhoneNumber(formData.NewPhone).NormalizeWithRegion(formData.CountryCode) + if normErr != nil { + data.Edit = true + l.renderRegisterSMS(w, r, authReq, data, normErr) + return + } + data.Phone = string(normalizedPhone) + _, err = l.command.ChangeUserPhone(ctx, authReq.UserID, string(normalizedPhone), l.userCodeAlg) if err != nil { // stay in edit more data.Edit = true diff --git a/internal/api/ui/login/static/i18n/bg.yaml b/internal/api/ui/login/static/i18n/bg.yaml index cac371325..ad1f99692 100644 --- a/internal/api/ui/login/static/i18n/bg.yaml +++ b/internal/api/ui/login/static/i18n/bg.yaml @@ -115,6 +115,7 @@ InitMFAOTPSMS: Title: 2-факторна проверка DescriptionPhone: Създайте своя 2-фактор. Въведете телефонния си номер, за да го потвърдите. DescriptionCode: Създайте своя 2-фактор. Въведете получения код, за да потвърдите своя телефонен номер. + CountryCodeLabel: Държава PhoneLabel: Тайна CodeLabel: Код EditButtonText: редактиране diff --git a/internal/api/ui/login/static/i18n/cs.yaml b/internal/api/ui/login/static/i18n/cs.yaml index a2888ed51..10101d6b0 100644 --- a/internal/api/ui/login/static/i18n/cs.yaml +++ b/internal/api/ui/login/static/i18n/cs.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2-Faktorové ověření DescriptionPhone: Vytvořte svou 2-faktorovou autentizaci. Zadejte své telefonní číslo k jeho ověření. DescriptionCode: Vytvořte svou 2-faktorovou autentizaci. Zadejte obdržený kód k ověření vašeho telefonního čísla. + CountryCodeLabel: Země PhoneLabel: Telefon CodeLabel: Kód EditButtonText: Upravit diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index 698b5bccc..a1f9958fa 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: Zweitfaktor hinzufügen DescriptionPhone: Erstelle deinen Zweitfaktor. Gib deine Telefonnummer ein, um sie zu verifizieren. DescriptionCode: Erstelle deinen Zweitfaktor. Gib den erhaltenen Code ein, um deine Telefonnummer zu verifizieren. + CountryCodeLabel: Land PhoneLabel: Telefonnummer CodeLabel: Code EditButtonText: Bearbeiten diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index df6435b47..5d6b43734 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2-Factor Verification DescriptionPhone: Create your 2-factor. Enter your phone number to verify it. DescriptionCode: Create your 2-factor. Enter the received code to verify your phone number. + CountryCodeLabel: Country PhoneLabel: Phone CodeLabel: Code EditButtonText: Edit diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml index 4f41ec6b3..7eaa21587 100644 --- a/internal/api/ui/login/static/i18n/es.yaml +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: siguiente CancelButtonText: cancelar -InitMFASMS: +InitMFAOTPSMS: Title: Verificación de doble factor DescriptionPhone: Crea tu doble factor de autenticación. Introduce tu número de teléfono para verificarlo. DescriptionCode: Crea tu doble factor de autenticación. Ingrese el código recibido para verificar su número de teléfono. + CountryCodeLabel: País PhoneLabel: Número de teléfono CodeLabel: Código EditButtonText: editar diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index a3e001525..79ec6b21c 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: Vérification à deux facteurs DescriptionPhone: Créez votre 2-facteurs. Entrez votre numéro de téléphone pour le vérifier. DescriptionCode: Créez votre 2-facteurs. Entrez le code reçu pour vérifier votre numéro de téléphone. + CountryCodeLabel: Pays PhoneLabel: Numéro de téléphone CodeLabel: Code EditButtonText: Modifier diff --git a/internal/api/ui/login/static/i18n/hu.yaml b/internal/api/ui/login/static/i18n/hu.yaml index 98a4d87c0..005aeb4c4 100644 --- a/internal/api/ui/login/static/i18n/hu.yaml +++ b/internal/api/ui/login/static/i18n/hu.yaml @@ -105,6 +105,7 @@ InitMFAOTPSMS: Title: Kétfaktoros hitelesítés DescriptionPhone: Hozd létre a kétfaktoros hitelesítést. Add meg a telefonszámod a hitelesítéshez. DescriptionCode: Hozd létre a kétfaktoros hitelesítést. Add meg a kapott kódot a telefonszámod hitelesítéséhez. + CountryCodeLabel: Ország PhoneLabel: Telefon CodeLabel: Kód EditButtonText: Szerkesztés diff --git a/internal/api/ui/login/static/i18n/id.yaml b/internal/api/ui/login/static/i18n/id.yaml index 375508e29..c5a651fe4 100644 --- a/internal/api/ui/login/static/i18n/id.yaml +++ b/internal/api/ui/login/static/i18n/id.yaml @@ -105,6 +105,7 @@ InitMFAOTPSMS: Title: Verifikasi 2 Faktor DescriptionPhone: 'Buat 2 faktor Anda. ' DescriptionCode: 'Buat 2 faktor Anda. ' + CountryCodeLabel: Negara PhoneLabel: Telepon CodeLabel: Kode EditButtonText: Sunting diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index c0eeffd2b..49f9efa2c 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: Avanti CancelButtonText: annulla -InitMFASMS: +InitMFAOTPSMS: Title: Verificazione a 2 fattori DescriptionPhone: Crea il tuo 2 fattori. Inserisci il tuo numero di telefono per verificarlo. DescriptionCode: Crea il tuo 2 fattori. Inserisci il codice ricevuto per verificare il tuo numero di telefono. + CountryCodeLabel: Paese PhoneLabel: Numero di telefono CodeLabel: Codice EditButtonText: Modifica diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index c2bb3a02a..5d73c0eef 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: 次へ CancelButtonText: キャンセル -InitMFASMS: +InitMFAOTPSMS: Title: 二要素認証 DescriptionPhone: 二要素認証を作成します。確認するには電話番号を入力してください。 DescriptionCode: 二要素認証を作成します。受信したコードを入力して電話番号を確認します。 + CountryCodeLabel: 国 PhoneLabel: 電話番号 CodeLabel: コード EditButtonText: 編集 diff --git a/internal/api/ui/login/static/i18n/ko.yaml b/internal/api/ui/login/static/i18n/ko.yaml index 70ce40dea..f26ed2e74 100644 --- a/internal/api/ui/login/static/i18n/ko.yaml +++ b/internal/api/ui/login/static/i18n/ko.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2단계 인증 DescriptionPhone: 2단계 인증을 설정하세요. 전화번호를 입력하여 인증하세요. DescriptionCode: 2단계 인증을 설정하세요. 받은 코드를 입력하여 전화번호를 인증하세요. + CountryCodeLabel: 국가 PhoneLabel: 전화번호 CodeLabel: 코드 EditButtonText: 수정 diff --git a/internal/api/ui/login/static/i18n/mk.yaml b/internal/api/ui/login/static/i18n/mk.yaml index ad87372a6..600f68e5d 100644 --- a/internal/api/ui/login/static/i18n/mk.yaml +++ b/internal/api/ui/login/static/i18n/mk.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: следно CancelButtonText: откажи -InitMFASMS: +InitMFAOTPSMS: Title: Потврда на 2-факторска автентикација DescriptionPhone: Направете двофакторна автентикација. Внесете го вашиот телефонски број за да го потврдите. DescriptionCode: Направете двофакторна автентикација. Внесете го примениот код за да го потврдите вашиот телефонски број. + CountryCodeLabel: Земја PhoneLabel: Телефонски број CodeLabel: Код EditButtonText: Уредување diff --git a/internal/api/ui/login/static/i18n/nl.yaml b/internal/api/ui/login/static/i18n/nl.yaml index 2b415506d..e2ba4e31b 100644 --- a/internal/api/ui/login/static/i18n/nl.yaml +++ b/internal/api/ui/login/static/i18n/nl.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2-Factor Verificatie DescriptionPhone: Maak uw 2-factor aan. Voer uw telefoonnummer in om te verifiëren. DescriptionCode: Maak uw 2-factor aan. Voer de ontvangen code in om uw telefoonnummer te verifiëren. + CountryCodeLabel: Land PhoneLabel: Telefoon CodeLabel: Code EditButtonText: Bewerken diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index 64647ce74..99ab6340a 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: dalej CancelButtonText: anuluj -InitMFASMS: +InitMFAOTPSMS: Title: Weryfikacja 2-etapowa DescriptionPhone: Utwórz uwierzytelnianie dwuskładnikowe. Wprowadź swój numer telefonu, aby go zweryfikować. DescriptionCode: Utwórz uwierzytelnianie dwuskładnikowe. Wprowadź otrzymany kod, aby zweryfikować swój numer telefonu. + CountryCodeLabel: Kraj PhoneLabel: Numer telefonu CodeLabel: Kod EditButtonText: edytować diff --git a/internal/api/ui/login/static/i18n/pt.yaml b/internal/api/ui/login/static/i18n/pt.yaml index b8bc10ef4..00c8a0dfd 100644 --- a/internal/api/ui/login/static/i18n/pt.yaml +++ b/internal/api/ui/login/static/i18n/pt.yaml @@ -114,10 +114,11 @@ InitMFAOTP: NextButtonText: próximo CancelButtonText: cancelar -InitMFASMS: +InitMFAOTPSMS: Title: Verificação de 2 fatores DescriptionPhone: Crie sua verificação de 2 fatores. Digite seu número de telefone para verificá-lo. DescriptionCode: Crie sua verificação de 2 fatores. Digite o código recebido para verificar seu número de telefone. + CountryCodeLabel: País PhoneLabel: Número de telefone CodeLabel: Código EditButtonText: editar diff --git a/internal/api/ui/login/static/i18n/ro.yaml b/internal/api/ui/login/static/i18n/ro.yaml index 450209429..20334132f 100644 --- a/internal/api/ui/login/static/i18n/ro.yaml +++ b/internal/api/ui/login/static/i18n/ro.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: Verificare în 2 pași DescriptionPhone: Creează-ți autentificarea cu 2 factori. Introdu numărul tău de telefon pentru a-l verifica. DescriptionCode: Creează-ți autentificarea cu 2 factori. Introdu codul primit pentru a-ți verifica numărul de telefon. + CountryCodeLabel: Țară PhoneLabel: Telefon CodeLabel: Cod EditButtonText: Editează diff --git a/internal/api/ui/login/static/i18n/ru.yaml b/internal/api/ui/login/static/i18n/ru.yaml index 1ffa8ccdc..4f8935d09 100644 --- a/internal/api/ui/login/static/i18n/ru.yaml +++ b/internal/api/ui/login/static/i18n/ru.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: Настройка двухфакторной аутентификации DescriptionPhone: Введите номер телефона для его подтверждения. DescriptionCode: Введите код из СМС для подтверждения номера телефона. + CountryCodeLabel: Страна PhoneLabel: Номер телефона CodeLabel: Код из СМС EditButtonText: Изменить diff --git a/internal/api/ui/login/static/i18n/sv.yaml b/internal/api/ui/login/static/i18n/sv.yaml index b8dfd1151..5e1cc1db3 100644 --- a/internal/api/ui/login/static/i18n/sv.yaml +++ b/internal/api/ui/login/static/i18n/sv.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: Tvåfaktor verifiering DescriptionPhone: Verifiera med en andra faktor. Ange ditt mobilnummer. DescriptionCode: Verifiera med en andra faktor. Ange koden du fått på SMS för att bekräfta ditt mobilnr. + CountryCodeLabel: Land PhoneLabel: Mobilnr CodeLabel: Kod EditButtonText: Ändra diff --git a/internal/api/ui/login/static/i18n/tr.yaml b/internal/api/ui/login/static/i18n/tr.yaml index 1b336ddf5..f02de3302 100644 --- a/internal/api/ui/login/static/i18n/tr.yaml +++ b/internal/api/ui/login/static/i18n/tr.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2-Faktör Doğrulama DescriptionPhone: 2-faktörünüzü oluşturun. Doğrulamak için telefon numaranızı girin. DescriptionCode: 2-faktörünüzü oluşturun. Telefon numaranızı doğrulamak için aldığınız kodu girin. + CountryCodeLabel: Ülke PhoneLabel: Telefon CodeLabel: Kod EditButtonText: Düzenle diff --git a/internal/api/ui/login/static/i18n/uk.yaml b/internal/api/ui/login/static/i18n/uk.yaml index 5b69b7d43..44f4ce4a1 100644 --- a/internal/api/ui/login/static/i18n/uk.yaml +++ b/internal/api/ui/login/static/i18n/uk.yaml @@ -118,6 +118,7 @@ InitMFAOTPSMS: Title: 2-факторна перевірка DescriptionPhone: Створіть вашу 2-факторну автентифікацію. Введіть ваш номер телефону для підтвердження. DescriptionCode: Створіть вашу 2-факторну автентифікацію. Введіть отриманий код для підтвердження вашого номера телефону. + CountryCodeLabel: Країна PhoneLabel: Телефон CodeLabel: Код EditButtonText: Редагувати diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index fd0f0d1b9..7d5c7f301 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -109,21 +109,22 @@ InitMFAOTP: Title: 双因素验证 Description: 创建你的双因素。如果你还没有,请下载一个认证器应用程序。 OTPDescription: 使用您的身份验证器应用程序(例如 Google Authenticator)扫描代码或复制密码并在下方插入生成的代码。 + SecretLabel: 秘钥 + CodeLabel: 验证码 + NextButtonText: 继续 + CancelButtonText: 取消 + +InitMFAOTPSMS: + Title: 双因素验证 + DescriptionPhone: 创建双因素身份验证。输入您的电话号码进行验证。 + DescriptionCode: 创建双因素身份验证。输入收到的代码以验证您的电话号码。 + CountryCodeLabel: 国家 PhoneLabel: 电话号码 CodeLabel: 验证码 EditButtonText: 编辑 ResendButtonText: 重发代码 NextButtonText: 继续 -InitMFASMS: - Title: 双因素验证 - DescriptionPhone: 创建双因素身份验证。输入您的电话号码进行验证。 - DescriptionCode: 创建双因素身份验证。输入收到的代码以验证您的电话号码。 - SecretLabel: 秘钥 - CodeLabel: 验证码 - NextButtonText: 继续 - CancelButtonText: 取消 - InitMFAU2F: Title: 添加安全密钥 Description: 安全密钥是一种验证方法,可以内置在手机中、使用蓝牙或直接插入计算机的 USB 端口。 diff --git a/internal/api/ui/login/static/templates/mfa_init_otp_sms.html b/internal/api/ui/login/static/templates/mfa_init_otp_sms.html index e409096c5..aa0711dcc 100644 --- a/internal/api/ui/login/static/templates/mfa_init_otp_sms.html +++ b/internal/api/ui/login/static/templates/mfa_init_otp_sms.html @@ -22,15 +22,24 @@
{{if .Edit}} +
+ + +
{{else}} +

{{.Phone}}

- +
{{end}} {{if and .Phone (not .Edit) }} diff --git a/internal/domain/human_phone.go b/internal/domain/human_phone.go index 2bf60bca8..3a75398b6 100644 --- a/internal/domain/human_phone.go +++ b/internal/domain/human_phone.go @@ -1,9 +1,13 @@ package domain import ( + "fmt" + "sort" "time" "github.com/ttacon/libphonenumber" + "golang.org/x/text/language" + "golang.org/x/text/language/display" "github.com/zitadel/zitadel/internal/crypto" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" @@ -15,10 +19,17 @@ const defaultRegion = "CH" type PhoneNumber string func (p PhoneNumber) Normalize() (PhoneNumber, error) { + return p.NormalizeWithRegion(defaultRegion) +} + +func (p PhoneNumber) NormalizeWithRegion(region string) (PhoneNumber, error) { if p == "" { return p, zerrors.ThrowInvalidArgument(nil, "PHONE-Zt0NV", "Errors.User.Phone.Empty") } - phoneNr, err := libphonenumber.Parse(string(p), defaultRegion) + if region == "" { + region = defaultRegion + } + phoneNr, err := libphonenumber.Parse(string(p), region) if err != nil { return p, zerrors.ThrowInvalidArgument(err, "PHONE-so0wa", "Errors.User.Phone.Invalid") } @@ -71,3 +82,44 @@ func (s PhoneState) Valid() bool { func (s PhoneState) Exists() bool { return s == PhoneStateActive } + +// CountryCode represents a country with its ISO code and calling code +type CountryCode struct { + ISOCode string + CallingCode string + Name string +} + +// GetCountryCodes returns a list of country codes for phone number input using libphonenumber +func GetCountryCodes(lang language.Tag) []CountryCode { + regions := libphonenumber.GetSupportedRegions() + namer := display.Regions(lang) + + codes := make([]CountryCode, 0, len(regions)) + for region := range regions { + countryCode := libphonenumber.GetCountryCodeForRegion(region) + if countryCode == 0 { + continue + } + + // Get the display name for the region + regionTag, err := language.ParseRegion(region) + name := region + if err == nil { + name = namer.Name(regionTag) + } + + codes = append(codes, CountryCode{ + ISOCode: region, + CallingCode: fmt.Sprintf("+%d", countryCode), + Name: name, + }) + } + + // Sort by country name + sort.Slice(codes, func(i, j int) bool { + return codes[i].Name < codes[j].Name + }) + + return codes +} diff --git a/internal/domain/human_phone_test.go b/internal/domain/human_phone_test.go index 1bcd2ee87..063f61ba0 100644 --- a/internal/domain/human_phone_test.go +++ b/internal/domain/human_phone_test.go @@ -1,8 +1,12 @@ package domain import ( + "sort" + "strings" "testing" + "golang.org/x/text/language" + "github.com/zitadel/zitadel/internal/zerrors" ) @@ -104,3 +108,116 @@ func TestFormatPhoneNumber(t *testing.T) { }) } } + +func TestGetCountryCodes(t *testing.T) { + codes := GetCountryCodes(language.English) + + t.Run("returns non-empty list", func(t *testing.T) { + if len(codes) == 0 { + t.Error("GetCountryCodes() returned empty list") + } + }) + + t.Run("returns more than 200 countries", func(t *testing.T) { + // libphonenumber supports 240+ regions + if len(codes) < 200 { + t.Errorf("GetCountryCodes() returned %d countries, expected at least 200", len(codes)) + } + }) + + t.Run("list is sorted by name", func(t *testing.T) { + sorted := sort.SliceIsSorted(codes, func(i, j int) bool { + return codes[i].Name < codes[j].Name + }) + if !sorted { + t.Error("GetCountryCodes() did not return a list sorted by name") + } + }) + + t.Run("all entries have valid format", func(t *testing.T) { + for _, code := range codes { + if code.ISOCode == "" { + t.Errorf("country code has empty ISOCode: %+v", code) + } + if len(code.ISOCode) < 2 { + t.Errorf("country code has invalid ISOCode length: %+v", code) + } + if !strings.HasPrefix(code.CallingCode, "+") { + t.Errorf("country code CallingCode does not start with '+': %+v", code) + } + if code.Name == "" { + t.Errorf("country code has empty Name: %+v", code) + } + } + }) +} + +func TestGetCountryCodes_ContainsExpectedCountries(t *testing.T) { + codes := GetCountryCodes(language.English) + + // Build a map for easier lookup + codeMap := make(map[string]CountryCode) + for _, code := range codes { + codeMap[code.ISOCode] = code + } + + tests := []struct { + name string + isoCode string + callingCode string + countryName string + }{ + { + name: "United States", + isoCode: "US", + callingCode: "+1", + countryName: "United States", + }, + { + name: "Switzerland", + isoCode: "CH", + callingCode: "+41", + countryName: "Switzerland", + }, + { + name: "United Kingdom", + isoCode: "GB", + callingCode: "+44", + countryName: "United Kingdom", + }, + { + name: "Germany", + isoCode: "DE", + callingCode: "+49", + countryName: "Germany", + }, + { + name: "Australia", + isoCode: "AU", + callingCode: "+61", + countryName: "Australia", + }, + { + name: "Japan", + isoCode: "JP", + callingCode: "+81", + countryName: "Japan", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + code, found := codeMap[tt.isoCode] + if !found { + t.Errorf("GetCountryCodes() does not contain %s (%s)", tt.name, tt.isoCode) + return + } + if code.CallingCode != tt.callingCode { + t.Errorf("GetCountryCodes() %s CallingCode = %s, want %s", tt.isoCode, code.CallingCode, tt.callingCode) + } + if code.Name != tt.countryName { + t.Errorf("GetCountryCodes() %s Name = %s, want %s", tt.isoCode, code.Name, tt.countryName) + } + }) + } +}