- TestFaqContentDecode, TestFaqModel and TestFaqSerialize (Docker-free): content as list, JSON text or row-keyed object, required export fields, the model contract, output sanitizing, null description, question text and the slug shape - TestFaqAPI and TestFaqImport (Postgres): active duplicate behind an inactive lower id, default locale per is_default, a disabled default, malformed slugs, keyed content order, malformed rows aborting before any write
77 lines
3.5 KiB
Go
77 lines
3.5 KiB
Go
package console
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestFaqContentDecode pins the Docker-free decoding of a Winter FAQ export
|
|
// row: content as a JSON list, as JSON text, as an object keyed by row
|
|
// number (in numeric order), or null; the required name, slug and locale
|
|
// code; PHP-shaped flags, ids and timestamps; and the refusals.
|
|
func TestFaqContentDecode(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
raw string
|
|
want string
|
|
}{
|
|
{`[{"question":"Q1","answer":"<p>A1</p>"},{"question":"Q2","answer":null}]`, `[map[answer:<p>A1</p> question:Q1] map[answer:<nil> question:Q2]]`},
|
|
{`"[{\"question\":\"Q\",\"answer\":\"A\"}]"`, `[map[answer:A question:Q]]`},
|
|
{`{"10":{"question":"ten"},"2":{"question":"two"},"1":{"question":"one"}}`, `[map[question:one] map[question:two] map[question:ten]]`},
|
|
{`[]`, `[]`},
|
|
{`null`, `[]`},
|
|
{`"null"`, `[]`},
|
|
{``, `[]`},
|
|
} {
|
|
got, err := decodeContent(json.RawMessage(tc.raw))
|
|
if err != nil {
|
|
t.Fatalf("decodeContent(%s): %v", tc.raw, err)
|
|
}
|
|
if s := fmt.Sprint(got); s != tc.want {
|
|
t.Fatalf("decodeContent(%s) = %s, want %s", tc.raw, s, tc.want)
|
|
}
|
|
}
|
|
for _, raw := range []string{`"x"`, `5`, `[1,2]`, `{"a":1}`, `true`} {
|
|
if _, err := decodeContent(json.RawMessage(raw)); err == nil {
|
|
t.Fatalf("decodeContent(%s) accepted", raw)
|
|
}
|
|
}
|
|
|
|
row := func(raw string) map[string]json.RawMessage {
|
|
t.Helper()
|
|
var out map[string]json.RawMessage
|
|
if err := json.Unmarshal([]byte(raw), &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return out
|
|
}
|
|
faq, code, err := decodeFAQ(row(`{"id": "12", "name": "Shipping", "slug": "shipping", "locale_code": "pl", "is_active": "1", "description": 5, "content": [{"question":"Q","answer":"A"}], "created_at": "2024-03-01 10:00:00.000000", "updated_at": null}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if faq.ID != 12 || faq.Name != "Shipping" || faq.Slug != "shipping" || code != "pl" || !faq.IsActive || faq.Description == nil || *faq.Description != "5" ||
|
|
len(faq.Items()) != 1 || !faq.Content.Valid || faq.CreatedAt == nil || !faq.CreatedAt.Equal(time.Date(2024, 3, 1, 10, 0, 0, 0, time.UTC)) || faq.UpdatedAt != nil {
|
|
t.Fatalf("decoded = %+v code=%s", faq, code)
|
|
}
|
|
if faq, _, err := decodeFAQ(row(`{"id": 1, "name": "A", "slug": "a", "locale_code": "en", "is_active": 0, "content": null}`)); err != nil || faq.IsActive || faq.Content.Valid || faq.Description != nil {
|
|
t.Fatalf("null content row = %+v %v", faq, err)
|
|
}
|
|
for raw, want := range map[string]string{
|
|
`{"id": 0, "name": "A", "slug": "a", "locale_code": "en"}`: "id",
|
|
`{"name": "A", "slug": "a", "locale_code": "en"}`: "id",
|
|
`{"id": 1, "slug": "a", "locale_code": "en"}`: "name is missing",
|
|
`{"id": 1, "name": "A", "slug": " ", "locale_code": "en"}`: "slug is missing",
|
|
`{"id": 1, "name": "A", "slug": "a"}`: "locale_code is missing",
|
|
`{"id": 1, "name": "A", "slug": "a", "locale_code": "en", "content": "oops"}`: "content",
|
|
`{"id": 1, "name": "A", "slug": "a", "locale_code": "en", "is_active": [1]}`: "is_active",
|
|
`{"id": 1, "name": "A", "slug": "a", "locale_code": "en", "created_at": "x"}`: "created_at",
|
|
`{"id": 1, "name": "A", "slug": "a", "locale_code": "en", "description": {}}`: "description",
|
|
} {
|
|
if _, _, err := decodeFAQ(row(raw)); err == nil || !strings.Contains(err.Error(), want) {
|
|
t.Errorf("decodeFAQ(%s) = %v, want %q", raw, err, want)
|
|
}
|
|
}
|
|
}
|