nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: moov-io/ach
Pull request #999 (2022-02-26 00:44:23)

fix: better date validation
Fixes: https://github.com/moov-io/ach/issues/996 


Patch:
diff --git a/validators.go b/validators.go
index 64a2b638..fe2623a8 100644
--- a/validators.go
+++ b/validators.go
@@ -23,6 +23,7 @@ import (
 	"math"
 	"regexp"
 	"strconv"
+	"time"
 	"unicode/utf8"
 )
 
@@ -61,14 +62,6 @@ func (v *validator) isCardTransactionType(code string) error {
 	return ErrCardTransactionType
 }
 
-// isYear validates a 2 digit year 00-99
-func (v *validator) isYear(s string) error {
-	if s < "00" || s > "99" {
-		return ErrValidYear
-	}
-	return nil
-}
-
 // isCreditCardYear validates a 2 digit year for credit cards, but
 // only accepts a range of years. 2018 to 2050
 func (v *validator) isCreditCardYear(s string) error {
@@ -92,7 +85,6 @@ func (v *validator) isMonth(s string) error {
 // isDay validates a 2 digit day based on a 2 digit month
 // month 01-12 day 01-31 based on month
 func (v *validator) isDay(m string, d string) error {
-	// ToDo: Future Consideration Leap Year - not sure if cards actually have 0229
 	switch m {
 	// February
 	case "02":
@@ -134,20 +126,11 @@ func (v *validator) isDay(m string, d string) error {
 // validateSimpleDate will return the incoming string only if it matches a valid YYMMDD
 // date format. (Y=Year, M=Month, D=Day)
 func (v *validator) validateSimpleDate(s string) string {
-	if length := utf8.RuneCountInString(s); length != 6 {
-		return ""
-	}
-	yy, mm, dd := s[:2], s[2:4], s[4:6]
-	if err := v.isYear(yy); err != nil {
-		return ""
-	}
-	if err := v.isMonth(mm); err != nil {
-		return ""
-	}
-	if err := v.isDay(mm, dd); err != nil {
+	_, err := time.Parse("060102", s) // YYMMDD
+	if err != nil {
 		return ""
 	}
-	return fmt.Sprintf("%s%s%s", yy, mm, dd)
+	return s
 }
 
 var (


Test patch:
diff --git a/validators_test.go b/validators_test.go
index 5783302f..83128917 100644
--- a/validators_test.go
+++ b/validators_test.go
@@ -50,7 +50,7 @@ func TestValidators__checkDigit(t *testing.T) {
 
 func TestValidators__isCreditCardYear(t *testing.T) {
 	cases := map[string]bool{
-		// invalid (or out of range)
+		// invalid (or out of range until 2051 or 2100-2117)
 		"10": false,
 		"00": false,
 		"51": false,
@@ -80,12 +80,13 @@ func TestValidators__validateSimpleDate(t *testing.T) {
 		"001240": "", // no 40th Day
 		"190001": "", // no 0th month
 		"190100": "", // no 0th day
+		"230229": "", // Feb 29th 2023 is not a leap year
 		// valid
 		"190101": "190101", // Jan 1st
 		"201231": "201231", // Dec 31st
 		"220731": "220731", // July 31st
 		"350430": "350430", // April 30th
-		"500229": "500229", // Feb 29th
+		"240229": "240229", // Feb 29th 2024 (Leap Year)
 	}
 
 	v := validator{}
Repository: moov-io/ach
Pull request #1023 (2022-05-20 22:17:58)

Add option to skip ascending batch sequence validation
https://github.com/moov-io/ach/issues/1022


Patch:
diff --git a/file.go b/file.go
index 813bc276..26864716 100644
--- a/file.go
+++ b/file.go
@@ -666,6 +666,9 @@ type ValidateOpts struct {
 	// UnequalServiceClassCode skips equality checks for the ServiceClassCode in each pair of BatchHeader
 	// and BatchControl records.
 	UnequalServiceClassCode bool `json:"unequalServiceClassCode"`
+
+	// AllowUnorderedBatchNumebrs allows a file to be read with unordered batch numbers.
+	AllowUnorderedBatchNumbers bool `json:"allowUnorderedBatchNumbers"`
 }
 
 // ValidateWith performs checks on each record according to Nacha guidelines.
@@ -710,8 +713,10 @@ func (f *File) ValidateWith(opts *ValidateOpts) error {
 		if err := f.isFileAmount(false); err != nil {
 			return err
 		}
-		if err := f.isSequenceAscending(); err != nil {
-			return err
+		if !opts.AllowUnorderedBatchNumbers {
+			if err := f.isSequenceAscending(); err != nil {
+				return err
+			}
 		}
 		return f.isEntryHash(false)
 	}


Test patch:
diff --git a/file_test.go b/file_test.go
index f98d7708..6fe8ebcc 100644
--- a/file_test.go
+++ b/file_test.go
@@ -2053,7 +2053,6 @@ func TestFile__FileHeaderFormattingWithValidation(t *testing.T) {
 	}
 }
 
-// testFileEntryHash validates entry hash
 func TestFile__AscendingBatchSequence(t *testing.T) {
 	tests := []struct {
 		desc        string
@@ -2097,6 +2096,11 @@ func TestFile__AscendingBatchSequence(t *testing.T) {
 				t.Fatal(err)
 			}
 
+			// None of the tests should error if unordered batch numbers are allowed
+			if err := file.ValidateWith(&ValidateOpts{AllowUnorderedBatchNumbers: true}); err != nil {
+				t.Fatal(err)
+			}
+
 			err := file.Validate()
 			if err != tt.expectedErr {
 				t.Errorf("got: %v, want: %v", err, tt.expectedErr)
Repository: moov-io/ach
Pull request #1128 (2022-11-08 14:11:50)

fix: allow mixed-case Addenda10 TransactionType codes
Fixes: https://github.com/moov-io/ach/issues/1126


Patch:
diff --git a/validators.go b/validators.go
index 738fa72b..170f62dc 100644
--- a/validators.go
+++ b/validators.go
@@ -23,6 +23,7 @@ import (
 	"math"
 	"regexp"
 	"strconv"
+	"strings"
 	"time"
 	"unicode/utf8"
 )
@@ -394,10 +395,11 @@ func StandardTransactionCode(code int) error {
 // WEB = Internet-Initiated Transaction, ARC = Accounts Receivable Entry, BOC = Back Office Conversion Entry,
 // POP = Point of Purchase Entry, RCK = Re-presented Check Entry
 func (v *validator) isTransactionTypeCode(s string) error {
-	switch s {
-	case "ANN", "BUS", "DEP", "LOA", "MIS", "MOR",
-		"PEN", "RLS", "REM", "SAL", "TAX", TEL, WEB,
-		ARC, BOC, POP, RCK:
+	switch strings.ToUpper(s) {
+	case
+		"ANN", "BUS", "DEP", "LOA", "MIS", "MOR",
+		"PEN", "REM", "RLS", "SAL", "TAX",
+		ARC, BOC, MTE, POP, POS, RCK, SHR, TEL, WEB:
 		return nil
 	}
 	return ErrTransactionTypeCode


Test patch:
diff --git a/validators_test.go b/validators_test.go
index 83128917..88a25e2b 100644
--- a/validators_test.go
+++ b/validators_test.go
@@ -20,6 +20,8 @@ package ach
 import (
 	"fmt"
 	"testing"
+
+	"github.com/stretchr/testify/require"
 )
 
 func TestValidators__checkDigit(t *testing.T) {
@@ -121,6 +123,16 @@ func TestValidators__validateSimpleTime(t *testing.T) {
 	}
 }
 
+func TestValidators__isTransactionTypeCode(t *testing.T) {
+	v := validator{}
+
+	require.NoError(t, v.isTransactionTypeCode("BUS"))
+	require.NoError(t, v.isTransactionTypeCode("tax"))
+
+	require.Error(t, v.isTransactionTypeCode("ZZZ"))
+	require.Error(t, v.isTransactionTypeCode("abc"))
+}
+
 func TestValidators__isAlphanumeric(t *testing.T) {
 	v := validator{}
Repository: moov-io/ach
Pull request #1138 (2022-11-21 20:34:32)

feat: add .SetMaxLines(int) to Reader
Fixes: https://github.com/moov-io/ach/issues/1120


Patch:
diff --git a/reader.go b/reader.go
index 097c1d94..90029a7a 100644
--- a/reader.go
+++ b/reader.go
@@ -29,12 +29,12 @@ import (
 )
 
 var (
-	// maxLines is the maximum number of lines a file can have. It is limited by the
+	// defaultMaxLines is the maximum number of lines a file can have. It is limited by the
 	// EntryAddendaCount field which has 8 digits, and the BatchCount field which has
 	// 6 digits in the File Control Record. So we can have at most the 2 file records,
 	// 2 records for each of 10^6 batches, 10^8 entry and addenda records, and 8 lines
 	// of 9's to round up to the nearest multiple of 10.
-	maxLines = 2 + 2000000 + 100000000 + 8
+	defaultMaxLines int = 2 + 2_000_000 + 100_000_000 + 8
 )
 
 // Reader reads records from an ACH-encoded file.
@@ -57,6 +57,9 @@ type Reader struct {
 	// line number of the file being parsed
 	lineNum int
 
+	// maxLines is the maximum number of lines to be consumed
+	maxLines int
+
 	// recordName holds the current record name being parsed.
 	recordName string
 
@@ -132,10 +135,15 @@ func ReadFiles(paths []string) ([]*File, error) {
 // NewReader returns a new ACH Reader that reads from r.
 func NewReader(r io.Reader) *Reader {
 	return &Reader{
-		scanner: bufio.NewScanner(r),
+		maxLines: defaultMaxLines,
+		scanner:  bufio.NewScanner(r),
 	}
 }
 
+func (r *Reader) SetMaxLines(max int) {
+	r.maxLines = max
+}
+
 // Read reads each line in the underlying io.Reader and returns a File and any errors encountered.
 //
 // Read enforces ACH formatting rules and the first character of each line determines which parser is used.
@@ -150,7 +158,7 @@ func (r *Reader) Read() (File, error) {
 	for r.scanner.Scan() {
 		line := r.scanner.Text()
 		r.lineNum++
-		if r.lineNum > maxLines {
+		if r.lineNum > r.maxLines {
 			r.errors.Add(ErrFileTooLong)
 			return r.File, r.errors
 		}


Test patch:
diff --git a/reader_test.go b/reader_test.go
index 90b63341..b0a5561f 100644
--- a/reader_test.go
+++ b/reader_test.go
@@ -1774,23 +1774,20 @@ func TestTwoFileADVControls(t *testing.T) {
 
 // testACHFileTooLongErr checks that it errors on a file that is too long
 func testACHFileTooLongErr(t testing.TB) {
-	// To make testing this more manageable, we'll artificially cap the size of the file to 200 lines
-	maxLines = 200
-
 	f, err := os.Open(filepath.Join("test", "testdata", "20110729A.ach"))
 	if err != nil {
 		t.Errorf("%T: %s", err, err)
 	}
 	defer f.Close()
+
 	r := NewReader(f)
+	// To make testing this more manageable, we'll artificially cap the size of the file to 200 lines
+	r.SetMaxLines(200)
 	_, err = r.Read()
 
 	if !base.Has(err, ErrFileTooLong) {
 		t.Errorf("%T: %s", err, err)
 	}
-
-	// reset maxLines to its original value
-	maxLines = 2 + 2000000 + 100000000 + 8
 }
 
 // TestCategoryAssignment ensures entry categories are set correctly
Repository: moov-io/ach
Pull request #1157 (2023-01-05 16:35:30)

fix: properly count string lengths when padding fields
Fixes: https://github.com/moov-io/ach/issues/1156


Patch:
diff --git a/converters.go b/converters.go
index ea07dc6c..a7cca040 100644
--- a/converters.go
+++ b/converters.go
@@ -21,6 +21,7 @@ import (
 	"math"
 	"strconv"
 	"strings"
+	"unicode/utf8"
 )
 
 // converters handles golang to ACH type Converters
@@ -54,7 +55,7 @@ func (c *converters) formatSimpleTime(s string) string {
 
 // alphaField Alphanumeric and Alphabetic fields are left-justified and space filled.
 func (c *converters) alphaField(s string, max uint) string {
-	ln := uint(len(s))
+	ln := uint(utf8.RuneCountInString(s))
 	if ln > max {
 		return s[:max]
 	}
@@ -65,7 +66,7 @@ func (c *converters) alphaField(s string, max uint) string {
 // numericField right-justified, unsigned, and zero filled
 func (c *converters) numericField(n int, max uint) string {
 	s := strconv.Itoa(n)
-	ln := uint(len(s))
+	ln := uint(utf8.RuneCountInString(s))
 	if ln > max {
 		return s[ln-max:]
 	}
@@ -75,7 +76,7 @@ func (c *converters) numericField(n int, max uint) string {
 
 // stringField slices to max length and zero filled
 func (c *converters) stringField(s string, max uint) string {
-	ln := uint(len(s))
+	ln := uint(utf8.RuneCountInString(s))
 	if ln > max {
 		return s[:max]
 	}
diff --git a/validators.go b/validators.go
index 6e8e2acc..8f1a6803 100644
--- a/validators.go
+++ b/validators.go
@@ -491,7 +491,7 @@ func roundUp10(n int) int {
 func (v *validator) validateSettlementDate(s string) string {
 	emptyField := "   "
 
-	if s == emptyField || len(s) != len(emptyField) {
+	if s == emptyField || utf8.RuneCountInString(s) != len(emptyField) {
 		return emptyField
 	}
 


Test patch:
diff --git a/converters_test.go b/converters_test.go
index 37a23e24..2231a0cb 100644
--- a/converters_test.go
+++ b/converters_test.go
@@ -17,7 +17,12 @@
 
 package ach
 
-import "testing"
+import (
+	"testing"
+	"unicode/utf8"
+
+	"github.com/stretchr/testify/require"
+)
 
 // testAlphaField ensures that padding and two long of strings get properly made
 func testAlphaFieldShort(t testing.TB) {
@@ -26,6 +31,11 @@ func testAlphaFieldShort(t testing.TB) {
 	if result != "ABC123    " {
 		t.Errorf("Left justified space filled got:'%v'", result)
 	}
+
+	answer := c.alphaField("Returned per ODFI’s Request", 44)
+	expected := "Returned per ODFI’s Request                 "
+	require.Equal(t, 44, utf8.RuneCountInString(expected))
+	require.Equal(t, expected, answer)
 }
 
 // TestAlphaFieldShort test ensures that padding and two long of strings get properly made
Repository: chrovis/cljam
Pull request #123 (2017-12-06 10:00:30)

Fix parsing SAM header values containing ':'.
#### Summary
Don't discard substring after second `:` when parsing SAM header.

#### Problem
Complex SAM headers can be modified unexpectedly.

##### input sam
```
@SQ	SN:A:B;100-200;++;chr1:100-200	LN:400
```

##### output sam
```
@SQ	SN:A	LN:400
```

#### Cause
```clojure
(clojure.string/split "SN:A:B;100-200;++;chr1:100-200" #":")
; => ["SN" "A" "B;100-200;++;chr1" "100-200"]
```
`clojure.string/split` with 2 args splits given string at all ocurrence of the regex.
#### Changes
Pass `2` as 3rd arg (`limit`) of `clojure.string/split`.

#### Affects
SAM/BAM I/O

#### Tests

- `lein test :all` 🆗


Patch:
diff --git a/src/cljam/io/sam/util.clj b/src/cljam/io/sam/util.clj
index 27347b9..60b411d 100644
--- a/src/cljam/io/sam/util.clj
+++ b/src/cljam/io/sam/util.clj
@@ -16,7 +16,7 @@
   (into
    {}
    (map (fn [kv]
-          (let [[k v] (cstr/split kv #":")]
+          (let [[k v] (cstr/split kv #":" 2)]
             [(keyword k)
              (case k
                "LN" (Integer/parseInt v)


Test patch:
diff --git a/test/cljam/io/sam/util_test.clj b/test/cljam/io/sam/util_test.clj
index 344e631..908073d 100644
--- a/test/cljam/io/sam/util_test.clj
+++ b/test/cljam/io/sam/util_test.clj
@@ -10,7 +10,12 @@
   (is (= (sam-util/parse-header "@HD	VN:1.3	SO:coordinate\n@SQ	SN:ref	LN:10\n@SQ	SN:ref2	LN:20\n@PG	ID:cljam	PN:cljam	VN:1.0	CL:java -jar cljam.jar")
          {:HD {:VN "1.3" :SO "coordinate"}
           :SQ [{:SN "ref" :LN 10} {:SN "ref2" :LN 20}]
-          :PG [{:ID "cljam" :PN "cljam" :VN "1.0" :CL "java -jar cljam.jar"}]})))
+          :PG [{:ID "cljam" :PN "cljam" :VN "1.0" :CL "java -jar cljam.jar"}]}))
+  (is (= (sam-util/parse-header "@HD\tVN:1.4\tSO:coordinate\n@SQ\tSN:FOO:BAR:10-100;BAZ|QUX,QUUX\tLN:1\n@PG\tID:cljam\tPN:cljam\tVN:1.0\tCL:cljam")
+         {:HD {:VN "1.4" :SO "coordinate"}
+          :SQ [{:SN "FOO:BAR:10-100;BAZ|QUX,QUUX"
+                :LN 1}]
+          :PG [{:ID "cljam" :PN "cljam" :VN "1.0" :CL "cljam"}]})))
 
 (def nibble-table "=ACMGRSVTWYHKDBN")
Repository: chrovis/cljam
Pull request #124 (2017-12-06 10:09:13)

Add some extensions for FASTA format.
Add some file extensions of FASTA format listed in https://en.wikipedia.org/wiki/FASTA_format#File_extension and http://fileformats.archiveteam.org/wiki/FASTA_and_FASTQ.


Patch:
diff --git a/src/cljam/io/fasta/core.clj b/src/cljam/io/fasta/core.clj
index e901af2..6c06e99 100644
--- a/src/cljam/io/fasta/core.clj
+++ b/src/cljam/io/fasta/core.clj
@@ -14,15 +14,16 @@
 
 (defn- fasta-index
   [fasta-path]
-  (if-let [fai-path (->> ["$1.fai" ".fai" "$1.FAI" ".FAI"]
-                         (eduction
-                          (comp
-                           (map #(cstr/replace fasta-path #"(?i)(\.fa(sta)?)$" %))
-                           (filter #(.isFile (cio/file %)))))
-                         first)]
-    (fai/reader fai-path)
-    (throw (FileNotFoundException.
-            (str "Could not find FASTA Index file for " fasta-path)))))
+  (let [fasta-exts #"(?i)(\.(fa|fasta|fas|fsa|seq|fna|faa|ffn|frn|mpfa)?)$"]
+    (if-let [fai-path (->> ["$1.fai" ".fai" "$1.FAI" ".FAI"]
+                           (eduction
+                            (comp
+                             (map #(cstr/replace fasta-path fasta-exts %))
+                             (filter #(.isFile (cio/file %)))))
+                           first)]
+      (fai/reader fai-path)
+      (throw (FileNotFoundException.
+              (str "Could not find FASTA Index file for " fasta-path))))))
 
 (defn ^FASTAReader reader
   [f]
diff --git a/src/cljam/io/util.clj b/src/cljam/io/util.clj
index a369d7a..9db3323 100644
--- a/src/cljam/io/util.clj
+++ b/src/cljam/io/util.clj
@@ -136,7 +136,7 @@
     #"(?i)\.bam$" :bam
     #"(?i)\.f(ast)?q" :fastq
     #"(?i)\.fai$" :fai
-    #"(?i)\.fa(sta)?" :fasta
+    #"(?i)\.(fa|fasta|fas|fsa|seq|fna|faa|ffn|frn|mpfa)" :fasta
     #"(?i)\.2bit$" :2bit
     #"(?i)\.vcf" :vcf
     #"(?i)\.bcf$" :bcf


Test patch:
diff --git a/test/cljam/io/util_test.clj b/test/cljam/io/util_test.clj
index 624e305..dc17f6f 100644
--- a/test/cljam/io/util_test.clj
+++ b/test/cljam/io/util_test.clj
@@ -30,6 +30,14 @@
     "foo.FA.GZ" :fasta
     "foo.FASTA" :fasta
     "foo.FASTA.GZ" :fasta
+    "foo.fas" :fasta
+    "foo.fsa" :fasta
+    "foo.seq" :fasta
+    "foo.fna" :fasta
+    "foo.faa" :fasta
+    "foo.ffn" :fasta
+    "foo.frn" :fasta
+    "foo.mpfa" :fasta
     "foo.fai"  :fai
     "foo.fa.fai"  :fai
     "foo.2bit" :2bit
Repository: chrovis/cljam
Pull request #127 (2017-12-20 14:22:28)

Fix wrong padding of last bases in 2bit writer
#### Summary
Fix a bug that the last 1~3 bases were wrongly padded in the 2bit writer.

#### Problem
When sequence length is not a multiple of 4, last 1~3 bases are not correctly written.

#### Cause
In 2bit writer, every 4 bases are packed into a single byte.
The last bases will be packed from MSB and padded with 2r00.
But there was a bug in padding calculation.

#### Changes
Fix the padding.

#### Affects
2bit writer.

#### Tests

- `lein test :all` 🆗


Patch:
diff --git a/src/cljam/io/twobit/writer.clj b/src/cljam/io/twobit/writer.clj
index 881c870..9c15e33 100644
--- a/src/cljam/io/twobit/writer.clj
+++ b/src/cljam/io/twobit/writer.clj
@@ -123,9 +123,9 @@
            unchecked-int
            (.write o)))
     (when (pos? (mod len 4))
-      (loop [b 0 i (mod len 4)]
+      (loop [b 0 i (mod len 4) j 1]
         (if (pos? i)
-          (recur (bit-or b (bit-shift-left (aget table (.get bb)) (* 2 (- 4 i)))) (dec i))
+          (recur (bit-or b (bit-shift-left (aget table (.get bb)) (* 2 (- 4 j)))) (dec i) (inc j))
           (.write o (unchecked-int b)))))))
 
 (defn- write-sequence!


Test patch:
diff --git a/test/cljam/io/sequence_test.clj b/test/cljam/io/sequence_test.clj
index 8872670..ba86ad9 100644
--- a/test/cljam/io/sequence_test.clj
+++ b/test/cljam/io/sequence_test.clj
@@ -267,4 +267,13 @@
       (with-open [r (cseq/twobit-reader test-twobit-n-file)
                   w (cseq/twobit-writer f)]
         (cseq/write-sequences w (cseq/read-all-sequences r {:mask? true})))
-      (is (same-file? f test-twobit-n-file)))))
+      (is (same-file? f test-twobit-n-file)))
+
+    (let [f (str temp-dir "/test-2.2bit")
+          s [{:name "SEQ1" :sequence "AAAA"}
+             {:name "SEQ2" :sequence "AAAAG"}
+             {:name "SEQ3" :sequence "AAAAGC"}
+             {:name "SEQ4" :sequence "AAAAGCT"}
+             {:name "SEQ5" :sequence "AAAAGCTA"}]]
+      (with-open [w (cseq/writer f)] (cseq/write-sequences w s))
+      (with-open [r (cseq/reader f)] (is (= (cseq/read-all-sequences r) s))))))
Repository: chrovis/cljam
Pull request #161 (2019-05-09 09:26:51)

Skip INFO value parsing if it's empty
#### Summary

This PR fixes VCF INFO parsing errors when empty values pass.

#### Cause

If `nil` comes to the line below,

https://github.com/chrovis/cljam/blob/773eb527a09b2d206ee567c8a93eeee27162f065/src/cljam/io/vcf/util.clj#L27

it transforms to an empty string.
So `NumberFormatException` in `Integer/parseInt` and `Float/parseFloat` occurs, because `dot-or-nil?` doesn't catch empty strings.

#### Changes

- Add empty check into `dot-or-nil?`

#### Affects

- VCF reader
- BCF reader

#### Tests

- `lein check` 🆗 
- `lein test :all` 🆗 
- `lein eastwood` 🆗 



Patch:
diff --git a/src/cljam/io/vcf/util.clj b/src/cljam/io/vcf/util.clj
index 78db709..ba0af93 100644
--- a/src/cljam/io/vcf/util.clj
+++ b/src/cljam/io/vcf/util.clj
@@ -6,6 +6,7 @@
   "Checks if given string is equal to \".\" or nil."
   [^String s]
   `(or (nil? ~s)
+       (empty? ~s)
        (and (= 1 (.length ~s))
             (= \. (.charAt ~s 0)))))
 


Test patch:
diff --git a/test/cljam/io/vcf/util_test.clj b/test/cljam/io/vcf/util_test.clj
index d7dc0ca..35411bf 100644
--- a/test/cljam/io/vcf/util_test.clj
+++ b/test/cljam/io/vcf/util_test.clj
@@ -30,7 +30,9 @@
       "NS=3;DP=9;AA=G" {:NS 3, :DP 9, :AA "G"}
       "NS=3;DP=14;AF=0.5;DB;H2" {:NS 3, :DP 14, :AF [0.5], :DB :exists :H2 :exists}
       "NS=3;DP=11;AF=0.017" {:NS 3, :DP 11, :AF [(float 0.017)]}
-      "NS=2;DP=10;AF=0.333,0.667;AA=T;DB" {:NS 2, :DP 10, :AF [(float 0.333) (float 0.667)] :AA "T", :DB :exists})))
+      "NS=2;DP=10;AF=0.333,0.667;AA=T;DB" {:NS 2, :DP 10, :AF [(float 0.333) (float 0.667)] :AA "T", :DB :exists}
+      "NS;DP=12" {:NS nil, :DP 12} ;; Not VCF standard. "NS=.;DP=12" is correct. However, some variant caller outputs such form.
+      )))
 
 (deftest about-parse-filter
   (are [?filter-str ?expected]
Repository: chrovis/cljam
Pull request #179 (2019-09-26 04:44:56)

Avoid an ArityException on creating an index for BAM files without alignments
#### Problem
`cljam.algo.bam-indexer/create-index` throws an `clojure.lang.ArityException` when the target BAM contains no alignments

#### Cause
An initial value is not provided for `(reduce merge-index [])` in `cljam.io.bam-index.writer/make-index-from-blocks`
https://github.com/chrovis/cljam/blob/2db7fe739047e2284e93e9d211be74cfdd6cc09b/src/cljam/io/bam_index/writer.clj#L310

#### Changes
Add a default value for `make-index-from-blocks`

#### Affects
- `cljam.algo.bam-indexer`
- `cljam.io.bam-index.writer`

#### Tests

- `lein check` ✅
- `lein test :all` ✅


Patch:
diff --git a/src/cljam/io/bam_index/writer.clj b/src/cljam/io/bam_index/writer.clj
index f8cee4f..fe8bcff 100644
--- a/src/cljam/io/bam_index/writer.clj
+++ b/src/cljam/io/bam_index/writer.clj
@@ -138,7 +138,7 @@
                        indices)]
         (recur rest rid' idx-status' no-coordinate-alns' indices'))
       (assoc indices rid idx-status
-                     :no-coordinate-alns no-coordinate-alns))))
+             :no-coordinate-alns no-coordinate-alns))))
 
 ;; Merging indices
 ;; -------------
@@ -148,9 +148,9 @@
   (MetaData. (let [f1 (.first-offset meta1)
                    f2 (.first-offset meta2)]
                (cond
-                (= f1 -1) f2
-                (= f2 -1) f1
-                :else (min f1 f2)))
+                 (= f1 -1) f2
+                 (= f2 -1) f1
+                 :else (min f1 f2)))
              (max (.last-offset meta1) (.last-offset meta2))
              (+ (.aligned-alns meta1) (.aligned-alns meta2))
              (+ (.unaligned-alns meta1) (.unaligned-alns meta2))))
@@ -307,7 +307,7 @@
                                                   (->> sub-blocks
                                                        (eduction (map bam-decoder/decode-pointer-block))
                                                        make-index*)))
-                                 (reduce merge-index)))))]
+                                 (reduce merge-index {:no-coordinate-alns 0})))))]
     (->> blocks
          make-index-fn
          (finalize-index nrefs))))


Test patch:
diff --git a/test/cljam/algo/bam_indexer_test.clj b/test/cljam/algo/bam_indexer_test.clj
index 03dd1ad..4b82398 100644
--- a/test/cljam/algo/bam_indexer_test.clj
+++ b/test/cljam/algo/bam_indexer_test.clj
@@ -106,3 +106,14 @@
     (is (not-throw? (bai/create-index temp-file-sorted
                                       (str temp-file-sorted ".bai"))))
     (is (.isFile (cio/file (str temp-file-sorted ".bai"))))))
+
+(deftest bam-without-alignments-test
+  (with-before-after {:before (prepare-cache!)
+                      :after (clean-cache!)}
+    (let [header {:SQ [{:SN "chr1", :LN 100}]}
+          target (cio/file temp-dir "no_aln.bam")
+          target-bai (cio/file temp-dir "no_aln.bam.bai")]
+      (with-open [w (sam/writer target)]
+        (sam/write-header w header)
+        (sam/write-refs w header))
+      (is (not-throw? (bai/create-index target target-bai))))))