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 }