wails/v3/internal/changelog/validator_test.go
2025-07-17 06:28:19 +10:00

440 lines
11 KiB
Go

package changelog
import (
"strings"
"testing"
)
func TestNewValidator(t *testing.T) {
validator := NewValidator()
if validator == nil {
t.Fatal("NewValidator() returned nil")
}
if validator.sectionHeaderRegex == nil {
t.Error("sectionHeaderRegex not initialized")
}
if validator.bulletPointRegex == nil {
t.Error("bulletPointRegex not initialized")
}
}
func TestValidateContent_ValidContent(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
## Added
- Add support for custom window icons in application options
- Add new SetWindowIcon() method to runtime API (#1234)
## Changed
- Update minimum Go version requirement to 1.21
- Improve error messages for invalid configuration files
## Fixed
- Fix memory leak in event system during window close operations (#5678)
- Fix crash when using context menus on Linux with Wayland`
result := validator.ValidateContent(content)
if !result.Valid {
t.Errorf("ValidateContent() should be valid, got errors: %s", result.GetValidationSummary())
}
if len(result.Errors) != 0 {
t.Errorf("Expected 0 errors, got %d", len(result.Errors))
}
}
func TestValidateContent_InvalidSectionHeaders(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
## InvalidSection
- Some content
### Added
- This should be ## not ###
##Added
- Missing space after ##`
result := validator.ValidateContent(content)
if result.Valid {
t.Error("ValidateContent() should be invalid for malformed section headers")
}
// Should have errors for invalid section headers
foundInvalidSection := false
foundMissingSpace := false
foundWrongLevel := false
for _, err := range result.Errors {
if strings.Contains(err.Message, "InvalidSection") {
foundInvalidSection = true
}
if strings.Contains(err.Message, "##Added") {
foundMissingSpace = true
}
if strings.Contains(err.Message, "### Added") {
foundWrongLevel = true
}
}
if !foundInvalidSection {
t.Error("Should have error for invalid section name")
}
if !foundMissingSpace {
t.Error("Should have error for missing space after ##")
}
if !foundWrongLevel {
t.Error("Should have error for wrong header level")
}
}
func TestValidateContent_BulletPointsOutsideSection(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
- This bullet point is outside any section
## Added
- This one is properly inside a section`
result := validator.ValidateContent(content)
if result.Valid {
t.Error("ValidateContent() should be invalid for bullet points outside sections")
}
foundOutsideError := false
for _, err := range result.Errors {
if strings.Contains(err.Message, "bullet point found outside of any section") {
foundOutsideError = true
break
}
}
if !foundOutsideError {
t.Error("Should have error for bullet point outside section")
}
}
func TestValidateContent_EmptyBulletPoints(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
## Added
- Valid bullet point
-
-
- Another valid bullet point`
result := validator.ValidateContent(content)
if result.Valid {
t.Error("ValidateContent() should be invalid for empty bullet points")
}
emptyBulletErrors := 0
for _, err := range result.Errors {
if strings.Contains(err.Message, "empty bullet point content") {
emptyBulletErrors++
}
}
if emptyBulletErrors != 2 {
t.Errorf("Expected 2 empty bullet point errors, got %d", emptyBulletErrors)
}
}
func TestValidateContent_MalformedBulletPoints(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
## Added
- Valid bullet point
-Invalid bullet point (no space)
* Valid asterisk bullet
*Invalid asterisk bullet (no space)`
result := validator.ValidateContent(content)
if result.Valid {
t.Error("ValidateContent() should be invalid for malformed bullet points")
}
malformedErrors := 0
for _, err := range result.Errors {
if strings.Contains(err.Message, "malformed bullet point") {
malformedErrors++
}
}
if malformedErrors != 2 {
t.Errorf("Expected 2 malformed bullet point errors, got %d", malformedErrors)
}
}
func TestValidateContent_NoValidSections(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
Some random content without proper sections.
More content here.`
result := validator.ValidateContent(content)
if result.Valid {
t.Error("ValidateContent() should be invalid when no valid sections found")
}
foundNoSectionsError := false
for _, err := range result.Errors {
if strings.Contains(err.Message, "no valid changelog sections found") {
foundNoSectionsError = true
break
}
}
if !foundNoSectionsError {
t.Error("Should have error for no valid sections")
}
}
func TestValidateContent_WithExampleSection(t *testing.T) {
validator := NewValidator()
content := `# Unreleased Changes
## Added
- Real feature addition
---
### Example Entries:
**Added:**
- Example feature that should be ignored
- Another example that should be ignored`
result := validator.ValidateContent(content)
if !result.Valid {
t.Errorf("ValidateContent() should be valid when example section is present, got errors: %s", result.GetValidationSummary())
}
}
func TestValidateEntry_ValidEntry(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{
Added: []string{"Add support for custom window icons in application options"},
Changed: []string{"Update minimum Go version requirement to 1.21"},
Fixed: []string{"Fix memory leak in event system during window close operations"},
}
result := validator.ValidateEntry(entry)
if !result.Valid {
t.Errorf("ValidateEntry() should be valid, got errors: %s", result.GetValidationSummary())
}
}
func TestValidateEntry_EmptyEntry(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{}
result := validator.ValidateEntry(entry)
if result.Valid {
t.Error("ValidateEntry() should be invalid for empty entry")
}
foundNoContentError := false
for _, err := range result.Errors {
if strings.Contains(err.Message, "changelog entry has no content") {
foundNoContentError = true
break
}
}
if !foundNoContentError {
t.Error("Should have error for empty entry")
}
}
func TestValidateEntry_ShortContent(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{
Added: []string{"Short", "Add feature with proper length description"},
Fixed: []string{"Fix bug"},
}
result := validator.ValidateEntry(entry)
if result.Valid {
t.Error("ValidateEntry() should be invalid for short content")
}
shortContentErrors := 0
for _, err := range result.Errors {
if strings.Contains(err.Message, "bullet point content too short") {
shortContentErrors++
}
}
if shortContentErrors != 2 {
t.Errorf("Expected 2 short content errors, got %d", shortContentErrors)
}
}
func TestValidateEntry_PlaceholderContent(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{
Added: []string{
"Add TODO feature that needs implementation",
"Add proper feature with good description",
"This is just a placeholder for now",
"FIXME: need to write proper description",
},
Fixed: []string{
"Fix example bug that was reported",
"TBD - will add description later",
},
}
result := validator.ValidateEntry(entry)
if result.Valid {
t.Error("ValidateEntry() should be invalid for placeholder content")
}
placeholderErrors := 0
for _, err := range result.Errors {
if strings.Contains(err.Message, "placeholder text") {
placeholderErrors++
}
}
// Expecting 4 placeholder errors: TODO, placeholder, example, TBD
// FIXME should also be caught but let's check what we actually get
if placeholderErrors < 2 {
t.Errorf("Expected at least 2 placeholder errors, got %d", placeholderErrors)
}
}
func TestValidateEntry_EmptyItems(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{
Added: []string{
"Valid addition with proper description",
"",
" ",
},
Fixed: []string{
"",
"Valid fix with proper description",
},
}
result := validator.ValidateEntry(entry)
if result.Valid {
t.Error("ValidateEntry() should be invalid for empty items")
}
emptyItemErrors := 0
for _, err := range result.Errors {
if strings.Contains(err.Message, "empty item") {
emptyItemErrors++
}
}
if emptyItemErrors != 3 {
t.Errorf("Expected 3 empty item errors, got %d", emptyItemErrors)
}
}
func TestValidateRequiredSections_HasContent(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{
Added: []string{"Add new feature"},
}
result := validator.ValidateRequiredSections(entry)
if !result.Valid {
t.Errorf("ValidateRequiredSections() should be valid when entry has content, got errors: %s", result.GetValidationSummary())
}
}
func TestValidateRequiredSections_EmptyEntry(t *testing.T) {
validator := NewValidator()
entry := &ChangelogEntry{}
result := validator.ValidateRequiredSections(entry)
// Currently, we don't enforce required sections, so this should pass
if !result.Valid {
t.Errorf("ValidateRequiredSections() should be valid (no strict requirements), got errors: %s", result.GetValidationSummary())
}
}
func TestValidationError_Error(t *testing.T) {
tests := []struct {
name string
err ValidationError
expected string
}{
{
name: "Error with line number",
err: ValidationError{
Field: "test_field",
Message: "test message",
Line: 42,
},
expected: "validation error at line 42 in test_field: test message",
},
{
name: "Error without line number",
err: ValidationError{
Field: "test_field",
Message: "test message",
Line: 0,
},
expected: "validation error in test_field: test message",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.err.Error(); got != tt.expected {
t.Errorf("ValidationError.Error() = %v, want %v", got, tt.expected)
}
})
}
}
func TestValidationResult_GetValidationSummary(t *testing.T) {
tests := []struct {
name string
result ValidationResult
contains []string
}{
{
name: "Valid result",
result: ValidationResult{
Valid: true,
Errors: []ValidationError{},
},
contains: []string{"Validation passed"},
},
{
name: "Invalid result with errors",
result: ValidationResult{
Valid: false,
Errors: []ValidationError{
{Field: "test1", Message: "error 1", Line: 1},
{Field: "test2", Message: "error 2", Line: 2},
},
},
contains: []string{"Validation failed", "2 error(s)", "error 1", "error 2"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
summary := tt.result.GetValidationSummary()
for _, expected := range tt.contains {
if !strings.Contains(summary, expected) {
t.Errorf("GetValidationSummary() should contain '%s', got: %s", expected, summary)
}
}
})
}
}