github.com/seeker-insurance/kit@v0.0.13/jsonapi/request_test.go (about)

     1  package jsonapi
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"sort"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  func TestUnmarshall_attrStringSlice(t *testing.T) {
    16  	out := &Book{}
    17  	tags := []string{"fiction", "sale"}
    18  	data := map[string]interface{}{
    19  		"data": map[string]interface{}{
    20  			"type":       "books",
    21  			"id":         "1",
    22  			"attributes": map[string]interface{}{"tags": tags},
    23  		},
    24  	}
    25  	b, err := json.Marshal(data)
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  
    30  	if err := UnmarshalPayload(bytes.NewReader(b), out); err != nil {
    31  		t.Fatal(err)
    32  	}
    33  
    34  	if e, a := len(tags), len(out.Tags); e != a {
    35  		t.Fatalf("Was expecting %d tags, got %d", e, a)
    36  	}
    37  
    38  	sort.Strings(tags)
    39  	sort.Strings(out.Tags)
    40  
    41  	for i, tag := range tags {
    42  		if e, a := tag, out.Tags[i]; e != a {
    43  			t.Fatalf("At index %d, was expecting %s got %s", i, e, a)
    44  		}
    45  	}
    46  }
    47  
    48  func TestUnmarshalToStructWithPointerAttr(t *testing.T) {
    49  	out := new(WithPointer)
    50  	in := map[string]interface{}{
    51  		"name":      "The name",
    52  		"is-active": true,
    53  		"int-val":   8,
    54  		"float-val": 1.1,
    55  	}
    56  	if err := UnmarshalPayload(sampleWithPointerPayload(in), out); err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	if *out.Name != "The name" {
    60  		t.Fatalf("Error unmarshalling to string ptr")
    61  	}
    62  	if !*out.IsActive {
    63  		t.Fatalf("Error unmarshalling to bool ptr")
    64  	}
    65  	if *out.IntVal != 8 {
    66  		t.Fatalf("Error unmarshalling to int ptr")
    67  	}
    68  	if *out.FloatVal != 1.1 {
    69  		t.Fatalf("Error unmarshalling to float ptr")
    70  	}
    71  }
    72  
    73  func TestUnmarshalPayload_ptrsAllNil(t *testing.T) {
    74  	out := new(WithPointer)
    75  	if err := UnmarshalPayload(
    76  		strings.NewReader(`{"data": {}}`), out); err != nil {
    77  		t.Fatalf("Error unmarshalling to Foo")
    78  	}
    79  
    80  	if out.ID != nil {
    81  		t.Fatalf("Error unmarshalling; expected ID ptr to be nil")
    82  	}
    83  }
    84  
    85  func TestUnmarshalPayloadWithPointerID(t *testing.T) {
    86  	out := new(WithPointer)
    87  	attrs := map[string]interface{}{}
    88  
    89  	if err := UnmarshalPayload(sampleWithPointerPayload(attrs), out); err != nil {
    90  		t.Fatalf("Error unmarshalling to Foo")
    91  	}
    92  
    93  	// these were present in the payload -- expect val to be not nil
    94  	if out.ID == nil {
    95  		t.Fatalf("Error unmarshalling; expected ID ptr to be not nil")
    96  	}
    97  	if e, a := uint64(2), *out.ID; e != a {
    98  		t.Fatalf("Was expecting the ID to have a value of %d, got %d", e, a)
    99  	}
   100  }
   101  
   102  func TestUnmarshalPayloadWithPointerAttr_AbsentVal(t *testing.T) {
   103  	out := new(WithPointer)
   104  	in := map[string]interface{}{
   105  		"name":      "The name",
   106  		"is-active": true,
   107  	}
   108  
   109  	if err := UnmarshalPayload(sampleWithPointerPayload(in), out); err != nil {
   110  		t.Fatalf("Error unmarshalling to Foo")
   111  	}
   112  
   113  	// these were present in the payload -- expect val to be not nil
   114  	if out.Name == nil || out.IsActive == nil {
   115  		t.Fatalf("Error unmarshalling; expected ptr to be not nil")
   116  	}
   117  
   118  	// these were absent in the payload -- expect val to be nil
   119  	if out.IntVal != nil || out.FloatVal != nil {
   120  		t.Fatalf("Error unmarshalling; expected ptr to be nil")
   121  	}
   122  }
   123  
   124  func TestUnmarshalToStructWithPointerAttr_BadType(t *testing.T) {
   125  	out := new(WithPointer)
   126  	in := map[string]interface{}{
   127  		"name": true, // This is the wrong type.
   128  	}
   129  	expectedErrorMessage := ErrUnsupportedPtrType.Error()
   130  
   131  	err := UnmarshalPayload(sampleWithPointerPayload(in), out)
   132  
   133  	if err == nil {
   134  		t.Fatalf("Expected error due to invalid type.")
   135  	}
   136  	if err.Error() != expectedErrorMessage {
   137  		t.Fatalf("Unexpected error message: %s", err.Error())
   138  	}
   139  }
   140  
   141  func TestStringPointerField(t *testing.T) {
   142  	// Build Book payload
   143  	description := "Hello World!"
   144  	data := map[string]interface{}{
   145  		"data": map[string]interface{}{
   146  			"type": "books",
   147  			"id":   "5",
   148  			"attributes": map[string]interface{}{
   149  				"author":      "aren55555",
   150  				"description": description,
   151  				"isbn":        "",
   152  			},
   153  		},
   154  	}
   155  	payload, err := json.Marshal(data)
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	// Parse JSON API payload
   161  	book := new(Book)
   162  	if err := UnmarshalPayload(bytes.NewReader(payload), book); err != nil {
   163  		t.Fatal(err)
   164  	}
   165  
   166  	if book.Description == nil {
   167  		t.Fatal("Was not expecting a nil pointer for book.Description")
   168  	}
   169  	if expected, actual := description, *book.Description; expected != actual {
   170  		t.Fatalf("Was expecting descript to be `%s`, got `%s`", expected, actual)
   171  	}
   172  }
   173  
   174  func TestMalformedTag(t *testing.T) {
   175  	out := new(BadModel)
   176  	err := UnmarshalPayload(samplePayload(), out)
   177  	if err == nil || err != ErrBadJSONAPIStructTag {
   178  		t.Fatalf("Did not error out with wrong number of arguments in tag")
   179  	}
   180  }
   181  
   182  func TestUnmarshalInvalidJSON(t *testing.T) {
   183  	in := strings.NewReader("{}")
   184  	out := new(Blog)
   185  
   186  	err := UnmarshalPayload(in, out)
   187  
   188  	if err == nil {
   189  		t.Fatalf("Did not error out the invalid JSON.")
   190  	}
   191  }
   192  
   193  func TestUnmarshalInvalidJSON_BadType(t *testing.T) {
   194  	var badTypeTests = []struct {
   195  		Field    string
   196  		BadValue interface{}
   197  		Error    error
   198  	}{ // The `Field` values here correspond to the `ModelBadTypes` jsonapi fields.
   199  		{Field: "string_field", BadValue: 0, Error: ErrUnknownFieldNumberType},  // Expected string.
   200  		{Field: "float_field", BadValue: "A string.", Error: ErrInvalidType},    // Expected float64.
   201  		{Field: "time_field", BadValue: "A string.", Error: ErrInvalidTime},     // Expected int64.
   202  		{Field: "time_ptr_field", BadValue: "A string.", Error: ErrInvalidTime}, // Expected *time / int64.
   203  	}
   204  	for _, test := range badTypeTests {
   205  		t.Run(fmt.Sprintf("Test_%s", test.Field), func(t *testing.T) {
   206  			out := new(ModelBadTypes)
   207  			in := map[string]interface{}{}
   208  			in[test.Field] = test.BadValue
   209  			expectedErrorMessage := test.Error.Error()
   210  
   211  			err := UnmarshalPayload(samplePayloadWithBadTypes(in), out)
   212  
   213  			if err == nil {
   214  				t.Fatalf("Expected error due to invalid type.")
   215  			}
   216  			if err.Error() != expectedErrorMessage {
   217  				t.Fatalf("Unexpected error message: %s", err.Error())
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func TestUnmarshalSetsID(t *testing.T) {
   224  	in := samplePayloadWithID()
   225  	out := new(Blog)
   226  
   227  	if err := UnmarshalPayload(in, out); err != nil {
   228  		t.Fatal(err)
   229  	}
   230  
   231  	if out.ID != 2 {
   232  		t.Fatalf("Did not set ID on dst interface")
   233  	}
   234  }
   235  
   236  func TestUnmarshal_nonNumericID(t *testing.T) {
   237  	data := samplePayloadWithoutIncluded()
   238  	data["data"].(map[string]interface{})["id"] = "non-numeric-id"
   239  	payload, _ := payload(data)
   240  	in := bytes.NewReader(payload)
   241  	out := new(Post)
   242  
   243  	if err := UnmarshalPayload(in, out); err != ErrBadJSONAPIID {
   244  		t.Fatalf(
   245  			"Was expecting a `%s` error, got `%s`",
   246  			ErrBadJSONAPIID,
   247  			err,
   248  		)
   249  	}
   250  }
   251  
   252  func TestUnmarshalSetsAttrs(t *testing.T) {
   253  	out, err := unmarshalSamplePayload()
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  
   258  	if out.CreatedAt.IsZero() {
   259  		t.Fatalf("Did not parse time")
   260  	}
   261  
   262  	if out.ViewCount != 1000 {
   263  		t.Fatalf("View count not properly serialized")
   264  	}
   265  }
   266  
   267  func TestUnmarshalParsesISO8601(t *testing.T) {
   268  	payload := &OnePayload{
   269  		Data: &Node{
   270  			Type: "timestamps",
   271  			Attributes: map[string]interface{}{
   272  				"timestamp": "2016-08-17T08:27:12Z",
   273  			},
   274  		},
   275  	}
   276  
   277  	in := bytes.NewBuffer(nil)
   278  	json.NewEncoder(in).Encode(payload)
   279  
   280  	out := new(Timestamp)
   281  
   282  	if err := UnmarshalPayload(in, out); err != nil {
   283  		t.Fatal(err)
   284  	}
   285  
   286  	expected := time.Date(2016, 8, 17, 8, 27, 12, 0, time.UTC)
   287  
   288  	if !out.Time.Equal(expected) {
   289  		t.Fatal("Parsing the ISO8601 timestamp failed")
   290  	}
   291  }
   292  
   293  func TestUnmarshalParsesISO8601TimePointer(t *testing.T) {
   294  	payload := &OnePayload{
   295  		Data: &Node{
   296  			Type: "timestamps",
   297  			Attributes: map[string]interface{}{
   298  				"next": "2016-08-17T08:27:12Z",
   299  			},
   300  		},
   301  	}
   302  
   303  	in := bytes.NewBuffer(nil)
   304  	json.NewEncoder(in).Encode(payload)
   305  
   306  	out := new(Timestamp)
   307  
   308  	if err := UnmarshalPayload(in, out); err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	expected := time.Date(2016, 8, 17, 8, 27, 12, 0, time.UTC)
   313  
   314  	if !out.Next.Equal(expected) {
   315  		t.Fatal("Parsing the ISO8601 timestamp failed")
   316  	}
   317  }
   318  
   319  func TestUnmarshalInvalidISO8601(t *testing.T) {
   320  	payload := &OnePayload{
   321  		Data: &Node{
   322  			Type: "timestamps",
   323  			Attributes: map[string]interface{}{
   324  				"timestamp": "17 Aug 16 08:027 MST",
   325  			},
   326  		},
   327  	}
   328  
   329  	in := bytes.NewBuffer(nil)
   330  	json.NewEncoder(in).Encode(payload)
   331  
   332  	out := new(Timestamp)
   333  
   334  	if err := UnmarshalPayload(in, out); err != ErrInvalidISO8601 {
   335  		t.Fatalf("Expected ErrInvalidISO8601, got %v", err)
   336  	}
   337  }
   338  
   339  func TestUnmarshalRelationshipsWithoutIncluded(t *testing.T) {
   340  	data, _ := payload(samplePayloadWithoutIncluded())
   341  	in := bytes.NewReader(data)
   342  	out := new(Post)
   343  
   344  	if err := UnmarshalPayload(in, out); err != nil {
   345  		t.Fatal(err)
   346  	}
   347  
   348  	// Verify each comment has at least an ID
   349  	for _, comment := range out.Comments {
   350  		if comment.ID == 0 {
   351  			t.Fatalf("The comment did not have an ID")
   352  		}
   353  	}
   354  }
   355  
   356  func TestUnmarshalRelationships(t *testing.T) {
   357  	out, err := unmarshalSamplePayload()
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  
   362  	if out.CurrentPost == nil {
   363  		t.Fatalf("Current post was not materialized")
   364  	}
   365  
   366  	if out.CurrentPost.Title != "Bas" || out.CurrentPost.Body != "Fuubar" {
   367  		t.Fatalf("Attributes were not set")
   368  	}
   369  
   370  	if len(out.Posts) != 2 {
   371  		t.Fatalf("There should have been 2 posts")
   372  	}
   373  }
   374  
   375  func TestUnmarshalNullRelationship(t *testing.T) {
   376  	sample := map[string]interface{}{
   377  		"data": map[string]interface{}{
   378  			"type": "posts",
   379  			"id":   "1",
   380  			"attributes": map[string]interface{}{
   381  				"body":  "Hello",
   382  				"title": "World",
   383  			},
   384  			"relationships": map[string]interface{}{
   385  				"latest_comment": map[string]interface{}{
   386  					"data": nil, // empty to-one relationship
   387  				},
   388  			},
   389  		},
   390  	}
   391  	data, err := json.Marshal(sample)
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  
   396  	in := bytes.NewReader(data)
   397  	out := new(Post)
   398  
   399  	if err := UnmarshalPayload(in, out); err != nil {
   400  		t.Fatal(err)
   401  	}
   402  
   403  	if out.LatestComment != nil {
   404  		t.Fatalf("Latest Comment was not set to nil")
   405  	}
   406  }
   407  
   408  func TestUnmarshalNullRelationshipInSlice(t *testing.T) {
   409  	sample := map[string]interface{}{
   410  		"data": map[string]interface{}{
   411  			"type": "posts",
   412  			"id":   "1",
   413  			"attributes": map[string]interface{}{
   414  				"body":  "Hello",
   415  				"title": "World",
   416  			},
   417  			"relationships": map[string]interface{}{
   418  				"comments": map[string]interface{}{
   419  					"data": []interface{}{}, // empty to-many relationships
   420  				},
   421  			},
   422  		},
   423  	}
   424  	data, err := json.Marshal(sample)
   425  	if err != nil {
   426  		t.Fatal(err)
   427  	}
   428  
   429  	in := bytes.NewReader(data)
   430  	out := new(Post)
   431  
   432  	if err := UnmarshalPayload(in, out); err != nil {
   433  		t.Fatal(err)
   434  	}
   435  
   436  	if len(out.Comments) != 0 {
   437  		t.Fatalf("Wrong number of comments; Comments should be empty")
   438  	}
   439  }
   440  
   441  func TestUnmarshalNestedRelationships(t *testing.T) {
   442  	out, err := unmarshalSamplePayload()
   443  	if err != nil {
   444  		t.Fatal(err)
   445  	}
   446  
   447  	if out.CurrentPost == nil {
   448  		t.Fatalf("Current post was not materialized")
   449  	}
   450  
   451  	if out.CurrentPost.Comments == nil {
   452  		t.Fatalf("Did not materialize nested records, comments")
   453  	}
   454  
   455  	if len(out.CurrentPost.Comments) != 2 {
   456  		t.Fatalf("Wrong number of comments")
   457  	}
   458  }
   459  
   460  func TestUnmarshalRelationshipsSerializedEmbedded(t *testing.T) {
   461  	out := sampleSerializedEmbeddedTestModel()
   462  
   463  	if out.CurrentPost == nil {
   464  		t.Fatalf("Current post was not materialized")
   465  	}
   466  
   467  	if out.CurrentPost.Title != "Foo" || out.CurrentPost.Body != "Bar" {
   468  		t.Fatalf("Attributes were not set")
   469  	}
   470  
   471  	if len(out.Posts) != 2 {
   472  		t.Fatalf("There should have been 2 posts")
   473  	}
   474  
   475  	if out.Posts[0].LatestComment.Body != "foo" {
   476  		t.Fatalf("The comment body was not set")
   477  	}
   478  }
   479  
   480  func TestUnmarshalNestedRelationshipsEmbedded(t *testing.T) {
   481  	out := bytes.NewBuffer(nil)
   482  	if err := MarshalOnePayloadEmbedded(out, testModel()); err != nil {
   483  		t.Fatal(err)
   484  	}
   485  
   486  	model := new(Blog)
   487  
   488  	if err := UnmarshalPayload(out, model); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  
   492  	if model.CurrentPost == nil {
   493  		t.Fatalf("Current post was not materialized")
   494  	}
   495  
   496  	if model.CurrentPost.Comments == nil {
   497  		t.Fatalf("Did not materialize nested records, comments")
   498  	}
   499  
   500  	if len(model.CurrentPost.Comments) != 2 {
   501  		t.Fatalf("Wrong number of comments")
   502  	}
   503  
   504  	if model.CurrentPost.Comments[0].Body != "foo" {
   505  		t.Fatalf("Comment body not set")
   506  	}
   507  }
   508  
   509  func TestUnmarshalRelationshipsSideloaded(t *testing.T) {
   510  	payload := samplePayloadWithSideloaded()
   511  	out := new(Blog)
   512  
   513  	if err := UnmarshalPayload(payload, out); err != nil {
   514  		t.Fatal(err)
   515  	}
   516  
   517  	if out.CurrentPost == nil {
   518  		t.Fatalf("Current post was not materialized")
   519  	}
   520  
   521  	if out.CurrentPost.Title != "Foo" || out.CurrentPost.Body != "Bar" {
   522  		t.Fatalf("Attributes were not set")
   523  	}
   524  
   525  	if len(out.Posts) != 2 {
   526  		t.Fatalf("There should have been 2 posts")
   527  	}
   528  }
   529  
   530  func TestUnmarshalNestedRelationshipsSideloaded(t *testing.T) {
   531  	payload := samplePayloadWithSideloaded()
   532  	out := new(Blog)
   533  
   534  	if err := UnmarshalPayload(payload, out); err != nil {
   535  		t.Fatal(err)
   536  	}
   537  
   538  	if out.CurrentPost == nil {
   539  		t.Fatalf("Current post was not materialized")
   540  	}
   541  
   542  	if out.CurrentPost.Comments == nil {
   543  		t.Fatalf("Did not materialize nested records, comments")
   544  	}
   545  
   546  	if len(out.CurrentPost.Comments) != 2 {
   547  		t.Fatalf("Wrong number of comments")
   548  	}
   549  
   550  	if out.CurrentPost.Comments[0].Body != "foo" {
   551  		t.Fatalf("Comment body not set")
   552  	}
   553  }
   554  
   555  func TestUnmarshalNestedRelationshipsEmbedded_withClientIDs(t *testing.T) {
   556  	model := new(Blog)
   557  
   558  	if err := UnmarshalPayload(samplePayload(), model); err != nil {
   559  		t.Fatal(err)
   560  	}
   561  
   562  	if model.Posts[0].ClientID == "" {
   563  		t.Fatalf("ClientID not set from request on related record")
   564  	}
   565  }
   566  
   567  func unmarshalSamplePayload() (*Blog, error) {
   568  	in := samplePayload()
   569  	out := new(Blog)
   570  
   571  	if err := UnmarshalPayload(in, out); err != nil {
   572  		return nil, err
   573  	}
   574  
   575  	return out, nil
   576  }
   577  
   578  func TestUnmarshalManyPayload(t *testing.T) {
   579  	sample := map[string]interface{}{
   580  		"data": []interface{}{
   581  			map[string]interface{}{
   582  				"type": "posts",
   583  				"id":   "1",
   584  				"attributes": map[string]interface{}{
   585  					"body":  "First",
   586  					"title": "Post",
   587  				},
   588  			},
   589  			map[string]interface{}{
   590  				"type": "posts",
   591  				"id":   "2",
   592  				"attributes": map[string]interface{}{
   593  					"body":  "Second",
   594  					"title": "Post",
   595  				},
   596  			},
   597  		},
   598  	}
   599  
   600  	data, err := json.Marshal(sample)
   601  	if err != nil {
   602  		t.Fatal(err)
   603  	}
   604  	in := bytes.NewReader(data)
   605  
   606  	posts, err := UnmarshalManyPayload(in, reflect.TypeOf(new(Post)))
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  
   611  	if len(posts) != 2 {
   612  		t.Fatal("Wrong number of posts")
   613  	}
   614  
   615  	for _, p := range posts {
   616  		_, ok := p.(*Post)
   617  		if !ok {
   618  			t.Fatal("Was expecting a Post")
   619  		}
   620  	}
   621  }
   622  
   623  func TestManyPayload_withLinks(t *testing.T) {
   624  	firstPageURL := "http://somesite.com/movies?page[limit]=50&page[offset]=50"
   625  	prevPageURL := "http://somesite.com/movies?page[limit]=50&page[offset]=0"
   626  	nextPageURL := "http://somesite.com/movies?page[limit]=50&page[offset]=100"
   627  	lastPageURL := "http://somesite.com/movies?page[limit]=50&page[offset]=500"
   628  
   629  	sample := map[string]interface{}{
   630  		"data": []interface{}{
   631  			map[string]interface{}{
   632  				"type": "posts",
   633  				"id":   "1",
   634  				"attributes": map[string]interface{}{
   635  					"body":  "First",
   636  					"title": "Post",
   637  				},
   638  			},
   639  			map[string]interface{}{
   640  				"type": "posts",
   641  				"id":   "2",
   642  				"attributes": map[string]interface{}{
   643  					"body":  "Second",
   644  					"title": "Post",
   645  				},
   646  			},
   647  		},
   648  		"links": map[string]interface{}{
   649  			KeyFirstPage:    firstPageURL,
   650  			KeyPreviousPage: prevPageURL,
   651  			KeyNextPage:     nextPageURL,
   652  			KeyLastPage:     lastPageURL,
   653  		},
   654  	}
   655  
   656  	data, err := json.Marshal(sample)
   657  	if err != nil {
   658  		t.Fatal(err)
   659  	}
   660  	in := bytes.NewReader(data)
   661  
   662  	payload := new(ManyPayload)
   663  	if err = json.NewDecoder(in).Decode(payload); err != nil {
   664  		t.Fatal(err)
   665  	}
   666  
   667  	if payload.Links == nil {
   668  		t.Fatal("Was expecting a non nil ptr Link field")
   669  	}
   670  
   671  	links := *payload.Links
   672  
   673  	first, ok := links[KeyFirstPage]
   674  	if !ok {
   675  		t.Fatal("Was expecting a non nil ptr Link field")
   676  	}
   677  	if e, a := firstPageURL, first; e != a {
   678  		t.Fatalf("Was expecting links.%s to have a value of %s, got %s", KeyFirstPage, e, a)
   679  	}
   680  
   681  	prev, ok := links[KeyPreviousPage]
   682  	if !ok {
   683  		t.Fatal("Was expecting a non nil ptr Link field")
   684  	}
   685  	if e, a := prevPageURL, prev; e != a {
   686  		t.Fatalf("Was expecting links.%s to have a value of %s, got %s", KeyPreviousPage, e, a)
   687  	}
   688  
   689  	next, ok := links[KeyNextPage]
   690  	if !ok {
   691  		t.Fatal("Was expecting a non nil ptr Link field")
   692  	}
   693  	if e, a := nextPageURL, next; e != a {
   694  		t.Fatalf("Was expecting links.%s to have a value of %s, got %s", KeyNextPage, e, a)
   695  	}
   696  
   697  	last, ok := links[KeyLastPage]
   698  	if !ok {
   699  		t.Fatal("Was expecting a non nil ptr Link field")
   700  	}
   701  	if e, a := lastPageURL, last; e != a {
   702  		t.Fatalf("Was expecting links.%s to have a value of %s, got %s", KeyLastPage, e, a)
   703  	}
   704  }
   705  
   706  func TestEmbeddedStructs_nonNilStructPtr(t *testing.T) {
   707  	originalVehicle := &Vehicle{
   708  		Make:  "VW",
   709  		Model: "R32",
   710  		Year:  2008,
   711  		Engine: Engine{
   712  			NumberOfCylinders: 6,
   713  			HorsePower:        250,
   714  		},
   715  		BlockHeater: &BlockHeater{
   716  			Watts: 150,
   717  		},
   718  	}
   719  
   720  	// Serialize as JSON
   721  	jsonVehicle, err := json.Marshal(originalVehicle)
   722  	if err != nil {
   723  		t.Fatal(err)
   724  	}
   725  
   726  	jsonUnmarshalledVehicle := &Vehicle{}
   727  	json.Unmarshal(jsonVehicle, jsonUnmarshalledVehicle)
   728  
   729  	// Proves that the JSON standard lib will allocate a BlockHeater
   730  	if jsonUnmarshalledVehicle.BlockHeater == nil {
   731  		t.Fatal("was expecting a non nil Block Heater ptr")
   732  	}
   733  	if e, a := originalVehicle.BlockHeater.Watts, jsonUnmarshalledVehicle.BlockHeater.Watts; e != a {
   734  		t.Fatalf("was expecting watts to be %v, got %v", e, a)
   735  	}
   736  
   737  	// Serialize as JSONAPI
   738  	jsonAPIVehicle := new(bytes.Buffer)
   739  	if err = MarshalPayload(jsonAPIVehicle, originalVehicle); err != nil {
   740  		t.Fatal(err)
   741  	}
   742  
   743  	jsonAPIUnmarshalledVehicle := &Vehicle{}
   744  	if err = UnmarshalPayload(jsonAPIVehicle, jsonAPIUnmarshalledVehicle); err != nil {
   745  		t.Fatal(err)
   746  	}
   747  
   748  	if jsonAPIUnmarshalledVehicle.BlockHeater == nil {
   749  		t.Fatal("was expecting a non nil Block Heater ptr")
   750  	}
   751  	if e, a := originalVehicle.BlockHeater.Watts, jsonAPIUnmarshalledVehicle.BlockHeater.Watts; e != a {
   752  		t.Fatalf("was expecting watts to be %v, got %v", e, a)
   753  	}
   754  }
   755  
   756  func TestEmbeddedStructs_nilStructPtr(t *testing.T) {
   757  	originalVehicle := &Vehicle{
   758  		Make:  "VW",
   759  		Model: "R32",
   760  		Year:  2008,
   761  		Engine: Engine{
   762  			NumberOfCylinders: 6,
   763  			HorsePower:        250,
   764  		},
   765  	}
   766  
   767  	// Serialize as JSON
   768  	jsonVehicle, err := json.Marshal(originalVehicle)
   769  	if err != nil {
   770  		t.Fatal(err)
   771  	}
   772  
   773  	jsonUnmarshalledVehicle := &Vehicle{}
   774  	json.Unmarshal(jsonVehicle, jsonUnmarshalledVehicle)
   775  
   776  	// Proves that the JSON standard lib will NOT allocate a BlockHeater
   777  	if e, a := originalVehicle.BlockHeater, jsonUnmarshalledVehicle.BlockHeater; e != a {
   778  		t.Fatalf("was expecting BlockHeater to be %v, got %v", e, a)
   779  	}
   780  
   781  	// Serialize as JSONAPI
   782  	jsonAPIVehicle := new(bytes.Buffer)
   783  	if err = MarshalPayload(jsonAPIVehicle, originalVehicle); err != nil {
   784  		t.Fatal(err)
   785  	}
   786  
   787  	jsonAPIUnmarshalledVehicle := &Vehicle{}
   788  	if err = UnmarshalPayload(jsonAPIVehicle, jsonAPIUnmarshalledVehicle); err != nil {
   789  		t.Fatal(err)
   790  	}
   791  
   792  	if e, a := originalVehicle.BlockHeater, jsonAPIUnmarshalledVehicle.BlockHeater; e != a {
   793  		t.Fatalf("was expecting BlockHeater to be %v, got %v", e, a)
   794  	}
   795  }
   796  
   797  func samplePayloadWithoutIncluded() map[string]interface{} {
   798  	return map[string]interface{}{
   799  		"data": map[string]interface{}{
   800  			"type": "posts",
   801  			"id":   "1",
   802  			"attributes": map[string]interface{}{
   803  				"body":  "Hello",
   804  				"title": "World",
   805  			},
   806  			"relationships": map[string]interface{}{
   807  				"comments": map[string]interface{}{
   808  					"data": []interface{}{
   809  						map[string]interface{}{
   810  							"type": "comments",
   811  							"id":   "123",
   812  						},
   813  						map[string]interface{}{
   814  							"type": "comments",
   815  							"id":   "456",
   816  						},
   817  					},
   818  				},
   819  				"latest_comment": map[string]interface{}{
   820  					"data": map[string]interface{}{
   821  						"type": "comments",
   822  						"id":   "55555",
   823  					},
   824  				},
   825  			},
   826  		},
   827  	}
   828  }
   829  
   830  func payload(data map[string]interface{}) (result []byte, err error) {
   831  	result, err = json.Marshal(data)
   832  	return
   833  }
   834  
   835  func samplePayload() io.Reader {
   836  	payload := &OnePayload{
   837  		Data: &Node{
   838  			Type: "blogs",
   839  			Attributes: map[string]interface{}{
   840  				"title":      "New blog",
   841  				"created_at": 1436216820,
   842  				"view_count": 1000,
   843  			},
   844  			Relationships: map[string]interface{}{
   845  				"posts": &RelationshipManyNode{
   846  					Data: []*Node{
   847  						{
   848  							Type: "posts",
   849  							Attributes: map[string]interface{}{
   850  								"title": "Foo",
   851  								"body":  "Bar",
   852  							},
   853  							ClientID: "1",
   854  						},
   855  						{
   856  							Type: "posts",
   857  							Attributes: map[string]interface{}{
   858  								"title": "X",
   859  								"body":  "Y",
   860  							},
   861  							ClientID: "2",
   862  						},
   863  					},
   864  				},
   865  				"current_post": &RelationshipOneNode{
   866  					Data: &Node{
   867  						Type: "posts",
   868  						Attributes: map[string]interface{}{
   869  							"title": "Bas",
   870  							"body":  "Fuubar",
   871  						},
   872  						ClientID: "3",
   873  						Relationships: map[string]interface{}{
   874  							"comments": &RelationshipManyNode{
   875  								Data: []*Node{
   876  									{
   877  										Type: "comments",
   878  										Attributes: map[string]interface{}{
   879  											"body": "Great post!",
   880  										},
   881  										ClientID: "4",
   882  									},
   883  									{
   884  										Type: "comments",
   885  										Attributes: map[string]interface{}{
   886  											"body": "Needs some work!",
   887  										},
   888  										ClientID: "5",
   889  									},
   890  								},
   891  							},
   892  						},
   893  					},
   894  				},
   895  			},
   896  		},
   897  	}
   898  
   899  	out := bytes.NewBuffer(nil)
   900  	json.NewEncoder(out).Encode(payload)
   901  
   902  	return out
   903  }
   904  
   905  func samplePayloadWithID() io.Reader {
   906  	payload := &OnePayload{
   907  		Data: &Node{
   908  			ID:   "2",
   909  			Type: "blogs",
   910  			Attributes: map[string]interface{}{
   911  				"title":      "New blog",
   912  				"view_count": 1000,
   913  			},
   914  		},
   915  	}
   916  
   917  	out := bytes.NewBuffer(nil)
   918  	json.NewEncoder(out).Encode(payload)
   919  
   920  	return out
   921  }
   922  
   923  func samplePayloadWithBadTypes(m map[string]interface{}) io.Reader {
   924  	payload := &OnePayload{
   925  		Data: &Node{
   926  			ID:         "2",
   927  			Type:       "badtypes",
   928  			Attributes: m,
   929  		},
   930  	}
   931  
   932  	out := bytes.NewBuffer(nil)
   933  	json.NewEncoder(out).Encode(payload)
   934  
   935  	return out
   936  }
   937  
   938  func sampleWithPointerPayload(m map[string]interface{}) io.Reader {
   939  	payload := &OnePayload{
   940  		Data: &Node{
   941  			ID:         "2",
   942  			Type:       "with-pointers",
   943  			Attributes: m,
   944  		},
   945  	}
   946  
   947  	out := bytes.NewBuffer(nil)
   948  	json.NewEncoder(out).Encode(payload)
   949  
   950  	return out
   951  }
   952  
   953  func testModel() *Blog {
   954  	return &Blog{
   955  		ID:        5,
   956  		ClientID:  "1",
   957  		Title:     "Title 1",
   958  		CreatedAt: time.Now(),
   959  		Posts: []*Post{
   960  			{
   961  				ID:    1,
   962  				Title: "Foo",
   963  				Body:  "Bar",
   964  				Comments: []*Comment{
   965  					{
   966  						ID:   1,
   967  						Body: "foo",
   968  					},
   969  					{
   970  						ID:   2,
   971  						Body: "bar",
   972  					},
   973  				},
   974  				LatestComment: &Comment{
   975  					ID:   1,
   976  					Body: "foo",
   977  				},
   978  			},
   979  			{
   980  				ID:    2,
   981  				Title: "Fuubar",
   982  				Body:  "Bas",
   983  				Comments: []*Comment{
   984  					{
   985  						ID:   1,
   986  						Body: "foo",
   987  					},
   988  					{
   989  						ID:   3,
   990  						Body: "bas",
   991  					},
   992  				},
   993  				LatestComment: &Comment{
   994  					ID:   1,
   995  					Body: "foo",
   996  				},
   997  			},
   998  		},
   999  		CurrentPost: &Post{
  1000  			ID:    1,
  1001  			Title: "Foo",
  1002  			Body:  "Bar",
  1003  			Comments: []*Comment{
  1004  				{
  1005  					ID:   1,
  1006  					Body: "foo",
  1007  				},
  1008  				{
  1009  					ID:   2,
  1010  					Body: "bar",
  1011  				},
  1012  			},
  1013  			LatestComment: &Comment{
  1014  				ID:   1,
  1015  				Body: "foo",
  1016  			},
  1017  		},
  1018  	}
  1019  }
  1020  
  1021  func samplePayloadWithSideloaded() io.Reader {
  1022  	testModel := testModel()
  1023  
  1024  	out := bytes.NewBuffer(nil)
  1025  	MarshalPayload(out, testModel)
  1026  
  1027  	return out
  1028  }
  1029  
  1030  func sampleSerializedEmbeddedTestModel() *Blog {
  1031  	out := bytes.NewBuffer(nil)
  1032  	MarshalOnePayloadEmbedded(out, testModel())
  1033  
  1034  	blog := new(Blog)
  1035  	UnmarshalPayload(out, blog)
  1036  
  1037  	return blog
  1038  }