fix(api): bound claim wire fields end to end
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -38,13 +39,16 @@ func TestValidateNormalizesManualDraft(t *testing.T) {
|
||||
func TestValidateRejectsInvalidFieldsAndURLs(t *testing.T) {
|
||||
base := Form{CreateKey: testKey, Title: "title", ProductURL: "https://mobile.yangkeduo.com/goods.html?goods_id=1", SKUColor: "black", SKUSize: "M", Quantity: "1", MaxTotalPrice: "1"}
|
||||
for name, update := range map[string]func(*Form){
|
||||
"empty title": func(form *Form) { form.Title = " " },
|
||||
"long color": func(form *Form) { form.SKUColor = string(make([]rune, maxSKUText+1)) },
|
||||
"fraction quantity": func(form *Form) { form.Quantity = "1.5" },
|
||||
"zero quantity": func(form *Form) { form.Quantity = "0" },
|
||||
"too many decimals": func(form *Form) { form.MaxTotalPrice = "1.234" },
|
||||
"trailing decimal": func(form *Form) { form.MaxTotalPrice = "1." },
|
||||
"zero money": func(form *Form) { form.MaxTotalPrice = "0.00" },
|
||||
"empty title": func(form *Form) { form.Title = " " },
|
||||
"invalid utf8 title": func(form *Form) { form.Title = string([]byte{0xff}) },
|
||||
"long title": func(form *Form) { form.Title = strings.Repeat("😀", MaxTitleCodePoints+1) },
|
||||
"long color": func(form *Form) { form.SKUColor = string(make([]rune, maxSKUText+1)) },
|
||||
"invalid utf8 size": func(form *Form) { form.SKUSize = string([]byte{0xff}) },
|
||||
"fraction quantity": func(form *Form) { form.Quantity = "1.5" },
|
||||
"zero quantity": func(form *Form) { form.Quantity = "0" },
|
||||
"too many decimals": func(form *Form) { form.MaxTotalPrice = "1.234" },
|
||||
"trailing decimal": func(form *Form) { form.MaxTotalPrice = "1." },
|
||||
"zero money": func(form *Form) { form.MaxTotalPrice = "0.00" },
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
form := base
|
||||
@@ -67,6 +71,7 @@ func TestValidateRejectsInvalidFieldsAndURLs(t *testing.T) {
|
||||
"https://mobile.yangkeduo.com/goods.html?goods_id=1%26goods_id%3D2",
|
||||
"https://mobile.yangkeduo.com/goods.html?goods_id=1;uin=bad",
|
||||
"https://mobile.yangkeduo.com/other.html?goods_id=1",
|
||||
"https://mobile.yangkeduo.com/goods.html?goods_id=" + strings.Repeat("1", MaxGoodsIDCharacters+1),
|
||||
} {
|
||||
if _, ok := CanonicalGoodsID(value); ok {
|
||||
t.Fatalf("CanonicalGoodsID accepted %q", value)
|
||||
@@ -75,19 +80,33 @@ func TestValidateRejectsInvalidFieldsAndURLs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNormalizeMoneyBoundaries(t *testing.T) {
|
||||
for value, want := range map[string]string{"1": "1.00", "1.2": "1.20", "000.01": "0.01", "999999999999999999": "999999999999999999.00"} {
|
||||
maximum := strings.Repeat("9", MaxMoneyASCIICharacters-3) + ".00"
|
||||
for value, want := range map[string]string{"1": "1.00", "1.2": "1.20", "000.01": "0.01", "999999999999999999": "999999999999999999.00", maximum: maximum} {
|
||||
got, ok := normalizeMoney(value)
|
||||
if !ok || got != want {
|
||||
t.Fatalf("normalizeMoney(%q) = (%q, %t), want (%q, true)", value, got, ok, want)
|
||||
}
|
||||
}
|
||||
for _, value := range []string{"0", "0.0", "0.00", "1.", ".1", "1.000", "-1", "1e2", " 1"} {
|
||||
for _, value := range []string{"0", "0.0", "0.00", "1.", ".1", "1.000", "-1", "1e2", " 1", strings.Repeat("9", MaxMoneyASCIICharacters-2) + ".00"} {
|
||||
if got, ok := normalizeMoney(value); ok {
|
||||
t.Fatalf("normalizeMoney(%q) = %q, want rejection", value, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAcceptsWorstLegalUnicodeFieldBounds(t *testing.T) {
|
||||
goodsID := strings.Repeat("1", MaxGoodsIDCharacters)
|
||||
draft, validation := Validate(Form{
|
||||
CreateKey: testKey, Title: strings.Repeat("😀", MaxTitleCodePoints),
|
||||
ProductURL: CanonicalURL(goodsID), SKUColor: strings.Repeat("色", MaxSKUTextCodePoints),
|
||||
SKUSize: strings.Repeat("码", MaxSKUTextCodePoints), Quantity: "1",
|
||||
MaxTotalPrice: strings.Repeat("9", MaxMoneyASCIICharacters-3) + ".00",
|
||||
})
|
||||
if !validation.Valid() || !ValidTaskWireFields(draft.Title, draft.GoodsID, draft.SKUColor, draft.SKUSize, draft.MaxTotalPrice) {
|
||||
t.Fatalf("worst legal draft = %#v, validation = %#v", draft, validation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCreateKeyIsUUIDv4(t *testing.T) {
|
||||
key, err := NewCreateKey()
|
||||
if err != nil {
|
||||
@@ -157,6 +176,36 @@ func TestSQLiteStoreCreatesListsAndHandlesIdempotency(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSQLiteStoreRejectsInvalidDraftAtPersistenceBoundary(t *testing.T) {
|
||||
mutations := map[string]func(*Draft){
|
||||
"untrimmed title": func(draft *Draft) { draft.Title = " title" },
|
||||
"invalid utf8 title": func(draft *Draft) { draft.Title = string([]byte{0xff}) },
|
||||
"long title": func(draft *Draft) { draft.Title = strings.Repeat("😀", MaxTitleCodePoints+1) },
|
||||
"long color": func(draft *Draft) { draft.SKUColor = strings.Repeat("色", MaxSKUTextCodePoints+1) },
|
||||
"long size": func(draft *Draft) { draft.SKUSize = strings.Repeat("码", MaxSKUTextCodePoints+1) },
|
||||
"long goods id": func(draft *Draft) { draft.GoodsID = strings.Repeat("1", MaxGoodsIDCharacters+1) },
|
||||
"long money": func(draft *Draft) { draft.MaxTotalPrice = strings.Repeat("1", MaxMoneyASCIICharacters-2) + ".00" },
|
||||
}
|
||||
for name, mutate := range mutations {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
database := migratedDatabase(t)
|
||||
store, err := NewSQLiteStore(database)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
draft := testDraft(testKey, "title")
|
||||
mutate(&draft)
|
||||
if _, err := store.CreateDraft(context.Background(), draft); !errors.Is(err, ErrInvalidDraft) {
|
||||
t.Fatalf("CreateDraft error = %v, want ErrInvalidDraft", err)
|
||||
}
|
||||
var count int
|
||||
if err := database.QueryRow("SELECT COUNT(*) FROM tasks").Scan(&count); err != nil || count != 0 {
|
||||
t.Fatalf("tasks after invalid create = %d, err %v", count, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSQLiteStoreRollsBackFailedCreate(t *testing.T) {
|
||||
database := migratedDatabase(t)
|
||||
store, err := NewSQLiteStore(database)
|
||||
|
||||
Reference in New Issue
Block a user