mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-10-13 04:55:28 +02:00
refactor: comprehensive code quality improvements
- Add logger to all exporters for consistent error handling - Add Course.Validate() method with ValidationError type - Create shared test fixtures in internal/testdata - Refactor config passing: NewArticulateParser now takes *config.Config - Add NewLoggerFromConfig() factory for logger creation - Add context cancellation with signal handling (SIGINT/SIGTERM) - Add godoc examples for loggers and exporters - Add HTML exporter error path tests - Add fuzz tests for parser and HTMLCleaner - Fix markdown output: trim whitespace, collapse blank lines - Remove test output files from repo
This commit is contained in:
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -664,6 +665,161 @@ func BenchmarkCourse_JSONUnmarshal(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCourse_Validate tests the Course.Validate method.
|
||||
func TestCourse_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
course Course
|
||||
wantErr bool
|
||||
errField string
|
||||
errSubstr string
|
||||
}{
|
||||
{
|
||||
name: "valid course",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
Lessons: []Lesson{
|
||||
{ID: "lesson-1", Title: "First Lesson"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid course with no lessons",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
Lessons: []Lesson{},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing share ID",
|
||||
course: Course{
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "ShareID",
|
||||
errSubstr: "share ID is required",
|
||||
},
|
||||
{
|
||||
name: "missing course ID",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
Title: "Test Course",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "Course.ID",
|
||||
errSubstr: "course ID is required",
|
||||
},
|
||||
{
|
||||
name: "missing course title",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "Course.Title",
|
||||
errSubstr: "course title is required",
|
||||
},
|
||||
{
|
||||
name: "lesson with empty ID",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
Lessons: []Lesson{
|
||||
{ID: "", Title: "First Lesson"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "Course.Lessons",
|
||||
errSubstr: "lesson at index 0 has empty ID",
|
||||
},
|
||||
{
|
||||
name: "lesson with empty title",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
Lessons: []Lesson{
|
||||
{ID: "lesson-1", Title: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "Course.Lessons",
|
||||
errSubstr: "lesson at index 0 has empty title",
|
||||
},
|
||||
{
|
||||
name: "second lesson invalid",
|
||||
course: Course{
|
||||
ShareID: "test-share-id",
|
||||
Course: CourseInfo{
|
||||
ID: "course-123",
|
||||
Title: "Test Course",
|
||||
Lessons: []Lesson{
|
||||
{ID: "lesson-1", Title: "First Lesson"},
|
||||
{ID: "lesson-2", Title: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
errField: "Course.Lessons",
|
||||
errSubstr: "lesson at index 1 has empty title",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.course.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Course.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.wantErr {
|
||||
var valErr *ValidationError
|
||||
if ok := errors.As(err, &valErr); !ok {
|
||||
t.Errorf("Expected ValidationError, got %T", err)
|
||||
return
|
||||
}
|
||||
if valErr.Field != tt.errField {
|
||||
t.Errorf("ValidationError.Field = %q, want %q", valErr.Field, tt.errField)
|
||||
}
|
||||
if valErr.Message != tt.errSubstr {
|
||||
t.Errorf("ValidationError.Message = %q, want %q", valErr.Message, tt.errSubstr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidationError_Error tests the ValidationError.Error method.
|
||||
func TestValidationError_Error(t *testing.T) {
|
||||
err := &ValidationError{Field: "TestField", Message: "test message"}
|
||||
expected := "validation error on TestField: test message"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("ValidationError.Error() = %q, want %q", err.Error(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
// compareMaps compares two any values that should be maps.
|
||||
func compareMaps(original, unmarshaled any) bool {
|
||||
origMap, origOk := original.(map[string]any)
|
||||
|
||||
Reference in New Issue
Block a user