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

423 lines
11 KiB
Go

// Copyright 2020 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 testcommands
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
cmd "github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions"
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures"
"github.com/dolthub/dolt/go/libraries/utils/config"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
"github.com/dolthub/dolt/go/store/datas"
"github.com/dolthub/dolt/go/store/datas/pull"
"github.com/dolthub/dolt/go/store/types"
)
const (
homePrefix = "home"
)
type MultiRepoTestSetup struct {
envs map[string]*env.DoltEnv
Remote string
DoltDBs map[string]*doltdb.DoltDB
DbNames []string
Root string
DbPaths map[string]string
Home string
Remotes map[string]env.Remote
Errhand func(args ...interface{})
}
const (
name = "billy bob"
email = "bigbillieb@fake.horse"
defaultBranch = "main"
)
func NewMultiRepoTestSetup(errhand func(args ...interface{})) *MultiRepoTestSetup {
dir, err := os.MkdirTemp("", "")
if err != nil {
errhand(err)
}
homeDir, err := os.MkdirTemp(dir, homePrefix)
if err != nil {
errhand(err)
}
return &MultiRepoTestSetup{
envs: make(map[string]*env.DoltEnv),
Remotes: make(map[string]env.Remote),
DoltDBs: make(map[string]*doltdb.DoltDB, 0),
DbNames: make([]string, 0),
Root: dir,
Home: homeDir,
DbPaths: make(map[string]string, 0),
Errhand: errhand,
}
}
func (mr *MultiRepoTestSetup) GetEnv(dbName string) *env.DoltEnv {
return mr.envs[dbName]
}
func (mr *MultiRepoTestSetup) homeProv() (string, error) {
return mr.Home, nil
}
func (mr *MultiRepoTestSetup) Close() {
for _, db := range mr.DoltDBs {
err := db.Close()
if err != nil {
mr.Errhand(err)
}
}
}
func (mr *MultiRepoTestSetup) Cleanup(dbName string) {
os.RemoveAll(mr.Root)
}
func (mr *MultiRepoTestSetup) NewDB(dbName string) {
ctx := context.Background()
repo := filepath.Join(mr.Root, dbName)
os.Mkdir(repo, os.ModePerm)
err := os.Chdir(repo)
if err != nil {
mr.Errhand(err)
}
// TODO sometimes tempfiles scrubber is racy with tempfolder deleter
dEnv := env.LoadWithoutDB(context.Background(), mr.homeProv, filesys.LocalFS, doltdb.LocalDirDoltDB, "test")
if err != nil {
mr.Errhand("Failed to initialize environment:" + err.Error())
}
cfg, _ := dEnv.Config.GetConfig(env.GlobalConfig)
cfg.SetStrings(map[string]string{
config.UserNameKey: name,
config.UserEmailKey: email,
})
err = dEnv.InitRepo(context.Background(), types.Format_DOLT, name, email, defaultBranch)
if err != nil {
mr.Errhand("Failed to initialize environment:" + err.Error())
}
ddb, err := doltdb.LoadDoltDB(ctx, types.Format_DOLT, doltdb.LocalDirDoltDB, filesys.LocalFS)
if err != nil {
mr.Errhand("Failed to initialize environment:" + err.Error())
}
dEnv = env.Load(context.Background(), mr.homeProv, filesys.LocalFS, doltdb.LocalDirDoltDB, "test")
mr.envs[dbName] = dEnv
mr.DoltDBs[dbName] = ddb
mr.DbNames = append(mr.DbNames, dbName)
mr.DbPaths[dbName] = repo
}
func (mr *MultiRepoTestSetup) NewRemote(remoteName string) {
remote := filepath.Join(mr.Root, remoteName)
os.Mkdir(remote, os.ModePerm)
remotePath := fmt.Sprintf("file:///%s", remote)
rem := env.NewRemote(remoteName, remotePath, nil)
for _, dEnv := range mr.envs {
dEnv.RepoState.AddRemote(rem)
dEnv.RepoState.Save(filesys.LocalFS)
}
mr.Remotes[remoteName] = rem
}
func (mr *MultiRepoTestSetup) NewBranch(ctx context.Context, dbName, branchName string) {
dEnv := mr.envs[dbName]
err := actions.CreateBranchWithStartPt(context.Background(), dEnv.DbData(ctx), branchName, "head", false, nil)
if err != nil {
mr.Errhand(err)
}
}
func (mr *MultiRepoTestSetup) CheckoutBranch(dbName, branchName string) {
dEnv := mr.envs[dbName]
var err error
cliCtx, err := cmd.NewArgFreeCliContext(context.Background(), dEnv, dEnv.FS)
if err != nil {
mr.Errhand(err)
}
defer cliCtx.Close()
queryist, err := cliCtx.QueryEngine(context.Background())
if err != nil {
mr.Errhand(err)
}
err = dprocedures.MoveWorkingSetToBranch(queryist.Context, branchName, false, false, true)
if err != nil {
mr.Errhand(err)
}
}
func (mr *MultiRepoTestSetup) CloneDB(fromRemote, dbName string) {
ctx := context.Background()
cloneDir := filepath.Join(mr.Root, dbName)
r := mr.GetRemote(fromRemote)
srcDB, err := r.GetRemoteDB(ctx, types.Format_DOLT, mr.envs[dbName])
if err != nil {
mr.Errhand(err)
}
dEnv := env.LoadWithoutDB(context.Background(), mr.homeProv, filesys.LocalFS, doltdb.LocalDirDoltDB, "test")
dEnv, err = actions.EnvForClone(ctx, srcDB.Format(), r, cloneDir, dEnv.FS, dEnv.Version, mr.homeProv)
if err != nil {
mr.Errhand(err)
}
pull.WithDiscardingStatsCh(func(statsCh chan pull.Stats) {
err = actions.CloneRemote(ctx, srcDB, r.Name, "", false, -1, dEnv, statsCh)
})
if err != nil {
mr.Errhand(err)
}
err = dbfactory.ClearDatabaseInProgress(dEnv.FS)
if err != nil {
mr.Errhand(err)
}
wd, err := os.Getwd()
if err != nil {
mr.Errhand(err)
}
err = os.Chdir(cloneDir)
if err != nil {
mr.Errhand(err)
}
defer os.Chdir(wd)
ddb := dEnv.DoltDB(ctx)
mr.envs[dbName] = dEnv
mr.DoltDBs[dbName] = ddb
mr.DbNames = append(mr.DbNames, dbName)
mr.DbPaths[dbName] = cloneDir
}
func (mr *MultiRepoTestSetup) GetRemote(remoteName string) env.Remote {
rem, ok := mr.Remotes[remoteName]
if !ok {
mr.Errhand("remote not found")
}
return rem
}
func (mr *MultiRepoTestSetup) GetDB(dbName string) *doltdb.DoltDB {
db, ok := mr.DoltDBs[dbName]
if !ok {
mr.Errhand("db not found")
}
return db
}
func (mr *MultiRepoTestSetup) CommitWithWorkingSet(dbName string) *doltdb.Commit {
ctx := sql.NewEmptyContext()
dEnv := mr.envs[dbName]
ws, err := dEnv.WorkingSet(ctx)
if err != nil {
panic("couldn't get working set: " + err.Error())
}
prevHash, err := ws.HashOf()
if err != nil {
panic("couldn't get working set: " + err.Error())
}
var mergeParentCommits []*doltdb.Commit
if ws.MergeActive() {
mergeParentCommits = []*doltdb.Commit{ws.MergeState().Commit()}
}
roots, err := dEnv.Roots(ctx)
if err != nil {
panic("couldn't get roots: " + err.Error())
}
ident := datas.CommitIdent{Name: name, Email: email}
commitStagedProps := actions.CommitStagedProps{
Message: "auto commit",
AllowEmpty: true,
Author: ident,
Committer: ident,
}
pendingCommit, err := actions.GetCommitStaged(ctx, doltdb.SimpleTableResolver{}, roots, ws, mergeParentCommits, dEnv.DbData(ctx).Ddb, commitStagedProps)
if err != nil {
panic("pending commit error: " + err.Error())
}
headRef, err := dEnv.RepoStateReader().CWBHeadRef(ctx)
if err != nil {
panic("couldn't get working set: " + err.Error())
}
commit, err := dEnv.DoltDB(ctx).CommitWithWorkingSet(
ctx,
headRef,
ws.Ref(),
pendingCommit,
ws.WithStagedRoot(pendingCommit.Roots.Staged).WithWorkingRoot(pendingCommit.Roots.Working).ClearMerge(),
prevHash,
doltdb.TodoWorkingSetMeta(),
nil,
)
if err != nil {
panic("couldn't commit: " + err.Error())
}
return commit
}
func (mr *MultiRepoTestSetup) CreateTable(ctx context.Context, dbName, tblName string) {
dEnv := mr.envs[dbName]
sch, err := dtestutils.Schema()
if err != nil {
mr.Errhand(err)
}
if err := createTestTable(dEnv, tblName, sch); err != nil {
mr.Errhand(err)
}
}
func (mr *MultiRepoTestSetup) StageAll(dbName string) {
dEnv := mr.envs[dbName]
ctx := context.Background()
roots, err := dEnv.Roots(ctx)
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to get roots: %s", dbName))
}
roots, err = actions.StageAllTables(ctx, roots, true)
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to stage tables: %s", dbName))
}
err = dEnv.UpdateRoots(ctx, roots)
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to update roots: %s", dbName))
}
}
func (mr *MultiRepoTestSetup) PushToRemote(dbName, remoteName, branchName string) {
ctx := context.Background()
dEnv := mr.envs[dbName]
ap := cli.CreatePushArgParser()
apr, err := ap.Parse([]string{remoteName, branchName})
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error()))
}
targets, remote, err := env.NewPushOpts(ctx, apr, dEnv.RepoStateReader(), dEnv.DoltDB(ctx), false, false, false, false)
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error()))
}
remoteDB, err := remote.GetRemoteDB(ctx, dEnv.DoltDB(ctx).ValueReadWriter().Format(), mr.envs[dbName])
if err != nil {
mr.Errhand(actions.HandleInitRemoteStorageClientErr(remote.Name, remote.Url, err))
}
tmpDir, err := dEnv.TempTableFilesDir()
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to access .dolt directory: %s", err.Error()))
}
pushOptions := &env.PushOptions[context.Context]{
Targets: targets,
Remote: remote,
Rsr: dEnv.RepoStateReader(),
Rsw: dEnv.RepoStateWriter(),
SrcDb: dEnv.DoltDB(ctx),
DestDb: remoteDB,
TmpDir: tmpDir,
}
pull.WithDiscardingStatsCh(func(statsCh chan pull.Stats) {
_, err = actions.DoPush(ctx, pushOptions, statsCh)
})
if err != nil {
mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error()))
}
}
// createTestTable creates a new test table with the name, schema, and rows given.
func createTestTable(dEnv *env.DoltEnv, tableName string, sch schema.Schema) error {
ctx := context.Background()
vrw := dEnv.DoltDB(ctx).ValueReadWriter()
ns := dEnv.DoltDB(ctx).NodeStore()
idx, err := durable.NewEmptyPrimaryIndex(ctx, vrw, ns, sch)
if err != nil {
return err
}
tbl, err := doltdb.NewTable(ctx, vrw, ns, sch, idx, nil, nil)
if err != nil {
return err
}
sch, err = tbl.GetSchema(ctx)
if err != nil {
return err
}
root, err := dEnv.WorkingRoot(ctx)
if err != nil {
return fmt.Errorf("%w: %v", doltdb.ErrNomsIO, err)
}
newRoot, err := root.PutTable(ctx, doltdb.TableName{Name: tableName}, tbl)
if err != nil {
return err
}
rootHash, err := root.HashOf()
if err != nil {
return err
}
newRootHash, err := newRoot.HashOf()
if err != nil {
return err
}
if rootHash == newRootHash {
return nil
}
return dEnv.UpdateWorkingRoot(ctx, newRoot)
}