mirror of
https://github.com/aclindsa/ofxgo.git
synced 2024-11-22 11:30:05 -05:00
Add test for banking responses
This also adds a generic response equality testing framework, a missing Status field to all current responses, and Equal() methods to all basic types.
This commit is contained in:
parent
6d6ee3ea1b
commit
6efd3ae921
@ -126,6 +126,7 @@ type Balance struct {
|
|||||||
type StatementResponse struct {
|
type StatementResponse struct {
|
||||||
XMLName xml.Name `xml:"STMTTRNRS"`
|
XMLName xml.Name `xml:"STMTTRNRS"`
|
||||||
TrnUID UID `xml:"TRNUID"`
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
Status Status `xml:"STATUS"`
|
||||||
CurDef String `xml:"STMTRS>CURDEF"`
|
CurDef String `xml:"STMTRS>CURDEF"`
|
||||||
BankAcctFrom BankAcct `xml:"STMTRS>BANKACCTFROM"`
|
BankAcctFrom BankAcct `xml:"STMTRS>BANKACCTFROM"`
|
||||||
BankTranList *TransactionList `xml:"STMTRS>BANKTRANLIST,omitempty"`
|
BankTranList *TransactionList `xml:"STMTRS>BANKTRANLIST,omitempty"`
|
||||||
|
141
banking_test.go
141
banking_test.go
@ -2,6 +2,8 @@ package ofxgo_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aclindsa/ofxgo"
|
"github.com/aclindsa/ofxgo"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -71,3 +73,142 @@ func TestMarshalBankStatementRequest(t *testing.T) {
|
|||||||
|
|
||||||
marshalCheckRequest(t, &request, expectedString)
|
marshalCheckRequest(t, &request, expectedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBankStatementResponse(t *testing.T) {
|
||||||
|
responseReader := strings.NewReader(`<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?OFX OFXHEADER="200" VERSION="203" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>
|
||||||
|
<OFX>
|
||||||
|
<SIGNONMSGSRSV1>
|
||||||
|
<SONRS>
|
||||||
|
<STATUS>
|
||||||
|
<CODE>0</CODE>
|
||||||
|
<SEVERITY>INFO</SEVERITY>
|
||||||
|
</STATUS>
|
||||||
|
<DTSERVER>20060115112303</DTSERVER>
|
||||||
|
<LANGUAGE>ENG</LANGUAGE>
|
||||||
|
<DTPROFUP>20050221091300</DTPROFUP>
|
||||||
|
<DTACCTUP>20060102160000</DTACCTUP>
|
||||||
|
<FI>
|
||||||
|
<ORG>BNK</ORG>
|
||||||
|
<FID>1987</FID>
|
||||||
|
</FI>
|
||||||
|
</SONRS>
|
||||||
|
</SIGNONMSGSRSV1>
|
||||||
|
<BANKMSGSRSV1>
|
||||||
|
<STMTTRNRS>
|
||||||
|
<TRNUID>1001</TRNUID>
|
||||||
|
<STATUS>
|
||||||
|
<CODE>0</CODE>
|
||||||
|
<SEVERITY>INFO</SEVERITY>
|
||||||
|
</STATUS>
|
||||||
|
<STMTRS>
|
||||||
|
<CURDEF>USD</CURDEF>
|
||||||
|
<BANKACCTFROM>
|
||||||
|
<BANKID>318398732</BANKID>
|
||||||
|
<ACCTID>78346129</ACCTID>
|
||||||
|
<ACCTTYPE>CHECKING</ACCTTYPE>
|
||||||
|
</BANKACCTFROM>
|
||||||
|
<BANKTRANLIST>
|
||||||
|
<DTSTART>20060101</DTSTART>
|
||||||
|
<DTEND>20060115</DTEND>
|
||||||
|
<STMTTRN>
|
||||||
|
<TRNTYPE>CHECK</TRNTYPE>
|
||||||
|
<DTPOSTED>20060104</DTPOSTED>
|
||||||
|
<TRNAMT>-200.00</TRNAMT>
|
||||||
|
<FITID>00592</FITID>
|
||||||
|
<CHECKNUM>2002</CHECKNUM>
|
||||||
|
</STMTTRN>
|
||||||
|
<STMTTRN>
|
||||||
|
<TRNTYPE>ATM</TRNTYPE>
|
||||||
|
<DTPOSTED>20060112</DTPOSTED>
|
||||||
|
<DTUSER>20060112</DTUSER>
|
||||||
|
<TRNAMT>-300.00</TRNAMT>
|
||||||
|
<FITID>00679</FITID>
|
||||||
|
</STMTTRN>
|
||||||
|
</BANKTRANLIST>
|
||||||
|
<LEDGERBAL>
|
||||||
|
<BALAMT>200.29</BALAMT>
|
||||||
|
<DTASOF>200601141600</DTASOF>
|
||||||
|
</LEDGERBAL>
|
||||||
|
<AVAILBAL>
|
||||||
|
<BALAMT>200.29</BALAMT>
|
||||||
|
<DTASOF>200601141600</DTASOF>
|
||||||
|
</AVAILBAL>
|
||||||
|
</STMTRS>
|
||||||
|
</STMTTRNRS>
|
||||||
|
</BANKMSGSRSV1>
|
||||||
|
</OFX>`)
|
||||||
|
var expected ofxgo.Response
|
||||||
|
GMT := time.FixedZone("GMT", 0)
|
||||||
|
|
||||||
|
expected.Version = "203"
|
||||||
|
expected.Signon.Status.Code = 0
|
||||||
|
expected.Signon.Status.Severity = "INFO"
|
||||||
|
expected.Signon.DtServer = ofxgo.Date(time.Date(2006, 1, 15, 11, 23, 03, 0, GMT))
|
||||||
|
expected.Signon.Language = "ENG"
|
||||||
|
dtprofup := ofxgo.Date(time.Date(2005, 2, 21, 9, 13, 0, 0, GMT))
|
||||||
|
expected.Signon.DtProfUp = &dtprofup
|
||||||
|
dtacctup := ofxgo.Date(time.Date(2006, 1, 2, 16, 0, 0, 0, GMT))
|
||||||
|
expected.Signon.DtAcctUp = &dtacctup
|
||||||
|
expected.Signon.Org = "BNK"
|
||||||
|
expected.Signon.Fid = "1987"
|
||||||
|
|
||||||
|
var trnamt1, trnamt2 big.Rat
|
||||||
|
trnamt1.SetFrac64(-20000, 100)
|
||||||
|
trnamt2.SetFrac64(-30000, 100)
|
||||||
|
dtuser2 := ofxgo.Date(time.Date(2006, 1, 12, 0, 0, 0, 0, GMT))
|
||||||
|
|
||||||
|
banktranlist := ofxgo.TransactionList{
|
||||||
|
DtStart: ofxgo.Date(time.Date(2006, 1, 1, 0, 0, 0, 0, GMT)),
|
||||||
|
DtEnd: ofxgo.Date(time.Date(2006, 1, 15, 0, 0, 0, 0, GMT)),
|
||||||
|
Transactions: []ofxgo.Transaction{
|
||||||
|
{
|
||||||
|
TrnType: "CHECK",
|
||||||
|
DtPosted: ofxgo.Date(time.Date(2006, 1, 4, 0, 0, 0, 0, GMT)),
|
||||||
|
TrnAmt: ofxgo.Amount(trnamt1),
|
||||||
|
FiTId: "00592",
|
||||||
|
CheckNum: "2002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TrnType: "ATM",
|
||||||
|
DtPosted: ofxgo.Date(time.Date(2006, 1, 12, 0, 0, 0, 0, GMT)),
|
||||||
|
DtUser: &dtuser2,
|
||||||
|
TrnAmt: ofxgo.Amount(trnamt2),
|
||||||
|
FiTId: "00679",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var balamt, availbalamt big.Rat
|
||||||
|
balamt.SetFrac64(20029, 100)
|
||||||
|
availbalamt.SetFrac64(20029, 100)
|
||||||
|
|
||||||
|
availdtasof := ofxgo.Date(time.Date(2006, 1, 14, 16, 0, 0, 0, GMT))
|
||||||
|
|
||||||
|
statementResponse := ofxgo.StatementResponse{
|
||||||
|
TrnUID: "1001",
|
||||||
|
Status: ofxgo.Status{
|
||||||
|
Code: 0,
|
||||||
|
Severity: "INFO",
|
||||||
|
},
|
||||||
|
CurDef: "USD",
|
||||||
|
BankAcctFrom: ofxgo.BankAcct{
|
||||||
|
BankId: "318398732",
|
||||||
|
AcctId: "78346129",
|
||||||
|
AcctType: "CHECKING",
|
||||||
|
},
|
||||||
|
BankTranList: &banktranlist,
|
||||||
|
BalAmt: ofxgo.Amount(balamt),
|
||||||
|
DtAsOf: ofxgo.Date(time.Date(2006, 1, 14, 16, 0, 0, 0, GMT)),
|
||||||
|
AvailBalAmt: (*ofxgo.Amount)(&availbalamt),
|
||||||
|
AvailDtAsOf: &availdtasof,
|
||||||
|
}
|
||||||
|
expected.Banking = append(expected.Banking, statementResponse)
|
||||||
|
|
||||||
|
response, err := ofxgo.ParseResponse(responseReader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error unmarshalling response: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkResponsesEqual(t, &expected, response)
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@ func (r *CCStatementRequest) Valid() (bool, error) {
|
|||||||
type CCStatementResponse struct {
|
type CCStatementResponse struct {
|
||||||
XMLName xml.Name `xml:"CCSTMTTRNRS"`
|
XMLName xml.Name `xml:"CCSTMTTRNRS"`
|
||||||
TrnUID UID `xml:"TRNUID"`
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
Status Status `xml:"STATUS"`
|
||||||
CurDef String `xml:"CCSTMTRS>CURDEF"`
|
CurDef String `xml:"CCSTMTRS>CURDEF"`
|
||||||
CCAcctFrom CCAcct `xml:"CCSTMTRS>CCACCTFROM"`
|
CCAcctFrom CCAcct `xml:"CCSTMTRS>CCACCTFROM"`
|
||||||
BankTranList *TransactionList `xml:"CCSTMTRS>BANKTRANLIST,omitempty"`
|
BankTranList *TransactionList `xml:"CCSTMTRS>BANKTRANLIST,omitempty"`
|
||||||
|
@ -97,6 +97,7 @@ func (msl *MessageSetList) UnmarshalXML(d *xml.Decoder, start xml.StartElement)
|
|||||||
type ProfileResponse struct {
|
type ProfileResponse struct {
|
||||||
XMLName xml.Name `xml:"PROFTRNRS"`
|
XMLName xml.Name `xml:"PROFTRNRS"`
|
||||||
TrnUID UID `xml:"TRNUID"`
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
Status Status `xml:"STATUS"`
|
||||||
MessageSetList MessageSetList `xml:"PROFRS>MSGSETLIST"`
|
MessageSetList MessageSetList `xml:"PROFRS>MSGSETLIST"`
|
||||||
SignonInfoList []SignonInfo `xml:"PROFRS>SIGNONINFOLIST>SIGNONINFO"`
|
SignonInfoList []SignonInfo `xml:"PROFRS>SIGNONINFOLIST>SIGNONINFO"`
|
||||||
DtProfUp Date `xml:"PROFRS>DTPROFUP"`
|
DtProfUp Date `xml:"PROFRS>DTPROFUP"`
|
||||||
|
@ -11,7 +11,7 @@ var ignoreSpacesRe = regexp.MustCompile(">[ \t\r\n]+<")
|
|||||||
func marshalCheckRequest(t *testing.T, request *ofxgo.Request, expected string) {
|
func marshalCheckRequest(t *testing.T, request *ofxgo.Request, expected string) {
|
||||||
buf, err := request.Marshal()
|
buf, err := request.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error marshalling request: %s\n", err)
|
t.Fatalf("%s: Unexpected error marshalling request: %s\n", t.Name(), err)
|
||||||
}
|
}
|
||||||
actualString := buf.String()
|
actualString := buf.String()
|
||||||
|
|
||||||
|
131
response_test.go
Normal file
131
response_test.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package ofxgo_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aclindsa/go/src/encoding/xml"
|
||||||
|
"github.com/aclindsa/ofxgo"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attempt to find a method on the provided Value called 'Equal' which is a
|
||||||
|
// receiver for the Value, takes one argument of the same type, and returns
|
||||||
|
// one bool. equalMethodOf() returns the nil value if the method couldn't be
|
||||||
|
// found.
|
||||||
|
func equalMethodOf(v reflect.Value) reflect.Value {
|
||||||
|
if equalMethod, ok := v.Type().MethodByName("Equal"); ok {
|
||||||
|
if !equalMethod.Func.IsNil() &&
|
||||||
|
equalMethod.Type.NumIn() == 2 &&
|
||||||
|
equalMethod.Type.In(0) == v.Type() &&
|
||||||
|
equalMethod.Type.In(1) == v.Type() &&
|
||||||
|
equalMethod.Type.NumOut() == 1 &&
|
||||||
|
equalMethod.Type.Out(0).Kind() == reflect.Bool {
|
||||||
|
return v.MethodByName("Equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to return a string representation of the value appropriate for its
|
||||||
|
// type by finding a method on the provided Value called 'String' which is a
|
||||||
|
// receiver for the Value, and returns one string. stringMethodOf() returns
|
||||||
|
// fmt.Sprintf("%s", v) if it can't find a String method.
|
||||||
|
func valueToString(v reflect.Value) string {
|
||||||
|
if equalMethod, ok := v.Type().MethodByName("String"); ok {
|
||||||
|
if !equalMethod.Func.IsNil() &&
|
||||||
|
equalMethod.Type.NumIn() == 1 &&
|
||||||
|
equalMethod.Type.In(0) == v.Type() &&
|
||||||
|
equalMethod.Type.NumOut() == 1 &&
|
||||||
|
equalMethod.Type.Out(0).Kind() == reflect.String {
|
||||||
|
out := v.MethodByName("String").Call([]reflect.Value{})
|
||||||
|
return out[0].String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check that the expected and actual Values are equal in value.
|
||||||
|
// If the two Values are equal in type and contain an appropriate Equal()
|
||||||
|
// method (see equalMethodOf()), that method is used for comparison. The
|
||||||
|
// provided testing.T is failed with a message if any inequality is found.
|
||||||
|
func checkEqual(t *testing.T, fieldName string, expected, actual reflect.Value) {
|
||||||
|
if expected.IsValid() && !actual.IsValid() {
|
||||||
|
t.Fatalf("%s: %s was unexpectedly nil\n", t.Name(), fieldName)
|
||||||
|
} else if !expected.IsValid() && actual.IsValid() {
|
||||||
|
t.Fatalf("%s: Expected %s to be nil (it wasn't)\n", t.Name(), fieldName)
|
||||||
|
} else if !expected.IsValid() && !actual.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected.Type() != actual.Type() {
|
||||||
|
t.Fatalf("%s: Expected %s type for %s, found %s\n", t.Name(), expected.Type().Name(), fieldName, actual.Type().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
equalMethod := equalMethodOf(expected)
|
||||||
|
if equalMethod.IsValid() {
|
||||||
|
in := []reflect.Value{actual}
|
||||||
|
out := equalMethod.Call(in)
|
||||||
|
if !out[0].Bool() {
|
||||||
|
t.Fatalf("%s: %s !Equal(): expected '%s', got '%s'\n", t.Name(), fieldName, valueToString(expected), valueToString(actual))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch expected.Kind() {
|
||||||
|
case reflect.Array:
|
||||||
|
for i := 0; i < expected.Len(); i++ {
|
||||||
|
checkEqual(t, fmt.Sprintf("%s[%d]", fieldName, i), expected.Index(i), actual.Index(i))
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if !expected.IsNil() && actual.IsNil() {
|
||||||
|
t.Fatalf("%s: %s was unexpectedly nil\n", t.Name(), fieldName)
|
||||||
|
} else if expected.IsNil() && !actual.IsNil() {
|
||||||
|
t.Fatalf("%s: Expected %s to be nil (it wasn't)\n", t.Name(), fieldName)
|
||||||
|
}
|
||||||
|
if expected.Len() != actual.Len() {
|
||||||
|
t.Fatalf("%s: Expected len(%s) to to be %d, was %d\n", t.Name(), fieldName, expected.Len(), actual.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < expected.Len(); i++ {
|
||||||
|
checkEqual(t, fmt.Sprintf("%s[%d]", fieldName, i), expected.Index(i), actual.Index(i))
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
if !expected.IsNil() && actual.IsNil() {
|
||||||
|
t.Fatalf("%s: %s was unexpectedly nil\n", t.Name(), fieldName)
|
||||||
|
} else if expected.IsNil() && !actual.IsNil() {
|
||||||
|
t.Fatalf("%s: Expected %s to be nil (it wasn't)\n", t.Name(), fieldName)
|
||||||
|
}
|
||||||
|
checkEqual(t, fieldName, expected.Elem(), actual.Elem())
|
||||||
|
case reflect.Ptr:
|
||||||
|
checkEqual(t, fieldName, expected.Elem(), actual.Elem())
|
||||||
|
case reflect.Struct:
|
||||||
|
structType := expected.Type()
|
||||||
|
for i, n := 0, expected.NumField(); i < n; i++ {
|
||||||
|
field := structType.Field(i)
|
||||||
|
// skip XMLName fields so we can be lazy and not fill them out in
|
||||||
|
// testing code
|
||||||
|
var xmlname xml.Name
|
||||||
|
if field.Name == "XMLName" && field.Type == reflect.TypeOf(xmlname) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a new field name for this field, containing the parent
|
||||||
|
// fieldName
|
||||||
|
newFieldName := fieldName
|
||||||
|
if fieldName != "" {
|
||||||
|
newFieldName = fieldName + "."
|
||||||
|
}
|
||||||
|
newFieldName = newFieldName + field.Name
|
||||||
|
checkEqual(t, newFieldName, expected.Field(i), actual.Field(i))
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
if expected.String() != actual.String() {
|
||||||
|
t.Fatalf("%s: %s expected to be '%s', found '%s'\n", t.Name(), fieldName, expected.String(), actual.String())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("%s: %s has unexpected type that didn't provide an Equal() method: %s\n", t.Name(), fieldName, expected.Type().Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponsesEqual(t *testing.T, expected, actual *ofxgo.Response) {
|
||||||
|
checkEqual(t, "", reflect.ValueOf(expected), reflect.ValueOf(actual))
|
||||||
|
}
|
@ -107,6 +107,7 @@ type AcctInfo struct {
|
|||||||
type AcctInfoResponse struct {
|
type AcctInfoResponse struct {
|
||||||
XMLName xml.Name `xml:"ACCTINFOTRNRS"`
|
XMLName xml.Name `xml:"ACCTINFOTRNRS"`
|
||||||
TrnUID UID `xml:"TRNUID"`
|
TrnUID UID `xml:"TRNUID"`
|
||||||
|
Status Status `xml:"STATUS"`
|
||||||
DtAcctUp Date `xml:"ACCTINFORS>DTACCTUP"`
|
DtAcctUp Date `xml:"ACCTINFORS>DTACCTUP"`
|
||||||
AcctInfo []AcctInfo `xml:"ACCTINFORS>ACCTINFO,omitempty"`
|
AcctInfo []AcctInfo `xml:"ACCTINFORS>ACCTINFO,omitempty"`
|
||||||
}
|
}
|
||||||
|
25
types.go
25
types.go
@ -33,6 +33,10 @@ func (i *Int) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i Int) Equal(o Int) bool {
|
||||||
|
return i == o
|
||||||
|
}
|
||||||
|
|
||||||
type Amount big.Rat
|
type Amount big.Rat
|
||||||
|
|
||||||
func (a *Amount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (a *Amount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
@ -66,6 +70,11 @@ func (a *Amount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||||||
return e.EncodeElement(a.String(), start)
|
return e.EncodeElement(a.String(), start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Amount) Equal(o Amount) bool {
|
||||||
|
ratA := (*big.Rat)(&a)
|
||||||
|
return ratA.Cmp((*big.Rat)(&o)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
type Date time.Time
|
type Date time.Time
|
||||||
|
|
||||||
var ofxDateFormats = []string{
|
var ofxDateFormats = []string{
|
||||||
@ -167,6 +176,10 @@ func (od *Date) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||||||
return e.EncodeElement(od.String(), start)
|
return e.EncodeElement(od.String(), start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (od Date) Equal(o Date) bool {
|
||||||
|
return time.Time(od).Equal(time.Time(o))
|
||||||
|
}
|
||||||
|
|
||||||
type String string
|
type String string
|
||||||
|
|
||||||
func (os *String) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (os *String) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
@ -183,6 +196,10 @@ func (os *String) String() string {
|
|||||||
return string(*os)
|
return string(*os)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (os String) Equal(o String) bool {
|
||||||
|
return os == o
|
||||||
|
}
|
||||||
|
|
||||||
type Boolean bool
|
type Boolean bool
|
||||||
|
|
||||||
func (ob *Boolean) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (ob *Boolean) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
@ -214,6 +231,10 @@ func (ob *Boolean) String() string {
|
|||||||
return fmt.Sprintf("%v", *ob)
|
return fmt.Sprintf("%v", *ob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ob Boolean) Equal(o Boolean) bool {
|
||||||
|
return ob == o
|
||||||
|
}
|
||||||
|
|
||||||
type UID string
|
type UID string
|
||||||
|
|
||||||
// The OFX specification recommends that UIDs follow the standard UUID
|
// The OFX specification recommends that UIDs follow the standard UUID
|
||||||
@ -228,6 +249,10 @@ func (ou UID) RecommendedFormat() (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ou UID) Equal(o UID) bool {
|
||||||
|
return ou == o
|
||||||
|
}
|
||||||
|
|
||||||
func RandomUID() (*UID, error) {
|
func RandomUID() (*UID, error) {
|
||||||
uidbytes := make([]byte, 16)
|
uidbytes := make([]byte, 16)
|
||||||
n, err := rand.Read(uidbytes[:])
|
n, err := rand.Read(uidbytes[:])
|
||||||
|
Loading…
Reference in New Issue
Block a user