2017-03-13 21:09:15 -04:00
package ofxgo_test
import (
"fmt"
2017-03-22 20:59:40 -04:00
"github.com/aclindsa/go/src/encoding/xml"
2017-03-27 20:14:18 -04:00
"github.com/aclindsa/ofxgo"
2017-03-13 21:09:15 -04:00
"reflect"
"testing"
"time"
)
func getTypeName ( i interface { } ) string {
val := reflect . ValueOf ( i )
// Do the same thing that encoding/xml does to get the name
for val . Kind ( ) == reflect . Interface || val . Kind ( ) == reflect . Ptr {
if val . IsNil ( ) {
return ""
}
val = val . Elem ( )
}
return val . Type ( ) . Name ( )
}
func marshalHelper ( t * testing . T , expected string , i interface { } ) {
typename := getTypeName ( i )
expectedstring := fmt . Sprintf ( "<%s>%s</%s>" , typename , expected , typename )
b , err := xml . Marshal ( i )
if err != nil {
t . Fatalf ( "Unexpected error on xml.Marshal(%T): %s\n" , i , err )
}
if string ( b ) != expectedstring {
t . Fatalf ( "Expected '%s', got '%s'\n" , expectedstring , string ( b ) )
}
}
func unmarshalHelper2 ( t * testing . T , input string , expected interface { } , overwritten interface { } , eq func ( a , b interface { } ) bool ) {
typename := getTypeName ( expected )
inputstring := fmt . Sprintf ( "<%s>%s</%s>" , typename , input , typename )
err := xml . Unmarshal ( [ ] byte ( inputstring ) , & overwritten )
if err != nil {
t . Fatalf ( "Unexpected error on xml.Unmarshal(%T): %s\n" , expected , err )
}
if ! eq ( overwritten , expected ) {
t . Fatalf ( "Expected '%s', got '%s'\n" , expected , overwritten )
}
}
func unmarshalHelper ( t * testing . T , input string , expected interface { } , overwritten interface { } ) {
eq := func ( a , b interface { } ) bool {
return reflect . DeepEqual ( a , b )
}
unmarshalHelper2 ( t , input , expected , overwritten , eq )
}
func TestMarshalInt ( t * testing . T ) {
var i ofxgo . Int = 927
marshalHelper ( t , "927" , & i )
i = 0
marshalHelper ( t , "0" , & i )
i = - 768276587425
marshalHelper ( t , "-768276587425" , & i )
}
func TestUnmarshalInt ( t * testing . T ) {
var i , overwritten ofxgo . Int = - 48394 , 0
unmarshalHelper ( t , "-48394" , & i , & overwritten )
i = 0
unmarshalHelper ( t , "0" , & i , & overwritten )
i = 198237198
unmarshalHelper ( t , "198237198" , & i , & overwritten )
2017-03-19 20:46:01 -04:00
// Make sure stray newlines are handled properly
unmarshalHelper ( t , "198237198\n" , & i , & overwritten )
2017-03-13 21:09:15 -04:00
}
func TestMarshalAmount ( t * testing . T ) {
var a ofxgo . Amount
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( 8 , 1 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "8" , & a )
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( 1 , 8 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "0.125" , & a )
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( - 1 , 200 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "-0.005" , & a )
2017-04-03 21:15:08 -04:00
a . SetInt64 ( 0 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "0" , & a )
2017-04-03 21:15:08 -04:00
a . SetInt64 ( - 768276587425 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "-768276587425" , & a )
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( 1 , 12 )
2017-03-13 21:09:15 -04:00
marshalHelper ( t , "0.0833333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333" , & a )
}
func TestUnmarshalAmount ( t * testing . T ) {
var a , overwritten ofxgo . Amount
// Amount/big.Rat needs a special equality test because reflect.DeepEqual
// doesn't always return equal for two values that big.Rat.Cmp() does
eq := func ( a , b interface { } ) bool {
if amountA , ok := a . ( * ofxgo . Amount ) ; ok {
if amountB , ok2 := b . ( * ofxgo . Amount ) ; ok2 {
2017-04-03 21:15:08 -04:00
return amountA . Cmp ( & amountB . Rat ) == 0
2017-03-13 21:09:15 -04:00
}
}
return false
}
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( 12 , 1 )
2017-03-13 21:09:15 -04:00
unmarshalHelper2 ( t , "12" , & a , & overwritten , eq )
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( - 21309 , 100 )
2017-03-13 21:09:15 -04:00
unmarshalHelper2 ( t , "-213.09" , & a , & overwritten , eq )
2017-04-03 21:15:08 -04:00
a . SetFrac64 ( 8192 , 1000 )
2017-03-13 21:09:15 -04:00
unmarshalHelper2 ( t , "8.192" , & a , & overwritten , eq )
unmarshalHelper2 ( t , "+8.192" , & a , & overwritten , eq )
2017-04-03 21:15:08 -04:00
a . SetInt64 ( 0 )
2017-03-13 21:09:15 -04:00
unmarshalHelper2 ( t , "0" , & a , & overwritten , eq )
unmarshalHelper2 ( t , "+0" , & a , & overwritten , eq )
unmarshalHelper2 ( t , "-0" , & a , & overwritten , eq )
2017-04-03 21:15:08 -04:00
a . SetInt64 ( - 19487135 )
2017-03-13 21:09:15 -04:00
unmarshalHelper2 ( t , "-19487135" , & a , & overwritten , eq )
2017-03-19 20:46:01 -04:00
// Make sure stray newlines are handled properly
unmarshalHelper2 ( t , "-19487135\n" , & a , & overwritten , eq )
2017-03-13 21:09:15 -04:00
}
2017-04-04 20:20:31 -04:00
func TestAmountEqual ( t * testing . T ) {
assertEq := func ( a , b ofxgo . Amount ) {
if ! a . Equal ( b ) {
t . Fatalf ( "Amounts should be equal but Equal returned false: %s and %s\n" , a , b )
}
}
assertNEq := func ( a , b ofxgo . Amount ) {
if a . Equal ( b ) {
t . Fatalf ( "Amounts should not be equal but Equal returned true: %s and %s\n" , a , b )
}
}
var a , b ofxgo . Amount
a . SetInt64 ( - 19487135 )
b . SetInt64 ( - 19487135 )
assertEq ( a , b )
b . SetInt64 ( 19487135 )
assertNEq ( a , b )
b . SetInt64 ( 0 )
assertNEq ( a , b )
a . SetInt64 ( - 0 )
assertEq ( a , b )
a . SetFrac64 ( 1 , 1000000000000000000 )
b . SetFrac64 ( 1 , 1000000000000000001 )
assertNEq ( a , b )
}
2017-03-13 21:09:15 -04:00
func TestMarshalDate ( t * testing . T ) {
2017-04-04 05:45:19 -04:00
var d * ofxgo . Date
2017-03-13 21:09:15 -04:00
UTC := time . FixedZone ( "UTC" , 0 )
GMT_nodesc := time . FixedZone ( "" , 0 )
EST := time . FixedZone ( "EST" , - 5 * 60 * 60 )
NPT := time . FixedZone ( "NPT" , ( 5 * 60 + 45 ) * 60 )
IST := time . FixedZone ( "IST" , ( 5 * 60 + 30 ) * 60 )
NST := time . FixedZone ( "NST" , - ( 3 * 60 + 30 ) * 60 )
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDateGMT ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 )
marshalHelper ( t , "20170314150926.053[0:GMT]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , NPT )
marshalHelper ( t , "20170314150926.053[5.75:NPT]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , EST )
marshalHelper ( t , "20170314150926.053[-5:EST]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , UTC )
marshalHelper ( t , "20170314150926.053[0:UTC]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , IST )
marshalHelper ( t , "20170314150926.053[5.50:IST]" , d )
d = ofxgo . NewDate ( 9999 , 11 , 1 , 23 , 59 , 59 , 1000 , EST )
marshalHelper ( t , "99991101235959.000[-5:EST]" , d )
d = ofxgo . NewDate ( 0 , 1 , 1 , 0 , 0 , 0 , 0 , IST )
marshalHelper ( t , "00000101000000.000[5.50:IST]" , d )
d = & ofxgo . Date { Time : time . Unix ( 0 , 0 ) . In ( UTC ) }
marshalHelper ( t , "19700101000000.000[0:UTC]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 26 , 53 * 1000 * 1000 , EST )
marshalHelper ( t , "20170314000026.053[-5:EST]" , d )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 26 , 53 * 1000 * 1000 , NST )
marshalHelper ( t , "20170314000026.053[-3.50:NST]" , d )
2017-03-13 21:09:15 -04:00
// Time zone without textual description
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , GMT_nodesc )
marshalHelper ( t , "20170314150926.053[0]" , d )
2017-03-13 21:09:15 -04:00
}
func TestUnmarshalDate ( t * testing . T ) {
2017-04-04 05:45:19 -04:00
var d * ofxgo . Date
var overwritten ofxgo . Date
2017-03-13 21:09:15 -04:00
GMT := time . FixedZone ( "GMT" , 0 )
EST := time . FixedZone ( "EST" , - 5 * 60 * 60 )
NPT := time . FixedZone ( "NPT" , ( 5 * 60 + 45 ) * 60 )
IST := time . FixedZone ( "IST" , ( 5 * 60 + 30 ) * 60 )
NST := time . FixedZone ( "NST" , - ( 3 * 60 + 30 ) * 60 )
NST_nodesc := time . FixedZone ( "" , - ( 3 * 60 + 30 ) * 60 )
2017-03-19 20:46:01 -04:00
eq := func ( a , b interface { } ) bool {
if dateA , ok := a . ( * ofxgo . Date ) ; ok {
if dateB , ok2 := b . ( * ofxgo . Date ) ; ok2 {
2017-04-04 05:45:19 -04:00
return dateA . Equal ( * dateB )
2017-03-19 20:46:01 -04:00
}
}
return false
}
2017-03-13 21:09:15 -04:00
// Ensure omitted fields default to the correct values
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDateGMT ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 )
unmarshalHelper2 ( t , "20170314150926.053[0]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 0 , 0 , GMT )
unmarshalHelper2 ( t , "20170314" , d , & overwritten , eq )
2017-03-13 21:09:15 -04:00
// Ensure all signs on time zone offsets are properly handled
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDateGMT ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 )
unmarshalHelper2 ( t , "20170314150926.053[0:GMT]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053[+0:GMT]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053[-0:GMT]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053[0]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053[+0]" , d , & overwritten , eq )
unmarshalHelper2 ( t , "20170314150926.053[-0]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , NPT )
unmarshalHelper2 ( t , "20170314150926.053[5.75:NPT]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , EST )
unmarshalHelper2 ( t , "20170314150926.053[-5:EST]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , GMT )
unmarshalHelper2 ( t , "20170314150926.053[0:GMT]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , IST )
unmarshalHelper2 ( t , "20170314150926.053[5.50:IST]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2018 , 11 , 1 , 23 , 59 , 58 , 0 , EST )
unmarshalHelper2 ( t , "20181101235958.000[-5:EST]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 0 , 1 , 1 , 0 , 0 , 0 , 0 , IST )
unmarshalHelper2 ( t , "00000101000000.000[5.50:IST]" , d , & overwritten , eq )
d = & ofxgo . Date { Time : time . Unix ( 0 , 0 ) . In ( GMT ) }
unmarshalHelper2 ( t , "19700101000000.000[0:GMT]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 26 , 53 * 1000 * 1000 , EST )
unmarshalHelper2 ( t , "20170314000026.053[-5:EST]" , d , & overwritten , eq )
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 26 , 53 * 1000 * 1000 , NST )
unmarshalHelper2 ( t , "20170314000026.053[-3.50:NST]" , d , & overwritten , eq )
2017-03-13 21:09:15 -04:00
// Autopopulate zone without textual description for GMT
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , GMT )
unmarshalHelper2 ( t , "20170314150926.053[0]" , d , & overwritten , eq )
2017-03-13 21:09:15 -04:00
// but not for others:
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDate ( 2017 , 3 , 14 , 0 , 0 , 26 , 53 * 1000 * 1000 , NST_nodesc )
unmarshalHelper2 ( t , "20170314000026.053[-3.50]" , d , & overwritten , eq )
2017-03-19 20:46:01 -04:00
// Make sure we handle poorly-formatted dates (from Vanguard)
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDate ( 2016 , 12 , 7 , 16 , 0 , 0 , 0 , EST )
unmarshalHelper2 ( t , "20161207160000.000[-5:EST]610900.500[-9:BST]" , d , & overwritten , eq ) // extra part intentionally different to ensure the first timezone is parsed
2017-03-19 20:46:01 -04:00
// Make sure we properly handle ending newlines
2017-04-04 05:45:19 -04:00
d = ofxgo . NewDate ( 2018 , 11 , 1 , 23 , 59 , 58 , 0 , EST )
unmarshalHelper2 ( t , "20181101235958.000[-5:EST]\n" , d , & overwritten , eq )
2017-03-13 21:09:15 -04:00
}
2017-04-04 19:51:35 -04:00
func TestDateEqual ( t * testing . T ) {
GMT := time . FixedZone ( "GMT" , 0 )
EST := time . FixedZone ( "EST" , - 5 * 60 * 60 )
assertEq := func ( a , b * ofxgo . Date ) {
if ! a . Equal ( * b ) {
t . Fatalf ( "Dates should be equal but Equal returned false: %s and %s\n" , * a , * b )
}
}
assertNEq := func ( a , b * ofxgo . Date ) {
if a . Equal ( * b ) {
t . Fatalf ( "Dates should not be equal but Equal returned true: %s and %s\n" , * a , * b )
}
}
// Ensure omitted fields default to the correct values
gmt1 := ofxgo . NewDateGMT ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 )
gmt2 := ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , GMT )
est1 := ofxgo . NewDate ( 2017 , 3 , 14 , 10 , 9 , 26 , 53 * 1000 * 1000 , EST )
est2 := ofxgo . NewDate ( 2017 , 3 , 14 , 10 , 9 , 26 , 53 * 1000 * 1000 + 1 , EST )
est3 := ofxgo . NewDate ( 2017 , 3 , 14 , 15 , 9 , 26 , 53 * 1000 * 1000 , EST )
assertEq ( gmt1 , gmt2 )
assertEq ( gmt2 , gmt1 )
assertEq ( gmt1 , est1 )
assertNEq ( gmt1 , est2 )
assertNEq ( est1 , est2 )
assertNEq ( gmt1 , est3 )
}
2017-03-13 21:09:15 -04:00
func TestMarshalString ( t * testing . T ) {
var s ofxgo . String = ""
marshalHelper ( t , "" , & s )
s = "foo&bar"
marshalHelper ( t , "foo&bar" , & s )
s = "\n"
marshalHelper ( t , "
" , & s )
s = "Some Name"
marshalHelper ( t , "Some Name" , & s )
}
func TestUnmarshalString ( t * testing . T ) {
var s , overwritten ofxgo . String = "" , ""
unmarshalHelper ( t , "" , & s , & overwritten )
s = "foo&bar"
unmarshalHelper ( t , "foo&bar" , & s , & overwritten )
// whitespace intentionally stripped because some OFX servers add newlines
// inside tags
s = "new\nline"
unmarshalHelper ( t , " new
line
" , & s , & overwritten )
s = "Some Name"
unmarshalHelper ( t , "Some Name" , & s , & overwritten )
2017-04-03 19:44:25 -04:00
// Make sure stray newlines are handled properly
unmarshalHelper ( t , "Some Name\n" , & s , & overwritten )
2017-03-13 21:09:15 -04:00
}
func TestMarshalBoolean ( t * testing . T ) {
var b ofxgo . Boolean = true
marshalHelper ( t , "Y" , & b )
b = false
marshalHelper ( t , "N" , & b )
}
func TestUnmarshalBoolean ( t * testing . T ) {
var b , overwritten ofxgo . Boolean = true , false
unmarshalHelper ( t , "Y" , & b , & overwritten )
b = false
unmarshalHelper ( t , "N" , & b , & overwritten )
2017-04-03 19:44:25 -04:00
// Make sure stray newlines are handled properly
unmarshalHelper ( t , "N\n" , & b , & overwritten )
2017-03-13 21:09:15 -04:00
}
func TestMarshalUID ( t * testing . T ) {
var u ofxgo . UID = "d1cf3d3d-9ef9-4a97-b180-81706829cb04"
marshalHelper ( t , "d1cf3d3d-9ef9-4a97-b180-81706829cb04" , & u )
}
func TestUnmarshalUID ( t * testing . T ) {
var u , overwritten ofxgo . UID = "d1cf3d3d-9ef9-4a97-b180-81706829cb04" , ""
unmarshalHelper ( t , "d1cf3d3d-9ef9-4a97-b180-81706829cb04" , & u , & overwritten )
2017-04-03 19:44:25 -04:00
// Make sure stray newlines are handled properly
u = "0f94ce83-13b7-7568-e4fc-c02c7b47e7ab"
unmarshalHelper ( t , "0f94ce83-13b7-7568-e4fc-c02c7b47e7ab\n" , & u , & overwritten )
2017-03-13 21:09:15 -04:00
}
2017-03-29 05:41:28 -04:00
func TestUIDRecommendedFormat ( t * testing . T ) {
2017-03-13 21:09:15 -04:00
var u ofxgo . UID = "d1cf3d3d-9ef9-4a97-b180-81706829cb04"
2017-03-29 05:41:28 -04:00
if ok , err := u . RecommendedFormat ( ) ; ! ok || err != nil {
2017-03-13 21:09:15 -04:00
t . Fatalf ( "UID unexpectedly failed validation\n" )
}
u = "d1cf3d3d-9ef9-4a97-b180-81706829cb0"
2017-03-29 05:41:28 -04:00
if ok , err := u . RecommendedFormat ( ) ; ok || err == nil {
2017-03-13 21:09:15 -04:00
t . Fatalf ( "UID should have failed validation because it's too short\n" )
}
u = "d1cf3d3d-9ef94a97-b180-81706829cb04"
2017-03-29 05:41:28 -04:00
if ok , err := u . RecommendedFormat ( ) ; ok || err == nil {
2017-03-13 21:09:15 -04:00
t . Fatalf ( "UID should have failed validation because it's missing hyphens\n" )
}
u = "d1cf3d3d-9ef9-4a97-b180981706829cb04"
2017-03-29 05:41:28 -04:00
if ok , err := u . RecommendedFormat ( ) ; ok || err == nil {
2017-03-13 21:09:15 -04:00
t . Fatalf ( "UID should have failed validation because its hyphens have been replaced\n" )
}
}