github.com/stripe/stripe-go/v76@v76.25.0/scripts/test_with_stripe_mock/main.go (about)

     1  // A script that wraps the run of the project test suite and starts stripe-mock
     2  // with a custom OpenAPI + fixtures bundle if one was found in the appropriate
     3  // spot (see `pathSpec` below).
     4  //
     5  // The script passes all its arguments to a Go's test command, and defaults to
     6  // `./...`. For example, both of the following are valid invocations:
     7  //
     8  //	go run test_with_stripe_mock/main.go
     9  //	go run test_with_stripe_mock/main.go ./charge
    10  //
    11  // The reason that we need a separate script for this is because Go's testing
    12  // infrastructure doesn't provide any kind of global hook that we can use to do
    13  // this work in only one place before any tests are run.
    14  package main
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"os"
    20  	"os/exec"
    21  	"regexp"
    22  	"time"
    23  )
    24  
    25  const (
    26  	defaultStripeMockPort = "12112"
    27  
    28  	pathSpec     = "./testing/openapi/spec3.json"
    29  	pathFixtures = "./testing/openapi/fixtures3.json"
    30  )
    31  
    32  var portMatch = regexp.MustCompile(` port: (\d+)`)
    33  
    34  func main() {
    35  	var stripeMockProcess *os.Process
    36  	autostart := true
    37  
    38  	port := os.Getenv("STRIPE_MOCK_PORT")
    39  	if port == "" {
    40  		port = defaultStripeMockPort
    41  	}
    42  
    43  	//
    44  	// Maybe start stripe-mock
    45  	//
    46  
    47  	autostartEnv := os.Getenv("STRIPE_MOCK_AUTOSTART")
    48  	if autostartEnv == "0" || autostartEnv == "false" {
    49  		fmt.Printf("STRIPE_MOCK_AUTOSTART=%s, "+
    50  			"assuming stripe-mock is already running on port %s\n", autostartEnv, port)
    51  		autostart = false
    52  	} else if _, err := os.Stat(pathSpec); os.IsNotExist(err) {
    53  		fmt.Printf("No custom spec file found, "+
    54  			"assuming stripe-mock is already running on port %s\n", port)
    55  		autostart = false
    56  	}
    57  
    58  	if autostart {
    59  		var err error
    60  		port, stripeMockProcess, err = startStripeMock()
    61  		if err != nil {
    62  			exitWithError(err)
    63  		}
    64  	}
    65  
    66  	//
    67  	// Run tests
    68  	//
    69  
    70  	err := runTests(port)
    71  	if err != nil {
    72  		stopStripeMock(stripeMockProcess)
    73  		exitWithError(err)
    74  	}
    75  
    76  	//
    77  	// Stop stripe-mock
    78  	//
    79  
    80  	// Try to cleanly stop stripe-mock, but in case we don't, it'll die anyway
    81  	// because it's executing as a subprocess.
    82  	stopStripeMock(stripeMockProcess)
    83  }
    84  
    85  //
    86  // Private functions
    87  //
    88  
    89  func exitWithError(err error) {
    90  	fmt.Fprintf(os.Stderr, "%v", err)
    91  	os.Exit(1)
    92  }
    93  
    94  func runTests(port string) error {
    95  	args := append([]string{"test"}, os.Args[1:]...)
    96  
    97  	// Defaults to `./...`, but also allows a specific package (or other CLI
    98  	// flags like `-test.v`) to be passed.
    99  	if len(args) == 1 {
   100  		args = append(args, "./...")
   101  	}
   102  
   103  	cmd := exec.Command("go", args...)
   104  
   105  	// Inherit this script's environment so that it's still possible to pass
   106  	// the test package flags like `GOCACHE=off`.
   107  	cmd.Env = append(
   108  		os.Environ(),
   109  		"STRIPE_MOCK_PORT="+port,
   110  	)
   111  
   112  	cmd.Stderr = os.Stderr
   113  	cmd.Stdout = os.Stdout
   114  
   115  	err := cmd.Run()
   116  	if err != nil {
   117  		return fmt.Errorf("Error running tests: %v", err)
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func startStripeMock() (string, *os.Process, error) {
   124  	fmt.Printf("Starting stripe-mock...\n")
   125  
   126  	cmd := exec.Command(
   127  		"stripe-mock",
   128  		"-https-port", "0", // stripe-mock will select a port
   129  		"-spec", pathSpec,
   130  		"-fixtures", pathFixtures,
   131  	)
   132  
   133  	cmd.Stderr = os.Stderr
   134  
   135  	stdout, err := cmd.StdoutPipe()
   136  	if err != nil {
   137  		return "", nil, fmt.Errorf("Error starting stripe-mock: %v", err)
   138  	}
   139  
   140  	err = cmd.Start()
   141  	if err != nil {
   142  		return "", nil, fmt.Errorf("Error starting stripe-mock: %v", err)
   143  	}
   144  
   145  	b := make([]byte, 1024)
   146  	var port string
   147  
   148  	// We store the entire captured output because the string we're looking for
   149  	// may have appeared across a read boundary.
   150  	var stdoutBuffer bytes.Buffer
   151  
   152  	for {
   153  		n, err := stdout.Read(b)
   154  		if err != nil {
   155  			return "", nil, err
   156  		}
   157  		stdoutBuffer.Write(b[0:n])
   158  
   159  		// Look for port in "Listening for HTTP on port: 50602"
   160  		matches := portMatch.FindStringSubmatch(stdoutBuffer.String())
   161  		if len(matches) > 0 {
   162  			port = matches[1]
   163  			break
   164  		}
   165  
   166  		time.Sleep(100 * time.Millisecond)
   167  	}
   168  
   169  	fmt.Printf("Started stripe-mock; PID = %v, port = %v\n", cmd.Process.Pid, port)
   170  	return port, cmd.Process, nil
   171  }
   172  
   173  func stopStripeMock(process *os.Process) {
   174  	if process == nil {
   175  		return
   176  	}
   177  
   178  	fmt.Printf("Stopping stripe-mock...\n")
   179  	process.Signal(os.Interrupt)
   180  	process.Wait()
   181  	fmt.Printf("Stopped stripe-mock\n")
   182  }