// 功能描述:推荐归属过滤、分类排序、分页和公开资料回归;版本:1.0.0。 package user import ( "encoding/json" "fmt" "github.com/DATA-DOG/go-sqlmock" "strings" "testing" ) // 两种入口均排除本人购物车,收藏入口另排除本人收藏;ORDER CASE必须实际进入查询。 func TestRecommendationsScopeAndPagination(t *testing.T) { for _, source := range []string{"cart", "favorites"} { t.Run(source, func(t *testing.T) { mock := primaryTestDB(t) expectAddressAccount(mock) query := `SELECT .* FROM "ec_product" WHERE \(status = \$1 AND stock_quantity > 0\).*NOT EXISTS .*ec_cart c.*c.user_account_id = \$2 AND c.status = \$3` if source == "favorites" { query += `.*NOT EXISTS .*ec_favorite f.*f.user_account_id = \$4 AND f.status = \$5` } query += `.*ec_product"\."deleted_at" IS NULL ORDER BY CASE WHEN EXISTS .*FROM ec_` if source == "cart" { query += `cart` } else { query += `favorite` } query += ` r .*r.user_account_id = .*AND r.status = .*THEN 0 ELSE 1 END, ec_product.updated_at desc, ec_product.identity LIMIT .* OFFSET` expected := mock.ExpectQuery(query) if source == "favorites" { expected.WithArgs(1, 1, 1, 1, 1, 1, 1, 3, 2) } else { expected.WithArgs(1, 1, 1, 1, 1, 3, 2) } expected.WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "name", "ec_category_id", "price_amount", "stock_quantity"}).AddRow(3, "p3", "减压阀", 7, 3900, 4).AddRow(5, "p5", "燃气管", 7, 5900, 9).AddRow(6, "p6", "更多", 8, 1900, 2)) mock.ExpectQuery(`SELECT .* FROM "ec_product_image".*ec_product_id IN`).WithArgs(3, 5, 6, 1).WillReturnRows(sqlmock.NewRows([]string{"ec_product_id", "image_uri"}).AddRow(3, "/cover.png").AddRow(5, "/five.png").AddRow(3, "/other.png")) mock.ExpectQuery(`SELECT .* FROM "ec_category".*id IN`).WithArgs(7, 8, 1).WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(7, "配件")) ctx, response := addressContext("", "") ctx.Request.URL.RawQuery = fmt.Sprintf("source=%s&page=2&page_size=2", source) Recommendations(ctx) var body struct { Code int `json:"code"` Details struct { Items []map[string]any `json:"items"` Page int `json:"page"` More bool `json:"has_more"` } `json:"details"` } if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil { t.Fatal(err) } if body.Code != 0 || body.Details.Page != 2 || !body.Details.More || len(body.Details.Items) != 2 { t.Fatal(response.Body.String()) } first := body.Details.Items[0] if first["identity"] != "p3" || first["image_url"] != "/cover.png" || first["category_name"] != "配件" || first["price_amount"] != float64(3900) || body.Details.Items[1]["image_url"] != "/five.png" { t.Fatal(response.Body.String()) } for _, row := range body.Details.Items { for _, key := range []string{"id", "ec_category_id", "user_account_id"} { if _, ok := row[key]; ok { t.Fatal("泄露内部字段", key) } } } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }) } } func TestRecommendationsEmptyAndInvalidQuery(t *testing.T) { for _, query := range []string{"", "source=other", "page=0", "page=10001", "page_size=21", "page_size=-1"} { t.Run(query, func(t *testing.T) { mock := primaryTestDB(t) expectAddressAccount(mock) if query == "" { mock.ExpectQuery(`SELECT .* FROM "ec_product".*ORDER BY CASE`).WithArgs(1, 1, 1, 1, 1, 3).WillReturnRows(sqlmock.NewRows([]string{"id"})) } ctx, response := addressContext("", "") ctx.Request.URL.RawQuery = query Recommendations(ctx) if query == "" { if !strings.Contains(response.Body.String(), `"items":[]`) || !strings.Contains(response.Body.String(), `"has_more":false`) { t.Fatal(response.Body.String()) } } else if !strings.Contains(response.Body.String(), `"code":1704`) { t.Fatal(response.Body.String()) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }) } }