Skip to content

Commit

Permalink
Merge 4735ea4 into 94bf9c7
Browse files Browse the repository at this point in the history
  • Loading branch information
mfdeveloper508 committed Jul 11, 2022
2 parents 94bf9c7 + 4735ea4 commit e6549fc
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 207 deletions.
4 changes: 0 additions & 4 deletions field/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ func (f *Binary) Pack() ([]byte, error) {
return nil, fmt.Errorf("failed to encode content: %w", err)
}

if len(packed) == 0 {
return []byte{}, nil
}

packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(packed))
if err != nil {
return nil, fmt.Errorf("failed to encode length: %w", err)
Expand Down
17 changes: 7 additions & 10 deletions field/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,6 @@ func TestBinaryField(t *testing.T) {
require.Equal(t, in, packed)
})

t.Run("Pack returns empty struct when given zero-length data", func(t *testing.T) {
bin := NewBinaryValue([]byte{})
bin.SetSpec(spec)

packed, err := bin.Pack()

require.NoError(t, err)
require.Equal(t, []byte{}, packed)
})

t.Run("String returns binary data encoded in HEX", func(t *testing.T) {
bin := NewBinary(spec)
bin.Value = in
Expand Down Expand Up @@ -119,4 +109,11 @@ func TestBinaryField(t *testing.T) {
require.NoError(t, err)
require.Equal(t, `"AB"`, string(marshalledJSON))
})

t.Run("returns error for zero value when fixed length and no padding specified", func(t *testing.T) {
bin := NewBinary(spec)
_, err := bin.Pack()

require.EqualError(t, err, "failed to encode length: field length: 0 should be fixed: 10")
})
}
27 changes: 8 additions & 19 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,6 @@ func (f *Composite) Pack() ([]byte, error) {
return nil, err
}

if len(packed) == 0 {
return []byte{}, nil
}

packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(packed))
if err != nil {
return nil, fmt.Errorf("failed to encode length: %w", err)
Expand All @@ -259,15 +255,10 @@ func (f *Composite) Unpack(data []byte) (int, error) {
return 0, fmt.Errorf("failed to decode length: %w", err)
}

hasPrefix := false
if offset != 0 {
hasPrefix = true
}

// data is stripped of the prefix before it is provided to unpack().
// Therefore, it is unaware of when to stop parsing unless we bound the
// length of the slice by the data length.
read, err := f.unpack(data[offset:offset+dataLen], hasPrefix)
read, err := f.unpack(data[offset : offset+dataLen])
if err != nil {
return 0, err
}
Expand All @@ -283,7 +274,7 @@ func (f *Composite) Unpack(data []byte) (int, error) {
// pack all subfields in full. However, unlike Unpack(), it requires the
// aggregate length of the subfields not to be encoded in the prefix.
func (f *Composite) SetBytes(data []byte) error {
_, err := f.unpack(data, false)
_, err := f.unpack(data)
return err
}

Expand Down Expand Up @@ -347,7 +338,6 @@ func (f *Composite) pack() ([]byte, error) {
field, ok := f.subfields[tag]
if !ok {
return nil, fmt.Errorf("no subfield for tag %s", tag)
// continue
}

if _, set := f.setSubfields[tag]; !set {
Expand All @@ -371,18 +361,20 @@ func (f *Composite) pack() ([]byte, error) {
return nil, fmt.Errorf("failed to pack subfield %v: %w", tag, err)
}
packed = append(packed, packedBytes...)

}

return packed, nil
}

func (f *Composite) unpack(data []byte, hasPrefix bool) (int, error) {
func (f *Composite) unpack(data []byte) (int, error) {
if f.spec.Tag.Enc != nil {
return f.unpackSubfieldsByTag(data)
}
return f.unpackSubfields(data, hasPrefix)
return f.unpackSubfields(data)
}

func (f *Composite) unpackSubfields(data []byte, hasPrefix bool) (int, error) {
func (f *Composite) unpackSubfields(data []byte) (int, error) {
offset := 0
for _, tag := range f.orderedSpecFieldTags {
field, ok := f.subfields[tag]
Expand All @@ -398,11 +390,8 @@ func (f *Composite) unpackSubfields(data []byte, hasPrefix bool) (int, error) {
f.setSubfields[tag] = struct{}{}

offset += read

if hasPrefix && offset >= len(data) {
break
}
}

return offset, nil
}

Expand Down
188 changes: 46 additions & 142 deletions field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func TestCompositePacking(t *testing.T) {
require.EqualError(t, err, "failed to pack subfield 1: failed to encode length: field length: 4 should be fixed: 2")
})

t.Run("Pack returns error on failure to encode length", func(t *testing.T) {
t.Run("Pack returns error when encoded data length is larger than specified fixed max length", func(t *testing.T) {
invalidSpec := &Spec{
// Base field length < summation of lengths of subfields
// This will throw an error when encoding the field's length.
Expand Down Expand Up @@ -330,7 +330,7 @@ func TestCompositePacking(t *testing.T) {
}

composite := NewComposite(invalidSpec)
err := composite.SetData(data)
err := composite.Marshal(data)
require.NoError(t, err)

_, err = composite.Pack()
Expand Down Expand Up @@ -450,11 +450,8 @@ func TestCompositePacking(t *testing.T) {
}),
},
}
data := &CompositeTestData{}

composite := NewComposite(invalidSpec)
err := composite.SetData(data)
require.NoError(t, err)

// Length of input too long, causing failure to decode length.
read, err := composite.Unpack([]byte("ABCD123"))
Expand Down Expand Up @@ -496,7 +493,7 @@ func TestCompositePacking(t *testing.T) {
}

func TestCompositePackingWithTags(t *testing.T) {
t.Run("Pack returns error on failure to encode length", func(t *testing.T) {
t.Run("Pack returns error when encoded data length is larger than specified fixed max length", func(t *testing.T) {
// Base field length < summation of (lengths of subfields + IDs).
// This will throw an error when encoding the field's length.
invalidSpec := &Spec{
Expand Down Expand Up @@ -542,6 +539,49 @@ func TestCompositePackingWithTags(t *testing.T) {
require.EqualError(t, err, "failed to encode length: field length: 12 should be fixed: 6")
})

t.Run("Pack returns error when encoded data length is larger than specified variable max length", func(t *testing.T) {
invalidSpec := &Spec{
Length: 8,
Pref: prefix.ASCII.LL,
Tag: &TagSpec{
Length: 2,
Enc: encoding.ASCII,
Pad: padding.Left('0'),
Sort: sort.StringsByInt,
},
Subfields: map[string]Field{
"1": NewString(&Spec{
Length: 2,
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"2": NewString(&Spec{
Length: 2,
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"3": NewNumeric(&Spec{
Length: 2,
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
},
}
data := &CompositeTestData{
F1: NewStringValue("AB"),
F2: NewStringValue("CD"),
F3: NewNumericValue(12),
}

composite := NewComposite(invalidSpec)
err := composite.Marshal(data)
require.NoError(t, err)

b, err := composite.Pack()
require.Nil(t, b)
require.EqualError(t, err, "failed to encode length: field length: 12 is larger than maximum: 8")
})

t.Run("Pack correctly serializes fully populated data to bytes", func(t *testing.T) {
data := &CompositeTestData{
F1: NewStringValue("AB"),
Expand Down Expand Up @@ -595,142 +635,6 @@ func TestCompositePackingWithTags(t *testing.T) {
require.Equal(t, "120102AB0202CD", string(packed))
})

t.Run("Pack correctly ignores excess subfields in excess of the length described by the prefix", func(t *testing.T) {
type ExcessSubfieldsTestData struct {
F1 *String
F2 *Numeric
F3 *String
F4 *String
F5 *String
F6 *String
F7 *String
F8 *String
F9 *Numeric
F10 *String
F11 *String
F12 *String
F13 *String
F14 *String
}

excessSubfieldsSpec := &Spec{
Length: 26,
Description: "POS Data",
Pref: prefix.ASCII.LLL,
Tag: &TagSpec{
Sort: sort.StringsByInt,
},
Subfields: map[string]Field{
"1": NewString(&Spec{
Length: 1,
Description: "POS Terminal Attendance",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"2": NewNumeric(&Spec{
Length: 1,
Description: "Reserved for Future Use",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"3": NewString(&Spec{
Length: 1,
Description: "POS Terminal Location",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"4": NewString(&Spec{
Length: 1,
Description: "POS Cardholder Presence",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"5": NewString(&Spec{
Length: 1,
Description: "POS Card Presence",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"6": NewString(&Spec{
Length: 1,
Description: "POS Card Capture Capabilities",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"7": NewString(&Spec{
Length: 1,
Description: "POS Transaction Status",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"8": NewString(&Spec{
Length: 1,
Description: "POS Transaction Security",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"9": NewNumeric(&Spec{
Length: 1,
Description: "Reserved for Future Use",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
Pad: padding.Left('0'),
}),
"10": NewString(&Spec{
Length: 1,
Description: "Cardholder-Activated Terminal Level",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"11": NewString(&Spec{
Length: 1,
Description: "POS Card Data Terminal Input Capability Indicator",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"12": NewString(&Spec{
Length: 2,
Description: "POS Authorization Life Cycle",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"13": NewString(&Spec{
Length: 3,
Description: "POS Country Code",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
"14": NewString(&Spec{
Length: 10,
Description: "POS Postal Code",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}),
},
}

data := &ExcessSubfieldsTestData{}

composite := NewComposite(excessSubfieldsSpec)
err := composite.SetData(data)
require.NoError(t, err)

// Subfield 12, 13 & 14 fall outside of the bounds of the
// 11 byte limit imposed by the prefix. [011 | 10000100012]
// Therefore, it won't be included in the packed bytes.

packed := []byte("01110000100012")

read, err := composite.Unpack(packed)

require.NoError(t, err)
require.Equal(t, 14, read)

packedBytes, err := composite.Pack()
require.NoError(t, err)
require.Equal(t, packedBytes, packed)
})

t.Run("Unpack returns an error on failure of subfield to unpack bytes", func(t *testing.T) {
data := &CompositeTestData{}

Expand Down
2 changes: 1 addition & 1 deletion field/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Field interface {
// a pointer it returns error.
Unmarshal(v interface{}) error

// Marshal sets field Value into provided v. If v is nil or not
// Marshal sets field Value from provided v. If v is nil or not
// a pointer it returns error.
Marshal(v interface{}) error

Expand Down
16 changes: 16 additions & 0 deletions field/numeric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ func TestNumericField(t *testing.T) {
require.Equal(t, 9876, data.Value)
}

func TestNumericPack(t *testing.T) {
t.Run("returns error for zero value when fixed length and no padding specified", func(t *testing.T) {
spec := &Spec{
Length: 10,
Description: "Field",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
}
numeric := NewNumeric(spec)
_, err := numeric.Pack()

// zero value for Numeric is 0, so we have default field length 1
require.EqualError(t, err, "failed to encode length: field length: 1 should be fixed: 10")
})
}

func TestNumericFieldUnmarshal(t *testing.T) {
str := NewNumericValue(9876)

Expand Down
4 changes: 0 additions & 4 deletions field/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ func (f *String) Pack() ([]byte, error) {
return nil, fmt.Errorf("failed to encode content: %w", err)
}

if len(packed) == 0 {
return []byte{}, nil
}

packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(packed))
if err != nil {
return nil, fmt.Errorf("failed to encode length: %w", err)
Expand Down
Loading

0 comments on commit e6549fc

Please sign in to comment.