79 lines
3.0 KiB
Go
79 lines
3.0 KiB
Go
package research
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestStoredAuthorShapes(t *testing.T) {
|
|
for _, test := range []struct {
|
|
raw string
|
|
want []string
|
|
}{
|
|
{`[]`, []string{}},
|
|
{`[["Buhrman","Harry","","CWI"],["Family","Given","Jr.","University"]]`, []string{"Harry Buhrman", "Given Family Jr."}},
|
|
{`["The BABAR Collaboration","B. Aubert"]`, []string{"The BABAR Collaboration", "B. Aubert"}},
|
|
{`[["Balázs","C.",""],["Berger","E. L.",""],["Smith","Ada","Jr."]]`, []string{"C. Balázs", "E. L. Berger", "Ada Smith Jr."}},
|
|
{`[" 张三 ",["Only family"],["","Only given",""],""]`, []string{"张三", "Only family", "Only given"}},
|
|
} {
|
|
got, err := decodeAuthors(test.raw)
|
|
if err != nil || !reflect.DeepEqual(got, test.want) {
|
|
t.Fatalf("%s: %#v, %v", test.raw, got, err)
|
|
}
|
|
encoded, _ := json.Marshal(got)
|
|
if string(encoded) == "null" {
|
|
t.Fatal("missing authors must remain an array")
|
|
}
|
|
}
|
|
for _, raw := range []string{`broken`, `{"unexpected":"object"}`, `[42]`, `[["a","b","c","d","e"]]`} {
|
|
if _, err := decodeAuthors(raw); err == nil {
|
|
t.Fatalf("accepted unsupported authors %s", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQueryValuesRemainBound(t *testing.T) {
|
|
query := `graph'); DROP TABLE papers; -- OR author:*`
|
|
where, args := whereClause(Filters{Query: query, Topic: "cs.LG", SearchIn: "text"})
|
|
if strings.Contains(where, query) || !reflect.DeepEqual(args, []any{query, "cs.LG"}) {
|
|
t.Fatalf("unbound user input: %s %#v", where, args)
|
|
}
|
|
if !strings.Contains(where, "plainto_tsquery('english'::regconfig,$1)") || !strings.Contains(where, "t.value=$2") {
|
|
t.Fatalf("unexpected search contract: %s", where)
|
|
}
|
|
where, args = whereClause(Filters{Query: "Ada Smith", SearchIn: "author"})
|
|
if !strings.Contains(where, "p.authors_json") || strings.Contains(where, "p.abstract") || !reflect.DeepEqual(args, []any{"Ada Smith"}) {
|
|
t.Fatalf("author search includes unrelated text: %s", where)
|
|
}
|
|
where, args = whereClause(Filters{Topic: "cs.AI"})
|
|
if !strings.Contains(where, "t.value=$1") || !reflect.DeepEqual(args, []any{"cs.AI"}) {
|
|
t.Fatalf("topic-only binding: %s %#v", where, args)
|
|
}
|
|
where, args = whereClause(Filters{})
|
|
if where != "" || len(args) != 0 {
|
|
t.Fatal("empty filters changed the corpus")
|
|
}
|
|
}
|
|
|
|
func TestSchemaCannotEscapeQualification(t *testing.T) {
|
|
for _, name := range []string{"", `research";DROP SCHEMA public;--`, "a.b", "Public", strings.Repeat("a", 37)} {
|
|
if _, err := NewPostgresRepository(&sql.DB{}, name); err == nil {
|
|
t.Fatalf("accepted schema %q", name)
|
|
}
|
|
}
|
|
if _, err := NewPostgresRepository(nil, "research"); err == nil {
|
|
t.Fatal("accepted nil database")
|
|
}
|
|
r, err := NewPostgresRepository(&sql.DB{}, "research_v2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := r.query(`SELECT p.id FROM {schema}papers p JOIN {schema}source_records s ON s.paper_id=p.id`)
|
|
if got != `SELECT p.id FROM "research_v2".papers p JOIN "research_v2".source_records s ON s.paper_id=p.id` {
|
|
t.Fatalf("unqualified corpus tables: %s", got)
|
|
}
|
|
}
|