github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/it/impl_cud_test.go (about)

     1  /*
     2   * Copyright (c) 2022-present unTill Pro, Ltd.
     3   */
     4  package sys_it
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"log"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/voedger/voedger/pkg/appdef"
    15  	"github.com/voedger/voedger/pkg/istructs"
    16  	coreutils "github.com/voedger/voedger/pkg/utils"
    17  	it "github.com/voedger/voedger/pkg/vit"
    18  )
    19  
    20  func TestBasicUsage_CUD(t *testing.T) {
    21  	require := require.New(t)
    22  	vit := it.NewVIT(t, &it.SharedConfig_App1)
    23  	defer vit.TearDown()
    24  
    25  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
    26  
    27  	t.Run("create", func(t *testing.T) {
    28  		body := `
    29  			{
    30  				"cuds": [
    31  					{
    32  						"fields": {
    33  							"sys.ID": 1,
    34  							"sys.QName": "app1pkg.articles",
    35  							"name": "cola",
    36  							"article_manual": 1,
    37  							"article_hash": 2,
    38  							"hideonhold": 3,
    39  							"time_active": 4,
    40  							"control_active": 5
    41  						}
    42  					}
    43  				]
    44  			}`
    45  		vit.PostWS(ws, "c.sys.CUD", body).Println()
    46  	})
    47  
    48  	var id float64
    49  	t.Run("read using collection", func(t *testing.T) {
    50  		body := `
    51  		{
    52  			"args":{
    53  				"Schema":"app1pkg.articles"
    54  			},
    55  			"elements":[
    56  				{
    57  					"fields": ["name", "control_active", "sys.ID"]
    58  				}
    59  			],
    60  			"orderBy":[{"field":"name"}]
    61  		}`
    62  		resp := vit.PostWS(ws, "q.sys.Collection", body)
    63  		actualName := resp.SectionRow()[0].(string)
    64  		actualControlActive := resp.SectionRow()[1].(float64)
    65  		id = resp.SectionRow()[2].(float64)
    66  		require.Equal("cola", actualName)
    67  		require.Equal(float64(5), actualControlActive)
    68  	})
    69  
    70  	t.Run("update", func(t *testing.T) {
    71  		body := fmt.Sprintf(`
    72  		{
    73  			"cuds": [
    74  				{
    75  					"sys.ID": %d,
    76  					"fields": {
    77  						"name": "cola1",
    78  						"article_manual": 11,
    79  						"article_hash": 21,
    80  						"hideonhold": 31,
    81  						"time_active": 41,
    82  						"control_active": 51
    83  					}
    84  				}
    85  			]
    86  		}`, int64(id))
    87  		vit.PostWS(ws, "c.sys.CUD", body)
    88  
    89  		body = `
    90  		{
    91  			"args":{
    92  				"Schema":"app1pkg.articles"
    93  			},
    94  			"elements":[
    95  				{
    96  					"fields": ["name", "control_active", "sys.ID"]
    97  				}
    98  			]
    99  		}`
   100  		resp := vit.PostWS(ws, "q.sys.Collection", body)
   101  		actualName := resp.SectionRow()[0].(string)
   102  		actualControlActive := resp.SectionRow()[1].(float64)
   103  		newID := resp.SectionRow()[2].(float64)
   104  		require.Equal("cola1", actualName)
   105  		require.Equal(float64(51), actualControlActive)
   106  		require.Equal(id, newID)
   107  
   108  		// CDoc
   109  		body = fmt.Sprintf(`
   110  			{
   111  				"args":{
   112  					"ID": %d
   113  				},
   114  				"elements":[
   115  					{
   116  						"fields": ["Result"]
   117  					}
   118  				]
   119  			}`, int64(id))
   120  		resp = vit.PostWS(ws, "q.sys.GetCDoc", body)
   121  		jsonBytes := []byte(resp.SectionRow()[0].(string))
   122  		cdoc := map[string]interface{}{}
   123  		require.NoError(json.Unmarshal(jsonBytes, &cdoc))
   124  		log.Println(string(jsonBytes))
   125  		log.Println(cdoc)
   126  	})
   127  
   128  	t.Run("404 on update unexisting", func(t *testing.T) {
   129  		body := fmt.Sprintf(`
   130  			{
   131  				"cuds": [
   132  					{
   133  						"sys.ID": %d,
   134  						"fields": {}
   135  					}
   136  				]
   137  			}`, istructs.NonExistingRecordID)
   138  		vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect404())
   139  	})
   140  }
   141  
   142  // Deprecated: use c.sys.CUD. Kept to not to break the exitsing events only
   143  func TestBasicUsage_Init(t *testing.T) {
   144  	require := require.New(t)
   145  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   146  	defer vit.TearDown()
   147  
   148  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   149  
   150  	body := `
   151  		{
   152  			"cuds": [
   153  				{
   154  					"fields": {
   155  						"sys.ID": 100000,
   156  						"sys.QName": "app1pkg.articles",
   157  						"name": "cola",
   158  						"article_manual": 11,
   159  						"article_hash": 21,
   160  						"hideonhold": 31,
   161  						"time_active": 41,
   162  						"control_active": 51
   163  					}
   164  				}
   165  			]
   166  		}`
   167  	vit.PostWSSys(ws, "c.sys.Init", body)
   168  
   169  	body = `
   170  		{
   171  			"args":{
   172  				"Schema":"app1pkg.articles"
   173  			},
   174  			"elements":[
   175  				{
   176  					"fields": ["name", "control_active", "sys.ID"]
   177  				}
   178  			],
   179  			"orderBy":[{"field":"name"}]
   180  		}`
   181  	resp := vit.PostWS(ws, "q.sys.Collection", body)
   182  	actualName := resp.SectionRow()[0].(string)
   183  	actualControlActive := resp.SectionRow()[1].(float64)
   184  	id := resp.SectionRow()[2].(float64)
   185  	require.Equal("cola", actualName)
   186  	require.Equal(float64(51), actualControlActive)
   187  	require.Greater(istructs.RecordID(id), istructs.MaxRawRecordID)
   188  }
   189  
   190  func TestBasicUsage_Singletons(t *testing.T) {
   191  	require := require.New(t)
   192  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   193  	defer vit.TearDown()
   194  
   195  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   196  
   197  	body := `
   198  		{
   199  			"cuds": [
   200  				{
   201  					"fields": {
   202  						"sys.ID": 1,
   203  						"sys.QName": "app1pkg.Config",
   204  						"Fld1": "42"
   205  					}
   206  				}
   207  			]
   208  		}`
   209  	resp := vit.PostWS(ws, "c.sys.CUD", body)
   210  	require.Empty(resp.NewIDs) // ничего не прошло через ID generator
   211  
   212  	// повторное создание -> ошибка
   213  	vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect409()).Println()
   214  
   215  	// запросим ID через collection
   216  	body = `{
   217  		"args":{ "Schema":"app1pkg.Config" },
   218  		"elements":[{ "fields": ["sys.ID"] }]
   219  	}`
   220  	resp = vit.PostWS(ws, "q.sys.Collection", body)
   221  	singletonID := int64(resp.SectionRow()[0].(float64))
   222  	log.Println(singletonID)
   223  	require.True(istructs.RecordID(singletonID) >= istructs.FirstSingletonID && istructs.RecordID(singletonID) <= istructs.MaxSingletonID)
   224  }
   225  
   226  func TestUnlinkReference(t *testing.T) {
   227  	require := require.New(t)
   228  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   229  	defer vit.TearDown()
   230  
   231  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   232  
   233  	// new `options` and `department` are linked to `department_options`
   234  	body := `
   235  		{
   236  			"cuds": [
   237  				{
   238  					"fields": {
   239  						"sys.ID": 1,
   240  						"sys.QName": "app1pkg.options"
   241  					}
   242  				},
   243  				{
   244  					"fields": {
   245  						"sys.ID": 2,
   246  						"sys.QName": "app1pkg.department",
   247  						"pc_fix_button": 1,
   248  						"rm_fix_button": 1
   249  					}
   250  				},
   251  				{
   252  					"fields": {
   253  						"sys.ID": 3,
   254  						"sys.QName": "app1pkg.department_options",
   255  						"id_options": 1,
   256  						"id_department": 2,
   257  						"sys.ParentID": 2,
   258  						"sys.Container": "department_options",
   259  						"option_type": 1
   260  					}
   261  				}
   262  			]
   263  		}`
   264  	resp := vit.PostWS(ws, "c.sys.CUD", body)
   265  
   266  	// unlink department_option from options
   267  	idDep := resp.NewIDs["2"]
   268  	idDepOpts := resp.NewIDs["3"]
   269  	body = fmt.Sprintf(`{"cuds": [{"sys.ID": %d, "fields": {"id_options": %d}}]}`, idDepOpts, istructs.NullRecordID)
   270  	vit.PostWS(ws, "c.sys.CUD", body)
   271  
   272  	// read the root department
   273  	body = fmt.Sprintf(`{"args":{"ID": %d},"elements":[{"fields": ["Result"]}]}`, idDep)
   274  	resp = vit.PostWS(ws, "q.sys.GetCDoc", body)
   275  	m := map[string]interface{}{}
   276  	require.NoError(json.Unmarshal([]byte(resp.SectionRow()[0].(string)), &m))
   277  	require.Zero(m["department_options"].([]interface{})[0].(map[string]interface{})["id_options"].(float64))
   278  }
   279  
   280  func TestRefIntegrity(t *testing.T) {
   281  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   282  	defer vit.TearDown()
   283  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   284  	appStructs, err := vit.IAppStructsProvider.AppStructs(istructs.AppQName_test1_app1)
   285  	require.NoError(t, err)
   286  	appDef := appStructs.AppDef()
   287  
   288  	t.Run("CUDs", func(t *testing.T) {
   289  		body := `{"cuds":[
   290  			{"fields":{"sys.ID":1,"sys.QName":"app1pkg.cdoc1"}},
   291  			{"fields":{"sys.ID":2,"sys.QName":"app1pkg.options"}},
   292  			{"fields":{"sys.ID":3,"sys.QName":"app1pkg.department","pc_fix_button": 1,"rm_fix_button": 1}}
   293  		]}`
   294  		resp := vit.PostWS(ws, "c.sys.CUD", body)
   295  		idCdoc1 := resp.NewIDs["1"]
   296  		idOption := resp.NewIDs["2"]
   297  		idDep := resp.NewIDs["3"]
   298  
   299  		t.Run("ref to unexisting -> 400 bad request", func(t *testing.T) {
   300  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field1": %d}}]}`, istructs.NonExistingRecordID)
   301  			vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect400RefIntegrity_Existence())
   302  
   303  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field2": %d}}]}`, istructs.NonExistingRecordID)
   304  			vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect400RefIntegrity_Existence())
   305  		})
   306  
   307  		t.Run("ref to existing, allowed QName", func(t *testing.T) {
   308  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field1": %d}}]}`, idCdoc1)
   309  			vit.PostWS(ws, "c.sys.CUD", body)
   310  
   311  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field2": %d}}]}`, idCdoc1)
   312  			vit.PostWS(ws, "c.sys.CUD", body)
   313  
   314  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field2": %d}}]}`, idDep)
   315  			vit.PostWS(ws, "c.sys.CUD", body)
   316  		})
   317  
   318  		t.Run("ref to existing wrong QName -> 400 bad request", func(t *testing.T) {
   319  			body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID": 2,"sys.QName":"app1pkg.cdoc2","field2": %d}}]}`, idOption)
   320  			vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect400RefIntegrity_QName())
   321  		})
   322  	})
   323  
   324  	t.Run("ODocs", func(t *testing.T) {
   325  		t.Run("args", func(t *testing.T) {
   326  			testArgsRefIntegrity(t, vit, ws, appDef, `{"args":{"sys.ID": 1,%s},"unloggedArgs":{"sys.ID":2}}`)
   327  		})
   328  
   329  		t.Run("unloggedArgs", func(t *testing.T) {
   330  			testArgsRefIntegrity(t, vit, ws, appDef, `{"args":{"sys.ID": 1},"unloggedArgs":{"sys.ID":2, %s}}`)
   331  		})
   332  	})
   333  }
   334  
   335  func testArgsRefIntegrity(t *testing.T, vit *it.VIT, ws *it.AppWorkspace, appDef appdef.IAppDef, urlTemplate string) {
   336  	body := `{"args":{"sys.ID": 1,"orecord1":[{"sys.ID":2,"sys.ParentID":1,"orecord2":[{"sys.ID":3,"sys.ParentID":2}]}]}}`
   337  	resp := vit.PostWS(ws, "c.app1pkg.CmdODocOne", body)
   338  	idOdoc1 := resp.NewIDs["1"]
   339  	idOrecord1 := resp.NewIDs["2"]
   340  	idOrecord2 := resp.NewIDs["3"]
   341  	body = `{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.cdoc1"}}]}`
   342  	idCDoc := vit.PostWS(ws, "c.sys.CUD", body).NewID()
   343  	t.Run("ref to unexisting -> 400 bad request", func(t *testing.T) {
   344  		oDoc := appDef.ODoc(it.QNameODoc2)
   345  		for _, oDoc1RefField := range oDoc.RefFields() {
   346  			t.Run(oDoc1RefField.Name(), func(t *testing.T) {
   347  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"%s":%d`, oDoc1RefField.Name(), istructs.NonExistingRecordID))
   348  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_Existence()).Println()
   349  			})
   350  		}
   351  	})
   352  
   353  	t.Run("ref to existing", func(t *testing.T) {
   354  		t.Run("ODoc", func(t *testing.T) {
   355  			t.Run("allowed QName", func(t *testing.T) {
   356  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToODoc1":%d`, idOdoc1))
   357  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   358  			})
   359  
   360  			t.Run("wrong QName CDoc-> 400 bad request", func(t *testing.T) {
   361  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToODoc1":%d`, idCDoc))
   362  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName()).Println()
   363  			})
   364  
   365  			t.Run("wrong QName ORecord -> 400 bad request", func(t *testing.T) {
   366  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToODoc1":%d`, idOrecord1))
   367  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName()).Println()
   368  			})
   369  		})
   370  		t.Run("ORecord", func(t *testing.T) {
   371  			t.Run("allowed QName ORecord1", func(t *testing.T) {
   372  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToORecord1":%d`, idOrecord1))
   373  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   374  			})
   375  
   376  			t.Run("allowed QName ORecord2", func(t *testing.T) {
   377  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToORecord2":%d`, idOrecord2))
   378  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   379  			})
   380  
   381  			t.Run("wrong QName CDoc -> 400 bad request", func(t *testing.T) {
   382  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToORecord1":%d`, idCDoc))
   383  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName()).Println()
   384  			})
   385  
   386  			t.Run("wrong QName ODoc ORecord1 -> 400 bad request", func(t *testing.T) {
   387  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToORecord1":%d`, idOdoc1))
   388  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName()).Println()
   389  			})
   390  
   391  			t.Run("wrong QName ODoc ORecord2 -> 400 bad request", func(t *testing.T) {
   392  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToORecord2":%d`, idOdoc1))
   393  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName()).Println()
   394  			})
   395  		})
   396  		t.Run("Any", func(t *testing.T) {
   397  			body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToAny":%d`, idCDoc))
   398  			vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   399  
   400  			body = fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToAny":%d`, idOdoc1))
   401  			vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   402  		})
   403  
   404  		t.Run("CDoc", func(t *testing.T) {
   405  			t.Run("allowed QName", func(t *testing.T) {
   406  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToCDoc1":%d`, idCDoc))
   407  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   408  			})
   409  			t.Run("wrong QName -> 400 bad request", func(t *testing.T) {
   410  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToCDoc1":%d`, idOdoc1))
   411  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName())
   412  			})
   413  		})
   414  
   415  		t.Run("CDoc or ODoc", func(t *testing.T) {
   416  			t.Run("allowed QName", func(t *testing.T) {
   417  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToCDoc1OrODoc1":%d`, idCDoc))
   418  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   419  
   420  				body = fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToCDoc1OrODoc1":%d`, idOdoc1))
   421  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body)
   422  			})
   423  			t.Run("wrong QName -> 400 bad request", func(t *testing.T) {
   424  				body := fmt.Sprintf(urlTemplate, fmt.Sprintf(`"refToCDoc1OrODoc1":%d`, idOrecord1))
   425  				vit.PostWS(ws, "c.app1pkg.CmdODocTwo", body, coreutils.Expect400RefIntegrity_QName())
   426  			})
   427  		})
   428  	})
   429  }
   430  
   431  // https://github.com/voedger/voedger/issues/54
   432  func TestEraseString(t *testing.T) {
   433  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   434  	defer vit.TearDown()
   435  
   436  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   437  	idAnyAirTablePlan := vit.GetAny("app1pkg.air_table_plan", ws)
   438  
   439  	body := fmt.Sprintf(`{"cuds":[{"sys.ID": %d,"fields":{"name":""}}]}`, idAnyAirTablePlan)
   440  	vit.PostWS(ws, "c.sys.CUD", body)
   441  
   442  	body = fmt.Sprintf(`{"args":{"Schema":"app1pkg.air_table_plan"},"elements":[{"fields": ["name","sys.ID"]}],"filters":[{"expr":"eq","args":{"field":"sys.ID","value":%d}}]}`, idAnyAirTablePlan)
   443  	resp := vit.PostWS(ws, "q.sys.Collection", body)
   444  
   445  	require.Equal(t, "", resp.SectionRow()[0].(string))
   446  }
   447  
   448  func TestEraseString1(t *testing.T) {
   449  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   450  	defer vit.TearDown()
   451  
   452  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   453  	body := `{"cuds": [{"fields": {"sys.ID": 1,"sys.QName": "app1pkg.articles","name": "cola","article_manual": 1,"article_hash": 2,"hideonhold": 3,"time_active": 4,"control_active": 5}}]}`
   454  	id := vit.PostWS(ws, "c.sys.CUD", body).NewID()
   455  
   456  	body = fmt.Sprintf(`{"cuds":[{"sys.ID": %d,"fields":{"name":""}}]}`, id)
   457  	vit.PostWS(ws, "c.sys.CUD", body)
   458  
   459  	body = fmt.Sprintf(`{"args":{"Schema":"app1pkg.articles"},"elements":[{"fields": ["name","sys.ID"]}],"filters":[{"expr":"eq","args":{"field":"sys.ID","value":%d}}]}`, id)
   460  	resp := vit.PostWS(ws, "q.sys.Collection", body)
   461  
   462  	require.Equal(t, "", resp.SectionRow()[0].(string))
   463  }
   464  
   465  func TestDenyCreateNonRawIDs(t *testing.T) {
   466  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   467  	defer vit.TearDown()
   468  
   469  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   470  	body := fmt.Sprintf(`{"cuds": [{"fields": {"sys.ID": %d,"sys.QName": "app1pkg.options"}}]}`, istructs.FirstBaseUserWSID)
   471  	vit.PostWS(ws, "c.sys.CUD", body, coreutils.Expect400())
   472  }