github.com/juranki/gonetable@v0.0.0-20220909144800-647dacb32e0b/schema_test.go (about)

     1  package gonetable_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/aws/aws-sdk-go-v2/aws"
    11  	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
    12  	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    13  	"github.com/juranki/gonetable"
    14  )
    15  
    16  func TestNewSchema(t *testing.T) {
    17  	type args struct {
    18  		docSamples []gonetable.Document
    19  	}
    20  	tests := []struct {
    21  		name    string
    22  		args    args
    23  		wantErr error
    24  	}{
    25  		{
    26  			name: "invalid index",
    27  			args: args{
    28  				docSamples: []gonetable.Document{&InvalidIndex{}},
    29  			},
    30  			wantErr: gonetable.ErrIndexName,
    31  		},
    32  		{
    33  			name: "no doc samples",
    34  			args: args{
    35  				docSamples: []gonetable.Document{},
    36  			},
    37  			wantErr: gonetable.ErrNoDocSamples,
    38  		},
    39  		{
    40  			name: "duplicate type id",
    41  			args: args{
    42  				docSamples: []gonetable.Document{&InvalidIndex{}, &InvalidIndex{}},
    43  			},
    44  			wantErr: gonetable.ErrDuplicateTypeID,
    45  		},
    46  		{
    47  			name: "simple doc",
    48  			args: args{
    49  				docSamples: []gonetable.Document{&MinimalDoc{}},
    50  			},
    51  			wantErr: nil,
    52  		},
    53  		{
    54  			name: "one index",
    55  			args: args{
    56  				docSamples: []gonetable.Document{&WithIndex{}},
    57  			},
    58  			wantErr: nil,
    59  		},
    60  	}
    61  	for _, tt := range tests {
    62  		t.Run(tt.name, func(t *testing.T) {
    63  			_, err := gonetable.NewSchema(tt.args.docSamples)
    64  			if err == nil {
    65  				if tt.wantErr != nil {
    66  					t.Error("Expected error")
    67  				}
    68  				return
    69  			}
    70  			if tt.wantErr.Error() == err.Error() {
    71  				return
    72  			}
    73  			t.Errorf("error = '%v', want '%v'", err.Error(), tt.wantErr.Error())
    74  		})
    75  	}
    76  }
    77  
    78  func TestSchema_AttributeDefinitions(t *testing.T) {
    79  	tests := []struct {
    80  		name       string
    81  		docSamples []gonetable.Document
    82  		want       []types.AttributeDefinition
    83  	}{
    84  		{
    85  			name:       "minimal",
    86  			docSamples: []gonetable.Document{&MinimalDoc{}},
    87  			want: []types.AttributeDefinition{
    88  				{
    89  					AttributeName: aws.String("PK"),
    90  					AttributeType: types.ScalarAttributeTypeS,
    91  				},
    92  				{
    93  					AttributeName: aws.String("SK"),
    94  					AttributeType: types.ScalarAttributeTypeS,
    95  				},
    96  			},
    97  		},
    98  		{
    99  			name:       "withIndex",
   100  			docSamples: []gonetable.Document{&WithIndex{}},
   101  			want: []types.AttributeDefinition{
   102  				{
   103  					AttributeName: aws.String("PK"),
   104  					AttributeType: types.ScalarAttributeTypeS,
   105  				},
   106  				{
   107  					AttributeName: aws.String("SK"),
   108  					AttributeType: types.ScalarAttributeTypeS,
   109  				},
   110  				{
   111  					AttributeName: aws.String("GSI1PK"),
   112  					AttributeType: types.ScalarAttributeTypeS,
   113  				},
   114  				{
   115  					AttributeName: aws.String("GSI1SK"),
   116  					AttributeType: types.ScalarAttributeTypeS,
   117  				},
   118  			},
   119  		},
   120  	}
   121  	for _, tt := range tests {
   122  		t.Run(tt.name, func(t *testing.T) {
   123  			s, err := gonetable.NewSchema(tt.docSamples)
   124  			if err != nil {
   125  				t.Fatal(err)
   126  			}
   127  			if got := s.AttributeDefinitions(); !reflect.DeepEqual(got, tt.want) {
   128  				json.NewEncoder(os.Stdout).Encode(got)
   129  				json.NewEncoder(os.Stdout).Encode(tt.want)
   130  				t.Errorf("Schema.AttributeDefinitions() = %v, want %v", got, tt.want)
   131  			}
   132  		})
   133  	}
   134  }
   135  
   136  func TestSchema_KeySchema(t *testing.T) {
   137  	tests := []struct {
   138  		name       string
   139  		docSamples []gonetable.Document
   140  		want       []types.KeySchemaElement
   141  	}{
   142  		{
   143  			name:       "minimal",
   144  			docSamples: []gonetable.Document{&MinimalDoc{}},
   145  			want: []types.KeySchemaElement{
   146  				{
   147  					AttributeName: aws.String("PK"),
   148  					KeyType:       types.KeyTypeHash,
   149  				},
   150  				{
   151  					AttributeName: aws.String("SK"),
   152  					KeyType:       types.KeyTypeRange,
   153  				},
   154  			},
   155  		},
   156  	}
   157  	for _, tt := range tests {
   158  		t.Run(tt.name, func(t *testing.T) {
   159  			s, err := gonetable.NewSchema(tt.docSamples)
   160  			if err != nil {
   161  				t.Fatal(err)
   162  			}
   163  			if got := s.KeySchema(); !reflect.DeepEqual(got, tt.want) {
   164  				t.Errorf("Schema.KeySchema() = %v, want %v", got, tt.want)
   165  			}
   166  		})
   167  	}
   168  }
   169  
   170  func TestSchema_GlobalSecondaryIndexes(t *testing.T) {
   171  	tests := []struct {
   172  		name       string
   173  		docSamples []gonetable.Document
   174  		want       []types.GlobalSecondaryIndex
   175  	}{
   176  		{
   177  			name:       "minimal",
   178  			docSamples: []gonetable.Document{&MinimalDoc{}},
   179  			want:       nil,
   180  		},
   181  		{
   182  			name:       "with index",
   183  			docSamples: []gonetable.Document{&WithIndex{}},
   184  			want: []types.GlobalSecondaryIndex{
   185  				{
   186  					IndexName: aws.String("GSI1"),
   187  					KeySchema: []types.KeySchemaElement{
   188  						{
   189  							AttributeName: aws.String("GSI1PK"),
   190  							KeyType:       types.KeyTypeHash,
   191  						},
   192  						{
   193  							AttributeName: aws.String("GSI1SK"),
   194  							KeyType:       types.KeyTypeRange,
   195  						},
   196  					},
   197  					Projection: &types.Projection{
   198  						ProjectionType: types.ProjectionTypeAll,
   199  					},
   200  				},
   201  			},
   202  		},
   203  	}
   204  	for _, tt := range tests {
   205  		t.Run(tt.name, func(t *testing.T) {
   206  			s, err := gonetable.NewSchema(tt.docSamples)
   207  			if err != nil {
   208  				t.Fatal(err)
   209  			}
   210  			if got := s.GlobalSecondaryIndexes(); !reflect.DeepEqual(got, tt.want) {
   211  				t.Errorf("Schema.GlobalSecondaryIndexes() = %v, want %v", got, tt.want)
   212  			}
   213  		})
   214  	}
   215  }
   216  
   217  func TestSchema_Marshal(t *testing.T) {
   218  	type args struct {
   219  		docSamples []gonetable.Document
   220  		doc        gonetable.Document
   221  	}
   222  	tests := []struct {
   223  		name    string
   224  		args    args
   225  		want    map[string]types.AttributeValue
   226  		wantErr bool
   227  	}{
   228  		{
   229  			name: "minimal",
   230  			args: args{
   231  				docSamples: []gonetable.Document{&MinimalDoc{}},
   232  				doc: &MinimalDoc{
   233  					Name: "hiihaa",
   234  				},
   235  			},
   236  			want: map[string]types.AttributeValue{
   237  				"Name":  MustMarshal("hiihaa"),
   238  				"PK":    MustMarshal("a#b"),
   239  				"SK":    MustMarshal("a#b"),
   240  				"_Type": MustMarshal("sd1"),
   241  			},
   242  			wantErr: false,
   243  		},
   244  		{
   245  			name: "with index",
   246  			args: args{
   247  				docSamples: []gonetable.Document{&WithIndex{}},
   248  				doc: &WithIndex{
   249  					Name: "hiihaa",
   250  				},
   251  			},
   252  			want: map[string]types.AttributeValue{
   253  				"Name":   MustMarshal("hiihaa"),
   254  				"PK":     MustMarshal("wi#hiihaa"),
   255  				"SK":     MustMarshal("wi"),
   256  				"GSI1PK": MustMarshal("wi#hiihaa"),
   257  				"GSI1SK": MustMarshal("wi"),
   258  				"_Type":  MustMarshal("wi1"),
   259  			},
   260  			wantErr: false,
   261  		},
   262  		{
   263  			name: "wrong document type",
   264  			args: args{
   265  				docSamples: []gonetable.Document{&WithIndex{}},
   266  				doc: &MinimalDoc{
   267  					Name: "hiihaa",
   268  				},
   269  			},
   270  			wantErr: true,
   271  		},
   272  	}
   273  	for _, tt := range tests {
   274  		t.Run(tt.name, func(t *testing.T) {
   275  			s, err := gonetable.NewSchema(tt.args.docSamples)
   276  			if err != nil {
   277  				t.Fatal(err)
   278  			}
   279  			got, err := s.Marshal(tt.args.doc)
   280  			if (err != nil) != tt.wantErr {
   281  				t.Errorf("Schema.Marshal() error = %v, wantErr %v", err, tt.wantErr)
   282  				return
   283  			}
   284  			if !reflect.DeepEqual(got, tt.want) {
   285  				json.NewEncoder(os.Stdout).Encode(got)
   286  				t.Errorf("Schema.Marshal() = %v, want %v", got, tt.want)
   287  			}
   288  		})
   289  	}
   290  }
   291  
   292  func BenchmarkSchema_Marshal(b *testing.B) {
   293  	s, err := gonetable.NewSchema([]gonetable.Document{&WithIndex{}})
   294  	if err != nil {
   295  		b.Fatal(err)
   296  	}
   297  	d := &WithIndex{
   298  		Name: "withindex",
   299  	}
   300  	for i := 0; i < 1000; i++ {
   301  		_, err = s.Marshal(d)
   302  		if err != nil {
   303  			b.Fatal(err)
   304  		}
   305  	}
   306  }
   307  
   308  func BenchmarkSchema_MarshalAlternative(b *testing.B) {
   309  	s := &AlternativeSchema{}
   310  	d := &WithIndex{
   311  		Name: "withindex",
   312  	}
   313  	for i := 0; i < 1000; i++ {
   314  		_, err := s.alternativeMarshal(d)
   315  		if err != nil {
   316  			b.Fatal(err)
   317  		}
   318  	}
   319  }
   320  
   321  type AlternativeSchema struct {
   322  }
   323  
   324  // this would be generated for each document type
   325  func (s *AlternativeSchema) alternativeMarshal(doc *WithIndex) (map[string]types.AttributeValue, error) {
   326  	av, err := attributevalue.MarshalMap(doc)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  	docType := doc.Gonetable_TypeID()
   331  	av["_Type"], err = attributevalue.Marshal(docType)
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  	cKey := doc.Gonetable_Key()
   336  	cKeyAV, err := cKey.Marshal()
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  	for k, v := range cKeyAV {
   341  		av[k] = v
   342  	}
   343  	cGSI1Key := doc.Gonetable_GSI1Key()
   344  	cGSI1KeyAV, err := cGSI1Key.Marshal()
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	for k, v := range cGSI1KeyAV {
   349  		av[fmt.Sprintf("GSI1%s", k)] = v
   350  	}
   351  
   352  	return av, nil
   353  }