github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/appdef/impl_view_test.go (about)

     1  /*
     2   * Copyright (c) 2021-present Sigma-Soft, Ltd.
     3   * @author: Nikolay Nikitin
     4   */
     5  
     6  package appdef
     7  
     8  import (
     9  	"regexp"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestAddView(t *testing.T) {
    16  	require := require.New(t)
    17  
    18  	adb := New()
    19  	adb.AddPackage("test", "test.com/test")
    20  
    21  	numName := NewQName("test", "natural")
    22  	_ = adb.AddData(numName, DataKind_int64, NullQName, MinExcl(0))
    23  
    24  	digsData := NewQName("test", "digs")
    25  	_ = adb.AddData(digsData, DataKind_string, NullQName, Pattern(`^\d+$`, "only digits allowed"))
    26  
    27  	docName := NewQName("test", "doc")
    28  	_ = adb.AddCDoc(docName)
    29  
    30  	kbName := NewQName("test", "KB")
    31  	_ = adb.AddData(kbName, DataKind_bytes, NullQName, MinLen(1), MaxLen(1024, "up to 1 KB"))
    32  
    33  	viewName := NewQName("test", "view")
    34  	vb := adb.AddView(viewName)
    35  
    36  	t.Run("must be ok to build view", func(t *testing.T) {
    37  
    38  		vb.SetComment("test view")
    39  
    40  		t.Run("must be ok to add partition key fields", func(t *testing.T) {
    41  			vb.Key().PartKey().AddDataField("pkF1", numName)
    42  			vb.Key().PartKey().AddField("pkF2", DataKind_bool)
    43  
    44  			t.Run("panic if field already exists in view", func(t *testing.T) {
    45  				require.Panics(func() {
    46  					vb.Key().PartKey().AddField("pkF1", DataKind_int64)
    47  				})
    48  			})
    49  
    50  			t.Run("panic if variable length field added to pk", func(t *testing.T) {
    51  				require.Panics(func() {
    52  					vb.Key().PartKey().AddField("pkF3", DataKind_string)
    53  				})
    54  				require.Panics(func() {
    55  					vb.Key().PartKey().AddDataField("pkF3", digsData)
    56  				})
    57  			})
    58  
    59  			t.Run("panic if unknown data type field added to pk", func(t *testing.T) {
    60  				require.Panics(func() {
    61  					vb.Key().PartKey().AddDataField("pkF3", NewQName("test", "unknown"))
    62  				})
    63  			})
    64  		})
    65  
    66  		t.Run("must be ok to add clustering columns fields", func(t *testing.T) {
    67  			vb.Key().ClustCols().AddField("ccF1", DataKind_int64)
    68  			vb.Key().ClustCols().AddRefField("ccF2", docName)
    69  
    70  			t.Run("panic if field already exists in view", func(t *testing.T) {
    71  				require.Panics(func() {
    72  					vb.Key().ClustCols().AddField("ccF1", DataKind_int64)
    73  				})
    74  			})
    75  
    76  			t.Run("panic if unknown data type field added to cc", func(t *testing.T) {
    77  				require.Panics(func() {
    78  					vb.Key().ClustCols().AddDataField("ccF3", NewQName("test", "unknown"))
    79  				})
    80  			})
    81  		})
    82  
    83  		t.Run("must be ok to add value fields", func(t *testing.T) {
    84  			vb.Value().
    85  				AddField("valF1", DataKind_bool, true).
    86  				AddDataField("valF2", digsData, false, MaxLen(100)).SetFieldComment("valF2", "up to 100 digits")
    87  		})
    88  	})
    89  
    90  	app, err := adb.Build()
    91  	require.NoError(err)
    92  	view := app.View(viewName)
    93  
    94  	t.Run("must be ok to read view", func(t *testing.T) {
    95  		require.Equal("test view", view.Comment())
    96  		require.Equal(viewName, view.QName())
    97  		require.Equal(TypeKind_ViewRecord, view.Kind())
    98  
    99  		checkValueValF2 := func(f IField) {
   100  			require.Equal("valF2", f.Name())
   101  			require.False(f.Required())
   102  			cnt := 0
   103  			for k, c := range f.Constraints() {
   104  				cnt++
   105  				switch k {
   106  				case ConstraintKind_MaxLen:
   107  					require.EqualValues(100, c.Value())
   108  				case ConstraintKind_Pattern:
   109  					require.EqualValues(`^\d+$`, c.Value().(*regexp.Regexp).String())
   110  					require.Equal("only digits allowed", c.Comment())
   111  				default:
   112  					require.Fail("unexpected constraint", "constraint: %v", c)
   113  				}
   114  			}
   115  			require.Equal("up to 100 digits", f.Comment())
   116  		}
   117  
   118  		require.Equal(7, view.FieldCount())
   119  		cnt := 0
   120  		for _, f := range view.Fields() {
   121  			cnt++
   122  			switch cnt {
   123  			case 1:
   124  				require.Equal(SystemField_QName, f.Name())
   125  				require.True(f.IsSys())
   126  			case 2:
   127  				require.Equal("pkF1", f.Name())
   128  				require.Equal(numName, f.Data().QName())
   129  				require.True(f.Required())
   130  			case 3:
   131  				require.Equal("pkF2", f.Name())
   132  				require.True(f.Required())
   133  			case 4:
   134  				require.Equal("ccF1", f.Name())
   135  				require.False(f.Required())
   136  			case 5:
   137  				require.Equal("ccF2", f.Name())
   138  				require.False(f.Required())
   139  			case 6:
   140  				require.Equal("valF1", f.Name())
   141  				require.True(f.Required())
   142  			case 7:
   143  				checkValueValF2(f)
   144  			default:
   145  				require.Fail("unexpected field «%s»", f.Name())
   146  			}
   147  		}
   148  		require.Equal(view.FieldCount(), cnt)
   149  
   150  		t.Run("must be ok to read view full key", func(t *testing.T) {
   151  			key := view.Key()
   152  			require.Equal(4, key.FieldCount())
   153  			cnt := 0
   154  			for _, f := range key.Fields() {
   155  				cnt++
   156  				switch cnt {
   157  				case 1:
   158  					require.Equal("pkF1", f.Name())
   159  					require.Equal(numName, f.Data().QName())
   160  					require.True(f.Required())
   161  				case 2:
   162  					require.Equal("pkF2", f.Name())
   163  					require.True(f.Required())
   164  				case 3:
   165  					require.Equal("ccF1", f.Name())
   166  					require.False(f.Required())
   167  				case 4:
   168  					require.Equal("ccF2", f.Name())
   169  					require.False(f.Required())
   170  				default:
   171  					require.Fail("unexpected field «%s»", f.Name())
   172  				}
   173  			}
   174  			require.Equal(key.FieldCount(), cnt)
   175  		})
   176  
   177  		t.Run("must be ok to read view partition key", func(t *testing.T) {
   178  			pk := view.Key().PartKey()
   179  			require.Equal(2, pk.FieldCount())
   180  			cnt := 0
   181  			for _, f := range pk.Fields() {
   182  				cnt++
   183  				switch cnt {
   184  				case 1:
   185  					require.Equal("pkF1", f.Name())
   186  					require.Equal(numName, f.Data().QName())
   187  					require.True(f.Required())
   188  				case 2:
   189  					require.Equal("pkF2", f.Name())
   190  					require.True(f.Required())
   191  				default:
   192  					require.Fail("unexpected field «%s»", f.Name())
   193  				}
   194  			}
   195  			require.Equal(pk.FieldCount(), cnt)
   196  			require.NotPanics(func() { pk.isPartKey() })
   197  		})
   198  
   199  		t.Run("must be ok to read view clustering columns", func(t *testing.T) {
   200  			cc := view.Key().ClustCols()
   201  			require.Equal(2, cc.FieldCount())
   202  			cnt := 0
   203  			for _, f := range cc.Fields() {
   204  				cnt++
   205  				switch cnt {
   206  				case 1:
   207  					require.Equal("ccF1", f.Name())
   208  					require.False(f.Required())
   209  				case 2:
   210  					require.Equal("ccF2", f.Name())
   211  					require.False(f.Required())
   212  				default:
   213  					require.Fail("unexpected field «%s»", f.Name())
   214  				}
   215  			}
   216  			require.Equal(cc.FieldCount(), cnt)
   217  			require.NotPanics(func() { cc.isClustCols() })
   218  		})
   219  
   220  		t.Run("must be ok to read view value", func(t *testing.T) {
   221  			val := view.Value()
   222  			require.Equal(3, val.FieldCount())
   223  			cnt := 0
   224  			for _, f := range val.Fields() {
   225  				cnt++
   226  				switch cnt {
   227  				case 1:
   228  					require.Equal(SystemField_QName, f.Name())
   229  					require.True(f.IsSys())
   230  				case 2:
   231  					require.Equal("valF1", f.Name())
   232  					require.True(f.Required())
   233  				case 3:
   234  					checkValueValF2(f)
   235  				default:
   236  					require.Fail("unexpected field «%s»", f.Name())
   237  				}
   238  			}
   239  			require.Equal(val.FieldCount(), cnt)
   240  			require.NotPanics(func() { val.isViewValue() })
   241  		})
   242  
   243  		t.Run("must be ok to cast Type() as IView", func(t *testing.T) {
   244  			typ := app.Type(viewName)
   245  			require.NotNil(typ)
   246  			require.Equal(TypeKind_ViewRecord, typ.Kind())
   247  
   248  			v, ok := typ.(IView)
   249  			require.True(ok)
   250  			require.Equal(v, view)
   251  		})
   252  
   253  		require.Nil(app.View(NewQName("test", "unknown")), "find unknown view must return nil")
   254  
   255  		t.Run("must be nil if not view", func(t *testing.T) {
   256  			require.Nil(app.View(docName))
   257  
   258  			typ := app.Type(docName)
   259  			require.NotNil(typ)
   260  			v, ok := typ.(IView)
   261  			require.False(ok)
   262  			require.Nil(v)
   263  		})
   264  	})
   265  
   266  	t.Run("must be ok to add fields to view after app build", func(t *testing.T) {
   267  		vb.Key().PartKey().
   268  			AddRefField("pkF3", docName).
   269  			SetFieldComment("pkF3", "test comment")
   270  
   271  		vb.Key().ClustCols().
   272  			AddDataField("ccF3", kbName).SetFieldComment("ccF3", "one KB")
   273  
   274  		t.Run("panic if add second variable length field", func(t *testing.T) {
   275  			require.Panics(func() {
   276  				vb.Key().ClustCols().AddField("ccF3_1", DataKind_bytes)
   277  			})
   278  			require.Panics(func() {
   279  				vb.Key().ClustCols().AddDataField("ccF3_1", kbName)
   280  			})
   281  		})
   282  
   283  		vb.Value().
   284  			AddRefField("valF3", false, docName).
   285  			AddField("valF4", DataKind_bytes, false, MaxLen(1024)).SetFieldComment("valF4", "test comment").
   286  			AddField("valF5", DataKind_bool, false).SetFieldVerify("valF5", VerificationKind_EMail)
   287  
   288  		_, err := adb.Build()
   289  		require.NoError(err)
   290  
   291  		require.Equal(3, view.Key().PartKey().FieldCount())
   292  		require.Equal(3, view.Key().ClustCols().FieldCount())
   293  		require.Equal(6, view.Key().FieldCount())
   294  
   295  		require.Equal(view.Key().FieldCount()+view.Value().FieldCount(), view.FieldCount())
   296  
   297  		require.Equal("test comment", view.Key().Field("pkF3").Comment())
   298  		require.Equal("one KB", view.Key().Field("ccF3").Comment())
   299  
   300  		require.Equal(5, view.Value().UserFieldCount())
   301  
   302  		cnt := 0
   303  		for _, f := range view.Value().Fields() {
   304  			cnt++
   305  			switch f.Name() {
   306  			case SystemField_QName:
   307  				require.Equal(DataKind_QName, f.DataKind())
   308  				require.True(f.IsSys())
   309  			case "valF1":
   310  				require.Equal(DataKind_bool, f.DataKind())
   311  				require.True(f.Required())
   312  			case "valF2":
   313  				require.Equal(DataKind_string, f.DataKind())
   314  				require.False(f.Required())
   315  				// valF2 constraints checked above
   316  			case "valF3":
   317  				require.Equal(DataKind_RecordID, f.DataKind())
   318  				require.False(f.Required())
   319  				require.EqualValues(QNames{docName}, f.(IRefField).Refs())
   320  			case "valF4":
   321  				require.Equal(DataKind_bytes, f.DataKind())
   322  				require.False(f.Required())
   323  				require.Equal("test comment", f.Comment())
   324  				cnt := 0
   325  				for _, c := range f.Constraints() {
   326  					cnt++
   327  					switch cnt {
   328  					case 1:
   329  						require.Equal(ConstraintKind_MaxLen, c.Kind())
   330  						require.EqualValues(1024, c.Value())
   331  					default:
   332  						require.Fail("unexpected constraint", "constraint: %v", c)
   333  					}
   334  				}
   335  				require.EqualValues(1, cnt)
   336  			case "valF5":
   337  				require.Equal(DataKind_bool, f.DataKind())
   338  				require.False(f.Required())
   339  				require.True(f.Verifiable())
   340  				require.True(f.VerificationKind(VerificationKind_EMail))
   341  				require.False(f.VerificationKind(VerificationKind_Phone))
   342  			default:
   343  				require.Fail("unexpected value field", "field name: %s", f.Name())
   344  			}
   345  		}
   346  		require.Equal(view.Value().UserFieldCount()+1, cnt)
   347  	})
   348  }
   349  
   350  func TestViewValidate(t *testing.T) {
   351  	require := require.New(t)
   352  
   353  	viewName := NewQName("test", "view")
   354  
   355  	adb := New()
   356  	adb.AddPackage("test", "test.com/test")
   357  
   358  	v := adb.AddView(viewName)
   359  	require.NotNil(v)
   360  
   361  	t.Run("must be error if no pkey fields", func(t *testing.T) {
   362  		_, err := adb.Build()
   363  		require.ErrorIs(err, ErrMissedError)
   364  	})
   365  
   366  	v.Key().PartKey().AddField("pk1", DataKind_bool)
   367  
   368  	t.Run("must be error if no ccols fields", func(t *testing.T) {
   369  		_, err := adb.Build()
   370  		require.ErrorIs(err, ErrMissedError)
   371  	})
   372  
   373  	v.Key().ClustCols().AddField("cc1", DataKind_string, MaxLen(100))
   374  	_, err := adb.Build()
   375  	require.NoError(err)
   376  }