github.com/ravendb/ravendb-go-client@v0.0.0-20240229102137-4474ee7aa0fa/tests/raven_test_driver_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"crypto/tls"
     7  	"crypto/x509"
     8  	"encoding/pem"
     9  	"fmt"
    10  	"github.com/hashicorp/go-multierror"
    11  	"github.com/ravendb/ravendb-go-client"
    12  	"github.com/stretchr/testify/assert"
    13  	"io"
    14  	"io/ioutil"
    15  	"math/rand"
    16  	"net/http"
    17  	"net/url"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"runtime/debug"
    22  	"runtime/pprof"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"sync/atomic"
    27  	"testing"
    28  	"time"
    29  )
    30  
    31  const (
    32  	RAVENDB_TEST_PORT_START int32 = 10_000
    33  	LOOPBACK                      = "127.0.0.1"
    34  	LOCALHOST                     = "localhost"
    35  )
    36  
    37  var (
    38  	// if 1 - no cluster
    39  	// should be 3, 5, 7 etc.
    40  	// can be changed via NODES_IN_CLUSTER env variable
    41  	numClusterNodes = 1
    42  
    43  	// a value in range 0..100
    44  	// it's a percentage chance that we'll kill the server
    45  	// when getting a store for a sub-test
    46  	// 0 means killing is disabled, 5 means it's a 5% chance
    47  	// 100 or more means it's certain
    48  	// can be changed via KILL_SERVER_CHANCE env variable
    49  	randomlyKillServersChance = 0
    50  
    51  	// can be changed via SHUFFLE_CLUSTER_NODES=true env variable
    52  	shuffleClusterNodes = false
    53  
    54  	ravendbWindowsDownloadURL = "https://hibernatingrhinos.com/downloads/RavenDB%20for%20Windows%20x64/54000" // for local usage
    55  
    56  	ravenWindowsZipPath = "ravendb-latest.zip"
    57  )
    58  
    59  type ravenProcess struct {
    60  	cmd          *exec.Cmd
    61  	pid          int
    62  	stdoutReader io.ReadCloser
    63  	stderrReader io.ReadCloser
    64  
    65  	// auto-detected url on which to contact the server
    66  	uri string
    67  }
    68  
    69  // Note: Java's RemoteTestBase is folded into RavenTestDriver
    70  type RavenTestDriver struct {
    71  	documentStores sync.Map // *DocumentStore => bool
    72  
    73  	dbNameCounter int32 // atomic
    74  
    75  	store           *ravendb.DocumentStore
    76  	serverProcesses []*ravenProcess
    77  
    78  	isSecure bool
    79  
    80  	customize func(*ravendb.DatabaseRecord)
    81  
    82  	profData    bytes.Buffer
    83  	isProfiling bool
    84  }
    85  
    86  var (
    87  	// if true, enables flaky tests
    88  	// can be enabled by setting ENABLE_FLAKY_TESTS env variable to "true"
    89  	enableFlakyTests = false
    90  
    91  	// if true, enable failing tests
    92  	// can be enabled by setting ENABLE_FAILING_TESTS env variable to "true"
    93  	enableFailingTests   = false
    94  	testsWereInitialized bool
    95  	muInitializeTests    sync.Mutex
    96  
    97  	ravendbServerExePath string
    98  
    99  	// passed to the server as --Security.Certificate.Path
   100  	certificatePath string
   101  
   102  	caCertificate     *x509.Certificate
   103  	clientCertificate *tls.Certificate
   104  
   105  	nextPort = RAVENDB_TEST_PORT_START
   106  )
   107  
   108  func must(err error) {
   109  	if err != nil {
   110  		panic(err.Error())
   111  	}
   112  }
   113  
   114  func panicIf(cond bool, format string, args ...interface{}) {
   115  	if cond {
   116  		err := fmt.Errorf(format, args...)
   117  		must(err)
   118  	}
   119  }
   120  
   121  var (
   122  	balanceBehaviors = []ravendb.ReadBalanceBehavior{
   123  		// ravendb.ReadBalanceBehaviorNone,
   124  		ravendb.ReadBalanceBehaviorRoundRobin,
   125  		ravendb.ReadBalanceBehaviorFastestNode,
   126  	}
   127  )
   128  
   129  func pickRandomBalanceBehavior() ravendb.ReadBalanceBehavior {
   130  	n := rand.Intn(len(balanceBehaviors))
   131  	return balanceBehaviors[n]
   132  }
   133  
   134  func killServer(proc *ravenProcess) {
   135  	if proc.cmd.ProcessState != nil && proc.cmd.ProcessState.Exited() {
   136  		fmt.Printf("RavenDB process has already exited with '%s'\n", proc.cmd.ProcessState)
   137  	}
   138  	err := proc.cmd.Process.Kill()
   139  	if err != nil {
   140  		fmt.Printf("cmd.Process.Kill() failed with '%s'\n", err)
   141  	} else {
   142  		s := strings.Join(proc.cmd.Args, " ")
   143  		fmt.Printf("RavenDB process %d terminated \nConfiguration arguments: '%s'\n on '%s'\n", proc.pid, s, proc.uri)
   144  	}
   145  }
   146  
   147  func getNextPort() int {
   148  	n := atomic.AddInt32(&nextPort, 1)
   149  	return int(n)
   150  }
   151  
   152  func startRavenServer(secure bool) (*ravenProcess, error) {
   153  	args, err := getServerConfiguration(secure)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	cmd := exec.Command(ravendbServerExePath, args...)
   159  	stdoutReader, err := cmd.StdoutPipe()
   160  	stderrReader, err := cmd.StderrPipe()
   161  
   162  	if false && ravenServerVerbose {
   163  		cmd.Stderr = os.Stderr
   164  		// cmd.StdoutPipe() sets cmd.Stdout to a pipe writer
   165  		// we multi-plex it into os.Stdout
   166  		// TODO: this doesn't seem to work. It makes reading from stdoutReader
   167  		// immediately fail. Maybe it's becuse writer returned by
   168  		// os.Pipe() (cmd.Stdout) blocks and MultiWriter() doesn't
   169  		cmd.Stdout = io.MultiWriter(cmd.Stdout, os.Stdout)
   170  	}
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	err = cmd.Start()
   175  	if err != nil {
   176  		fmt.Printf("exec.Command(%s, %v) failed with %s\n", ravendbServerExePath, args, err)
   177  		return nil, err
   178  	}
   179  
   180  	proc := &ravenProcess{
   181  		cmd:          cmd,
   182  		stdoutReader: stdoutReader,
   183  		stderrReader: stderrReader,
   184  		pid:          cmd.Process.Pid,
   185  	}
   186  
   187  	// parse stdout of the server to extract server listening port from line:
   188  	// Server available on: http://127.0.0.1:50386
   189  	wantedPrefix := "Server available on: "
   190  	scanner := bufio.NewScanner(stdoutReader)
   191  	startTime := time.Now()
   192  	var outputCopy bytes.Buffer
   193  	for scanner.Scan() {
   194  		dur := time.Since(startTime)
   195  		if dur > 3*time.Minute {
   196  			break
   197  		}
   198  		s := scanner.Text()
   199  		if ravenServerVerbose {
   200  			fmt.Printf("server: %s\n", s)
   201  		}
   202  		outputCopy.WriteString(s + "\n")
   203  		if !strings.HasPrefix(s, wantedPrefix) {
   204  			continue
   205  		}
   206  		s = strings.TrimPrefix(s, wantedPrefix)
   207  		proc.uri = strings.TrimSpace(s)
   208  		break
   209  	}
   210  	if scanner.Err() != nil {
   211  		fmt.Printf("startRavenServer: scanner.Err() returned '%s'\n", err)
   212  		killServer(proc)
   213  		return nil, scanner.Err()
   214  	}
   215  	if proc.uri == "" {
   216  		s := string(outputCopy.Bytes())
   217  		errorStr, _ := ioutil.ReadAll(stderrReader)
   218  		fmt.Printf("startRavenServer: couldn't detect start url. Server output: %s\nError:\n%s\n", s, string(errorStr))
   219  		killServer(proc)
   220  		return nil, fmt.Errorf("Unable to start server")
   221  	}
   222  	if ravenServerVerbose {
   223  		go func() {
   224  			_, err = io.Copy(os.Stdout, stdoutReader)
   225  			if !(err == nil || err == io.EOF) {
   226  				fmt.Printf("io.Copy() failed with %s\n", err)
   227  			}
   228  		}()
   229  	}
   230  
   231  	time.Sleep(time.Millisecond * 100) // TODO: probably not necessary
   232  
   233  	return proc, nil
   234  }
   235  
   236  func getServerConfiguration(secure bool) ([]string, error) {
   237  	var httpUrl, tcpUrl url.URL
   238  	httpPort := getNextPort()
   239  	tcpPort := getNextPort()
   240  	if secure {
   241  		httpUrl = url.URL{
   242  			Host:   LOCALHOST + ":" + strconv.Itoa(httpPort),
   243  			Scheme: "https",
   244  		}
   245  		tcpUrl = url.URL{
   246  			Host:   LOCALHOST + ":" + strconv.Itoa(tcpPort),
   247  			Scheme: "tcp",
   248  		}
   249  	} else {
   250  		httpUrl = url.URL{
   251  			Host:   LOOPBACK + ":" + strconv.Itoa(httpPort),
   252  			Scheme: "http",
   253  		}
   254  		tcpUrl = url.URL{
   255  			Host:   LOOPBACK + ":" + strconv.Itoa(tcpPort),
   256  			Scheme: "tcp",
   257  		}
   258  	}
   259  
   260  	args := []string{
   261  		"--RunInMemory=true",
   262  		"--License.Eula.Accepted=true",
   263  		"--Setup.Mode=None",
   264  		"--Testing.ParentProcessId=" + getProcessId(),
   265  	}
   266  
   267  	if secure {
   268  		secureArgs := []string{
   269  			"--PublicServerUrl=" + httpUrl.String(),
   270  			"--PublicServerUrl.Tcp=" + tcpUrl.String(),
   271  			"--ServerUrl=https://0.0.0.0:" + strconv.Itoa(httpPort),
   272  			"--ServerUrl.Tcp=tcp://0.0.0.0:" + strconv.Itoa(tcpPort),
   273  			"--Security.Certificate.Path=" + certificatePath,
   274  		}
   275  		args = append(args, secureArgs...)
   276  	} else {
   277  		unsecureArgs := []string{
   278  			"--ServerUrl=" + httpUrl.String(),
   279  			"--ServerUrl.Tcp=" + tcpUrl.String(),
   280  		}
   281  		args = append(args, unsecureArgs...)
   282  	}
   283  	return args, nil
   284  }
   285  
   286  func runServersMust(n int, secure bool) ([]*ravenProcess, error) {
   287  	var wg sync.WaitGroup
   288  	errorsChannel := make(chan error, n)
   289  	if secure {
   290  		n = 1
   291  	}
   292  	var procs []*ravenProcess
   293  	for i := 0; i < n; i++ {
   294  		wg.Add(1)
   295  		go func(secureCopy bool) {
   296  			proc, err := startRavenServer(secureCopy)
   297  			if err != nil {
   298  				errorsChannel <- err
   299  			}
   300  			args := strings.Join(proc.cmd.Args, " ")
   301  			fmt.Printf("Started server '%s' on port '%s' pid: %d\n", args, proc.uri, proc.pid)
   302  			procs = append(procs, proc)
   303  			wg.Done()
   304  		}(secure)
   305  	}
   306  	wg.Wait()
   307  	close(errorsChannel)
   308  
   309  	var result error
   310  
   311  	for err := range errorsChannel {
   312  		result = multierror.Append(result, err)
   313  	}
   314  
   315  	return procs, result
   316  }
   317  
   318  func setupRevisions(store *ravendb.DocumentStore, purgeOnDelete bool, minimumRevisionsToKeep int64) (*ravendb.ConfigureRevisionsOperationResult, error) {
   319  
   320  	revisionsConfiguration := &ravendb.RevisionsConfiguration{}
   321  	defaultCollection := &ravendb.RevisionsCollectionConfiguration{}
   322  	defaultCollection.PurgeOnDelete = purgeOnDelete
   323  	defaultCollection.MinimumRevisionsToKeep = minimumRevisionsToKeep
   324  
   325  	revisionsConfiguration.DefaultConfig = defaultCollection
   326  	operation := ravendb.NewConfigureRevisionsOperation(revisionsConfiguration)
   327  
   328  	err := store.Maintenance().Send(operation)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	return operation.Command.Result, nil
   334  }
   335  
   336  func (d *RavenTestDriver) customizeDbRecord(dbRecord *ravendb.DatabaseRecord) {
   337  	if d.customize != nil {
   338  		d.customize(dbRecord)
   339  	}
   340  }
   341  
   342  func (d *RavenTestDriver) maybeKillServer() bool {
   343  	if len(d.serverProcesses) < numClusterNodes || len(d.serverProcesses) < 2 {
   344  		return false
   345  	}
   346  	// randomly kill a server
   347  	n := rand.Intn(100)
   348  	if n >= randomlyKillServersChance {
   349  		return false
   350  	}
   351  	// don't kill the first server as it's used by main store to create
   352  	// databases / store for other commands
   353  	idx := 1
   354  	proc := d.serverProcesses[idx]
   355  	d.serverProcesses = append(d.serverProcesses[:idx], d.serverProcesses[idx+1:]...)
   356  	fmt.Printf("Randomly killing a server with pid %d\n", proc.pid)
   357  	killServer(proc)
   358  	return true
   359  }
   360  
   361  func (d *RavenTestDriver) getDocumentStore2(dbName string, waitForIndexingTimeout time.Duration) (*ravendb.DocumentStore, error) {
   362  
   363  	var err error
   364  
   365  	// we're called for each sub-test
   366  	if d.store == nil {
   367  		d.store, err = d.createMainStore()
   368  		if err != nil {
   369  			return nil, err
   370  		}
   371  	} else {
   372  		d.maybeKillServer()
   373  	}
   374  
   375  	n := int(atomic.AddInt32(&d.dbNameCounter, 1))
   376  	name := fmt.Sprintf("%s_%d", dbName, n)
   377  	databaseRecord := ravendb.NewDatabaseRecord()
   378  	databaseRecord.DatabaseName = name
   379  	d.customizeDbRecord(databaseRecord)
   380  
   381  	// replicationFactor seems to be a minimum number of nodes with the data
   382  	// so it must be less than 3 (we have 3 nodes and might kill one, leaving
   383  	// only 2)
   384  	createDatabaseOperation := ravendb.NewCreateDatabaseOperation(databaseRecord, 1)
   385  	err = d.store.Maintenance().Server().Send(createDatabaseOperation)
   386  	if err != nil {
   387  		fmt.Printf("d.store.Maintenance().Server().Send(createDatabaseOperation) failed with %s\n", err)
   388  		return nil, err
   389  	}
   390  
   391  	uris := d.store.GetUrls()
   392  	var store *ravendb.DocumentStore
   393  	if shuffleClusterNodes {
   394  		// randomly shuffle urls so that if we kill a server, there's a higher
   395  		// chance we'll hit it
   396  		var shuffledURIs []string
   397  		r := rand.New(rand.NewSource(time.Now().Unix()))
   398  		for _, i := range r.Perm(len(uris)) {
   399  			shuffledURIs = append(shuffledURIs, uris[i])
   400  		}
   401  		store = ravendb.NewDocumentStore(shuffledURIs, name)
   402  	} else {
   403  		store = ravendb.NewDocumentStore(uris, name)
   404  	}
   405  	conventions := store.GetConventions()
   406  	conventions.ReadBalanceBehavior = pickRandomBalanceBehavior()
   407  	store.SetConventions(conventions)
   408  
   409  	if d.isSecure {
   410  		store.Certificate = clientCertificate
   411  		store.TrustStore = caCertificate
   412  	}
   413  
   414  	// TODO: is over-written by CustomSerializationTest
   415  	// customizeStore(Store);
   416  	d.hookLeakedConnectionCheck(store)
   417  
   418  	d.setupDatabase(store)
   419  	err = store.Initialize()
   420  	if err != nil {
   421  		fmt.Printf("getDocumentStore2: store.Initialize() failed with '%s'\n", err)
   422  		return nil, err
   423  	}
   424  
   425  	fn := func(store *ravendb.DocumentStore) {
   426  		_, ok := d.documentStores.Load(store)
   427  		if !ok {
   428  			// TODO: shouldn't happen?
   429  			return
   430  		}
   431  
   432  		operation := ravendb.NewDeleteDatabasesOperation(store.GetDatabase(), true)
   433  		err = store.Maintenance().Server().Send(operation)
   434  	}
   435  
   436  	store.AddAfterCloseListener(fn)
   437  
   438  	if waitForIndexingTimeout > 0 {
   439  		err = d.waitForIndexing(store, name, waitForIndexingTimeout)
   440  		if err != nil {
   441  			fmt.Printf("getDocumentStore2:  d.waitForIndexing() failed with '%s'\n", err)
   442  			store.Close()
   443  			return nil, err
   444  		}
   445  	}
   446  
   447  	d.documentStores.Store(store, true)
   448  	d.maybeStartProfiling()
   449  
   450  	return store, nil
   451  }
   452  
   453  func (d *RavenTestDriver) hookLeakedConnectionCheck(store *ravendb.DocumentStore) {
   454  	// TODO: no-op for now. Not sure if I have enough info
   455  	// to replicate this functionality in Go
   456  }
   457  
   458  // Note: it's virtual in Java but there's only one implementation
   459  // that is a no-op
   460  func (d *RavenTestDriver) setupDatabase(documentStore *ravendb.DocumentStore) {
   461  	// empty by design
   462  }
   463  
   464  func setupCluster(store *ravendb.DocumentStore) error {
   465  	uris := store.GetUrls()
   466  	if len(uris) < 2 {
   467  		return nil
   468  	}
   469  
   470  	re := store.GetRequestExecutor(store.GetDatabase())
   471  	httpClient, err := re.GetHTTPClient()
   472  	if err != nil {
   473  		fmt.Printf("setupCluster: re.GetHTTPClient() failed with '%s'\n", err)
   474  		return err
   475  	}
   476  	firstServerURL := uris[0]
   477  	for _, uri := range uris[1:] {
   478  		// https://ravendb.net/docs/article-page/4.1/csharp/server/clustering/cluster-api#delete-node-from-the-cluster
   479  		cmdURI := firstServerURL + "/admin/cluster/node?url=" + url.QueryEscape(uri)
   480  		req, err := newHttpPut(cmdURI, nil)
   481  		if err != nil {
   482  			fmt.Printf("setupCluster: newHttpPutt() failed with '%s'\n", err)
   483  		}
   484  		rsp, err := httpClient.Do(req)
   485  		if err != nil {
   486  			fmt.Printf("setupCluster: httpClient.Do() failed with '%s' for url '%s'\n", err, cmdURI)
   487  		}
   488  		defer rsp.Body.Close()
   489  		if rsp.StatusCode >= 400 {
   490  			fmt.Printf("setupCluster: httpClient.Do() returned status code '%s' for url '%s'\n", rsp.Status, cmdURI)
   491  			return fmt.Errorf("setupCluster: httpClient.Do() returned status code '%s' for url '%s'\n", rsp.Status, cmdURI)
   492  		}
   493  
   494  		fmt.Printf("Added node to cluster with '%s', status code: %d\n", cmdURI, rsp.StatusCode)
   495  	}
   496  	return nil
   497  }
   498  
   499  func newHttpPut(uri string, data []byte) (*http.Request, error) {
   500  	var body io.Reader
   501  	if len(data) > 0 {
   502  		body = bytes.NewReader(data)
   503  	}
   504  	req, err := http.NewRequest(http.MethodPut, uri, body)
   505  	if err != nil {
   506  		return nil, err
   507  	}
   508  	req.Header.Add("User-Agent", "ravendb-go-client/4.0.0")
   509  	if len(data) > 0 {
   510  		req.Header.Add("Content-Type", "application/json; charset=UTF-8")
   511  	}
   512  	return req, nil
   513  }
   514  
   515  func (d *RavenTestDriver) createMainStore() (*ravendb.DocumentStore, error) {
   516  	var err error
   517  	panicIf(len(d.serverProcesses) > 0, "len(d.serverProcesses): %d", len(d.serverProcesses))
   518  
   519  	d.serverProcesses, err = runServersMust(numClusterNodes, d.isSecure)
   520  	if err != nil {
   521  		return nil, err
   522  	}
   523  
   524  	var uris []string
   525  	for _, proc := range d.serverProcesses {
   526  		uris = append(uris, proc.uri)
   527  	}
   528  
   529  	mainStoreURLS := uris
   530  	if len(mainStoreURLS) > 1 {
   531  		mainStoreURLS = mainStoreURLS[1:]
   532  	}
   533  	store := ravendb.NewDocumentStore(uris, "test.manager")
   534  
   535  	conventions := store.GetConventions()
   536  	// main store is only used to create databases / other stores
   537  	// so we don't want cluster behavior
   538  	conventions.SetDisableTopologyUpdates(true)
   539  	conventions.ReadBalanceBehavior = pickRandomBalanceBehavior()
   540  
   541  	if d.isSecure {
   542  		store.Certificate = clientCertificate
   543  		store.TrustStore = caCertificate
   544  	}
   545  	err = store.Initialize()
   546  	if err != nil {
   547  		fmt.Printf("createMainStore: store.Initialize() failed with '%s'\n", err)
   548  		store.Close()
   549  		return nil, err
   550  	}
   551  	err = setupCluster(store)
   552  	if err != nil {
   553  		store.Close()
   554  		return nil, err
   555  	}
   556  	return store, nil
   557  }
   558  
   559  func (d *RavenTestDriver) waitForIndexing(store *ravendb.DocumentStore, database string, timeout time.Duration) error {
   560  	return waitForIndexing(store, database, timeout)
   561  }
   562  
   563  func waitForIndexing(store *ravendb.DocumentStore, database string, timeout time.Duration) error {
   564  	admin := store.Maintenance().ForDatabase(database)
   565  	if timeout == 0 {
   566  		timeout = time.Minute
   567  	}
   568  
   569  	sp := time.Now()
   570  	for time.Since(sp) < timeout {
   571  		op := ravendb.NewGetStatisticsOperation("")
   572  		err := admin.Send(op)
   573  		if err != nil {
   574  			return err
   575  		}
   576  		databaseStatistics := op.Command.Result
   577  		isDone := true
   578  		hasError := false
   579  		for _, index := range databaseStatistics.Indexes {
   580  			if index.State == ravendb.IndexStateDisabled {
   581  				continue
   582  			}
   583  			if index.IsStale || strings.HasPrefix(index.Name, ravendb.IndexingSideBySideIndexNamePrefix) {
   584  				isDone = false
   585  			}
   586  			if index.State == ravendb.IndexStateError {
   587  				hasError = true
   588  			}
   589  		}
   590  		if isDone {
   591  			return nil
   592  		}
   593  		if hasError {
   594  			break
   595  		}
   596  		time.Sleep(time.Millisecond * 100)
   597  	}
   598  
   599  	op := ravendb.NewGetIndexErrorsOperation(nil)
   600  	err := admin.Send(op)
   601  	if err != nil {
   602  		return err
   603  	}
   604  	allIndexErrorsText := ""
   605  	/*
   606  		// TODO: port this
   607  		Function<IndexErrors, String> formatIndexErrors = indexErrors -> {
   608  				String errorsListText = Arrays.stream(indexErrors.getErrors()).map(x -> "-" + x).collect(Collectors.joining(System.lineSeparator()));
   609  				return "Index " + indexErrors.GetName() + " (" + indexErrors.getErrors().length + " errors): "+ System.lineSeparator() + errorsListText;
   610  			};
   611  
   612  			if (errors != null && errors.length > 0) {
   613  				allIndexErrorsText = Arrays.stream(errors).map(x -> formatIndexErrors.apply(x)).collect(Collectors.joining(System.lineSeparator()));
   614  			}
   615  	*/
   616  	return ravendb.NewTimeoutError("The indexes stayed stale for more than %s.%s", timeout, allIndexErrorsText)
   617  }
   618  
   619  func (d *RavenTestDriver) killServerProcesses() {
   620  	for _, proc := range d.serverProcesses {
   621  		killServer(proc)
   622  	}
   623  	d.serverProcesses = nil
   624  
   625  	d.store = nil
   626  }
   627  
   628  func (d *RavenTestDriver) getDocumentStoreMust(t *testing.T) *ravendb.DocumentStore {
   629  	d.isSecure = false
   630  	store, err := d.getDocumentStore2("test_db", 0)
   631  	assert.NoError(t, err)
   632  	assert.NotNil(t, store)
   633  	return store
   634  }
   635  
   636  func (d *RavenTestDriver) getSecuredDocumentStoreMust(t *testing.T) *ravendb.DocumentStore {
   637  	d.isSecure = true
   638  	store, err := d.getDocumentStore2("test_db", 0)
   639  	assert.NoError(t, err)
   640  	assert.NotNil(t, store)
   641  	return store
   642  }
   643  
   644  func (d *RavenTestDriver) Close() {
   645  	// fmt.Print("RavenTestDriver.Close()\n")
   646  	// debug.PrintStack()
   647  
   648  	fn := func(key, value interface{}) bool {
   649  		documentStore := key.(*ravendb.DocumentStore)
   650  		documentStore.Close()
   651  		d.documentStores.Delete(key)
   652  		return true
   653  	}
   654  	d.documentStores.Range(fn)
   655  	if d.store != nil {
   656  		d.store.Close()
   657  	}
   658  	d.killServerProcesses()
   659  }
   660  
   661  func shutdownTests() {
   662  	// no-op
   663  }
   664  
   665  func openSessionMust(t *testing.T, store *ravendb.DocumentStore) *ravendb.DocumentSession {
   666  	session, err := store.OpenSession("")
   667  	assert.NoError(t, err)
   668  	assert.NotNil(t, session)
   669  	return session
   670  }
   671  
   672  func openSessionMustWithOptions(t *testing.T, store *ravendb.DocumentStore, options *ravendb.SessionOptions) *ravendb.DocumentSession {
   673  	session, err := store.OpenSessionWithOptions(options)
   674  	assert.NoError(t, err)
   675  	assert.NotNil(t, session)
   676  	return session
   677  }
   678  
   679  func isUpper(c byte) bool {
   680  	return c >= 'A' && c <= 'Z'
   681  }
   682  
   683  // converts "TestIndexesFromClient" => "indexes_from_client"
   684  func testNameToFileName(s string) string {
   685  	s = strings.TrimPrefix(s, "Test")
   686  	lower := strings.ToLower(s)
   687  	var res []byte
   688  	n := len(s)
   689  	for i := 0; i < n; i++ {
   690  		c := s[i]
   691  		if i > 0 && isUpper(c) {
   692  			res = append(res, '_')
   693  		}
   694  		res = append(res, lower[i])
   695  	}
   696  	return string(res)
   697  }
   698  
   699  func getLogDir() string {
   700  	// if this is not full path, raven will put it in it's own Logs directory
   701  	// next to server executable
   702  	cwd, _ := os.Getwd()
   703  	dir, file := filepath.Split(cwd)
   704  	if file != "tests" {
   705  		dir = cwd
   706  	}
   707  	dir = filepath.Join(dir, "logs")
   708  	_ = os.MkdirAll(dir, 0755)
   709  	return dir
   710  }
   711  
   712  func (d *RavenTestDriver) maybeStartProfiling() {
   713  	if !isEnvVarTrue("ENABLE_PROFILING") || d.isProfiling {
   714  		return
   715  	}
   716  	if err := pprof.StartCPUProfile(&d.profData); err != nil {
   717  		fmt.Printf("pprof.StartCPUProfile() failed with '%s'\n", err)
   718  	} else {
   719  		d.isProfiling = true
   720  		fmt.Printf("started cpu profiling\n")
   721  	}
   722  }
   723  
   724  func (d *RavenTestDriver) maybeStopProfiling() {
   725  	if !d.isProfiling {
   726  		return
   727  	}
   728  	pprof.StopCPUProfile()
   729  	path := "cpu.prof"
   730  	pd := d.profData.Bytes()
   731  	err := ioutil.WriteFile(path, pd, 0644)
   732  	if err != nil {
   733  		fmt.Printf("failed to write cpu profile data to '%s'. Error: '%s'\n", path, err)
   734  	} else {
   735  		fmt.Printf("wrote cpu profile data to '%s'\n", path)
   736  	}
   737  }
   738  
   739  // called for every Test* function
   740  func createTestDriver(t *testing.T) *RavenTestDriver {
   741  	fmt.Printf("\nStarting test %s\n", t.Name())
   742  	setupLogging(t)
   743  	driver := &RavenTestDriver{}
   744  	return driver
   745  }
   746  
   747  func destroyDriver(t *testing.T, d *RavenTestDriver) {
   748  	if t.Failed() {
   749  		maybePrintFailedRequestsLog()
   750  	}
   751  	if d != nil {
   752  		d.Close()
   753  	}
   754  	d.maybeStopProfiling()
   755  	finishLogging()
   756  }
   757  
   758  func recoverTest(t *testing.T, destroyDriver func()) {
   759  	r := recover()
   760  	destroyDriver()
   761  	if r != nil {
   762  		fmt.Printf("Panic: '%v'\n", r)
   763  		debug.PrintStack()
   764  		t.Fail()
   765  	}
   766  }
   767  
   768  func downloadServerIfNeededWindows() {
   769  	// hacky: if we're in tests directory, cd .. for duration of this function
   770  	panicIf(ravendbServerExePath != "", "ravendb exe already found in %s", ravendbServerExePath)
   771  
   772  	cwd, err := os.Getwd()
   773  	must(err)
   774  	if strings.HasSuffix(cwd, "tests") {
   775  		path := filepath.Clean(filepath.Join(cwd, ".."))
   776  		err = os.Chdir(path)
   777  		must(err)
   778  		defer func() {
   779  			err := os.Chdir(cwd)
   780  			must(err)
   781  		}()
   782  	}
   783  
   784  	exists := fileExists(ravenWindowsZipPath)
   785  	if !exists {
   786  		fmt.Printf("Downloading %s...", ravendbWindowsDownloadURL)
   787  		startTime := time.Now()
   788  		err = httpDl(ravendbWindowsDownloadURL, ravenWindowsZipPath)
   789  		must(err)
   790  		fmt.Printf(" took %s\n", time.Since(startTime))
   791  	}
   792  	destDir := "RavenDB"
   793  	fmt.Printf("Unzipping %s to %s...", ravenWindowsZipPath, destDir)
   794  	startTime := time.Now()
   795  	err = unzip(ravenWindowsZipPath, destDir)
   796  	must(err)
   797  	fmt.Printf(" took %s\n", time.Since(startTime))
   798  }
   799  
   800  func detectRavendbExePath() string {
   801  	// auto-detect env variables if not explicitly set
   802  	path := os.Getenv("RAVENDB_SERVER_PATH")
   803  
   804  	defer func() {
   805  		if path != "" {
   806  			fmt.Printf("Server exe: %s\n", path)
   807  		}
   808  	}()
   809  
   810  	if fileExists(path) {
   811  		return path
   812  	}
   813  
   814  	cwd, err := os.Getwd()
   815  	must(err)
   816  
   817  	path = filepath.Join(cwd, "..", "RavenDB", "Server", "Raven.Server")
   818  	if isWindows() {
   819  		path += ".exe"
   820  	}
   821  	path = filepath.Clean(path)
   822  	if fileExists(path) {
   823  		return path
   824  	}
   825  
   826  	path = filepath.Join(cwd, "RavenDB", "Server", "Raven.Server")
   827  	if isWindows() {
   828  		path += ".exe"
   829  	}
   830  	path = filepath.Clean(path)
   831  	if fileExists(path) {
   832  		return path
   833  	}
   834  	return ""
   835  }
   836  
   837  func loadTestClientCertificate(path string) *tls.Certificate {
   838  	cert, err := loadCertficateAndKeyFromFile(path)
   839  	must(err)
   840  	return cert
   841  }
   842  
   843  func loadTestCaCertificate(path string) *x509.Certificate {
   844  	certPEM, err := ioutil.ReadFile(path)
   845  	must(err)
   846  	block, _ := pem.Decode([]byte(certPEM))
   847  	panicIf(block == nil, "failed to decode cert PEM from %s", path)
   848  	cert, err := x509.ParseCertificate(block.Bytes)
   849  	must(err)
   850  	return cert
   851  }
   852  
   853  // for CI we set RAVEN_License env variable to dev license, so that
   854  // we can run replication tests. On local machines I have dev license
   855  // as a file raven_license.json
   856  func detectRavenDevLicense() {
   857  	if len(os.Getenv("RAVEN_License")) > 0 {
   858  		fmt.Print("RAVEN_License env variable is set\n")
   859  		return
   860  	}
   861  
   862  	path := os.Getenv("RAVEN_License_Path")
   863  	cwd, err := os.Getwd()
   864  	must(err)
   865  	if !fileExists(path) {
   866  		path = filepath.Clean(filepath.Join(cwd, "..", "raven_license.json"))
   867  		if !fileExists(path) {
   868  			path = filepath.Clean(filepath.Join(cwd, "..", "..", "raven_license.json"))
   869  			if !fileExists(path) {
   870  				fmt.Printf("Replication tests are disabled because RAVEN_License_Path not set and file %s doesn't exist.\n", path)
   871  				return
   872  			}
   873  		}
   874  		_ = os.Setenv("RAVEN_License_Path", path)
   875  		fmt.Printf("Setting RAVEN_License_Path to '%s'\n", path)
   876  	}
   877  }
   878  
   879  // note: in Java for tests marked as @DisabledOn41Server
   880  func isRunningOn41Server() bool {
   881  	v := os.Getenv("RAVENDB_SERVER_VERSION")
   882  	return strings.HasPrefix(v, "4.1")
   883  }
   884  
   885  func initializeTests() {
   886  	muInitializeTests.Lock()
   887  	defer muInitializeTests.Unlock()
   888  	if testsWereInitialized {
   889  		return
   890  	}
   891  
   892  	if !enableFlakyTests && isEnvVarTrue("ENABLE_FLAKY_TESTS") {
   893  		enableFlakyTests = true
   894  		fmt.Printf("Setting enableFlakyTests to true\n")
   895  	}
   896  
   897  	if !enableFailingTests && isEnvVarTrue("ENABLE_FAILING_TESTS") {
   898  		enableFailingTests = true
   899  		fmt.Printf("Setting enableFailingTests to true\n")
   900  	}
   901  
   902  	{
   903  		s := os.Getenv("NODES_IN_CLUSTER")
   904  		n, err := strconv.Atoi(s)
   905  		if err == nil && n > 1 {
   906  			numClusterNodes = n
   907  			fmt.Printf("Setting numClusterNodes=%d from NODES_IN_CLUSTER env variable\n", n)
   908  		}
   909  	}
   910  
   911  	{
   912  		s := os.Getenv("KILL_SERVER_CHANCE")
   913  		n, err := strconv.Atoi(s)
   914  		if err == nil {
   915  			randomlyKillServersChance = n
   916  			fmt.Printf("Setting randomlyKillServersChance=%d from KILL_SERVER_CHANCE env variable\n", n)
   917  		}
   918  	}
   919  
   920  	if !shuffleClusterNodes && isEnvVarTrue("SHUFFLE_CLUSTER_NODES") {
   921  		shuffleClusterNodes = true
   922  		fmt.Printf("Setting shuffleClusterNodes to true because SHUFFLE_CLUSTER_NODES env variable is %s\n", os.Getenv("SHUFFLE_CLUSTER_NODES"))
   923  	}
   924  
   925  	setLoggingStateFromEnv()
   926  	detectRavenDevLicense()
   927  
   928  	ravendbServerExePath = detectRavendbExePath()
   929  	if ravendbServerExePath == "" {
   930  		if isWindows() {
   931  			downloadServerIfNeededWindows()
   932  			ravendbServerExePath = detectRavendbExePath()
   933  		}
   934  	}
   935  
   936  	if ravendbServerExePath == "" {
   937  		fmt.Printf("Didn't find ravendb server exe. Set RAVENDB_SERVER_PATH env variable\n")
   938  		os.Exit(1)
   939  	}
   940  
   941  	// find top-level directory
   942  	// wd should be "tests" sub-directory
   943  	wd, _ := os.Getwd()
   944  	rootDir := filepath.Clean(filepath.Join(wd, ".."))
   945  	path := filepath.Join(rootDir, "certs", "server.pfx")
   946  	if !fileExists(path) {
   947  		fmt.Printf("rootDir '%s' doesn't seem correct because can't find file '%s'\n", rootDir, path)
   948  		os.Exit(1)
   949  	}
   950  
   951  	// detect paths of files needed to run the tests
   952  	// either get them from env variables (set by test scripts)
   953  	// or try to auto-detect (helps running tests from within
   954  	// Visual Studio Code or GoLand where env variables are not set)
   955  	{
   956  		path := os.Getenv("RAVENDB_TEST_CERTIFICATE_PATH")
   957  		// wd should be
   958  		if !fileExists(path) {
   959  			path = filepath.Join(rootDir, "certs", "server.pfx")
   960  		}
   961  		if !fileExists(path) {
   962  			fmt.Printf("Didn't find server.pfx file at '%s'. Set RAVENDB_TEST_CERTIFICATE_PATH env variable\n", path)
   963  			os.Exit(1)
   964  		}
   965  		certificatePath = path
   966  		fmt.Printf("Server ertificate file found at '%s'\n", certificatePath)
   967  	}
   968  
   969  	{
   970  		path := os.Getenv("RAVENDB_TEST_CA_PATH")
   971  		if !fileExists(path) {
   972  			path = filepath.Join(rootDir, "certs", "ca.crt")
   973  		}
   974  		if !fileExists(path) {
   975  			fmt.Printf("Didn't find ca.cert file at '%s'. Set RAVENDB_TEST_CA_PATH env variable\n", path)
   976  			os.Exit(1)
   977  		}
   978  		caCertificate = loadTestCaCertificate(path)
   979  		fmt.Printf("Loaded ca certificate from '%s'\n", path)
   980  	}
   981  
   982  	{
   983  		path := os.Getenv("RAVENDB_TEST_CLIENT_CERTIFICATE_PATH")
   984  		if !fileExists(path) {
   985  			path = filepath.Join(rootDir, "certs", "go.pem")
   986  		}
   987  		if !fileExists(path) {
   988  			fmt.Printf("Didn't find cert.pem file at '%s'. Set RAVENDB_TEST_CLIENT_CERTIFICATE_PATH env variable\n", path)
   989  			os.Exit(1)
   990  		}
   991  		clientCertificate = loadTestClientCertificate(path)
   992  		fmt.Printf("Loaded client certificate from '%s'\n", path)
   993  	}
   994  
   995  	testsWereInitialized = true
   996  }
   997  
   998  func TestMain(m *testing.M) {
   999  
  1000  	//ravenServerVerbose = true
  1001  
  1002  	var code int
  1003  
  1004  	// make sure it's called even if panic happens
  1005  	defer func() {
  1006  		shutdownTests()
  1007  
  1008  		//logGoroutines()
  1009  		os.Exit(code)
  1010  	}()
  1011  
  1012  	initializeTests()
  1013  
  1014  	code = m.Run()
  1015  }