127 lines
4.2 KiB
Go
127 lines
4.2 KiB
Go
package store
|
|
|
|
import (
|
|
"io/fs"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// Diese Tests laufen immer, auch ohne PostgreSQL. Die Tests gegen eine echte
|
|
// PostgreSQL-Datenbank stehen in postgres_test.go.
|
|
|
|
func TestPlatzhalterNummerieren(t *testing.T) {
|
|
faelle := []struct {
|
|
name, ein, aus string
|
|
}{
|
|
{"ohne Platzhalter", `SELECT 1`, `SELECT 1`},
|
|
{"der Reihe nach", `SELECT a FROM t WHERE x = ? AND y = ?`, `SELECT a FROM t WHERE x = $1 AND y = $2`},
|
|
{"IN-Liste", `WHERE id IN (?, ?, ?) LIMIT ?`, `WHERE id IN ($1, $2, $3) LIMIT $4`},
|
|
{"Text bleibt", `WHERE a = 'Wie geht das?' AND b = ?`, `WHERE a = 'Wie geht das?' AND b = $1`},
|
|
{"verdoppeltes Hochkomma", `WHERE a = 'It''s ?' AND b = ?`, `WHERE a = 'It''s ?' AND b = $1`},
|
|
{"Bezeichner bleibt", `SELECT "was?" FROM t WHERE a = ?`, `SELECT "was?" FROM t WHERE a = $1`},
|
|
{"Zeilenkommentar", "SELECT a -- warum?\nFROM t WHERE b = ?", "SELECT a -- warum?\nFROM t WHERE b = $1"},
|
|
{"Blockkommentar", `SELECT /* wer? */ a FROM t WHERE b = ?`, `SELECT /* wer? */ a FROM t WHERE b = $1`},
|
|
{"offener Text am Ende", `WHERE a = ? AND b = 'kaputt?`, `WHERE a = $1 AND b = 'kaputt?`},
|
|
{"Kommentar am Ende ohne Zeilenumbruch", `WHERE a = ? -- fertig?`, `WHERE a = $1 -- fertig?`},
|
|
{"zweistellig", `(?,?,?,?,?,?,?,?,?,?,?)`, `($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`},
|
|
}
|
|
for _, f := range faelle {
|
|
t.Run(f.name, func(t *testing.T) {
|
|
if got := platzhalterNummerieren(f.ein); got != f.aus {
|
|
t.Errorf("\nein: %s\nist: %s\nsoll: %s", f.ein, got, f.aus)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSQLiteRebindLaesstAbfrageUnveraendert(t *testing.T) {
|
|
abfrage := `SELECT a FROM t WHERE x = ?`
|
|
if got := (sqliteDialekt{}).rebind(abfrage); got != abfrage {
|
|
t.Errorf("SQLite hat die Abfrage verändert: %s", got)
|
|
}
|
|
}
|
|
|
|
// TestMigrationsordnerGleich prüft, dass sqlite/ und postgres/ dieselben
|
|
// Migrationen enthalten. Eine Migration, die nur in einem Ordner liegt, würde
|
|
// die beiden Datenbanken unbemerkt auseinanderlaufen lassen — siehe
|
|
// AUSBAUSTUFEN.md 4.10.
|
|
//
|
|
// Ob die Migrationen auch dasselbe SCHEMA ergeben, prüft TestSchemaGleich
|
|
// (braucht PostgreSQL).
|
|
func TestMigrationsordnerGleich(t *testing.T) {
|
|
for _, quelle := range []struct {
|
|
fsys fs.FS
|
|
dir string
|
|
}{
|
|
{migrationsFS, "migrations"},
|
|
{controlMigrationsFS, "migrations_control"},
|
|
} {
|
|
dateien := func(unterordner string) []string {
|
|
eintraege, err := fs.ReadDir(quelle.fsys, quelle.dir+"/"+unterordner)
|
|
if err != nil {
|
|
t.Fatalf("%s/%s: %v", quelle.dir, unterordner, err)
|
|
}
|
|
var namen []string
|
|
for _, e := range eintraege {
|
|
namen = append(namen, e.Name())
|
|
}
|
|
return namen
|
|
}
|
|
|
|
sqlite := dateien((sqliteDialekt{}).migrationsUnterordner())
|
|
postgres := dateien((postgresDialekt{}).migrationsUnterordner())
|
|
if len(sqlite) == 0 {
|
|
t.Errorf("%s: keine Migrationen gefunden", quelle.dir)
|
|
}
|
|
if !slices.Equal(sqlite, postgres) {
|
|
t.Errorf("%s: Ordner unterscheiden sich\nsqlite: %v\npostgres: %v", quelle.dir, sqlite, postgres)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWorkspaceSchema(t *testing.T) {
|
|
got, err := WorkspaceSchema("mue9bh3x-1c0e1120")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "munin_ws_mue9bh3x_1c0e1120" {
|
|
t.Errorf("Schema = %q", got)
|
|
}
|
|
|
|
// Eine echte ID muss immer passen.
|
|
if _, err := WorkspaceSchema(NewID()); err != nil {
|
|
t.Errorf("NewID() ergibt keinen gültigen Schemanamen: %v", err)
|
|
}
|
|
|
|
// Alles, was nicht wie eine ID aussieht, darf nie in einen Schemanamen.
|
|
for _, kaputt := range []string{"", "Groß", "a b", "a;DROP", `a"b`, "../x", "ä"} {
|
|
if _, err := WorkspaceSchema(kaputt); err == nil {
|
|
t.Errorf("WorkspaceSchema(%q) hätte abgelehnt werden müssen", kaputt)
|
|
}
|
|
}
|
|
|
|
// PostgreSQL würde längere Namen stillschweigend kürzen.
|
|
lang := ""
|
|
for len(lang) < 60 {
|
|
lang += "abcdefghij"
|
|
}
|
|
if _, err := WorkspaceSchema(lang); err == nil {
|
|
t.Error("zu lange ID wurde nicht abgelehnt")
|
|
}
|
|
}
|
|
|
|
func TestTextLiteral(t *testing.T) {
|
|
faelle := map[string]string{
|
|
"Vertrieb": `E'Vertrieb'`,
|
|
"O'Brien": `E'O''Brien'`,
|
|
`C:\temp`: `E'C:\\temp'`,
|
|
`\'; DROP x; --`: `E'\\''; DROP x; --'`,
|
|
"mit\x00Null": `E'mitNull'`,
|
|
"Ärger & Öl": `E'Ärger & Öl'`,
|
|
}
|
|
for ein, soll := range faelle {
|
|
if got := textLiteral(ein); got != soll {
|
|
t.Errorf("textLiteral(%q) = %s, erwartet %s", ein, got, soll)
|
|
}
|
|
}
|
|
}
|