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
93 lines
1.9 KiB
Go
93 lines
1.9 KiB
Go
package fileutil_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/juanatsap/cv-site/internal/fileutil"
|
|
)
|
|
|
|
func TestFindDataFile(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
filename string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Existing file - cv-en.json",
|
|
filename: "data/cv-en.json",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Existing file - cv-es.json",
|
|
filename: "data/cv-es.json",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Existing file - ui-en.json",
|
|
filename: "data/ui-en.json",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "Non-existent file",
|
|
filename: "data/non-existent.json",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Empty filename",
|
|
filename: "",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := fileutil.FindDataFile(tt.filename)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("FindDataFile() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !tt.wantErr && got == "" {
|
|
t.Error("FindDataFile() returned empty path for existing file")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadJSON(t *testing.T) {
|
|
// Test with actual CV data
|
|
t.Run("Load valid CV JSON", func(t *testing.T) {
|
|
type TestCV struct {
|
|
Personal struct {
|
|
Name string `json:"name"`
|
|
} `json:"personal"`
|
|
}
|
|
|
|
var cv TestCV
|
|
err := fileutil.LoadJSON("data/cv-en.json", &cv)
|
|
if err != nil {
|
|
t.Fatalf("LoadJSON() unexpected error: %v", err)
|
|
}
|
|
|
|
if cv.Personal.Name == "" {
|
|
t.Error("LoadJSON() loaded CV but name is empty")
|
|
}
|
|
})
|
|
|
|
// Test with non-existent file
|
|
t.Run("Load non-existent file", func(t *testing.T) {
|
|
var data map[string]interface{}
|
|
err := fileutil.LoadJSON("data/does-not-exist.json", &data)
|
|
if err == nil {
|
|
t.Error("LoadJSON() expected error for non-existent file")
|
|
}
|
|
})
|
|
|
|
// Test with invalid target
|
|
t.Run("Load with nil target", func(t *testing.T) {
|
|
err := fileutil.LoadJSON("data/cv-en.json", nil)
|
|
if err == nil {
|
|
t.Error("LoadJSON() expected error for nil target")
|
|
}
|
|
})
|
|
}
|