github.com/atlassian/git-lob@v0.0.0-20150806085256-2386a5ed291a/providers/smart/persistent_test.go (about)

     1  package smart
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"strings"
    11  
    12  	. "github.com/atlassian/git-lob/Godeps/_workspace/src/github.com/onsi/ginkgo"
    13  	. "github.com/atlassian/git-lob/Godeps/_workspace/src/github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("Persistent Transport", func() {
    17  
    18  	Context("Test JSON marshalling", func() {
    19  		type TestStruct struct {
    20  			Name      string
    21  			Something int
    22  		}
    23  		It("Encodes JSON requests correctly", func() {
    24  
    25  			params := &TestStruct{Name: "Steve", Something: 99}
    26  			req, err := NewJsonRequest("TestMethod", params)
    27  			Expect(err).To(BeNil(), "Should create request without error")
    28  			reqbytes, err := json.Marshal(req)
    29  			Expect(err).To(BeNil(), "Should marshal without error")
    30  			Expect(string(reqbytes)).To(Equal(`{"Id":1,"Method":"TestMethod","Params":{"Name":"Steve","Something":99}}`), "Encoded JSON should be correct")
    31  
    32  		})
    33  		It("Decodes JSON requests correctly", func() {
    34  			inputstruct := TestStruct{Name: "Steve", Something: 99}
    35  			resp, err := NewJsonResponse(1, inputstruct)
    36  			Expect(err).To(BeNil(), "Should create response without error")
    37  			outputstruct := TestStruct{}
    38  			// Now unmarshal nested result; need to extract json first
    39  			innerbytes, err := resp.Result.MarshalJSON()
    40  			Expect(err).To(BeNil(), "Extracting JSON from RawMessage should succeed")
    41  			err = json.Unmarshal(innerbytes, &outputstruct)
    42  			Expect(outputstruct).To(Equal(inputstruct), "Unmarshalled nested struct should match")
    43  		})
    44  
    45  	})
    46  
    47  	Context("Test individual server requests", func() {
    48  		metasThatExist := []string{
    49  			"0000000000000000000000000000000000000000",
    50  			"0000012300000004560000000000789000000000",
    51  		}
    52  		chunksThatExist := []string{
    53  			"0000000000000000000000000000000000000000",
    54  			"0000012300000004560000000000789000000000",
    55  		}
    56  		chunkIndexesThatExist := [][]int{
    57  			[]int{0, 1},
    58  			[]int{0, 1, 3, 4, 5, 7},
    59  		}
    60  		chunkSizes := [][]int64{ // only for first couple of chunks, testing only
    61  			[]int64{16777216, 150},
    62  			[]int64{16777216, 3210},
    63  		}
    64  
    65  		testsha := "5e0865e76e8956900c3ef6fec2d2af1c05f31ec4"
    66  		metacontent := `{"SHA":"5e0865e76e8956900c3ef6fec2d2af1c05f31ec4","Size":21982,"NumChunks":4}`
    67  		// Content doesn't actually matter here, just create some random data
    68  		// Make sure it's big enough to require > 1 callback
    69  		testchunkdatasz := PersistentTransportBufferSize*3 + 157
    70  		testchunkidx := 3
    71  		testlobsize := 10002
    72  		var testchunkdata []byte
    73  		pickloblist := []string{"1234567890abcdef1234567890abcdef12345678", testsha, "0000000000000000000011111111112222222222"}
    74  		deltaBaseSHA := "1234567890abcdef1234567890abcdef12345678"
    75  		deltaTargetSHA := "5e0865e76e8956900c3ef6fec2d2af1c05f31ec4"
    76  
    77  		BeforeEach(func() {
    78  			testchunkdata = make([]byte, testchunkdatasz)
    79  			// put something interesting in it so we can detect it at each end
    80  			testchunkdata[0] = 'a'
    81  			testchunkdata[1] = '1'
    82  			testchunkdata[2] = '6'
    83  			testchunkdata[testchunkdatasz-1] = 'z'
    84  			testchunkdata[testchunkdatasz-2] = '2'
    85  			testchunkdata[testchunkdatasz-3] = '5'
    86  
    87  		})
    88  
    89  		// Test server function here, just called over a pipe to test
    90  		serve := func(conn net.Conn) {
    91  			defer GinkgoRecover()
    92  			defer conn.Close()
    93  			// Run in a goroutine, be the server you seek
    94  			// Read a request
    95  			rdr := bufio.NewReader(conn)
    96  			for {
    97  				jsonbytes, err := rdr.ReadBytes(byte(0))
    98  				if err != nil {
    99  					if err == io.EOF {
   100  						break
   101  					}
   102  					Fail(fmt.Sprintf("Test persistent server: unable to read from client: %v", err.Error()))
   103  				}
   104  				// slice off the terminator
   105  				jsonbytes = jsonbytes[:len(jsonbytes)-1]
   106  				var req JsonRequest
   107  				err = json.Unmarshal(jsonbytes, &req)
   108  				Expect(err).To(BeNil(), fmt.Sprintf("Test persistent server: unable to unmarshal json request from client:%v", string(jsonbytes)))
   109  				var resp *JsonResponse
   110  				allowedCaps := []string{"Feature1", "Feature2", "OMGSOAWESOME"}
   111  				switch req.Method {
   112  				case "QueryCaps":
   113  					result := QueryCapsResponse{Caps: allowedCaps}
   114  					resp, err = NewJsonResponse(req.Id, result)
   115  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   116  				case "SetEnabledCaps":
   117  					capsreq := SetEnabledCapsRequest{}
   118  					ExtractStructFromJsonRawMessage(req.Params, &capsreq)
   119  					result := SetEnabledCapsResponse{}
   120  					resp, err = NewJsonResponse(req.Id, result)
   121  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   122  					// test for error condition
   123  					for _, c := range capsreq.EnableCaps {
   124  						ok := false
   125  						for _, s := range allowedCaps {
   126  							if c == s {
   127  								ok = true
   128  								break
   129  							}
   130  						}
   131  						if !ok {
   132  							resp.Error = fmt.Sprintf("Unsupported capability: %v", c)
   133  							break
   134  						}
   135  
   136  					}
   137  				case "FileExists":
   138  					fereq := FileExistsRequest{}
   139  					var strerr string
   140  					ExtractStructFromJsonRawMessage(req.Params, &fereq)
   141  					result := FileExistsResponse{}
   142  					if fereq.Type == "chunk" {
   143  						for i, chunk := range chunksThatExist {
   144  							if fereq.LobSHA == chunk {
   145  								for _, chunkidx := range chunkIndexesThatExist[i] {
   146  									if fereq.ChunkIdx == chunkidx {
   147  										result.Exists = true
   148  										result.Size = testchunkdatasz
   149  										break
   150  									}
   151  								}
   152  							}
   153  							if result.Exists == true {
   154  								break
   155  							}
   156  						}
   157  					} else if fereq.Type == "meta" {
   158  						for _, meta := range metasThatExist {
   159  							if fereq.LobSHA == meta {
   160  								result.Exists = true
   161  								result.Size = int64(len(metacontent))
   162  								break
   163  							}
   164  						}
   165  
   166  					} else {
   167  						strerr = fmt.Sprintf("Unsupported type: %v", fereq.Type)
   168  					}
   169  
   170  					if strerr != "" {
   171  						resp = NewJsonErrorResponse(req.Id, strerr)
   172  					} else {
   173  						resp, err = NewJsonResponse(req.Id, result)
   174  						Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   175  					}
   176  
   177  				case "FileExistsOfSize":
   178  					fereq := FileExistsOfSizeRequest{}
   179  					ExtractStructFromJsonRawMessage(req.Params, &fereq)
   180  					result := FileExistsOfSizeResponse{}
   181  					for i, chunk := range chunksThatExist {
   182  						if fereq.LobSHA == chunk {
   183  							for chunkidx, sz := range chunkSizes[i] {
   184  								if fereq.ChunkIdx == chunkidx {
   185  									result.Result = (fereq.Size == sz)
   186  									break
   187  								}
   188  							}
   189  							break
   190  						}
   191  					}
   192  					resp, err = NewJsonResponse(req.Id, result)
   193  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   194  				case "LOBExists":
   195  					fereq := LOBExistsRequest{}
   196  					ExtractStructFromJsonRawMessage(req.Params, &fereq)
   197  					result := LOBExistsResponse{}
   198  					if fereq.LobSHA == testsha {
   199  						result.Exists = true
   200  						result.Size = int64(testlobsize)
   201  					}
   202  
   203  					resp, err = NewJsonResponse(req.Id, result)
   204  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   205  				case "UploadFile":
   206  					upreq := UploadFileRequest{}
   207  					ExtractStructFromJsonRawMessage(req.Params, &upreq)
   208  					Expect(upreq.LobSHA).To(Equal(testsha), "Test persistent server: SHA incorrect")
   209  					if upreq.Type == "chunk" {
   210  						Expect(upreq.ChunkIdx).To(BeEquivalentTo(testchunkidx), "Test persistent server: Chunk index incorrect")
   211  					}
   212  					startresult := UploadFileStartResponse{}
   213  					startresult.OKToSend = true
   214  					// Send start response immediately
   215  					resp, err = NewJsonResponse(req.Id, startresult)
   216  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   217  					responseBytes, err := json.Marshal(resp)
   218  					Expect(err).To(BeNil(), "Test persistent server: unable to marshal response")
   219  					// null terminate response
   220  					responseBytes = append(responseBytes, byte(0))
   221  					conn.Write(responseBytes)
   222  					// Next should by byte stream
   223  					// Must read from buffered reader since bytes may have been read already
   224  					receivedresult := UploadFileCompleteResponse{}
   225  					receivedresult.ReceivedOK = true
   226  					var receiveerr error
   227  					// make pre-sized buffer
   228  					contentbuf := bytes.NewBuffer(make([]byte, 0, upreq.Size))
   229  					bytesLeft := upreq.Size
   230  					for bytesLeft > 0 {
   231  						c := PersistentTransportBufferSize
   232  						if c > bytesLeft {
   233  							c = bytesLeft
   234  						}
   235  						n, err := io.CopyN(contentbuf, rdr, c)
   236  						bytesLeft -= int64(n)
   237  						if err != nil {
   238  							receivedresult.ReceivedOK = false
   239  							receiveerr = fmt.Errorf("Test persistent server: unable to read data: %v", err.Error())
   240  							break
   241  						}
   242  					}
   243  					// Check we received what we expected to receive
   244  					if upreq.Type == "meta" {
   245  						if string(contentbuf.Bytes()) != metacontent {
   246  							receiveerr = fmt.Errorf("Test persistent server: data didn't match")
   247  						}
   248  					} else {
   249  						// test first & last 5 bytes
   250  						contentbytes := contentbuf.Bytes()
   251  						for i := 0; i < 5; i++ {
   252  							if contentbytes[i] != testchunkdata[i] {
   253  								receiveerr = fmt.Errorf("Test persistent server: data didn't match")
   254  								break
   255  							}
   256  						}
   257  						for i := len(contentbytes) - 5; i < len(contentbytes); i++ {
   258  							if contentbytes[i] != testchunkdata[i] {
   259  								receiveerr = fmt.Errorf("Test persistent server: data didn't match")
   260  								break
   261  							}
   262  						}
   263  					}
   264  					// After we've read all the content bytes, send received response
   265  					if receiveerr != nil {
   266  						resp = NewJsonErrorResponse(req.Id, receiveerr.Error())
   267  					} else {
   268  						resp, _ = NewJsonResponse(req.Id, receivedresult)
   269  					}
   270  				case "DownloadFilePrepare":
   271  					downreq := DownloadFilePrepareRequest{}
   272  					ExtractStructFromJsonRawMessage(req.Params, &downreq)
   273  					Expect(downreq.LobSHA).To(Equal(testsha), "Test persistent server: SHA incorrect")
   274  					if downreq.Type == "chunk" {
   275  						Expect(downreq.ChunkIdx).To(BeEquivalentTo(testchunkidx), "Test persistent server: Chunk index incorrect")
   276  					}
   277  					result := DownloadFilePrepareResponse{}
   278  					if downreq.Type == "meta" {
   279  						result.Size = int64(len(metacontent))
   280  					} else {
   281  						result.Size = int64(len(testchunkdata))
   282  					}
   283  					resp, err = NewJsonResponse(req.Id, result)
   284  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   285  				case "DownloadFileStart":
   286  					// Can't return any error responses here (byte stream response only), have to just fail
   287  					downreq := DownloadFileStartRequest{}
   288  					ExtractStructFromJsonRawMessage(req.Params, &downreq)
   289  					// there is no response to this
   290  					var sz int64
   291  					var datasrc io.Reader
   292  					if downreq.Type == "meta" {
   293  						sz = int64(len(metacontent))
   294  						datasrc = strings.NewReader(metacontent)
   295  					} else {
   296  						sz = int64(len(testchunkdata))
   297  						datasrc = bytes.NewReader(testchunkdata)
   298  					}
   299  					// confirm size for testing
   300  					Expect(sz).To(BeEquivalentTo(downreq.Size), "Test persistent server: download size incorrect")
   301  
   302  					bytesLeft := sz
   303  					for bytesLeft > 0 {
   304  						c := PersistentTransportBufferSize
   305  						if c > bytesLeft {
   306  							c = bytesLeft
   307  						}
   308  						n, err := io.CopyN(conn, datasrc, c)
   309  						bytesLeft -= int64(n)
   310  						Expect(err).To(BeNil(), "Test persistent server: unable to read data")
   311  					}
   312  				case "PickCompleteLOB":
   313  					params := GetFirstCompleteLOBFromListRequest{}
   314  					ExtractStructFromJsonRawMessage(req.Params, &params)
   315  					// check it's the list we expected
   316  					Expect(params.LobSHAs).To(ConsistOf(pickloblist), "Server should receive correct params")
   317  					result := GetFirstCompleteLOBFromListResponse{}
   318  					result.FirstSHA = testsha
   319  					resp, err = NewJsonResponse(req.Id, result)
   320  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   321  
   322  				case "UploadDelta":
   323  					upreq := UploadDeltaRequest{}
   324  					ExtractStructFromJsonRawMessage(req.Params, &upreq)
   325  					Expect(upreq.BaseLobSHA).To(Equal(deltaBaseSHA), "Test persistent server: base SHA incorrect")
   326  					Expect(upreq.TargetLobSHA).To(Equal(deltaTargetSHA), "Test persistent server: base SHA incorrect")
   327  					startresult := UploadDeltaStartResponse{}
   328  					startresult.OKToSend = true
   329  					// Send start response immediately
   330  					resp, err = NewJsonResponse(req.Id, startresult)
   331  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   332  					responseBytes, err := json.Marshal(resp)
   333  					Expect(err).To(BeNil(), "Test persistent server: unable to marshal response")
   334  					// null terminate response
   335  					responseBytes = append(responseBytes, byte(0))
   336  					conn.Write(responseBytes)
   337  					// Next should by byte stream
   338  					// Must read from buffered reader since bytes may have been read already
   339  					receivedresult := UploadDeltaCompleteResponse{}
   340  					receivedresult.ReceivedOK = true
   341  					var receiveerr error
   342  					// make pre-sized buffer
   343  					contentbuf := bytes.NewBuffer(make([]byte, 0, upreq.Size))
   344  					bytesLeft := upreq.Size
   345  					for bytesLeft > 0 {
   346  						c := PersistentTransportBufferSize
   347  						if c > bytesLeft {
   348  							c = bytesLeft
   349  						}
   350  						n, err := io.CopyN(contentbuf, rdr, c)
   351  						bytesLeft -= int64(n)
   352  						if err != nil {
   353  							receivedresult.ReceivedOK = false
   354  							receiveerr = fmt.Errorf("Test persistent server: unable to read data: %v", err.Error())
   355  							break
   356  						}
   357  					}
   358  					// Check we received what we expected to receive
   359  					// test first & last 5 bytes
   360  					contentbytes := contentbuf.Bytes()
   361  					for i := 0; i < 5; i++ {
   362  						if contentbytes[i] != testchunkdata[i] {
   363  							receiveerr = fmt.Errorf("Test persistent server: data didn't match")
   364  							break
   365  						}
   366  					}
   367  					for i := len(contentbytes) - 5; i < len(contentbytes); i++ {
   368  						if contentbytes[i] != testchunkdata[i] {
   369  							receiveerr = fmt.Errorf("Test persistent server: data didn't match")
   370  							break
   371  						}
   372  					}
   373  					// After we've read all the content bytes, send received response
   374  					if receiveerr != nil {
   375  						resp = NewJsonErrorResponse(req.Id, receiveerr.Error())
   376  					} else {
   377  						resp, _ = NewJsonResponse(req.Id, receivedresult)
   378  					}
   379  				case "DownloadDeltaPrepare":
   380  					downreq := DownloadDeltaPrepareRequest{}
   381  					ExtractStructFromJsonRawMessage(req.Params, &downreq)
   382  					Expect(downreq.BaseLobSHA).To(Equal(deltaBaseSHA), "Test persistent server: base SHA incorrect")
   383  					Expect(downreq.TargetLobSHA).To(Equal(deltaTargetSHA), "Test persistent server: base SHA incorrect")
   384  					result := DownloadDeltaPrepareResponse{}
   385  					result.Size = int64(len(testchunkdata))
   386  					resp, err = NewJsonResponse(req.Id, result)
   387  					Expect(err).To(BeNil(), "Test persistent server: unable to create response")
   388  				case "DownloadDeltaStart":
   389  					// Can't return any error responses here (byte stream response only), have to just fail
   390  					downreq := DownloadDeltaStartRequest{}
   391  					ExtractStructFromJsonRawMessage(req.Params, &downreq)
   392  					// there is no response to this
   393  					Expect(downreq.BaseLobSHA).To(Equal(deltaBaseSHA), "Test persistent server: base SHA incorrect")
   394  					Expect(downreq.TargetLobSHA).To(Equal(deltaTargetSHA), "Test persistent server: base SHA incorrect")
   395  					sz := int64(len(testchunkdata))
   396  					datasrc := bytes.NewReader(testchunkdata)
   397  					// confirm size for testing
   398  					Expect(sz).To(BeEquivalentTo(downreq.Size), "Test persistent server: download size incorrect")
   399  
   400  					bytesLeft := sz
   401  					for bytesLeft > 0 {
   402  						c := PersistentTransportBufferSize
   403  						if c > bytesLeft {
   404  							c = bytesLeft
   405  						}
   406  						n, err := io.CopyN(conn, datasrc, c)
   407  						bytesLeft -= int64(n)
   408  						Expect(err).To(BeNil(), "Test persistent server: unable to read data")
   409  					}
   410  				default:
   411  					resp = NewJsonErrorResponse(req.Id, fmt.Sprintf("Unknown method %v", req.Method))
   412  				}
   413  				if resp != nil {
   414  					responseBytes, err := json.Marshal(resp)
   415  					Expect(err).To(BeNil(), "Test persistent server: unable to marshal response")
   416  					// null terminate response
   417  					responseBytes = append(responseBytes, byte(0))
   418  					conn.Write(responseBytes)
   419  				}
   420  			}
   421  			conn.Close()
   422  		}
   423  		It("Queries capabilities (client)", func() {
   424  			cli, srv := net.Pipe()
   425  			go serve(srv)
   426  			defer cli.Close()
   427  
   428  			trans := NewPersistentTransport(cli)
   429  			caps, err := trans.QueryCaps()
   430  			Expect(err).To(BeNil(), "Should be no error")
   431  			Expect(caps).To(ConsistOf([]string{"Feature1", "Feature2", "OMGSOAWESOME"}), "Capabilities should match server")
   432  
   433  		})
   434  		It("Sets capabilities (client)", func() {
   435  			cli, srv := net.Pipe()
   436  			go serve(srv)
   437  			defer cli.Close()
   438  
   439  			trans := NewPersistentTransport(cli)
   440  			err := trans.SetEnabledCaps([]string{"OMGSOAWESOME", "Feature1"})
   441  			Expect(err).To(BeNil(), "Should be no error")
   442  
   443  		})
   444  		It("Queries file existence (client)", func() {
   445  			// This also tests multiple requests in sequence (JSON only)
   446  			cli, srv := net.Pipe()
   447  			go serve(srv)
   448  			defer cli.Close()
   449  
   450  			trans := NewPersistentTransport(cli)
   451  			for _, meta := range metasThatExist {
   452  				exists, sz, err := trans.MetadataExists(meta)
   453  				Expect(err).To(BeNil(), "Should be no error")
   454  				Expect(exists).To(BeTrue(), "Metafile should exist")
   455  				Expect(sz).To(BeEquivalentTo(len(metacontent)), "Metafile should be right size")
   456  			}
   457  			for i, chunk := range chunksThatExist {
   458  				for _, chunkidx := range chunkIndexesThatExist[i] {
   459  					exists, sz, err := trans.ChunkExists(chunk, chunkidx)
   460  					Expect(err).To(BeNil(), "Should be no error")
   461  					Expect(exists).To(BeTrue(), "Chunk should exist")
   462  					Expect(sz).To(BeEquivalentTo(testchunkdatasz), "Chunk should be right size")
   463  				}
   464  			}
   465  
   466  			// Now try a few that should fail
   467  			exists, _, err := trans.MetadataExists("9999999999999999999999999999999999999999")
   468  			Expect(err).To(BeNil(), "Should be no error")
   469  			Expect(exists).To(BeFalse(), "Chunk should not exist")
   470  			exists, _, err = trans.ChunkExists("9999999999999999999999999999999999999999", 0)
   471  			Expect(err).To(BeNil(), "Should be no error")
   472  			Expect(exists).To(BeFalse(), "Chunk should not exist")
   473  			exists, _, err = trans.ChunkExists(chunksThatExist[0], 99)
   474  			Expect(err).To(BeNil(), "Should be no error")
   475  			Expect(exists).To(BeFalse(), "Chunk should not exist")
   476  
   477  		})
   478  
   479  		It("Queries file sizes (client)", func() {
   480  			// This also tests multiple requests in sequence (JSON only)
   481  			cli, srv := net.Pipe()
   482  			go serve(srv)
   483  			defer cli.Close()
   484  
   485  			trans := NewPersistentTransport(cli)
   486  			for i, chunksz := range chunkSizes {
   487  				for chunkidx, sz := range chunksz {
   488  					sha := chunksThatExist[i]
   489  					exists, err := trans.ChunkExistsAndIsOfSize(sha, chunkidx, sz)
   490  					Expect(err).To(BeNil(), "Should be no error")
   491  					Expect(exists).To(BeTrue(), "Chunk should exist at this size")
   492  
   493  					// Try a failure
   494  					exists, err = trans.ChunkExistsAndIsOfSize(sha, chunkidx, sz+16)
   495  					Expect(err).To(BeNil(), "Should be no error")
   496  					Expect(exists).To(BeFalse(), "Chunk size shouldn't match")
   497  
   498  				}
   499  			}
   500  			// Also test just a case of not being there at all
   501  			exists, err := trans.ChunkExistsAndIsOfSize("9999999999999999999999999999999999999999", 0, 150)
   502  			Expect(err).To(BeNil(), "Should be no error")
   503  			Expect(exists).To(BeFalse(), "Chunk should not exist")
   504  
   505  			// Test entire LOB exists
   506  			exists, sz, err := trans.LOBExists(testsha)
   507  			Expect(err).To(BeNil(), "Should be no error")
   508  			Expect(exists).To(BeTrue(), "LOB should exist")
   509  			Expect(sz).To(BeEquivalentTo(testlobsize), "LOB should exist")
   510  
   511  		})
   512  
   513  		It("Detects errors", func() {
   514  			// Request an invalid capability (as defined by this server)
   515  			cli, srv := net.Pipe()
   516  			go serve(srv)
   517  			defer cli.Close()
   518  
   519  			trans := NewPersistentTransport(cli)
   520  			err := trans.SetEnabledCaps([]string{"Feature1", "THISISWRONG"})
   521  			Expect(err).ToNot(BeNil(), "Should be an error")
   522  		})
   523  
   524  		It("Uploads metadata", func() {
   525  			rdr := strings.NewReader(metacontent)
   526  
   527  			cli, srv := net.Pipe()
   528  			go serve(srv)
   529  			defer cli.Close()
   530  
   531  			trans := NewPersistentTransport(cli)
   532  			err := trans.UploadMetadata(testsha, int64(len(metacontent)), rdr)
   533  			Expect(err).To(BeNil(), "Should not be an error in UploadFile")
   534  			Expect(rdr.Len()).To(BeZero(), "Server should have read all bytes")
   535  		})
   536  
   537  		It("Uploads chunk data", func() {
   538  			rdr := bytes.NewReader(testchunkdata)
   539  
   540  			cli, srv := net.Pipe()
   541  			go serve(srv)
   542  			defer cli.Close()
   543  
   544  			numCallbacks := 0
   545  			var totalBytesDone int64
   546  			var totalBytesReported int64
   547  			callback := func(bytesDone, totalBytes int64) {
   548  				totalBytesDone = bytesDone
   549  				totalBytesReported = totalBytes
   550  				numCallbacks++
   551  			}
   552  
   553  			trans := NewPersistentTransport(cli)
   554  			err := trans.UploadChunk(testsha, testchunkidx, testchunkdatasz, rdr, callback)
   555  			Expect(err).To(BeNil(), "Should not be an error in UploadFile")
   556  			Expect(rdr.Len()).To(BeZero(), "Server should have read all bytes")
   557  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   558  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   559  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   560  
   561  		})
   562  
   563  		It("Downloads metadata", func() {
   564  			var buf bytes.Buffer
   565  
   566  			cli, srv := net.Pipe()
   567  			go serve(srv)
   568  			defer cli.Close()
   569  
   570  			trans := NewPersistentTransport(cli)
   571  			err := trans.DownloadMetadata(testsha, &buf)
   572  			Expect(err).To(BeNil(), "Should not be an error in DownloadFile")
   573  			Expect(string(buf.Bytes())).To(Equal(metacontent), "Should download expected metadata content")
   574  		})
   575  		It("Downloads chunk data", func() {
   576  			var buf bytes.Buffer
   577  
   578  			cli, srv := net.Pipe()
   579  			go serve(srv)
   580  			defer cli.Close()
   581  
   582  			numCallbacks := 0
   583  			var totalBytesDone int64
   584  			var totalBytesReported int64
   585  			callback := func(bytesDone, totalBytes int64) {
   586  				totalBytesDone = bytesDone
   587  				totalBytesReported = totalBytes
   588  				numCallbacks++
   589  			}
   590  
   591  			trans := NewPersistentTransport(cli)
   592  			err := trans.DownloadChunk(testsha, testchunkidx, &buf, callback)
   593  			Expect(err).To(BeNil(), "Should not be an error in DownloadFile")
   594  			Expect(buf.Len()).To(BeEquivalentTo(testchunkdatasz), "Should download the correct number of bytes")
   595  			// Just check start & end of buffers
   596  			contentbytes := buf.Bytes()
   597  			Expect(contentbytes[:20]).To(Equal(testchunkdata[:20]), "Start of downloaded buffer should match")
   598  			Expect(contentbytes[testchunkdatasz-20:]).To(Equal(testchunkdata[testchunkdatasz-20:]), "Start of downloaded buffer should match")
   599  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   600  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   601  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   602  		})
   603  
   604  		It("Picks complete LOB from list", func() {
   605  			cli, srv := net.Pipe()
   606  			go serve(srv)
   607  			defer cli.Close()
   608  			trans := NewPersistentTransport(cli)
   609  			sha, err := trans.GetFirstCompleteLOBFromList(pickloblist)
   610  			Expect(err).To(BeNil(), "Should not be an error picking LOB")
   611  			Expect(sha).To(Equal(testsha), "Should pick the correct sha")
   612  		})
   613  
   614  		It("Uploads a delta", func() {
   615  			rdr := bytes.NewReader(testchunkdata)
   616  
   617  			cli, srv := net.Pipe()
   618  			go serve(srv)
   619  			defer cli.Close()
   620  
   621  			numCallbacks := 0
   622  			var totalBytesDone int64
   623  			var totalBytesReported int64
   624  			callback := func(bytesDone, totalBytes int64) {
   625  				totalBytesDone = bytesDone
   626  				totalBytesReported = totalBytes
   627  				numCallbacks++
   628  			}
   629  
   630  			trans := NewPersistentTransport(cli)
   631  			ok, err := trans.UploadDelta(deltaBaseSHA, deltaTargetSHA, testchunkdatasz, rdr, callback)
   632  			Expect(err).To(BeNil(), "Should not be an error in UploadDelta")
   633  			Expect(ok).To(BeTrue(), "UploadDelta should have worked")
   634  			Expect(rdr.Len()).To(BeZero(), "Server should have read all bytes")
   635  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   636  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   637  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   638  
   639  		})
   640  		It("Downloads a delta", func() {
   641  			var buf bytes.Buffer
   642  
   643  			cli, srv := net.Pipe()
   644  			go serve(srv)
   645  			defer cli.Close()
   646  
   647  			numCallbacks := 0
   648  			var totalBytesDone int64
   649  			var totalBytesReported int64
   650  			callback := func(bytesDone, totalBytes int64) {
   651  				totalBytesDone = bytesDone
   652  				totalBytesReported = totalBytes
   653  				numCallbacks++
   654  			}
   655  
   656  			trans := NewPersistentTransport(cli)
   657  			ok, err := trans.DownloadDelta(deltaBaseSHA, deltaTargetSHA, 10000000, &buf, callback)
   658  			Expect(err).To(BeNil(), "Should not be an error in DownloadFile")
   659  			Expect(ok).To(BeTrue(), "DownloadDelta should have worked")
   660  			Expect(buf.Len()).To(BeEquivalentTo(testchunkdatasz), "Should download the correct number of bytes")
   661  			// Just check start & end of buffers
   662  			contentbytes := buf.Bytes()
   663  			Expect(contentbytes[:20]).To(Equal(testchunkdata[:20]), "Start of downloaded buffer should match")
   664  			Expect(contentbytes[testchunkdatasz-20:]).To(Equal(testchunkdata[testchunkdatasz-20:]), "Start of downloaded buffer should match")
   665  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   666  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   667  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   668  
   669  			// Now check that a size limit works
   670  			buf.Reset()
   671  			ok, err = trans.DownloadDelta(deltaBaseSHA, deltaTargetSHA, 100, &buf, callback)
   672  			Expect(err).To(BeNil(), "Should not be an error in DownloadFile")
   673  			Expect(ok).To(BeFalse(), "DownloadDelta should return false when delta size is exceeded")
   674  			Expect(buf.Len()).To(BeZero(), "Nothing should be downloaded when size exceeded")
   675  
   676  		})
   677  
   678  		It("Deals with chained server requests over one connection with interleaved data", func() {
   679  			cli, srv := net.Pipe()
   680  			go serve(srv)
   681  			defer cli.Close()
   682  			trans := NewPersistentTransport(cli)
   683  
   684  			// JSON request
   685  			caps, err := trans.QueryCaps()
   686  			Expect(err).To(BeNil(), "Should be no error")
   687  			Expect(caps).To(ConsistOf([]string{"Feature1", "Feature2", "OMGSOAWESOME"}), "Capabilities should match server")
   688  
   689  			// Byte transfer (upload)
   690  			rdr := strings.NewReader(metacontent)
   691  			err = trans.UploadMetadata(testsha, int64(len(metacontent)), rdr)
   692  			Expect(err).To(BeNil(), "Should not be an error in UploadFile")
   693  			Expect(rdr.Len()).To(BeZero(), "Server should have read all bytes")
   694  
   695  			// Another byte transfer (chunk)
   696  			rdr2 := bytes.NewReader(testchunkdata)
   697  
   698  			numCallbacks := 0
   699  			var totalBytesDone int64
   700  			var totalBytesReported int64
   701  			callback := func(bytesDone, totalBytes int64) {
   702  				totalBytesDone = bytesDone
   703  				totalBytesReported = totalBytes
   704  				numCallbacks++
   705  			}
   706  			err = trans.UploadChunk(testsha, testchunkidx, testchunkdatasz, rdr2, callback)
   707  			Expect(err).To(BeNil(), "Should not be an error in UploadFile")
   708  			Expect(rdr2.Len()).To(BeZero(), "Server should have read all bytes")
   709  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   710  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   711  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   712  
   713  			// Another JSON request (pick LOB)
   714  			sha, err := trans.GetFirstCompleteLOBFromList(pickloblist)
   715  			Expect(err).To(BeNil(), "Should not be an error picking LOB")
   716  			Expect(sha).To(Equal(testsha), "Should pick the correct sha")
   717  
   718  			// Final byte transfer (delta)
   719  			numCallbacks = 0
   720  			var buf bytes.Buffer
   721  			ok, err := trans.DownloadDelta(deltaBaseSHA, deltaTargetSHA, 10000000, &buf, callback)
   722  			Expect(err).To(BeNil(), "Should not be an error in DownloadFile")
   723  			Expect(ok).To(BeTrue(), "DownloadDelta should have worked")
   724  			Expect(buf.Len()).To(BeEquivalentTo(testchunkdatasz), "Should download the correct number of bytes")
   725  			// Just check start & end of buffers
   726  			contentbytes := buf.Bytes()
   727  			Expect(contentbytes[:20]).To(Equal(testchunkdata[:20]), "Start of downloaded buffer should match")
   728  			Expect(contentbytes[testchunkdatasz-20:]).To(Equal(testchunkdata[testchunkdatasz-20:]), "Start of downloaded buffer should match")
   729  			Expect(totalBytesDone).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported all bytes done")
   730  			Expect(totalBytesReported).To(BeEquivalentTo(testchunkdatasz), "Callback should have reported totalBytes correctly")
   731  			Expect(numCallbacks).To(BeEquivalentTo(4), "Should have been 4 callbacks in total")
   732  		})
   733  	})
   734  
   735  })