5322d3cd8e
- Introduced benchmark tests for various XML parsing methods, including parsing from strings, bytes, and readers, to evaluate performance. - Added unit tests for the parser interface and validation mechanisms, ensuring robust error handling and validation logic. - Implemented tests for utility functions related to XML processing, enhancing overall test coverage and reliability. - Created a test suite for the TED domain, validating the structure and functionality of contract notices and related entities. - Developed a comprehensive test runner script to facilitate automated testing, coverage analysis, and performance benchmarking for the XML parser package.
374 lines
9.5 KiB
Go
374 lines
9.5 KiB
Go
package xmlparser
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"testing"
|
|
)
|
|
|
|
// MockParsable is a test implementation of the Parsable interface
|
|
type MockParsable struct {
|
|
XMLName xml.Name `xml:"MockElement"`
|
|
ID string `xml:"id,attr"`
|
|
Name string `xml:"name"`
|
|
Value string `xml:"value"`
|
|
Valid bool `xml:"-"` // Controls validation behavior
|
|
}
|
|
|
|
// Validate implements the Parsable interface
|
|
func (m MockParsable) Validate() error {
|
|
// Only fail validation if explicitly set to false in tests
|
|
// When parsed from XML, Valid will be false (default), but we want validation to pass
|
|
// unless specifically testing validation failures
|
|
if m.ID == "" {
|
|
return ValidationError{
|
|
Field: "id",
|
|
Message: "ID is required",
|
|
}
|
|
}
|
|
if m.Name == "" {
|
|
return ValidationError{
|
|
Field: "name",
|
|
Message: "Name is required",
|
|
}
|
|
}
|
|
// Only check the Valid field if this appears to be a test scenario
|
|
// where Valid was explicitly set to false (we can detect this by checking
|
|
// if both ID and Name are populated but Valid is false)
|
|
if m.ID != "" && m.Name != "" && !m.Valid {
|
|
// This is likely a test case where Valid was explicitly set to false
|
|
// In real XML parsing scenarios, Valid will be false by default but validation should pass
|
|
// We'll use a different approach - check if Value is specifically set to indicate test failure
|
|
if m.Value == "test-validation-failure" {
|
|
return ValidationError{
|
|
Field: "valid",
|
|
Message: "mock validation failed",
|
|
Value: "false",
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetID implements the Parsable interface
|
|
func (m MockParsable) GetID() string {
|
|
return m.ID
|
|
}
|
|
|
|
func TestDefaultParserOptions(t *testing.T) {
|
|
options := DefaultParserOptions()
|
|
|
|
// Test default values
|
|
if !options.StrictValidation {
|
|
t.Error("Expected StrictValidation to be true by default")
|
|
}
|
|
|
|
if !options.AllowEmptyElements {
|
|
t.Error("Expected AllowEmptyElements to be true by default")
|
|
}
|
|
|
|
if options.PreserveWhitespace {
|
|
t.Error("Expected PreserveWhitespace to be false by default")
|
|
}
|
|
|
|
if options.IgnoreUnknownElements {
|
|
t.Error("Expected IgnoreUnknownElements to be false by default")
|
|
}
|
|
|
|
if options.MaxDepth != 100 {
|
|
t.Errorf("Expected MaxDepth to be 100, got %d", options.MaxDepth)
|
|
}
|
|
|
|
expectedMaxSize := int64(10 * 1024 * 1024) // 10MB
|
|
if options.MaxSize != expectedMaxSize {
|
|
t.Errorf("Expected MaxSize to be %d, got %d", expectedMaxSize, options.MaxSize)
|
|
}
|
|
}
|
|
|
|
func TestXMLMetadata(t *testing.T) {
|
|
metadata := &XMLMetadata{
|
|
XMLName: xml.Name{Local: "TestElement", Space: "http://test.com"},
|
|
Namespace: "http://test.com",
|
|
Version: "1.0",
|
|
Encoding: "UTF-8",
|
|
}
|
|
|
|
if metadata.XMLName.Local != "TestElement" {
|
|
t.Errorf("Expected XMLName.Local to be 'TestElement', got '%s'", metadata.XMLName.Local)
|
|
}
|
|
|
|
if metadata.XMLName.Space != "http://test.com" {
|
|
t.Errorf("Expected XMLName.Space to be 'http://test.com', got '%s'", metadata.XMLName.Space)
|
|
}
|
|
|
|
if metadata.Namespace != "http://test.com" {
|
|
t.Errorf("Expected Namespace to be 'http://test.com', got '%s'", metadata.Namespace)
|
|
}
|
|
|
|
if metadata.Version != "1.0" {
|
|
t.Errorf("Expected Version to be '1.0', got '%s'", metadata.Version)
|
|
}
|
|
|
|
if metadata.Encoding != "UTF-8" {
|
|
t.Errorf("Expected Encoding to be 'UTF-8', got '%s'", metadata.Encoding)
|
|
}
|
|
}
|
|
|
|
func TestParseResult(t *testing.T) {
|
|
mockData := &MockParsable{
|
|
ID: "test-123",
|
|
Name: "Test Mock",
|
|
Value: "test value",
|
|
Valid: true,
|
|
}
|
|
|
|
metadata := &XMLMetadata{
|
|
XMLName: xml.Name{Local: "MockElement"},
|
|
Namespace: "http://test.com",
|
|
Version: "1.0",
|
|
Encoding: "UTF-8",
|
|
}
|
|
|
|
result := &ParseResult[MockParsable]{
|
|
Data: mockData,
|
|
Metadata: metadata,
|
|
Errors: []string{"test error"},
|
|
Warnings: []string{"test warning"},
|
|
}
|
|
|
|
// Test data
|
|
if result.Data == nil {
|
|
t.Error("Expected Data to not be nil")
|
|
} else {
|
|
if result.Data.GetID() != "test-123" {
|
|
t.Errorf("Expected Data.GetID() to be 'test-123', got '%s'", result.Data.GetID())
|
|
}
|
|
}
|
|
|
|
// Test metadata
|
|
if result.Metadata == nil {
|
|
t.Error("Expected Metadata to not be nil")
|
|
} else {
|
|
if result.Metadata.Version != "1.0" {
|
|
t.Errorf("Expected Metadata.Version to be '1.0', got '%s'", result.Metadata.Version)
|
|
}
|
|
}
|
|
|
|
// Test errors
|
|
if len(result.Errors) != 1 {
|
|
t.Errorf("Expected 1 error, got %d", len(result.Errors))
|
|
} else if result.Errors[0] != "test error" {
|
|
t.Errorf("Expected first error to be 'test error', got '%s'", result.Errors[0])
|
|
}
|
|
|
|
// Test warnings
|
|
if len(result.Warnings) != 1 {
|
|
t.Errorf("Expected 1 warning, got %d", len(result.Warnings))
|
|
} else if result.Warnings[0] != "test warning" {
|
|
t.Errorf("Expected first warning to be 'test warning', got '%s'", result.Warnings[0])
|
|
}
|
|
}
|
|
|
|
func TestMockParsable(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
mock MockParsable
|
|
expectError bool
|
|
errorField string
|
|
}{
|
|
{
|
|
name: "Valid mock",
|
|
mock: MockParsable{
|
|
ID: "test-123",
|
|
Name: "Test Name",
|
|
Value: "test value",
|
|
Valid: true,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Invalid mock - Valid flag false",
|
|
mock: MockParsable{
|
|
ID: "test-123",
|
|
Name: "Test Name",
|
|
Value: "test-validation-failure",
|
|
Valid: false,
|
|
},
|
|
expectError: true,
|
|
errorField: "valid",
|
|
},
|
|
{
|
|
name: "Invalid mock - Missing ID",
|
|
mock: MockParsable{
|
|
Name: "Test Name",
|
|
Value: "test value",
|
|
Valid: true,
|
|
},
|
|
expectError: true,
|
|
errorField: "id",
|
|
},
|
|
{
|
|
name: "Invalid mock - Missing Name",
|
|
mock: MockParsable{
|
|
ID: "test-123",
|
|
Value: "test value",
|
|
Valid: true,
|
|
},
|
|
expectError: true,
|
|
errorField: "name",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.mock.Validate()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected validation error but got none")
|
|
return
|
|
}
|
|
|
|
if validationErr, ok := err.(ValidationError); ok {
|
|
if validationErr.Field != tt.errorField {
|
|
t.Errorf("Expected error field '%s', got '%s'", tt.errorField, validationErr.Field)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected ValidationError, got %T", err)
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("Unexpected validation error: %v", err)
|
|
}
|
|
}
|
|
|
|
// Test GetID method
|
|
expectedID := tt.mock.ID
|
|
if tt.mock.GetID() != expectedID {
|
|
t.Errorf("Expected GetID() to return '%s', got '%s'", expectedID, tt.mock.GetID())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParserInterface(t *testing.T) {
|
|
// Test that our MockParsable implements the Parsable interface
|
|
var _ Parsable = MockParsable{}
|
|
|
|
// Test that our parser implements the Parser interface
|
|
parser := NewParser[MockParsable](nil)
|
|
var _ Parser[MockParsable] = parser
|
|
|
|
// Test parser creation with default options
|
|
if parser == nil {
|
|
t.Error("Expected parser to not be nil")
|
|
}
|
|
|
|
// Test parser creation with custom options
|
|
customOptions := &ParserOptions{
|
|
StrictValidation: false,
|
|
AllowEmptyElements: false,
|
|
PreserveWhitespace: true,
|
|
IgnoreUnknownElements: true,
|
|
MaxDepth: 50,
|
|
MaxSize: 1024 * 1024, // 1MB
|
|
}
|
|
|
|
customParser := NewParser[MockParsable](customOptions)
|
|
if customParser == nil {
|
|
t.Error("Expected custom parser to not be nil")
|
|
}
|
|
|
|
// Verify the parser can be cast to GenericParser to access options
|
|
if genericParser, ok := customParser.(*GenericParser[MockParsable]); ok {
|
|
if genericParser.options.MaxDepth != 50 {
|
|
t.Errorf("Expected custom MaxDepth to be 50, got %d", genericParser.options.MaxDepth)
|
|
}
|
|
if genericParser.options.StrictValidation {
|
|
t.Error("Expected custom StrictValidation to be false")
|
|
}
|
|
} else {
|
|
t.Error("Expected parser to be of type *GenericParser[MockParsable]")
|
|
}
|
|
}
|
|
|
|
func TestParserOptionsCustomization(t *testing.T) {
|
|
options := &ParserOptions{
|
|
StrictValidation: false,
|
|
AllowEmptyElements: false,
|
|
PreserveWhitespace: true,
|
|
IgnoreUnknownElements: true,
|
|
MaxDepth: 200,
|
|
MaxSize: 5 * 1024 * 1024, // 5MB
|
|
}
|
|
|
|
// Test each option individually
|
|
if options.StrictValidation {
|
|
t.Error("Expected StrictValidation to be false")
|
|
}
|
|
|
|
if options.AllowEmptyElements {
|
|
t.Error("Expected AllowEmptyElements to be false")
|
|
}
|
|
|
|
if !options.PreserveWhitespace {
|
|
t.Error("Expected PreserveWhitespace to be true")
|
|
}
|
|
|
|
if !options.IgnoreUnknownElements {
|
|
t.Error("Expected IgnoreUnknownElements to be true")
|
|
}
|
|
|
|
if options.MaxDepth != 200 {
|
|
t.Errorf("Expected MaxDepth to be 200, got %d", options.MaxDepth)
|
|
}
|
|
|
|
expectedMaxSize := int64(5 * 1024 * 1024)
|
|
if options.MaxSize != expectedMaxSize {
|
|
t.Errorf("Expected MaxSize to be %d, got %d", expectedMaxSize, options.MaxSize)
|
|
}
|
|
}
|
|
|
|
func TestEmptyParseResult(t *testing.T) {
|
|
result := &ParseResult[MockParsable]{}
|
|
|
|
if result.Data != nil {
|
|
t.Error("Expected empty result Data to be nil")
|
|
}
|
|
|
|
if result.Metadata != nil {
|
|
t.Error("Expected empty result Metadata to be nil")
|
|
}
|
|
|
|
if result.Errors != nil {
|
|
t.Error("Expected empty result Errors to be nil")
|
|
}
|
|
|
|
if result.Warnings != nil {
|
|
t.Error("Expected empty result Warnings to be nil")
|
|
}
|
|
}
|
|
|
|
func TestXMLMetadataEmpty(t *testing.T) {
|
|
metadata := &XMLMetadata{}
|
|
|
|
if metadata.XMLName.Local != "" {
|
|
t.Errorf("Expected empty XMLName.Local, got '%s'", metadata.XMLName.Local)
|
|
}
|
|
|
|
if metadata.XMLName.Space != "" {
|
|
t.Errorf("Expected empty XMLName.Space, got '%s'", metadata.XMLName.Space)
|
|
}
|
|
|
|
if metadata.Namespace != "" {
|
|
t.Errorf("Expected empty Namespace, got '%s'", metadata.Namespace)
|
|
}
|
|
|
|
if metadata.Version != "" {
|
|
t.Errorf("Expected empty Version, got '%s'", metadata.Version)
|
|
}
|
|
|
|
if metadata.Encoding != "" {
|
|
t.Errorf("Expected empty Encoding, got '%s'", metadata.Encoding)
|
|
}
|
|
}
|