Skip to content

Commit

Permalink
feat/SSM-34: introduce lpc parser sanitizer & validator
Browse files Browse the repository at this point in the history
  • Loading branch information
j1mb0b committed Jan 30, 2025
1 parent 8004ef5 commit 968f4c2
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 58 deletions.
11 changes: 11 additions & 0 deletions service-app/internal/parser/lpc_parser/lpc_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package lpc_parser

import (
"github.com/ministryofjustice/opg-scanning/internal/parser"
"github.com/ministryofjustice/opg-scanning/internal/types/lpc_types"
)

func Parse(data []byte) (interface{}, error) {
doc := &lpc_types.LPCDocument{}
return parser.DocumentParser(data, doc)
}
38 changes: 38 additions & 0 deletions service-app/internal/parser/lpc_parser/lpc_sanitizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package lpc_parser

import (
"fmt"

"github.com/ministryofjustice/opg-scanning/internal/parser"
"github.com/ministryofjustice/opg-scanning/internal/types/lpc_types"
)

type Sanitizer struct {
doc *lpc_types.LPCDocument
baseSanitizer *parser.BaseSanitizer
}

func NewSanitizer() *Sanitizer {
return &Sanitizer{
doc: &lpc_types.LPCDocument{},
}
}

func (v *Sanitizer) Setup(doc interface{}) error {
if doc == nil {
return fmt.Errorf("document is nil")
}

v.doc = doc.(*lpc_types.LPCDocument)
v.baseSanitizer = parser.NewBaseSanitizer(v.doc)

return nil
}

func (s *Sanitizer) Sanitize() (interface{}, error) {
if err := s.baseSanitizer.SanitizeStruct(s.doc); err != nil {
return nil, err
}

return s.doc, nil
}
47 changes: 47 additions & 0 deletions service-app/internal/parser/lpc_parser/lpc_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package lpc_parser

import (
"fmt"

"github.com/ministryofjustice/opg-scanning/internal/parser"
"github.com/ministryofjustice/opg-scanning/internal/types/lpc_types"
)

type Validator struct {
doc *lpc_types.LPCDocument
baseValidator *parser.BaseValidator
}

func NewValidator() *Validator {
return &Validator{
doc: &lpc_types.LPCDocument{},
}
}

func (v *Validator) Setup(doc interface{}) error {
if doc == nil {
return fmt.Errorf("document is nil")
}

v.doc = doc.(*lpc_types.LPCDocument)
v.baseValidator = parser.NewBaseValidator(v.doc)

return nil
}

func (v *Validator) Validate() error {
// Common witness validations
v.baseValidator.WitnessSignatureFullNameAddressValidator("Page10", "Section9")

// Section validations
v.baseValidator.ValidateSignatureDate("Page11", "Section10", "")

// TODO add more validation for LPC

// Return errors if any
if messages := v.baseValidator.GetValidatorErrorMessages(); len(messages) > 0 {
return fmt.Errorf("failed to validate LP1H document: %v", messages)
}

return nil
}
71 changes: 71 additions & 0 deletions service-app/internal/parser/lpc_parser/lpc_validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package lpc_parser

import (
"regexp"
"testing"

"github.com/ministryofjustice/opg-scanning/internal/parser"
"github.com/ministryofjustice/opg-scanning/internal/util"
"github.com/stretchr/testify/require"
)

func TestValidLPCXML(t *testing.T) {
validator := getLPCValidator(t, "LPC-valid.xml")
err := validator.Validate()
require.NoError(t, err, "Expected no errors for valid LPC XML")
}

func TestInvalidLPCXML(t *testing.T) {
validator := getLPCValidator(t, "LPC-invalid.xml")
err := validator.Validate()
require.Error(t, err, "Expected validation errors for invalid LPC XML, but got none")

messages := validator.(*Validator).baseValidator.GetValidatorErrorMessages()

expectedErrMsgs := []string{
"(?i)found only 1 attorney block; 2 required", // minOccurs=2 but only 1 present
"(?i)missing Donor element in ContinuationSheet1", // required <Donor> is missing
"(?i)invalid element BURNN instead of BURN", // if there is a typo in the element name
"(?i)missing PhysicalPage element", // required element not found
}

t.Log("Actual messages from validation:")
t.Log(messages)

for _, pattern := range expectedErrMsgs {
regex, compErr := regexp.Compile(pattern)
require.NoError(t, compErr, "Failed to compile regex for pattern: %s", pattern)

found := false
for _, msg := range messages {
if regex.MatchString(msg) {
found = true
break
}
}

require.True(t, found, "Expected error message pattern not found: %s", pattern)
}
}

func TestInvalidLPCDateOrderXML(t *testing.T) {
validator := getLPCValidator(t, "LPC-invalid-dates.xml")
err := validator.Validate()
require.Error(t, err, "Expected validation errors due to date ordering but got none")

messages := validator.(*Validator).baseValidator.GetValidatorErrorMessages()

found := util.Contains(messages, "all form dates must be before the earliest applicant signature date")
require.True(t, found, "Expected date ordering validation error not found in messages")
}

func getLPCValidator(t *testing.T, fileName string) parser.CommonValidator {
xml := util.LoadXMLFileTesting(t, "../../../xml/"+fileName)

doc, err := Parse(xml)
require.NoError(t, err, "Failed to parse %s", fileName)

validator := NewValidator()
validator.Setup(doc)
return validator
}
31 changes: 31 additions & 0 deletions service-app/xml/LPC-invalid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<LPC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="example.xsd">
<Page1>
<ContinuationSheet1>
<!-- Only ONE Attorney here, which violates minOccurs="2" -->
<Attorney>
<Attorney>true</Attorney>
<ReplacementAttorney>false</ReplacementAttorney>
<PersonToNotify>false</PersonToNotify>
<Title>Mr</Title>
<FirstName>Harry</FirstName>
<LastName>Potter</LastName>
<DOB>1980-07-31</DOB>
<Address>
<Address1>4 Privet Drive</Address1>
<Address2>Little Whinging</Address2>
<Address3>Surrey</Address3>
<Postcode>HP1 1HP</Postcode>
</Address>
<Email>[email protected]</Email>
</Attorney>

<!-- Donor is missing entirely. The XSD requires <Donor> here. -->
</ContinuationSheet1>

<!-- BURN is present but spelled incorrectly: "BURNN" (typo) -->
<BURNN>Typo in element name</BURNN>

<!-- Missing <PhysicalPage> altogether -->
</Page1>
</LPC>
29 changes: 0 additions & 29 deletions service-app/xml/LPC-section4-replacement-attorney-trust-corp.xml

This file was deleted.

29 changes: 0 additions & 29 deletions service-app/xml/LPC-section4-replacement-attorney.xml

This file was deleted.

48 changes: 48 additions & 0 deletions service-app/xml/LPC-valid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<LPC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="example.xsd">
<Page1>
<ContinuationSheet1>
<Attorney>
<Attorney>true</Attorney>
<ReplacementAttorney>false</ReplacementAttorney>
<PersonToNotify>false</PersonToNotify>
<Title>Mr</Title>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<DOB>1970-01-01</DOB>
<Address>
<Address1>1 Example Road</Address1>
<Address2>Example Town</Address2>
<Address3>Example County</Address3>
<Postcode>EX4 MPL</Postcode>
</Address>
<Email>[email protected]</Email>
</Attorney>
<Attorney>
<Attorney>false</Attorney>
<ReplacementAttorney>true</ReplacementAttorney>
<PersonToNotify>false</PersonToNotify>
<Title>Mrs</Title>
<FirstName>Mary</FirstName>
<LastName>Smith</LastName>
<DOB>1975-05-05</DOB>
<Address>
<Address1>2 Example Avenue</Address1>
<Address2>Example Town</Address2>
<Address3>Example County</Address3>
<Postcode>EX4 MPL</Postcode>
</Address>
<Email>[email protected]</Email>
</Attorney>

<!-- Required Donor block -->
<Donor>
<FullName>Jane Donor</FullName>
<Signature>true</Signature>
<Date>2025-01-29</Date>
</Donor>
</ContinuationSheet1>
<BURN>Some text here</BURN>
<PhysicalPage>1</PhysicalPage>
</Page1>
</LPC>

0 comments on commit 968f4c2

Please sign in to comment.