5416: Add integration tests.
authorTom Clegg <tom@curoverse.com>
Mon, 16 Mar 2015 20:27:06 +0000 (16:27 -0400)
committerTom Clegg <tom@curoverse.com>
Mon, 16 Mar 2015 20:27:06 +0000 (16:27 -0400)
services/arv-git-httpd/auth_handler.go
services/arv-git-httpd/main.go
services/arv-git-httpd/server.go
services/arv-git-httpd/server_test.go [new file with mode: 0644]

index f182bca583cf206480ff9f8256a8ffbb8f22743d..3d299939d786348725d623b1542c9f0ce3bdc493 100644 (file)
@@ -13,10 +13,6 @@ import (
 )
 
 func newArvadosClient() interface{} {
-       // MakeArvadosClient returns an error if token is unset (even
-       // though we don't need to do anything requiring
-       // authentication yet).
-       os.Setenv("ARVADOS_API_TOKEN", "xxx")
        arv, err := arvadosclient.MakeArvadosClient()
        if err != nil {
                log.Println("MakeArvadosClient:", err)
index 47758f6867347900ee8677d09c3d693676ed3732..0e92393e29ffc916e9a128f4cfe10aaa7930ef5e 100644 (file)
@@ -26,6 +26,14 @@ func init() {
        }
        flag.StringVar(&theConfig.Root, "repo-root", cwd,
                "Path to git repositories.")
+
+       // MakeArvadosClient returns an error if token is unset (even
+       // though we don't need to do anything requiring
+       // authentication yet). We can't do this in newArvadosClient()
+       // just before calling MakeArvadosClient(), though, because
+       // that interferes with the env var needed by "run test
+       // servers".
+       os.Setenv("ARVADOS_API_TOKEN", "xxx")
 }
 
 func main() {
index 393b6c414494c4ea5b01e66e9d618e28222888d5..716b2760f3a890860138580b03be7f262f20d7c5 100644 (file)
@@ -13,7 +13,7 @@ type server struct {
        Addr     string // host:port where the server is listening.
        err      error
        cond     *sync.Cond
-       done     bool
+       running  bool
        listener *net.TCPListener
        wantDown bool
 }
@@ -54,13 +54,14 @@ func (srv *server) Start() error {
 
        mutex := &sync.RWMutex{}
        srv.cond = sync.NewCond(mutex.RLocker())
+       srv.running = true
        go func() {
                err = srv.Serve(tcpKeepAliveListener{srv.listener})
                if !srv.wantDown {
                        srv.err = err
                }
                mutex.Lock()
-               srv.done = true
+               srv.running = false
                srv.cond.Broadcast()
                mutex.Unlock()
        }()
@@ -69,9 +70,12 @@ func (srv *server) Start() error {
 
 // Wait returns when the server has shut down.
 func (srv *server) Wait() error {
+       if srv.cond == nil {
+               return nil
+       }
        srv.cond.L.Lock()
        defer srv.cond.L.Unlock()
-       for !srv.done {
+       for srv.running {
                srv.cond.Wait()
        }
        return srv.err
diff --git a/services/arv-git-httpd/server_test.go b/services/arv-git-httpd/server_test.go
new file mode 100644 (file)
index 0000000..c3eed3d
--- /dev/null
@@ -0,0 +1,152 @@
+package main
+
+import (
+       "errors"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "strings"
+       "testing"
+
+       check "gopkg.in/check.v1"
+       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
+)
+
+var _ = check.Suite(&IntegrationSuite{})
+
+// IntegrationSuite tests need an API server and an arv-git-httpd server
+type IntegrationSuite struct {
+       tmpRepoRoot string
+       tmpWorkdir  string
+       testServer  *server
+}
+
+func (s *IntegrationSuite) TestReadonly(c *check.C) {
+       // Spectator token
+       os.Setenv("ARVADOS_API_TOKEN", "zw2f4gwx8hw8cjre7yp6v1zylhrhn3m5gvjq73rtpwhmknrybu")
+       err := s.runGit(c, "fetch", "foo.git")
+       c.Assert(err, check.Equals, nil)
+       err = s.runGit(c, "push", "foo.git", "master:newbranchfail")
+       c.Assert(err, check.ErrorMatches, `.*HTTP code = 403.*`)
+       _, err = os.Stat(s.tmpRepoRoot + "/.git/refs/heads/newbranchfail")
+       c.Assert(err, check.FitsTypeOf, &os.PathError{})
+}
+
+func (s *IntegrationSuite) TestReadwrite(c *check.C) {
+       // Active user token
+       os.Setenv("ARVADOS_API_TOKEN", "3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi")
+       err := s.runGit(c, "fetch", "foo.git")
+       c.Assert(err, check.Equals, nil)
+       err = s.runGit(c, "push", "foo.git", "master:newbranch")
+       c.Assert(err, check.Equals, nil)
+       _, err = os.Stat(s.tmpRepoRoot + "/foo/.git/refs/heads/newbranch")
+       c.Assert(err, check.Equals, nil)
+}
+
+func (s *IntegrationSuite) TestNonexistent(c *check.C) {
+       // Spectator token
+       os.Setenv("ARVADOS_API_TOKEN", "zw2f4gwx8hw8cjre7yp6v1zylhrhn3m5gvjq73rtpwhmknrybu")
+       err := s.runGit(c, "fetch", "thisrepodoesnotexist.git")
+       c.Assert(err, check.ErrorMatches, `.* not found:.*`)
+}
+
+func (s *IntegrationSuite) TestNoPermission(c *check.C) {
+       // Anonymous token
+       os.Setenv("ARVADOS_API_TOKEN", "4kg6k6lzmp9kj4cpkcoxie964cmvjahbt4fod9zru44k4jqdmi")
+       for _, repo := range []string{"foo.git", "foo/.git", "foo/bar.git", "foo/bar/.git"} {
+               err := s.runGit(c, "fetch", repo)
+               c.Assert(err, check.ErrorMatches, `.* not found:.*`)
+       }
+}
+
+func (s *IntegrationSuite) SetUpSuite(c *check.C) {
+       arvadostest.StartAPI()
+}
+
+func (s *IntegrationSuite) SetUpTest(c *check.C) {
+       arvadostest.ResetEnv()
+       s.testServer = &server{}
+       var err error
+       s.tmpRepoRoot, err = ioutil.TempDir("", "arv-git-httpd")
+       c.Assert(err, check.Equals, nil)
+       s.tmpWorkdir, err = ioutil.TempDir("", "arv-git-httpd")
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("git", "init", "--bare", s.tmpRepoRoot + "/arvados.git").Output()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("git", "--git-dir", s.tmpRepoRoot + "/arvados.git", "fetch", "../../.git", "master:master").Output()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("git", "init", s.tmpRepoRoot + "/foo").Output()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("sh", "-c", "cd " + s.tmpRepoRoot + "/foo && echo test >test && git add test && git commit -am 'foo: test'").CombinedOutput()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("git", "init", s.tmpWorkdir).Output()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("sh", "-c", "cd " + s.tmpWorkdir + " && echo work >work && git add work && git commit -am 'workdir: test'").CombinedOutput()
+       c.Assert(err, check.Equals, nil)
+
+       theConfig = &config{
+               Addr: ":",
+               GitCommand: "/usr/bin/git",
+               Root: s.tmpRepoRoot,
+       }
+       err = s.testServer.Start()
+       c.Assert(err, check.Equals, nil)
+
+       // Clear ARVADOS_API_TOKEN after starting up the server, to
+       // make sure arv-git-httpd doesn't use it.
+       os.Setenv("ARVADOS_API_TOKEN", "")
+
+       _, err = exec.Command("git", "config",
+               "--file", s.tmpWorkdir + "/.git/config",
+               "credential.http://" + s.testServer.Addr + "/.helper",
+               "!foo(){ echo password=$ARVADOS_API_TOKEN; };foo").Output()
+       c.Assert(err, check.Equals, nil)
+       _, err = exec.Command("git", "config",
+               "--file", s.tmpWorkdir + "/.git/config",
+               "credential.http://" + s.testServer.Addr + "/.username",
+               "none").Output()
+       c.Assert(err, check.Equals, nil)
+}
+
+func (s *IntegrationSuite) TearDownTest(c *check.C) {
+       var err error
+       if s.testServer != nil {
+               err = s.testServer.Close()
+       }
+       c.Check(err, check.Equals, nil)
+       if s.tmpRepoRoot != "" {
+               err = os.RemoveAll(s.tmpRepoRoot)
+               c.Check(err, check.Equals, nil)
+       }
+       if s.tmpWorkdir != "" {
+               err = os.RemoveAll(s.tmpWorkdir)
+               c.Check(err, check.Equals, nil)
+       }
+}
+
+func (s *IntegrationSuite) runGit(c *check.C, gitCmd, repo string, args ...string) error {
+       cwd, err := os.Getwd()
+       c.Assert(err, check.Equals, nil)
+       defer os.Chdir(cwd)
+       os.Chdir(s.tmpWorkdir)
+
+       gitargs := append([]string{
+               gitCmd, "http://" + s.testServer.Addr + "/" + repo,
+       }, args...)
+       cmd := exec.Command("git", gitargs...)
+       w, err := cmd.StdinPipe()
+       c.Assert(err, check.Equals, nil)
+       go w.Close()
+       output, err := cmd.CombinedOutput()
+       c.Log("git ", gitargs, " => ", err)
+       if err != nil {
+               // Easier to match error strings without newlines.
+               err = errors.New(strings.Replace(string(output), "\n", " // ", -1))
+       }
+       return err
+}
+
+// Gocheck boilerplate
+func Test(t *testing.T) {
+       check.TestingT(t)
+}