Escape state_dir in the db_url derived from it (closes #290)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
When db_url is omitted, pixa derives it from state_dir, and it put the directory name into a file: URL as it was, so a state_dir holding %, ? or # opened another file and lost the journal mode. The derived URL is now built with net/url, which percent-encodes the directory name; an ordinary path gives the same URL as before. When the path written into the URL starts with //, as for a state_dir of / or one starting with //, the empty host is kept so SQLite does not read the first name in the path as a host. Model: opus-5-5
This commit was merged in pull request #292.
This commit is contained in:
@@ -135,6 +135,104 @@ func TestDefaultDBURLOpensTheDatabaseInWALMode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDerivedDBURLOpensStateDirHoldingURLCharacters opens the db_url
|
||||
// derived from a state_dir whose name holds %41, ? and #, and checks that
|
||||
// the database is state.sqlite3 in that directory, in WAL mode. Put into
|
||||
// a file: URL as it is, the name would lose its meaning: %41 is read as
|
||||
// A, and the path ends at the ?, which also loses the journal mode.
|
||||
func TestDerivedDBURLOpensStateDirHoldingURLCharacters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stateDir := filepath.Join(t.TempDir(), "a%41b?c#d")
|
||||
|
||||
err := os.Mkdir(stateDir, 0o750)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create %q: %v", stateDir, err)
|
||||
}
|
||||
|
||||
// Quoted so YAML takes the whole name as it is.
|
||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: '"+stateDir+"'\n")
|
||||
if err != nil {
|
||||
t.Fatalf("config with state_dir %q should be valid, got: %v", stateDir, err)
|
||||
}
|
||||
|
||||
t.Logf("state_dir %q gives db_url %q", stateDir, c.DBURL)
|
||||
|
||||
checkDBURLOpensFileInWALMode(t, c.DBURL, filepath.Join(stateDir, "state.sqlite3"))
|
||||
}
|
||||
|
||||
// TestDerivedDBURLOpensStateDirStartingWithTwoSlashes opens the db_url
|
||||
// derived from a state_dir that starts with //, and checks that the
|
||||
// database is state.sqlite3 in that directory, in WAL mode. Written right
|
||||
// after file:, such a name gives a URL starting file://, and SQLite reads
|
||||
// the first directory name as a host and refuses to open it.
|
||||
func TestDerivedDBURLOpensStateDirStartingWithTwoSlashes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
stateDir := "/" + dir
|
||||
|
||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: "+stateDir+"\n")
|
||||
if err != nil {
|
||||
t.Fatalf("config with state_dir %q should be valid, got: %v", stateDir, err)
|
||||
}
|
||||
|
||||
t.Logf("state_dir %q gives db_url %q", stateDir, c.DBURL)
|
||||
|
||||
checkDBURLOpensFileInWALMode(t, c.DBURL, filepath.Join(dir, "state.sqlite3"))
|
||||
}
|
||||
|
||||
// TestDerivedDBURLForRootStateDir checks the db_url derived from a
|
||||
// state_dir of /. The path written into the URL is then
|
||||
// //state.sqlite3, and written right after file: it gives a URL starting
|
||||
// file://, where SQLite reads state.sqlite3 as a host and refuses to open
|
||||
// it. The test only checks the URL, since it cannot write to /.
|
||||
func TestDerivedDBURLForRootStateDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c, err := configFromYAML(t, signingKeyLine+"state_dir: /\n")
|
||||
if err != nil {
|
||||
t.Fatalf("config with state_dir / should be valid, got: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("state_dir / gives db_url %q", c.DBURL)
|
||||
|
||||
wantDBURL := "file:////state.sqlite3?_pragma=journal_mode(WAL)"
|
||||
if c.DBURL != wantDBURL {
|
||||
t.Errorf("DBURL = %q, want %q", c.DBURL, wantDBURL)
|
||||
}
|
||||
}
|
||||
|
||||
// checkDBURLOpensFileInWALMode opens dbURL with the SQLite driver pixad
|
||||
// uses, and checks that the database is in WAL mode and is the file
|
||||
// dbFile.
|
||||
func checkDBURLOpensFileInWALMode(t *testing.T, dbURL, dbFile string) {
|
||||
t.Helper()
|
||||
|
||||
db, err := sql.Open("sqlite", dbURL)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open %q: %v", dbURL, err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
var journalMode string
|
||||
|
||||
err = db.QueryRowContext(t.Context(), "PRAGMA journal_mode").Scan(&journalMode)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read the journal mode of %q: %v", dbURL, err)
|
||||
}
|
||||
|
||||
if journalMode != "wal" {
|
||||
t.Errorf("journal mode of %q = %q, want wal", dbURL, journalMode)
|
||||
}
|
||||
|
||||
_, err = os.Stat(dbFile)
|
||||
if err != nil {
|
||||
t.Errorf("opening %q did not create %q: %v", dbURL, dbFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExplicitValidValuesAreUsed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user