package xml
import (
"bytes"
"encoding/xml"
"io"
"reflect"
"strings"
"testing"
)
func TestXMLNodeDecoder_Token(t *testing.T) {
cases := map[string]struct {
responseBody io.Reader
expectedStartElement xml.StartElement
expectedDone bool
expectedError string
}{
"simple success case": {
responseBody: bytes.NewReader([]byte(`abc`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "",
},
},
expectedDone: true,
},
"no value": {
responseBody: bytes.NewReader([]byte(``)),
expectedDone: true,
},
"empty body": {
responseBody: bytes.NewReader([]byte(``)),
expectedError: "EOF",
},
"with indentation": {
responseBody: bytes.NewReader([]byte(` abc`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "Struct",
},
Attr: []xml.Attr{},
},
},
"with comment and indentation": {
responseBody: bytes.NewReader([]byte(` abc`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "Struct",
},
Attr: []xml.Attr{},
},
},
"attr with namespace": {
responseBody: bytes.NewReader([]byte(``)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "Grantee",
},
Attr: []xml.Attr{
{
Name: xml.Name{
Space: "xmlns",
Local: "xsi",
},
Value: "http://www.w3.org/2001/XMLSchema-instance",
},
{
Name: xml.Name{
Space: "xsi",
Local: "type",
},
Value: "CanonicalUser",
},
},
},
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
xmlDecoder := xml.NewDecoder(c.responseBody)
st, err := FetchRootElement(xmlDecoder)
if err != nil {
if len(c.expectedError) == 0 {
t.Fatalf("Expected no error, got %v", err)
}
if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) {
t.Fatalf("expected error to contain %v, found %v", e, a.Error())
}
}
nodeDecoder := WrapNodeDecoder(xmlDecoder, st)
token, done, err := nodeDecoder.Token()
if err != nil {
if len(c.expectedError) == 0 {
t.Fatalf("Expected no error, got %v", err)
}
if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) {
t.Fatalf("expected error to contain %v, found %v", e, a.Error())
}
}
if e, a := c.expectedDone, done; e != a {
t.Fatalf("expected a valid end element token for the xml document, got none")
}
if !reflect.DeepEqual(c.expectedStartElement, token) {
t.Fatalf("Found diff : %v != %v", c.expectedStartElement, token)
}
})
}
}
func TestXMLNodeDecoder_TokenExample(t *testing.T) {
responseBody := bytes.NewReader([]byte(`abc`))
xmlDecoder := xml.NewDecoder(responseBody)
// Fetches tag as start element.
st, err := FetchRootElement(xmlDecoder)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// nodeDecoder will track tag as root node of the document
nodeDecoder := WrapNodeDecoder(xmlDecoder, st)
// Retrieves tag
token, done, err := nodeDecoder.Token()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
expect := xml.StartElement{Name: xml.Name{Local: "Response"}, Attr: []xml.Attr{}}
if !reflect.DeepEqual(expect, token) {
t.Fatalf("Found diff : %v != %v", expect, token)
}
if done {
t.Fatalf("expected decoding to not be done yet")
}
// Skips the value and gets that is the end token of previously retrieved tag.
// The way node decoder works it only keeps track of the root start tag using which it was initialized.
// Here is used to initialize, while is end element corresponding to already read
// tag. We won't be done until we receive
token, done, err = nodeDecoder.Token()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
expect = xml.StartElement{Name: xml.Name{Local: ""}, Attr: nil}
if !reflect.DeepEqual(expect, token) {
t.Fatalf("Found diff : %v != %v", expect, token)
}
if done {
t.Fatalf("expected decoding to not be done yet")
}
// Retrieves end element tag corresponding to tag.
// Since we got the end element that corresponds to the start element being track, we are done decoding.
token, done, err = nodeDecoder.Token()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(expect, token) {
t.Fatalf("%v != %v", expect, token)
}
if !done {
t.Fatalf("expected decoding to be done as we fetched the end element ")
}
}
func TestXMLNodeDecoder_Value(t *testing.T) {
cases := map[string]struct {
responseBody io.Reader
expectedValue []byte
expectedDone bool
expectedError string
}{
"simple success case": {
responseBody: bytes.NewReader([]byte(`abc`)),
expectedValue: []byte(`abc`),
},
"no value": {
responseBody: bytes.NewReader([]byte(``)),
expectedValue: []byte{},
},
"self-closing": {
responseBody: bytes.NewReader([]byte(``)),
expectedValue: []byte{},
},
"empty body": {
responseBody: bytes.NewReader([]byte(``)),
expectedError: "EOF",
},
"start element retrieved": {
responseBody: bytes.NewReader([]byte(`abc`)),
expectedError: "expected value for Response element, got xml.StartElement type",
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
xmlDecoder := xml.NewDecoder(c.responseBody)
st, err := FetchRootElement(xmlDecoder)
if err != nil {
if len(c.expectedError) == 0 {
t.Fatalf("Expected no error, got %v", err)
}
if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) {
t.Fatalf("expected error to contain %v, found %v", e, a.Error())
}
}
nodeDecoder := WrapNodeDecoder(xmlDecoder, st)
token, err := nodeDecoder.Value()
if err != nil {
if len(c.expectedError) == 0 {
t.Fatalf("Expected no error, got %v", err)
}
if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) {
t.Fatalf("expected error to contain %v, found %v", e, a.Error())
}
}
if !reflect.DeepEqual(c.expectedValue, token) {
t.Fatalf("%v != %v", c.expectedValue, token)
}
})
}
}
func Test_FetchXMLRootElement(t *testing.T) {
cases := map[string]struct {
responseBody io.Reader
expectedStartElement xml.StartElement
expectedError string
}{
"simple success case": {
responseBody: bytes.NewReader([]byte(`abc`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "Response",
},
Attr: []xml.Attr{},
},
},
"empty body": {
responseBody: bytes.NewReader([]byte(``)),
expectedError: "EOF",
},
"with indentation": {
responseBody: bytes.NewReader([]byte(`
Sender
InvalidGreeting
Hi
setting
foo-id
`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "ErrorResponse",
},
Attr: []xml.Attr{},
},
},
"with preamble": {
responseBody: bytes.NewReader([]byte(`
Sender
InvalidGreeting
Hi
setting
foo-id
`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "ErrorResponse",
},
Attr: []xml.Attr{},
},
},
"with comments": {
responseBody: bytes.NewReader([]byte(`
Sender
InvalidGreeting
Hi
setting
foo-id
`)),
expectedStartElement: xml.StartElement{
Name: xml.Name{
Local: "ErrorResponse",
},
Attr: []xml.Attr{},
},
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
decoder := xml.NewDecoder(c.responseBody)
st, err := FetchRootElement(decoder)
if err != nil {
if len(c.expectedError) == 0 {
t.Fatalf("Expected no error, got %v", err)
}
if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) {
t.Fatalf("expected error to contain %v, found %v", e, a.Error())
}
}
if !reflect.DeepEqual(c.expectedStartElement, st) {
t.Fatalf("Found diff : %v != %v", c.expectedStartElement, st)
}
})
}
}