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