go.mercari.io/datastore@v1.8.2/testsuite/realworld/tbf/main.go (about)

     1  package tbf
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"go.mercari.io/datastore"
    15  	"go.mercari.io/datastore/testsuite"
    16  	netcontext "golang.org/x/net/context"
    17  	"google.golang.org/appengine"
    18  )
    19  
    20  // TestSuite contains all the test cases that this package provides.
    21  var TestSuite = map[string]testsuite.Test{
    22  	"RealWorld_TBF": tbf,
    23  }
    24  
    25  func init() {
    26  	testsuite.MergeTestSuite(TestSuite)
    27  }
    28  
    29  type contextClient struct{}
    30  type contextBatch struct{}
    31  
    32  func timeNow() time.Time {
    33  	l, err := time.LoadLocation("Asia/Tokyo")
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  	return time.Date(2017, 11, 8, 10, 11, 12, 13, l)
    38  }
    39  
    40  func tbf(ctx context.Context, t *testing.T, client datastore.Client) {
    41  	defer func() {
    42  		err := client.Close()
    43  		if err != nil {
    44  			t.Fatal(err)
    45  		}
    46  	}()
    47  
    48  	// それぞれのCircleに4枚の画像を持たせて、Circleを10件Getしたい。RPCは Query 1回 + BatchGet 1回 の合計2回がいい
    49  
    50  	// clientは複数作ってぶん回すとどっかでブロックされて処理が返ってこなくなるので使いまわす
    51  	ctx = context.WithValue(ctx, contextClient{}, client)
    52  	// batchは再利用可能
    53  	batch := client.Batch()
    54  	ctx = context.WithValue(ctx, contextBatch{}, batch)
    55  
    56  	rpcCount := 0
    57  	inMemcacheTestSuite := false
    58  	if testsuite.IsAEDatastoreClient(ctx) {
    59  		// checking rpc count when testing in ae.
    60  		ctx = appengine.WithAPICallFunc(ctx, func(ctx netcontext.Context, service, method string, in, out proto.Message) error {
    61  			t.Log(service, method)
    62  			if service == "datastore_v3" {
    63  				rpcCount++
    64  			}
    65  			if service == "memcache" {
    66  				// if memcache service called, this test in the TestAEDatastoreWithAEMemcacheTestSuite.
    67  				inMemcacheTestSuite = true
    68  			}
    69  
    70  			return appengine.APICall(ctx, service, method, in, out)
    71  		})
    72  	}
    73  
    74  	const circleLimit = 10
    75  	const imageLimit = 4
    76  
    77  	// Prepare entities
    78  	for i := 0; i < circleLimit; i++ {
    79  		// NOTE Don't use client.AllocateIDs for JSON format consistency
    80  		circleID := CircleID(1000000 + 10000*i)
    81  		circleKey := circleID.ToKey(client)
    82  
    83  		circle := &Circle{
    84  			ID:   circleID,
    85  			Name: fmt.Sprintf("サークル #%d", i+1),
    86  		}
    87  		for j := 0; j < imageLimit; j++ {
    88  			// NOTE Don't use client.AllocateIDs for JSON format consistency
    89  			imageID := imageID(circleKey.ID() + 1000 + int64(10*j))
    90  			imageKey := imageID.ToKey(client)
    91  
    92  			image := &Image{
    93  				ID:            imageID,
    94  				OwnerCircleID: circleID,
    95  				GCSPath:       fmt.Sprintf("%d/%d.jpg", circleKey.ID(), imageKey.ID()),
    96  			}
    97  			batch.Put(imageKey, image, nil)
    98  
    99  			circle.ImageIDs = append(circle.ImageIDs, image.ID)
   100  			circle.Images = append(circle.Images, image)
   101  		}
   102  
   103  		batch.Put(circleKey, circle, nil)
   104  	}
   105  	err := batch.Exec(ctx)
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  	if testsuite.IsAEDatastoreClient(ctx) && !inMemcacheTestSuite {
   110  		if rpcCount != 1 {
   111  			t.Errorf("unexpected: %v", rpcCount)
   112  		}
   113  	}
   114  
   115  	// fetch entities
   116  	rpcCount = 0
   117  	q := client.NewQuery(kindCircle)
   118  	var circleList []*Circle
   119  	_, err = client.GetAll(ctx, q, &circleList)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	err = batch.Exec(ctx)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if testsuite.IsAEDatastoreClient(ctx) && !inMemcacheTestSuite {
   128  		if rpcCount != 2 {
   129  			t.Errorf("unexpected: %v", rpcCount)
   130  		}
   131  	}
   132  
   133  	if v := len(circleList); v != circleLimit {
   134  		t.Errorf("unexpected: %v", v)
   135  	}
   136  	for _, circle := range circleList {
   137  		if v := len(circle.Images); v != imageLimit {
   138  			t.Errorf("unexpected: %v", v)
   139  		}
   140  		for _, image := range circle.Images {
   141  			if v := image.GCSPath; !strings.HasPrefix(v, fmt.Sprintf("%d/", circle.ID)) {
   142  				t.Errorf("unexpected: %v", v)
   143  			}
   144  		}
   145  	}
   146  
   147  	{ // obj <-> JSON
   148  		b, err := json.MarshalIndent(circleList, "", "  ")
   149  		if err != nil {
   150  			t.Fatal(err)
   151  		}
   152  
   153  		const filePath = "../testsuite/realworld/tbf/realworld-tbf.json"
   154  		expected, err := ioutil.ReadFile(filePath)
   155  		if err != nil {
   156  			err = ioutil.WriteFile(filePath, b, 0644)
   157  			if err != nil {
   158  				t.Fatal(err)
   159  			}
   160  			expected = b
   161  		}
   162  
   163  		if !bytes.Equal(b, expected) {
   164  			t.Fatalf("unexpected json format. hint: rm %s", filePath)
   165  		}
   166  
   167  		var newCircleList []*Circle
   168  		err = json.Unmarshal(b, &newCircleList)
   169  		if err != nil {
   170  			t.Fatal(err)
   171  		}
   172  
   173  		if v := len(newCircleList); v != circleLimit {
   174  			t.Errorf("unexpected: %v", v)
   175  		}
   176  		for _, circle := range newCircleList {
   177  			if v := len(circle.Images); v != imageLimit {
   178  				t.Errorf("unexpected: %v", v)
   179  			}
   180  			for _, image := range circle.Images {
   181  				if v := image.GCSPath; !strings.HasPrefix(v, fmt.Sprintf("%d/", circle.ID)) {
   182  					t.Errorf("unexpected: %v", v)
   183  				}
   184  			}
   185  		}
   186  	}
   187  
   188  	{ // Naked Get
   189  		q := client.NewQuery(kindImage)
   190  		var pss []datastore.PropertyList
   191  		_, err = client.GetAll(ctx, q, &pss)
   192  		if err != nil {
   193  			t.Fatal(err)
   194  		}
   195  
   196  		if v := len(pss); v != circleLimit*imageLimit {
   197  			t.Errorf("unexpected: %v", v)
   198  		}
   199  		for _, ps := range pss {
   200  			for _, p := range ps {
   201  				if !strings.HasSuffix(p.Name, "ID") {
   202  					continue
   203  				}
   204  
   205  				_, ok := p.Value.(datastore.Key)
   206  				if !ok {
   207  					t.Errorf("unexpected: %T", p.Value)
   208  				}
   209  			}
   210  		}
   211  	}
   212  }