github.com/speedoops/go-gqlrest-demo@v0.0.0-20220710122636-5c6460aeab4f/graph/todo.resolvers_test.go (about) 1 package graph 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "encoding/json" 9 10 gqlcli "github.com/99designs/gqlgen/client" 11 "github.com/speedoops/go-gqlrest-demo/graph/engine" 12 "github.com/speedoops/go-gqlrest-demo/graph/model" 13 restcli "github.com/speedoops/go-gqlrest/client" 14 "github.com/stretchr/testify/require" 15 ) 16 17 const T9527 = "T9527" // 这是一个特定的值,第一个创建的 Todo 的 ID 18 19 type Todo struct { 20 ID string `json:"id"` 21 Text string `json:"text"` 22 Done bool `json:"done"` 23 User model.User `json:"user"` 24 Type *model.TodoType `json:"type"` 25 Categories []model.Category `json:"categories"` 26 } 27 28 func TestTodo(t *testing.T) { 29 srv := engine.NewMockServer(&Resolver{}) 30 c := gqlcli.New(srv, gqlcli.Path("/query")) 31 32 t.Run("mutation.createTodo", func(t *testing.T) { 33 var resp struct { 34 Todo Todo `json:"createTodo"` 35 } 36 37 mutation := ` 38 mutation createTodo($text:String!) { 39 createTodo(input: {userID:"uid", text:$text}){ 40 id,text,done 41 } 42 } 43 ` 44 45 genRandomText := func() string { 46 return fmt.Sprintf("text_%s", time.Now().Format("2006-01-02 15:04:05")) 47 } 48 49 c.MustPost(mutation, &resp, gqlcli.Var("text", genRandomText())) 50 c.MustPost(mutation, &resp, gqlcli.Var("text", genRandomText())) 51 c.MustPost(mutation, &resp, gqlcli.Var("text", genRandomText())) 52 53 require.NotEmpty(t, resp.Todo) 54 t.Logf("%+v", resp.Todo) 55 }) 56 57 t.Run("mutation.completeTodo", func(t *testing.T) { 58 var resp struct { 59 Todo Todo `json:"completeTodo"` 60 } 61 62 mutation := ` 63 mutation completeTodo($id: ID!) { 64 completeTodo(id: $id){ 65 id,text,done 66 } 67 } 68 ` 69 c.MustPost(mutation, &resp, gqlcli.Var("id", T9527)) 70 71 require.NotEmpty(t, resp.Todo) 72 t.Logf("%+v", resp.Todo) 73 }) 74 75 t.Run("mutation.updateTodo", func(t *testing.T) { 76 var resp struct { 77 Todo Todo `json:"updateTodo"` 78 } 79 80 mutation := ` 81 mutation updateTodo($id: ID!) { 82 updateTodo(input: {id: $id, userID:"uid", text:"9527.Updated"}){ 83 id,text,done 84 } 85 } 86 ` 87 c.MustPost(mutation, &resp, gqlcli.Var("id", T9527)) 88 89 require.NotEmpty(t, resp.Todo) 90 t.Logf("%+v", resp.Todo) 91 }) 92 93 t.Run("query.todos should contain T9527", func(t *testing.T) { 94 var resp struct { 95 Todos []Todo `json:"todos"` 96 } 97 query := ` 98 query todos { 99 todos(ids:["T9527"],types:[TypeA,TypeB],text2:["text1","text2"],done2:[true,false]) { 100 id,text,done 101 } 102 } 103 ` 104 c.MustPost(query, &resp) 105 106 require.NotEmpty(t, resp.Todos) 107 for _, v := range resp.Todos { 108 if v.ID == T9527 { 109 return 110 } 111 } 112 require.Fail(t, "T9527 not found") 113 t.Logf("%+v", resp.Todos) 114 }) 115 116 t.Run("mutation.deleteTodo", func(t *testing.T) { 117 var resp struct { 118 Result bool `json:"deleteTodo"` 119 } 120 121 mutation := ` 122 mutation deleteTodo($id: ID!) { 123 deleteTodo(id: $id) 124 } 125 ` 126 c.MustPost(mutation, &resp, gqlcli.Var("id", T9527)) 127 128 require.NotEmpty(t, resp.Result) 129 t.Logf("%+v", resp.Result) 130 }) 131 132 t.Run("query.todos should not contain T9527", func(t *testing.T) { 133 var resp struct { 134 Todos []Todo `json:"todos"` 135 } 136 query := ` 137 query todos { 138 todos(types:[TypeA,TypeB],text2:["text1","text2"],done2:[true,false]) { 139 id,text,done 140 } 141 } 142 ` 143 c.MustPost(query, &resp) 144 145 require.NotEmpty(t, resp.Todos) 146 for _, v := range resp.Todos { 147 require.NotEqual(t, v.ID, T9527) 148 } 149 t.Logf("%+v", resp.Todos) 150 }) 151 } 152 153 func FoundT9527(data []Todo) bool { 154 for _, v := range data { 155 if v.ID == T9527 { 156 return true 157 } 158 } 159 return false 160 } 161 162 func TestTodos_REST(t *testing.T) { 163 s := engine.NewMockServer(&Resolver{}) 164 c := restcli.New(s, restcli.Prefix("")) 165 166 t.Run("rest.createTodo", func(t *testing.T) { 167 var resp struct { 168 Code int 169 Message string 170 Data Todo 171 } 172 173 payload := `{"input": {"userID":"uid", "text":"$text"}}` 174 err := c.Post("/api/v1/todos", &resp, restcli.Body(payload)) 175 _ = c.Post("/api/v1/todos", &resp, restcli.Body(payload)) 176 _ = c.Post("/api/v1/todos", &resp, restcli.Body(payload)) 177 require.Nil(t, err) 178 require.NotEmpty(t, resp.Data) 179 180 t.Logf("%+v", resp.Data) 181 }) 182 183 t.Run("rest.completeTodo", func(t *testing.T) { 184 var resp struct { 185 Code int 186 Message string 187 Data Todo 188 } 189 190 err := c.Post("/api/v1/todo/T9527/complete", &resp) 191 require.Nil(t, err) 192 require.NotEmpty(t, resp.Data) 193 194 t.Logf("%+v", resp.Data) 195 }) 196 197 t.Run("rest.completeTodos", func(t *testing.T) { 198 var resp struct { 199 Code int 200 Message string 201 Data Todo 202 } 203 204 payload := `{"ids": ["uid", "text"]}` 205 err := c.Post("/api/v1/todos/bulk-complete", &resp, restcli.Body(payload)) 206 require.Nil(t, err) 207 208 t.Logf("%+v", resp.Data) 209 }) 210 211 t.Run("rest.updateTodo", func(t *testing.T) { 212 var resp struct { 213 Code int 214 Message string 215 Data Todo 216 } 217 218 payload := `{"input": {"userID":"uid", "text":"$text.Updated"}}` 219 err := c.Put("/api/v1/todo/T9527", &resp, restcli.Body(payload)) 220 require.Nil(t, err) 221 require.NotEmpty(t, resp.Data) 222 223 t.Logf("%+v", resp.Data) 224 }) 225 226 t.Run("rest.todos: filter by ids", func(t *testing.T) { 227 var resp struct { 228 Code int 229 Message string 230 Data []Todo 231 } 232 233 err := c.Get("/api/v1/todos?ids=T9527&types=TypeA,TypeB&text2=text1,text2&done2=true,false", &resp) 234 require.Nil(t, err) 235 if !FoundT9527(resp.Data) { 236 t.Fail() 237 } 238 t.Logf("%+v", resp.Data) 239 240 err = c.Get("/api/v1/todos?ids=FakeID111,FakeID222&types=TypeA,TypeB&text2=text1,text2&done2=true,false", &resp) 241 require.Nil(t, err) 242 if FoundT9527(resp.Data) { 243 t.Fail() 244 } 245 t.Logf("%+v", resp.Data) 246 }) 247 248 t.Run("rest.deleteTodo", func(t *testing.T) { 249 err := c.Delete("/api/v1/todo/T9527", nil) 250 require.Nil(t, err) 251 }) 252 253 t.Run("rest.todos: check after deletion", func(t *testing.T) { 254 var resp struct { 255 Code int 256 Message string 257 Data []Todo 258 } 259 260 err := c.Get("/api/v1/todos?ids=T9527,T666&types=TypeA,TypeB&text2=text1,text2&done2=true,false", &resp) 261 require.Nil(t, err) 262 263 for _, v := range resp.Data { 264 if v.ID == T9527 { 265 t.Fail() 266 } 267 } 268 t.Logf("%+v", resp.Data) 269 }) 270 } 271 272 func TestTodos_POST(t *testing.T) { 273 s := engine.NewMockServer(&Resolver{}) 274 c := restcli.New(s, restcli.Prefix("")) 275 276 t.Run("rest.createTodo", func(t *testing.T) { 277 var resp struct { 278 Code int 279 Message string 280 Data Todo 281 } 282 283 payload := `{"userID":"uid", "text":"$text"}` // 方式1 284 //payload = fmt.Sprintf(`{"input": %s}`, payload) // 方式2 285 286 err := c.Post("/api/v1/todos", &resp, restcli.Body(payload)) 287 require.Nil(t, err) 288 require.NotEmpty(t, resp.Data) 289 290 t.Logf("%+v", resp) 291 }) 292 } 293 294 func TestTodos_GET(t *testing.T) { 295 s := engine.NewMockServer(&Resolver{}) 296 c := restcli.New(s, restcli.Prefix("")) 297 298 t.Run("rest.todos", func(t *testing.T) { 299 var resp struct { 300 Code int 301 Message string 302 Data []Todo 303 } 304 305 err := c.Get("/api/v1/todos?ids=T9527&types=TypeA,TypeB&text2=text1,text2&done2=true,false", &resp) 306 require.Nil(t, err) 307 308 for _, v := range resp.Data { 309 if v.ID == T9527 { 310 t.Fail() 311 } 312 } 313 t.Logf("%+v", resp.Data) 314 }) 315 } 316 317 func TestTodos_PUT(t *testing.T) { 318 s := engine.NewMockServer(&Resolver{}) 319 c := restcli.New(s, restcli.Prefix("")) 320 321 t.Run("rest.updateTodo", func(t *testing.T) { 322 var resp struct { 323 //nolint:staticcheck,revive // ignore SA5008: unknown JSON option "squash" 324 Todo `json:",squash"` 325 } 326 327 payload := `{"text":"$text.Updated"}` 328 329 err := c.Put("/api/v1/todo/T9527?userID=uid", &resp, restcli.Body(payload)) 330 require.NotNil(t, err) 331 t.Logf("%+v", err) 332 require.Contains(t, err.Error(), "http 404") 333 }) 334 } 335 336 func TestTodos_DELETE(t *testing.T) { 337 s := engine.NewMockServer(&Resolver{}) 338 c := restcli.New(s, restcli.Prefix("")) 339 340 t.Run("rest.deleteTodo", func(t *testing.T) { 341 err := c.Delete("/api/v1/todo/T9527", nil) 342 require.NotNil(t, err) 343 t.Logf("%+v", err) 344 require.Contains(t, err.Error(), "http 404") 345 }) 346 347 t.Run("rest.deleteTodoByUser", func(t *testing.T) { 348 err := c.Delete("/api/v1/todos/?userID=T9527", nil) 349 require.NotNil(t, err) 350 t.Logf("%+v", err) 351 require.Contains(t, err.Error(), "http 404") 352 }) 353 } 354 355 func TestCreateTodo_REST(t *testing.T) { 356 s := engine.NewMockServer(&Resolver{}) 357 c := restcli.New(s, restcli.Prefix("")) 358 359 t.Run("测试普通字符串", func(t *testing.T) { 360 var resp struct { 361 Code int `json:"code"` 362 Message string `json:"message"` 363 Data Todo `json:"data"` 364 } 365 366 payload := `{"input": {"userID":"uid", "text":"$text"}}` 367 err := c.Post("/api/v1/todos", &resp, restcli.Body(payload)) 368 require.Nil(t, err) 369 require.NotEmpty(t, resp.Data) 370 371 t.Logf("%+v", resp.Data) 372 }) 373 374 t.Run("测试字符串,包含一个ASCII字符\u0001", func(t *testing.T) { 375 var resp struct { 376 Code int 377 Message string 378 Data Todo 379 } 380 381 var input struct { 382 Input model.NewTodoInput `json:"input"` 383 } 384 done := true 385 input.Input.UserID = "uid" 386 input.Input.Done = &done 387 input.Input.Text = "测试字符串,包含一个ASCII字符\u0001" 388 payload, err := json.Marshal(input) 389 require.Nil(t, err) 390 391 err = c.Post("/api/v1/todos", &resp, restcli.Body(string(payload))) 392 require.Nil(t, err) 393 require.NotEmpty(t, resp.Data) 394 395 t.Logf("%+v", resp.Data) 396 }) 397 398 t.Run("测试字符串,包含多个ASCII字符\u0001", func(t *testing.T) { 399 var resp struct { 400 Code int 401 Message string 402 Data Todo 403 } 404 405 var input struct { 406 Input model.NewTodoInput `json:"input"` 407 } 408 409 done := true 410 input.Input.Done = &done 411 input.Input.UserID = "uid" 412 input.Input.Text = "\u0001测试字符串,包含多个\u0001包含ASCII字符\u0001" 413 payload, err := json.Marshal(input) 414 require.Nil(t, err) 415 416 err = c.Post("/api/v1/todos", &resp, restcli.Body(string(payload))) 417 require.Nil(t, err) 418 require.NotEmpty(t, resp.Data) 419 420 t.Logf("%+v", resp.Data) 421 }) 422 423 t.Run("测试字符串存储json对象,对象包含ASCII字符\u0001", func(t *testing.T) { 424 var resp struct { 425 Code int 426 Message string 427 Data Todo 428 } 429 430 var obj struct { 431 Seq int `json:"seq"` 432 Data string `json:"data"` 433 } 434 435 obj.Seq = 100 436 obj.Data = "\u0001json对象包含多个\u0001ASCII字符\u0001" 437 byteData, err := json.Marshal(obj) 438 439 var input struct { 440 Input model.NewTodoInput `json:"input"` 441 } 442 443 done := true 444 input.Input.Done = &done 445 input.Input.UserID = "uid" 446 input.Input.Text = "字符串存储json对象\u0001," + string(byteData) 447 448 payload, err := json.Marshal(input) 449 require.Nil(t, err) 450 451 err = c.Post("/api/v1/todos", &resp, restcli.Body(string(payload))) 452 require.Nil(t, err) 453 require.NotEmpty(t, resp.Data) 454 455 t.Logf("%+v", resp.Data) 456 }) 457 }