Files
wehub-resource-sync 5357c39144
Race tests / Go race tests (ubuntu-22.04) (push) Waiting to run
Fuzzer / Run Fuzzer (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

108 lines
3.9 KiB
Go

// Copyright 2026 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"database/sql"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
driver "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils/sql_server_driver"
)
// startServerOverOrphanedDir seeds a healthy database named good, lets |makeOrphan| place an orphaned database
// directory in the data directory, and starts a server that must log |logMatch| and still serve the healthy
// database. It returns a connection to the server and the data directory. The orphaned state is reproduced
// directly rather than by racing a real kill, so tests stay deterministic.
func startServerOverOrphanedDir(t *testing.T, makeOrphan func(rs driver.RepoStore), logMatch string) (*sql.Conn, string) {
t.Helper()
u, err := driver.NewDoltUser()
require.NoError(t, err)
t.Cleanup(func() { u.Cleanup() })
rs, err := u.MakeRepoStore()
require.NoError(t, err)
good, err := rs.MakeRepo("good")
require.NoError(t, err)
require.NoError(t, good.DoltExec("sql", "-q", "CREATE TABLE t (i INT PRIMARY KEY)"))
makeOrphan(rs)
var ports DynamicResources
ports.global = &GlobalPorts
ports.t = t
server := MakeServer(t, rs, &driver.Server{
Args: []string{"--port", `{{get_port "server_one"}}`},
DynamicPort: "server_one",
LogMatches: []string{logMatch},
}, &ports)
server.DBName = "good"
db, err := server.DB(driver.Connection{User: "root"})
require.NoError(t, err)
t.Cleanup(func() { db.Close() })
conn, err := db.Conn(t.Context())
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })
// An orphaned directory must not break INFORMATION_SCHEMA queries server-wide.
var count int
require.NoError(t, conn.QueryRowContext(t.Context(),
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'good'").Scan(&count))
require.Equal(t, 1, count)
return conn, rs.Dir
}
func TestServerIgnoresInterruptedCreateDatabase(t *testing.T) {
// See https://github.com/dolthub/dolt/issues/11206
t.Parallel()
conn, dataDir := startServerOverOrphanedDir(t, func(rs driver.RepoStore) {
_, err := rs.MakeRepo("pending")
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(rs.Dir, "pending", ".dolt_safe_to_ignore"), nil, 0o644))
}, "skipping in-progress database directory")
var served string
require.NoError(t, conn.QueryRowContext(t.Context(),
"SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata WHERE schema_name IN ('good', 'pending')").Scan(&served))
require.Equal(t, "good", served, "only the healthy database must be served")
// Ignoring is non-destructive because another process could be completing the same create.
_, statErr := os.Stat(filepath.Join(dataDir, "pending"))
require.NoError(t, statErr, "orphaned directory should be left on disk, not deleted")
}
func TestServerSkipsIncompleteDatabaseDirectory(t *testing.T) {
// See https://github.com/dolthub/dolt/issues/11206
t.Parallel()
startServerOverOrphanedDir(t, func(rs driver.RepoStore) {
// A database whose creation was interrupted leaves behind storage files
// without the repo state that marks it ready.
incompleteNoms := filepath.Join(rs.Dir, "incomplete", ".dolt", "noms")
require.NoError(t, os.MkdirAll(incompleteNoms, 0o755))
require.NoError(t, os.WriteFile(filepath.Join(incompleteNoms, "LOCK"), nil, 0o644))
}, "skipping incomplete database directory")
}