Repository: moov-io/ach
Pull request #114 (2017-07-29 21:49:14)

Reader.go Increase test coverage
Read() now utilizes a switch for different actions on length of the input line. 

Increase Reader.gp code coverage

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12609355/badge)](https://coveralls.io/builds/12609355)

Coverage increased (+1.9%) to 97.129% when pulling **364056b16f509b9e50f38dfb6880845925a5195e on wadearnold:Reader.go-Increase-test-coverage** into **deab39589aa43b2162bd4dedfaded0c61d7663f3 on moov-io:master**.


Patch:
diff --git a/reader.go b/reader.go
index 8a1365be..ca58457a 100644
--- a/reader.go
+++ b/reader.go
@@ -75,38 +75,27 @@ func (r *Reader) Read() (File, error) {
 		r.lineNum++
 		lineLength := len(line)
 
-		// handle the situation where there are no line breaks
-		if r.lineNum == 1 && lineLength > RecordLength && lineLength%RecordLength == 0 {
+		switch {
+		case r.lineNum == 1 && lineLength > RecordLength && lineLength%RecordLength == 0:
 			if err := r.processFixedWidthFile(&line); err != nil {
 				return r.File, err
 			}
-			break
-		}
-
-		// Only 94 ASCII characters to a line
-		if lineLength != RecordLength {
+		case lineLength != RecordLength:
 			msg := fmt.Sprintf(msgRecordLength, lineLength)
 			err := &FileError{FieldName: "RecordLength", Value: strconv.Itoa(lineLength), Msg: msg}
 			return r.File, r.error(err)
-		}
-
-		r.line = line
-
-		if err := r.parseLine(); err != nil {
-			return r.File, err
+		default:
+			r.line = line
+			if err := r.parseLine(); err != nil {
+				return r.File, err
+			}
 		}
 	}
-
-	if err := r.scanner.Err(); err != nil {
-		return r.File, err
-	}
-
 	if (FileHeader{}) == r.File.Header {
 		// Their must be at least one File Header
 		r.recordName = "FileHeader"
 		return r.File, r.error(&FileError{Msg: msgFileHeader})
 	}
-
 	if (FileControl{}) == r.File.Control {
 		// Their must be at least one File Control
 		r.recordName = "FileControl"
@@ -156,7 +145,7 @@ func (r *Reader) parseLine() error {
 		}
 		if err := r.currentBatch.Validate(); err != nil {
 			r.recordName = "Batches"
-			return err
+			return r.error(err)
 		}
 		r.File.AddBatch(r.currentBatch)
 		r.currentBatch = nil
@@ -168,14 +157,10 @@ func (r *Reader) parseLine() error {
 		if err := r.parseFileControl(); err != nil {
 			return err
 		}
-		if err := r.File.Validate(); err != nil {
-			return err
-		}
 	default:
 		msg := fmt.Sprintf(msgUnknownRecordType, r.line[:1])
 		return r.error(&FileError{FieldName: "recordType", Value: r.line[:1], Msg: msg})
 	}
-
 	return nil
 }
 
@@ -271,11 +256,15 @@ func (r *Reader) parseAddenda() error {
 			msg := fmt.Sprintf(msgBatchAddendaIndicator)
 			return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg})
 		}
-	// case "CCD":
-	default:
-		msg := fmt.Sprintf(msgFileNoneSEC, sec)
-		return r.error(&FileError{FieldName: "StandardEntryClassCode", Msg: msg})
 	}
+	// case "CCD":
+	// this code can only be executed if the EntryDetail supports the SEC code
+	/*
+		default:
+			msg := fmt.Sprintf(msgFileNoneSEC, sec)
+			return r.error(&FileError{FieldName: "StandardEntryClassCode", Msg: msg})
+		}
+	*/
 	return nil
 }
 


Test patch:
diff --git a/reader_test.go b/reader_test.go
index f6b8bbb7..8a4a1a19 100644
--- a/reader_test.go
+++ b/reader_test.go
@@ -5,6 +5,8 @@
 package ach
 
 import (
+	"bytes"
+	"fmt"
 	"os"
 	"strings"
 	"testing"
@@ -326,6 +328,98 @@ func TestFileFileControlErr(t *testing.T) {
 		t.Errorf("%T: %s", err, err)
 	}
 }
+func TestFileBatchHeaderSEC(t *testing.T) {
+	bh := mockBatchHeader()
+	bh.StandardEntryClassCode = "ABC"
+	r := NewReader(strings.NewReader(bh.String()))
+	_, err := r.Read()
+	if p, ok := err.(*ParseError); ok {
+		if e, ok := p.Err.(*FileError); ok {
+			if e.FieldName != "StandardEntryClassCode" {
+				t.Errorf("%T: %s", e, e)
+			}
+		}
+	} else {
+		t.Errorf("%T: %s", err, err)
+	}
+}
+
+func TestFileFileControlNoCurrentBatch(t *testing.T) {
+	bc := mockBatchControl()
+	r := NewReader(strings.NewReader(bc.String()))
+	_, err := r.Read()
+	if p, ok := err.(*ParseError); ok {
+		if p.Record != "BatchControl" {
+			t.Errorf("%T: %s", p, p)
+		}
+	} else {
+		t.Errorf("%T: %s", err, err)
+	}
+}
+
+func TestFileBatchControlValidate(t *testing.T) {
+	bh := mockBatchHeader()
+	ed := mockEntryDetail()
+	bc := mockBatchControl()
+	bc.CompanyIdentification = "B1G C0M@NY"
+	line := bh.String() + "\n" + ed.String() + "\n" + bc.String()
+	r := NewReader(strings.NewReader(line))
+	_, err := r.Read()
+	if p, ok := err.(*ParseError); ok {
+		if e, ok := p.Err.(*FieldError); ok {
+			if e.FieldName != "CompanyIdentification" {
+				t.Errorf("%T: %s", e, e)
+			}
+		}
+	} else {
+		t.Errorf("%T: %s", err, err)
+	}
+}
+
+func TestFileAddBatchValidation(t *testing.T) {
+	bh := mockBatchHeader()
+	ed := mockEntryDetail()
+	bc := mockBatchControl()
+	line := bh.String() + "\n" + ed.String() + "\n" + bc.String()
+	r := NewReader(strings.NewReader(line))
+	_, err := r.Read()
+	if p, ok := err.(*ParseError); ok {
+		if e, ok := p.Err.(*BatchError); ok {
+			if e.FieldName != "EntryAddendaCount" {
+				t.Errorf("%T: %s", e, e)
+			}
+		}
+	} else {
+		t.Errorf("%T: %s", err, err)
+	}
+}
+
+func TestFileHeaderExists(t *testing.T) {
+	file := mockFilePPD()
+	file.SetHeader(FileHeader{})
+	buf := new(bytes.Buffer)
+	w := NewWriter(buf)
+	w.Write(file)
+	w.Flush()
+	//println(buf.String())
+	r := NewReader(strings.NewReader(buf.String()))
+	f, err := r.Read()
+	if err != nil {
+		if p, ok := err.(*ParseError); ok {
+			if e, ok := p.Err.(*FileError); ok {
+				if e.Msg != msgFileHeader {
+					t.Errorf("%T: %s", e, e)
+				}
+			}
+		} else {
+			// error is nil if the file was parsed properly.
+			t.Errorf("%T: %s", err, err)
+		}
+	} else {
+		fmt.Println(f.Header.String())
+	}
+
+}
 
 // TestFileLongErr Batch Header Service Class is 000 which does not validate
 func TestFileLongErr(t *testing.T) {
@@ -340,3 +434,20 @@ func TestFileLongErr(t *testing.T) {
 		}
 	}
 }
+
+func TestFileAddendaOutsideEntry(t *testing.T) {
+	bh := mockBatchHeader()
+	addenda := mockAddenda()
+	line := bh.String() + "\n" + addenda.String()
+	r := NewReader(strings.NewReader(line))
+	_, err := r.Read()
+	if p, ok := err.(*ParseError); ok {
+		if e, ok := p.Err.(*FileError); ok {
+			if e.FieldName != "Addenda" {
+				t.Errorf("%T: %s", e, e)
+			}
+		}
+	} else {
+		t.Errorf("%T: %s", err, err)
+	}
+}