9240a863d1
Part 1: Shared Utilities - Create internal/fileutil package with FindDataFile() and LoadJSON() - Create internal/lang package with language constants and validation - Eliminate 46 lines of code duplication between cv/loader.go and ui/loader.go - Simplify cv/loader.go from 69 to 36 lines (-48%) - Simplify ui/loader.go from 56 to 24 lines (-57%) Part 2: Validation Layer - Add comprehensive validation in internal/models/cv/validation.go - Validate Personal (name, email format, URLs) - Validate Experience (required fields, dates) - Validate Education (required fields) - Validate Skills (proficiency ranges 1-5, categories) - Validate Languages (proficiency levels 1-5) - Validate Projects (title, URLs) - Validate Meta (version, language) - Integrate validation into LoadCV() - automatic on load - Create ValidationError and ValidationErrors types for clear error reporting - Report all validation errors at once (better UX) Testing: - Add comprehensive tests for fileutil package (FindDataFile, LoadJSON) - Add tests for lang package (IsValid, Validate, All) - Add 280+ validation test cases covering edge cases - All tests pass with real CV data (cv-en.json, cv-es.json) - Fixed validation to allow both URLs and local paths for gitRepoUrl Documentation: - Create _go-learning/refactorings/002-shared-utilities-validation.md - Document architecture, benefits, testing, and interview talking points - Explain WHY decisions were made (DRY, type safety, data integrity) Benefits: - DRY: Single source of truth for utilities - Type safety: Language constants instead of magic strings - Data integrity: Validation catches errors at load time - Better errors: Clear messages showing all issues at once - Maintainability: Centralized utilities easier to update
122 lines
2.0 KiB
Go
122 lines
2.0 KiB
Go
package lang_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/juanatsap/cv-site/internal/lang"
|
|
)
|
|
|
|
func TestIsValid(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
language string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "Valid - English",
|
|
language: "en",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Valid - Spanish",
|
|
language: "es",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Invalid - French",
|
|
language: "fr",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Invalid - Empty",
|
|
language: "",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Invalid - Uppercase",
|
|
language: "EN",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := lang.IsValid(tt.language); got != tt.want {
|
|
t.Errorf("IsValid() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
language string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Valid - English",
|
|
language: "en",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Valid - Spanish",
|
|
language: "es",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Invalid - French",
|
|
language: "fr",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Invalid - Empty",
|
|
language: "",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := lang.Validate(tt.language)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
if err != nil && tt.wantErr {
|
|
// Check that error message includes supported languages
|
|
errMsg := err.Error()
|
|
if errMsg == "" {
|
|
t.Error("Validate() error message is empty")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAll(t *testing.T) {
|
|
all := lang.All()
|
|
|
|
if len(all) != 2 {
|
|
t.Errorf("All() returned %d languages, want 2", len(all))
|
|
}
|
|
|
|
// Check that it contains en and es
|
|
hasEN := false
|
|
hasES := false
|
|
for _, l := range all {
|
|
if l == "en" {
|
|
hasEN = true
|
|
}
|
|
if l == "es" {
|
|
hasES = true
|
|
}
|
|
}
|
|
|
|
if !hasEN {
|
|
t.Error("All() missing 'en'")
|
|
}
|
|
if !hasES {
|
|
t.Error("All() missing 'es'")
|
|
}
|
|
}
|