diff options
author | Nick Thomas <nick@bytemark.co.uk> | 2016-05-07 16:40:56 +0300 |
---|---|---|
committer | Nick Thomas <me@ur.gs> | 2016-09-09 01:57:16 +0300 |
commit | 411cab5081471655a6472ca5c750f51eb03dabcf (patch) | |
tree | 1fc8592406d3538141cf1e4153a2a09eb55adc65 | |
parent | e5d0aa9a1f8301b4e4359c0fd5cbc9d7d7ca3ffe (diff) |
Add a simple test for MultiStringFlag
-rw-r--r-- | acceptance_test.go | 128 | ||||
-rw-r--r-- | domain_test.go | 35 | ||||
-rw-r--r-- | helpers_test.go | 172 | ||||
-rw-r--r-- | multi_string_flag_test.go | 19 |
4 files changed, 216 insertions, 138 deletions
diff --git a/acceptance_test.go b/acceptance_test.go index 37c5ea43..2e36acd1 100644 --- a/acceptance_test.go +++ b/acceptance_test.go @@ -2,127 +2,42 @@ package main import ( "flag" - "fmt" "github.com/stretchr/testify/assert" - "net" "net/http" - "os" - "os/exec" "testing" - "time" ) -// TODO: Use TCP port 0 everywhere to avoid conflicts. The binary could output -// the actual port (and type of listener) for us to read in place of the -// hardcoded values below. -type listenSpec struct { - Type string - Host string - Port string -} - -func (l listenSpec) URL(suffix string) string { - scheme := "http" - if l.Type == "https" { - scheme = "https" - } - - return fmt.Sprintf("%s://%s/%s", scheme, l.JoinHostPort(), suffix) -} - -// Returns only once the TCP server is open -func (l listenSpec) WaitUntilListening() { - for { - conn, _ := net.Dial("tcp", l.JoinHostPort()) - if conn != nil { - conn.Close() - break - } - } -} - -func (l listenSpec) JoinHostPort() string { - return net.JoinHostPort(l.Host, l.Port) -} - var shouldRun = flag.Bool("run-acceptance-tests", false, "Run the acceptance tests?") var pagesBinary = flag.String("gitlab-pages-binary", "./gitlab-pages", "Path to the gitlab-pages binary") -var listenHTTP = []listenSpec{ - {"http", "127.0.0.1", "3700"}, - {"http", "::1", "3700"}, -} - -// TODO: listenHTTPS will require TLS configuration -var listenHTTPS = []listenSpec{} - -var listenProxy = []listenSpec{ - {"proxy", "127.0.0.1", "3702"}, +// TODO: Use TCP port 0 everywhere to avoid conflicts. The binary could output +// the actual port (and type of listener) for us to read in place of the +// hardcoded values below. +var listeners = []ListenSpec{ + {"http", "127.0.0.1", "37000"}, + {"http", "::1", "37000"}, + {"https", "127.0.0.1", "37001"}, + {"https", "::1", "37001"}, + {"proxy", "127.0.0.1", "37002"}, {"proxy", "::1", "37002"}, } -var listeners = append(listenHTTP, append(listenHTTPS, listenProxy...)...) - -// TODO: start one pages process for all tests? -func runPages(t *testing.T) *exec.Cmd { - if !*shouldRun { - t.Log("Acceptance tests disabled") - t.SkipNow() - } - - if _, err := os.Stat(*pagesBinary); err != nil { - t.Logf("Can't find Gitlab Pages binary (%s): %s", *pagesBinary, err) - t.FailNow() +func skipUnlessEnabled(t *testing.T) { + if *shouldRun { + return } - var args []string - for _, spec := range listeners { - args = append(args, "-listen-"+spec.Type, spec.JoinHostPort()) - } - - cmd := exec.Command(*pagesBinary, args...) - t.Logf("Running %s %v", *pagesBinary, args) - cmd.Start() - - // Wait for all TCP servers to be open. Even with this, gitlab-pages - // will sometimes return 404 if a HTTP request comes in before it has - // updated its set of domains. This usually takes < 1ms, hence the sleep - // for now. Without it, intermittent failures occur. - // - // TODO: replace this with explicit status from the pages binary - // TODO: fix the first-request race - for _, spec := range listeners { - spec.WaitUntilListening() - } - time.Sleep(50 * time.Millisecond) - - return cmd -} - -func stopPages(cmd *exec.Cmd) { - cmd.Process.Kill() - cmd.Process.Wait() -} - -func getPage(t *testing.T, spec listenSpec, host, urlsuffix string) (*http.Response, error) { - url := spec.URL(urlsuffix) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - - req.Host = host - - t.Logf("curl -H'Host: %s' %s", host, url) - return http.DefaultClient.Do(req) + t.Log("Acceptance tests disabled") + t.SkipNow() } func TestUnknownHostReturnsNotFound(t *testing.T) { - cmd := runPages(t) - defer stopPages(cmd) + skipUnlessEnabled(t) + teardown := RunPagesProcess(t, *pagesBinary, listeners) + defer teardown() for _, spec := range listeners { - rsp, err := getPage(t, spec, "invalid.invalid", "") + rsp, err := GetPageFromListener(t, spec, "invalid.invalid", "") if assert.NoError(t, err) { rsp.Body.Close() @@ -132,11 +47,12 @@ func TestUnknownHostReturnsNotFound(t *testing.T) { } func TestKnownHostReturns200(t *testing.T) { - cmd := runPages(t) - defer stopPages(cmd) + skipUnlessEnabled(t) + teardown := RunPagesProcess(t, *pagesBinary, listeners) + defer teardown() for _, spec := range listeners { - rsp, err := getPage(t, spec, "group.gitlab-example.com", "project/") + rsp, err := GetPageFromListener(t, spec, "group.gitlab-example.com", "project/") if assert.NoError(t, err) { rsp.Body.Close() diff --git a/domain_test.go b/domain_test.go index 9032aeee..7298c22b 100644 --- a/domain_test.go +++ b/domain_test.go @@ -139,38 +139,9 @@ func TestDomainCertificate(t *testing.T) { Group: "group", Project: "project2", Config: &domainConfig{ - Domain: "test.domain.com", - Certificate: `-----BEGIN CERTIFICATE----- -MIICWDCCAcGgAwIBAgIJAMyzCfoGEwVNMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTYwMjExMTcxNzM2WhcNMjYwMjA4MTcxNzM2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQC2ZSzGIlv2zRsELkmEA1JcvIdsFv80b0NbBftewDAQRuyPlhGNifFx6v7+3O1F -5+f+So43N0QbdrHu11K+ZuXNc6hUy0ofG/eRqXniGZEn8paUdQ98sWsbWelBDNeg -WX4FQomynjyxbG+3IuJR5UHoLWhrJ9+pbPrT915eObbaTQIDAQABo1AwTjAdBgNV -HQ4EFgQUGAhDu+gfckg4IkHRCQWBn4ltKV4wHwYDVR0jBBgwFoAUGAhDu+gfckg4 -IkHRCQWBn4ltKV4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAaGx5U -JRW5HC9dXADLf9OnmJRqWi3VNXEXWFk5XgHKc1z7KIBWMsdj+1gzm5ltRO7hkHw9 -bx6jQKZBRiUxyqTFw9Ywrk1fYFAxk8hxuqVYcGdonImJarHZTdVMBRWut9+EZBVm -77eYbz2zASNpy++QIg85YgQum9uqREClHRBsxQ== ------END CERTIFICATE-----`, - Key: `-----BEGIN PRIVATE KEY----- -MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALZlLMYiW/bNGwQu -SYQDUly8h2wW/zRvQ1sF+17AMBBG7I+WEY2J8XHq/v7c7UXn5/5Kjjc3RBt2se7X -Ur5m5c1zqFTLSh8b95GpeeIZkSfylpR1D3yxaxtZ6UEM16BZfgVCibKePLFsb7ci -4lHlQegtaGsn36ls+tP3Xl45ttpNAgMBAAECgYAAqZFmDs3isY/9jeV6c0CjUZP0 -UokOubC27eihyXTjOj61rsfVicC0tzPB3S+HZ3YyODcYAD1hFCdFRMbqJhmDiewK -5GfATdNQeNARCfJdjYn57NKaXm7rc4C3so1YfxTL6k9QGJgTcybXiClQPDrhkZt3 -YLIeeJbY3OppLqjzgQJBAN5AzwyUqX5eQIUncQKcFY0PIjfFTku62brT7hq+TlqY -1B6n3GUtIX+tyYg1qusy4KUUSzMslXJubHsxKanGqZ0CQQDSFwzK7KEYoZol5OMX -mRsavc3iXmmEkkNRdNb1R4UqrlasPeeIeO1CfoD2RPcQhZCwFtR8xS8u6X9ncfC4 -qyxxAkAhpQvy6ppR7/Cyd4sLCxfUF8NlT/APVMTbHHQCBmcUHeiWj3C0vEVC78r/ -XKh4HGaXdt//ajNhdEflykZ1VgadAkB6Zh934mEA3rXWOgHsb7EQ5WAb8HF9YVGD -FZVfFaoJ8cRhWTeZlQp14Qn1cLyYjZh8XvCxOJiCtlsZw5JBpMihAkBA6ltWb+aZ -EBjC8ZRwZE+cAzmxaYPSs2J7JhS7X7H7Ax7ShhvHI4br3nqf00H4LkvtcHkn5d9G -MwE1w2r4Deww ------END PRIVATE KEY-----`, + Domain: "test.domain.com", + Certificate: CertificateFixture, + Key: KeyFixture, }, } diff --git a/helpers_test.go b/helpers_test.go index 5d564891..3bab08e0 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,8 +1,17 @@ package main import ( + "crypto/tls" + "fmt" + "github.com/stretchr/testify/assert" + "io/ioutil" "log" + "net" + "net/http" "os" + "os/exec" + "testing" + "time" ) var chdirSet = false @@ -19,3 +28,166 @@ func setUpTests() { chdirSet = true } } + +// The HTTPS certificate isn't signed by anyone. This http client is set up +// so it can talk to servers using it. +var InsecureHTTPSClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, +} + +var CertificateFixture = `-----BEGIN CERTIFICATE----- +MIICWDCCAcGgAwIBAgIJAMyzCfoGEwVNMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwMjExMTcxNzM2WhcNMjYwMjA4MTcxNzM2WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQC2ZSzGIlv2zRsELkmEA1JcvIdsFv80b0NbBftewDAQRuyPlhGNifFx6v7+3O1F +5+f+So43N0QbdrHu11K+ZuXNc6hUy0ofG/eRqXniGZEn8paUdQ98sWsbWelBDNeg +WX4FQomynjyxbG+3IuJR5UHoLWhrJ9+pbPrT915eObbaTQIDAQABo1AwTjAdBgNV +HQ4EFgQUGAhDu+gfckg4IkHRCQWBn4ltKV4wHwYDVR0jBBgwFoAUGAhDu+gfckg4 +IkHRCQWBn4ltKV4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAaGx5U +JRW5HC9dXADLf9OnmJRqWi3VNXEXWFk5XgHKc1z7KIBWMsdj+1gzm5ltRO7hkHw9 +bx6jQKZBRiUxyqTFw9Ywrk1fYFAxk8hxuqVYcGdonImJarHZTdVMBRWut9+EZBVm +77eYbz2zASNpy++QIg85YgQum9uqREClHRBsxQ== +-----END CERTIFICATE-----` + +var KeyFixture = `-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALZlLMYiW/bNGwQu +SYQDUly8h2wW/zRvQ1sF+17AMBBG7I+WEY2J8XHq/v7c7UXn5/5Kjjc3RBt2se7X +Ur5m5c1zqFTLSh8b95GpeeIZkSfylpR1D3yxaxtZ6UEM16BZfgVCibKePLFsb7ci +4lHlQegtaGsn36ls+tP3Xl45ttpNAgMBAAECgYAAqZFmDs3isY/9jeV6c0CjUZP0 +UokOubC27eihyXTjOj61rsfVicC0tzPB3S+HZ3YyODcYAD1hFCdFRMbqJhmDiewK +5GfATdNQeNARCfJdjYn57NKaXm7rc4C3so1YfxTL6k9QGJgTcybXiClQPDrhkZt3 +YLIeeJbY3OppLqjzgQJBAN5AzwyUqX5eQIUncQKcFY0PIjfFTku62brT7hq+TlqY +1B6n3GUtIX+tyYg1qusy4KUUSzMslXJubHsxKanGqZ0CQQDSFwzK7KEYoZol5OMX +mRsavc3iXmmEkkNRdNb1R4UqrlasPeeIeO1CfoD2RPcQhZCwFtR8xS8u6X9ncfC4 +qyxxAkAhpQvy6ppR7/Cyd4sLCxfUF8NlT/APVMTbHHQCBmcUHeiWj3C0vEVC78r/ +XKh4HGaXdt//ajNhdEflykZ1VgadAkB6Zh934mEA3rXWOgHsb7EQ5WAb8HF9YVGD +FZVfFaoJ8cRhWTeZlQp14Qn1cLyYjZh8XvCxOJiCtlsZw5JBpMihAkBA6ltWb+aZ +EBjC8ZRwZE+cAzmxaYPSs2J7JhS7X7H7Ax7ShhvHI4br3nqf00H4LkvtcHkn5d9G +MwE1w2r4Deww +-----END PRIVATE KEY-----` + +func CreateHTTPSFixtureFiles(t *testing.T) (key string, cert string) { + keyfile, err := ioutil.TempFile("", "https-fixture") + assert.NoError(t, err) + key = keyfile.Name() + keyfile.Close() + + certfile, err := ioutil.TempFile("", "https-fixture") + assert.NoError(t, err) + cert = certfile.Name() + certfile.Close() + + assert.NoError(t, ioutil.WriteFile(key, []byte(KeyFixture), 0644)) + assert.NoError(t, ioutil.WriteFile(cert, []byte(CertificateFixture), 0644)) + + return keyfile.Name(), certfile.Name() +} + +// ListenSpec is used to point at a gitlab-pages http server, preserving the +// type of port it is (http, https, proxy) +type ListenSpec struct { + Type string + Host string + Port string +} + +func (l ListenSpec) URL(suffix string) string { + scheme := "http" + if l.Type == "https" { + scheme = "https" + } + + return fmt.Sprintf("%s://%s/%s", scheme, l.JoinHostPort(), suffix) +} + +// Returns only once this spec points at a working TCP server +func (l ListenSpec) WaitUntilListening() { + for { + conn, _ := net.Dial("tcp", l.JoinHostPort()) + if conn != nil { + conn.Close() + break + } + } +} + +func (l ListenSpec) JoinHostPort() string { + return net.JoinHostPort(l.Host, l.Port) +} + +// RunPagesProcess will start a gitlab-pages process with the specified listeners +// and return a function you can call to shut it down again. Use +// GetPageFromProcess to do a HTTP GET against a listener. +// +// If run as root via sudo, the gitlab-pages process will drop privileges +func RunPagesProcess(t *testing.T, pagesPath string, listeners []ListenSpec) (teardown func()) { + var tempfiles []string + var args []string + var hasHTTPS bool + + _, err := os.Stat(pagesPath) + assert.NoError(t, err) + + for _, spec := range listeners { + args = append(args, "-listen-"+spec.Type, spec.JoinHostPort()) + + if spec.Type == "https" { + hasHTTPS = true + } + } + + if hasHTTPS { + key, cert := CreateHTTPSFixtureFiles(t) + tempfiles = []string{key, cert} + args = append(args, "-root-key", key, "-root-cert", cert) + } + + if os.Geteuid() == 0 && os.Getenv("SUDO_UID") != "" && os.Getenv("SUDO_GID") != "" { + t.Log("Pages process will drop privileges") + args = append(args, "-daemon-uid", os.Getenv("SUDO_UID"), "-daemon-gid", os.Getenv("SUDO_GID")) + } + + cmd := exec.Command(pagesPath, args...) + t.Logf("Running %s %v", pagesPath, args) + cmd.Start() + + // Wait for all TCP servers to be open. Even with this, gitlab-pages + // will sometimes return 404 if a HTTP request comes in before it has + // updated its set of domains. This usually takes < 1ms, hence the sleep + // for now. Without it, intermittent failures occur. + // + // TODO: replace this with explicit status from the pages binary + // TODO: fix the first-request race + for _, spec := range listeners { + spec.WaitUntilListening() + } + time.Sleep(50 * time.Millisecond) + + return func() { + cmd.Process.Kill() + cmd.Process.Wait() + for _, tempfile := range tempfiles { + os.Remove(tempfile) + } + } +} + +// Does an insecure HTTP GET against the listener specified, setting a fake +// Host: and constructing the URL from the listener and the URL suffix. +func GetPageFromListener(t *testing.T, spec ListenSpec, host, urlsuffix string) (*http.Response, error) { + url := spec.URL(urlsuffix) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + req.Host = host + + t.Logf("curl -H'Host: %s' %s", host, url) + + return InsecureHTTPSClient.Do(req) +} diff --git a/multi_string_flag_test.go b/multi_string_flag_test.go new file mode 100644 index 00000000..bbbdea3d --- /dev/null +++ b/multi_string_flag_test.go @@ -0,0 +1,19 @@ +package main + +import ( + "flag" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMultiStringFlagAppendsOnSet(t *testing.T) { + var concrete MultiStringFlag + var iface flag.Value + + iface = &concrete + + assert.NoError(t, iface.Set("foo")) + assert.NoError(t, iface.Set("bar")) + + assert.Equal(t, MultiStringFlag{"foo", "bar"}, concrete) +} |