commit - 544f91b1a634777e37327f09f162916807bbfea2
commit + ff42d81ed53b26b9adf21df9618b77f17429c26a
blob - /dev/null
blob + 66fd13c903cac02eb9657cd53fb227823484401d (mode 644)
--- /dev/null
+++ backends/.gitignore
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
blob - /dev/null
blob + 7052f6db01b4e33bcaa870e1217606ac878a3386 (mode 644)
--- /dev/null
+++ backends/README.md
+# testres-backends
+
+is a library to import test results.
+
+## Building
+
+```
+$ go get ./...
+$ go test -v ./...
+```
blob - /dev/null
blob + 120c4a3c6414f3596d492fc3ad6b2013b039c6a6 (mode 644)
--- /dev/null
+++ backends/TODO.md
+### TODO
+
+- конверторы для форматов в `formats/common.go`
+- SubUnit V2
+ - https://github.com/msgpack/msgpack-go/blob/master/unpack.go
+ - https://github.com/hashicorp/go-msgpack/blob/master/codec/decode.go
+- импорт результатов только старше даты последнего результата из базы
+- добавлять кастомные сертификаты для http клиента (`ca.go`)
+- сохранять в структуре бранч, название пайплайна, коммиты
+- поддержка Lava
+- поддержка Cirrus CI
+- опция `-limit`
+
+### GitHub Actions
+
+- https://help.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts
+- https://developer.github.com/v3/actions/
+- ? https://github.com/google/go-github
+- Documentation: https://docs.github.com/en/rest/reference/actions#artifacts
+
+### Zuul
+
+- Example: https://zuul.opendev.org/t/openstack/builds?project=openstack/glance
+- Example: https://zuul.opendev.org/t/openstack/builds?project=openstack/ceilometer
+- Example: https://zuul.opendev.org/t/openstack/builds?project=openstack/heat
+- https://zuul.opendev.org/openapi
+
+### BitBucket
+
+- https://github.com/ktrysmt/go-bitbucket
+
+### Lava
+
+- https://staging.validation.linaro.org/api/help/
+- XML RPC Client https://github.com/kolo/xmlrpc
+
+### Kernel CI
+
+- https://api.kernelci.org
+- https://github.com/kernelci/kernelci-backend
+- Where is a Golang API?
+- token is required
+
+### Codefresh
+
+- https://github.com/codefresh-io/go-sdk
+- Example: https://github.com/nemequ/portable-snippets
+- test results unavailable via API
+
+### Patchwork/Patchew
+
+- https://patchwork-freedesktop.readthedocs.io/en/latest/rest.html
+- https://github.com/patchew-project/patchew
+- Where is Golang API?
+
+### TestRail
+
+- http://docs.gurock.com/testrail-api2/start
+- https://github.com/gurock/testrail-api
+- Go: https://github.com/educlos/testrail
+- Go: https://godoc.org/github.com/Etienne42/testrail
+- https://secure.gurock.com/customers/testrail/trial/
+- github.com/educlos/testrail
+
+### Beaker
+
+- https://beaker-project.org/docs/server-api/
+- Where is Golang API?
+
+### BuildBot
+
+- REST API: https://github.com/buildbot/buildbot/blob/master/master/docs/developer/rest.rst
+- Example: https://github.com/buildbot/buildbot/wiki/SuccessStories
+- Example: http://buildbot.suricata-ids.org
+- Example: https://buildbot.python.org/
+- Example: http://212.201.121.110:38010/
+- Example: https://buildbot.openinfosecfoundation.org/
+- Example: https://ci.chromium.org/p/chromium/g/main/console
+- Example https://chromium.googlesource.com/infra/luci/luci-go/+/master/grpc/prpc/talk/buildbot/client/main.go
+- LUCI: http://bit.ly/2kgyE9U
+- https://docs.buildbot.net/latest/developer/rest.html
+- "go.chromium.org/luci/grpc/prpc/talk/buildbot/proto"
+- "go.chromium.org/luci/milo/buildsource/buildbot"
+- "go.chromium.org/luci/milo/buildsource/buildbot/buildbotapi"
+- "go.chromium.org/luci/milo/buildsource/buildbot/buildstore"
+
+### Drone CI
+
+- Publish test results in artifacts: https://github.com/drone/docs.drone.io/blob/master/artifacts.markdown
+- Enterprise only: https://0-8-0.docs.drone.io/publish-unit-test-results/
+- API: https://docs.drone.io/api/endpoints/builds/build_list/
+- https://github.com/drone/drone/issues/239
+- Golang API: https://github.com/drone/drone-go
+
+### CDash
+
+- https://my.cdash.org/viewProjects.php
+- https://www.paraview.org/Wiki/CDash:API
+- https://open.cdash.org/viewProjects.php
+- https://open.cdash.org/viewTest.php?buildid=6227968
+- https://open.cdash.org/viewTest.php?buildid=6227571
+- https://my.cdash.org/viewTest.php?buildid=1735823
+- Where is Golang API?
+
+### AWS CodePipeline
+
+- https://docs.aws.amazon.com/en_us/codepipeline/latest/userguide/welcome.html
+- GoDoc: https://docs.aws.amazon.com/sdk-for-go/api/service/codepipeline/
+- GoDoc: https://docs.aws.amazon.com/sdk-for-go/api/service/codepipeline/#Artifact
+
+### Appveyor
+
+- API: https://www.appveyor.com/docs/api/projects-builds/
+- Can't access to test results via REST API https://github.com/appveyor/ci/issues/3226
+- Where is a Golang API? https://github.com/appveyor/ci/issues/3225
+- Example: https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master/tests
+- Example: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master/tests
+- Example: https://ci.appveyor.com/project/quixdb/portable-snippets/branch/master
+
+### CodeShip CI
+
+- https://apidocs.codeship.com/v2/introduction/basic-vs-pro
+- GoDoc: https://godoc.org/github.com/codeship/codeship-go#Build
+- How to get a testing results?
+
+### Atlassian Bamboo
+
+- https://developer.atlassian.com/server/bamboo/rest-apis/
+- https://github.com/rcarmstrong/go-bamboo
+- TODO: How to get test results?
+- https://community.atlassian.com/t5/Answers-Developer-Questions/How-to-Get-Bamboo-Test-Results-via-REST/qaq-p/475715
+
+### Concourse CI
+
+- https://ci.spearow.io/teams/main/pipelines/oregano/jobs/make-release/builds/30
+
+### Report Portal
+
+- Where is an API documentation? https://github.com/reportportal/service-api/issues/1094
+- Go API https://github.com/avarabyeu/goRP
+- Example: https://rp.epam.com/ui/
+- Example: http://web.demo.reportportal.io/ui/
+- https://github.com/ihar-kahadouski/dev-guide/blob/master/reporting.md
+
+### JetBrains Space
+
+- Golang API?
+- Example?
+
+### Bitrise
+
+- https://api-docs.bitrise.io/
+- https://devcenter.bitrise.io/testing/test-reports/
+- https://devcenter.bitrise.io/testing/exporting-to-test-reports-from-custom-script-steps/
blob - /dev/null
blob + 79e8b10a05801db5d59d6b5720e1f783c877e0bd (mode 644)
--- /dev/null
+++ backends/backend_azure_devops.go
+package backends
+
+import (
+ "context"
+ "github.com/microsoft/azure-devops-go-api/azuredevops"
+ "github.com/microsoft/azure-devops-go-api/azuredevops/build"
+ "github.com/microsoft/azure-devops-go-api/azuredevops/core"
+ "github.com/microsoft/azure-devops-go-api/azuredevops/pipelines"
+ "github.com/microsoft/azure-devops-go-api/azuredevops/testresults"
+ "github.com/ligurio/testres-db/formats"
+ "log"
+ "net/http"
+)
+
+func getBuilds(ctx context.Context, connection *azuredevops.Connection, ProjectName *string, BranchName *string) (*[]build.Build, error) {
+ buildClient, err := build.NewClient(ctx, connection)
+ if err != nil {
+ return nil, err
+ }
+
+ buildsArgs := build.GetBuildsArgs{Project: ProjectName}
+ if *BranchName != "" {
+ buildsArgs.BranchName = BranchName
+ }
+ responseValue, err := buildClient.GetBuilds(ctx, buildsArgs)
+ if err != nil {
+ return nil, err
+ }
+
+ var builds *[]build.Build = nil
+ builds = &(*responseValue).Value
+ for responseValue != nil {
+ // FIXME: builds = append(*builds, &(*responseValue).Value)
+ for _, teamBuildReference := range (*responseValue).Value {
+ log.Printf("Build %v, %s", *teamBuildReference.BuildNumber, *teamBuildReference.SourceBranch)
+ }
+
+ if responseValue.ContinuationToken != "" {
+ buildsArgs := build.GetBuildsArgs{
+ ContinuationToken: &responseValue.ContinuationToken,
+ }
+ buildsArgs.ContinuationToken = &responseValue.ContinuationToken
+ responseValue, err = buildClient.GetBuilds(ctx, buildsArgs)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ responseValue = nil
+ }
+ }
+
+ testresultsClient := testresults.NewClient(ctx, connection)
+ for _, bld := range *builds {
+ log.Println("Build Num", *bld.Id)
+ TestResultDetailsForBuildArgs := testresults.GetTestResultDetailsForBuildArgs{Project: ProjectName, BuildId: bld.Id}
+ TestResults, err := testresultsClient.GetTestResultDetailsForBuild(ctx, TestResultDetailsForBuildArgs)
+ if err != nil {
+ log.Println("Unsupported?", err)
+ continue
+ }
+ if TestResults != nil {
+ log.Println("TestResults", (*TestResults).GroupByField)
+ // log.Println("TestResults #%v", (*TestResults).ResultsForGroup)
+ }
+ }
+
+ return builds, err
+}
+
+func getPipelineRef(ctx context.Context, connection *azuredevops.Connection, Project *string, PipelineName *string) (*pipelines.Pipeline, error) {
+ pipelineClient := pipelines.NewClient(ctx, connection)
+ responseValue, err := pipelineClient.ListPipelines(ctx, pipelines.ListPipelinesArgs{Project: Project})
+ if err != nil {
+ return nil, err
+ }
+
+ var PipelineRef *pipelines.Pipeline = nil
+ for _, teamPipelineReference := range (*responseValue).Value {
+ // log.Printf("Pipeline = %v", *teamPipelineReference.Name)
+ if *teamPipelineReference.Name == *PipelineName {
+ PipelineRef = &teamPipelineReference
+ break
+ }
+ }
+
+ return PipelineRef, nil
+}
+
+func getProjectRef(ctx context.Context, connection *azuredevops.Connection, ProjectName *string) (*core.TeamProjectReference, error) {
+ coreClient, err := core.NewClient(ctx, connection)
+ if err != nil {
+ return nil, err
+ }
+
+ responseValue, err := coreClient.GetProjects(ctx, core.GetProjectsArgs{})
+ if err != nil {
+ return nil, err
+ }
+
+ index := 0
+ var Project *core.TeamProjectReference = nil
+ for responseValue != nil {
+ for _, teamProjectReference := range (*responseValue).Value {
+ // log.Printf("Name[%v] = %v", index, *teamProjectReference.Name)
+ if *teamProjectReference.Name == *ProjectName {
+ Project = &teamProjectReference
+ break
+ }
+ index++
+ }
+
+ if responseValue.ContinuationToken != "" {
+ projectArgs := core.GetProjectsArgs{
+ ContinuationToken: &responseValue.ContinuationToken,
+ }
+ responseValue, err = coreClient.GetProjects(ctx, projectArgs)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ responseValue = nil
+ }
+ }
+
+ return Project, nil
+
+}
+
+// Using custom http client: https://github.com/microsoft/azure-devops-go-api/issues/52
+func SyncAzureDevOps(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ if b.Username != "" {
+ log.Println("Username is specified but unused", b.Username)
+ }
+
+ connection := azuredevops.NewPatConnection(b.Base, b.Secret)
+ ctx := context.Background()
+
+ project, err := getProjectRef(ctx, connection, &b.Project)
+ if err != nil {
+ return nil, err
+ }
+ if project.Url != nil {
+ log.Println("URL:", *project.Url)
+ }
+ log.Println("Last Update Time:", project.LastUpdateTime)
+
+ if project.Abbreviation != nil {
+ log.Println(*project.Abbreviation)
+ }
+
+ pipeline, err := getPipelineRef(ctx, connection, &b.Project, &b.Pipeline)
+ if err != nil {
+ return nil, err
+ }
+ log.Println("Pipeline:", *pipeline.Name)
+ builds, err := getBuilds(ctx, connection, project.Name, &b.Branch)
+ if err != nil {
+ return nil, err
+ }
+
+ if builds == nil {
+ log.Println("list of builds is empty")
+ return nil, err
+ }
+ for _, bld := range *builds {
+ log.Println("Build", *bld.BuildNumber, *bld.SourceBranch)
+ }
+
+ return nil, nil
+}
+
+/*
+http://localhost:6060/pkg/github.com/microsoft/azure-devops-go-api/azuredevops/testplan/
+http://localhost:6060/pkg/github.com/microsoft/azure-devops-go-api/azuredevops/test/
+http://localhost:6060/pkg/github.com/microsoft/azure-devops-go-api/azuredevops/testresults/
+func AzureDevopsTMS_fn(b *Backend) ([]*formats.TestReport, error) {
+ log.Println("not implemented")
+ return nil, nil
+}
+*/
blob - /dev/null
blob + 34bd3fdcf20c351369628222c8df79b84e535e49 (mode 644)
--- /dev/null
+++ backends/backend_azure_devops_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+
+package backends
+
+import "testing"
+
+func TestSyncAzureDevOps(t *testing.T) {
+ t.Log("TestSyncAzureDevOps")
+}
blob - /dev/null
blob + 2a33f857ce99c99d81cbb498d987ab8d6b61fac5 (mode 644)
--- /dev/null
+++ backends/backend_circleci.go
+package backends
+
+// Documentation: https://circleci.com/docs/2.0/artifacts/
+
+import (
+ "github.com/jszwedko/go-circleci"
+ "github.com/ligurio/testres-db/formats"
+ "log"
+ "net/http"
+ "strings"
+)
+
+func SyncCircleCI(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ project_path := strings.Split(b.Project, "/")
+ if len(project_path) != 2 {
+ log.Println("Perhaps wrong project name specified")
+ }
+
+ account := project_path[0]
+ repo := project_path[1]
+
+ connection := &circleci.Client{Token: b.Secret, HTTPClient: client, Debug: true}
+ builds, err := connection.ListRecentBuildsForProject(account, repo, b.Branch, "", -1, 0)
+ if err != nil {
+ log.Println(err)
+ return nil, err
+ }
+
+ for _, build := range builds {
+ log.Printf("Found build: %d, status %s\n", build.BuildNum, build.Status)
+ metadata, err := connection.ListTestMetadata(account, repo, build.BuildNum)
+ if err != nil {
+ log.Println(err)
+ return nil, err
+ }
+
+ artifacts, err := connection.ListBuildArtifacts(account, repo, build.BuildNum)
+ for _, artifact := range artifacts {
+ log.Println("Found artifact:", artifact.URL)
+ }
+
+ for _, test := range metadata {
+ log.Println("Found test:", test.Result, test.Name, test.RunTime)
+ }
+ }
+
+ return nil, nil
+}
blob - /dev/null
blob + 036bd24a3d6f4d2c4daf823e22c49e5c090194fe (mode 644)
--- /dev/null
+++ backends/backend_circleci_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+
+package backends
+
+import "testing"
+
+func TestSyncCircleCI(t *testing.T) {
+ t.Log("TestSyncCircleCI")
+}
blob - /dev/null
blob + b8a4f49dd4208ff17e87a571d3c1476e4fef540e (mode 644)
--- /dev/null
+++ backends/backend_cirrusci.go
+// https://cirrus-ci.org/api/
+// https://github.com/cirruslabs/cirrus-ci-web/blob/master/schema.graphql
+
+/*
+
+#!/bin/sh
+
+# https://github.com/cirruslabs/cirrus-ci-web/blob/master/schema.graphql
+
+curl -s -X POST --data \
+'{
+ "query": "query BuildBySHAQuery($owner: String!, $name: String!, $SHA: String) { searchBuilds(repositoryOwner: $owner, repositoryName: $name, SHA: $SHA) { id } }",
+ "variables": {
+ "owner": "qemu",
+ "name": "qemu",
+ "SHA": "43d1455cf84283466e5c22a217db5ef4b8197b14"
+ }
+}' \
+https://api.cirrus-ci.com/graphql | python -m json.tool
+*/
+
+package backends
+
+import (
+ "github.com/machinebox/graphql"
+ "github.com/ligurio/testres-db/formats"
+ "golang.org/x/net/context"
+ "net/http"
+)
+
+func SyncCirrusCI(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ graphql_scheme := "https://api.cirrus-ci.com/graphql"
+ ClientOption := graphql.WithHTTPClient(client)
+ connection := graphql.NewClient(graphql_scheme, ClientOption)
+ request := ""
+ req := graphql.NewRequest(request)
+
+ type response struct {
+ Name string
+ Items struct {
+ Records []struct {
+ Title string
+ }
+ }
+ }
+
+ var respData response
+ ctx := context.Background()
+ if err := connection.Run(ctx, req, &respData); err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+}
+
+/*
+type Root {
+ viewer: User
+ repository(id: ID!): Repository
+ githubRepository(owner: String!, name: String!): Repository
+ githubRepositories(owner: String!): [Repository]
+ githubOrganizationInfo(organization: String!): GitHubOrganizationInfo
+ build(id: ID!): Build
+ searchBuilds(repositoryOwner: String!, repositoryName: String!, SHA: String): [Build]
+ task(id: ID!): Task
+ webhookDelivery(id: String!): WebHookDelivery
+}
+
+type Build {
+ id: ID!
+ repositoryId: ID!
+ branch: String!
+ changeIdInRepo: String!
+ changeMessageTitle: String
+ changeMessage: String
+ durationInSeconds: Int
+ clockDurationInSeconds: Int
+ pullRequest: Int
+ checkSuiteId: Int
+ isSenderUserCollaborator: Boolean
+ senderUserPermissions: String
+ changeTimestamp: Int!
+ buildCreatedTimestamp: Int!
+ status: BuildStatus
+ notifications: [Notification]
+ tasks: [Task]
+ taskGroupsAmount: Int
+ latestGroupTasks: [Task]
+ repository: Repository!
+ viewerPermission: PermissionType!
+}
+
+type Task {
+ id: ID!
+ buildId: ID!
+ repositoryId: ID!
+ name: String!
+ status: TaskStatus
+ notifications: [Notification]
+ commands: [TaskCommand]
+ artifacts: [Artifacts]
+ commandLogsTail(name: String!): [String]
+ statusTimestamp: Int!
+ creationTimestamp: Int!
+ scheduledTimestamp: Int!
+ executingTimestamp: Int!
+ finalStatusTimestamp: Int!
+ durationInSeconds: Int!
+ labels: [String]
+ uniqueLabels: [String]
+ requiredPRLabels: [String]
+ optional: Boolean
+ statusDurations: [TaskStatusDuration]
+ repository: Repository!
+ build: Build!
+ previousRuns: [Task]
+ allOtherRuns: [Task]
+ dependencies: [Task]
+ automaticReRun: Boolean!
+ useComputeCredits: Boolean!
+ usedComputeCredits: Boolean!
+ transaction: AccountTransaction
+ triggerType: TaskTriggerType!
+ instanceResources: InstanceResources
+}
+
+
+
+*/
blob - /dev/null
blob + 500df8262601f0e5a8f62822b1c6e6a77789f59d (mode 644)
--- /dev/null
+++ backends/backend_cirrusci_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+
+package backends
+
+import "testing"
+
+func TestSyncCirrusCI(t *testing.T) {
+ t.Log("TestSyncCirrusCI")
+}
blob - /dev/null
blob + 0e2d89eb711612206f1df7348fa41e333d7aad21 (mode 644)
--- /dev/null
+++ backends/backend_common.go
+package backends
+
+import (
+ "crypto/tls"
+ "errors"
+ "github.com/ligurio/testres-db/formats"
+ "io"
+ "log"
+ "net/http"
+ "os"
+)
+
+type fnSyncBackend func(client *http.Client, b *Backend) (*[]formats.TestResult, error)
+
+var backend = map[string]fnSyncBackend{
+ "azure_devops": SyncAzureDevOps,
+ "circleci": SyncCircleCI,
+ "cirrusci": SyncCirrusCI,
+ "gitlab": SyncGitLab,
+ "jenkins": SyncJenkins,
+ "teamcity": SyncTeamCity,
+ "travisci": SyncTravisCI,
+}
+
+type Backend struct {
+ Name string
+ Base string
+ Project string
+ Branch string
+ Username string
+ Secret string
+ Pipeline string
+ Type string
+ Artifacts []Artifact
+}
+
+type Artifact struct {
+ Path string
+}
+
+var (
+ errUnknownBackend = errors.New("Unknown backend")
+)
+
+// https://stackoverflow.com/questions/38822764/how-to-send-a-https-request-with-a-certificate-golang/38825553#38825553
+func NewAPIClient() *http.Client {
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ }
+ client := &http.Client{Transport: tr}
+
+ return client
+}
+
+func (b *Backend) GetTestResults() (*[]formats.TestResult, error) {
+ log.Println("Backend:", b.Type)
+ fn := backend[b.Type]
+ if fn == nil {
+ return nil, errUnknownBackend
+ }
+ client := NewAPIClient()
+ result, err := fn(client, b)
+ if err != nil {
+ return nil, err
+ }
+
+ return result, nil
+}
+
+func DownloadFile(filename string, url string) error {
+ resp, err := http.Get(url)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ out, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, resp.Body)
+ return err
+}
blob - /dev/null
blob + 8cf732270551d2c04e4c75558243d22f4efac9fa (mode 644)
--- /dev/null
+++ backends/backend_gitlab.go
+package backends
+
+import (
+ "fmt"
+ "github.com/ligurio/testres-db/formats"
+ gitlab "github.com/xanzy/go-gitlab"
+ "log"
+ "net/http"
+ "path/filepath"
+)
+
+func SyncGitLab(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ if b.Pipeline != "" {
+ log.Println("Option pipeline is specified, but unused")
+ }
+
+ gl := gitlab.NewClient(client, b.Secret)
+ gl.SetBaseURL(b.Base)
+
+ projOpt := &gitlab.GetProjectOptions{
+ Statistics: gitlab.Bool(false),
+ License: gitlab.Bool(false),
+ WithCustomAttributes: gitlab.Bool(false),
+ }
+
+ p, _, err := gl.Projects.GetProject(b.Project, projOpt, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ /*
+
+ const (
+ Pending BuildStateValue = "pending"
+ Running BuildStateValue = "running"
+ Success BuildStateValue = "success"
+ Failed BuildStateValue = "failed"
+ Canceled BuildStateValue = "canceled"
+ Skipped BuildStateValue = "skipped"
+ Manual BuildStateValue = "manual"
+ )
+ */
+
+ projectOpt := &gitlab.ListProjectPipelinesOptions{
+ Scope: gitlab.String("finished"),
+ Status: gitlab.BuildState(gitlab.Success), // FIXME: use at least failed status
+ Ref: gitlab.String(b.Branch),
+ OrderBy: gitlab.String("updated_at"),
+ Sort: gitlab.String("asc"),
+ }
+
+ pipelines, _, err := gl.Pipelines.ListProjectPipelines(p.ID, projectOpt)
+ if err != nil {
+ return nil, err
+ }
+
+ jobsOpt := &gitlab.ListJobsOptions{
+ ListOptions: gitlab.ListOptions{Page: 1, PerPage: 10},
+ Scope: []gitlab.BuildStateValue{"created", "pending", "running", "failed", "success", "canceled", "skipped"},
+ }
+ for _, pipeline := range pipelines {
+ log.Printf("Found pipeline: %d, status %s", pipeline.ID, pipeline.Status)
+ log.Printf("SHA %s, Ref %s", pipeline.SHA, pipeline.Ref)
+ jobs, _, err := gl.Jobs.ListPipelineJobs(p.ID, pipeline.ID, jobsOpt, nil)
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+ for _, job := range jobs {
+ log.Println("Found job", job.ID, job.Name, job.Status)
+ for _, artifact := range job.Artifacts {
+ log.Printf("Found file %s (%d)", artifact.Filename, artifact.Size)
+ fnParse := formats.Parser[filepath.Ext(artifact.Filename)]
+ if fnParse == nil {
+ continue
+ }
+ fileUrl := artifact.Filename
+ if err := DownloadFile(artifact.Filename, fileUrl); err != nil {
+ log.Println(err)
+ continue
+ }
+ report, err := fnParse(artifact.Filename)
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+ report.Name = fmt.Sprintf("%d", job.ID)
+ /* FIXME: report.CreatedAt = job.CreatedAt */
+ }
+ }
+ }
+
+ return nil, nil
+}
blob - /dev/null
blob + 59625003d2687755f49cb819f727e227e253567c (mode 644)
--- /dev/null
+++ backends/backend_gitlab_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+package backends
+
+import "testing"
+
+func TestSyncGitLab(t *testing.T) {
+ t.Log("TestSyncGitLab")
+}
blob - /dev/null
blob + efaa13f34f77727574619f5b1c99af0208c27adc (mode 644)
--- /dev/null
+++ backends/backend_jenkins.go
+package backends
+
+import (
+ "github.com/bndr/gojenkins"
+ "github.com/ligurio/testres-db/formats"
+ "log"
+ "net/http"
+)
+
+func SyncJenkins(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ var jenkins *gojenkins.Jenkins
+ jenkins = gojenkins.CreateJenkins(client, b.Base, b.Username, b.Secret)
+ _, err := jenkins.Init()
+ if err != nil {
+ return nil, err
+ }
+
+ jobBuilds, err := jenkins.GetAllBuildIds(b.Pipeline)
+ if err != nil {
+ return nil, err
+ }
+
+ results := make([]formats.TestResult, len(jobBuilds))
+ for _, jobBuild := range jobBuilds {
+ buildNum, err := jenkins.GetBuild(b.Pipeline, jobBuild.Number)
+ if err != nil {
+ return &results, err
+ }
+ log.Println(jobBuild.URL, buildNum.GetResult())
+ TestResult, err := buildNum.GetResultSet()
+ if err != nil {
+ return &results, err
+ }
+ var testcases []formats.TestCase
+ for _, suite := range TestResult.Suites {
+ var testcase formats.TestCase
+ for _, test := range suite.Cases {
+ testcase = formats.TestCase{Name: test.Name}
+ }
+ testcases = append(testcases, testcase)
+ }
+ buildInfo := buildNum.Info()
+ var result = formats.TestResult{Name: buildInfo.ID, TestCases: testcases}
+ results = append(results, result)
+ }
+
+ return &results, nil
+}
blob - /dev/null
blob + 2cd221cd08cb22dae115b87846d232691ab88dad (mode 644)
--- /dev/null
+++ backends/backend_jenkins_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+package backends
+
+import "testing"
+
+func TestSyncJenkins(t *testing.T) {
+ t.Log("TestSyncJenkins")
+}
blob - /dev/null
blob + c4e4e043f581325d356cf695e7824fe7d5f735a5 (mode 644)
--- /dev/null
+++ backends/backend_teamcity.go
+// https://confluence.jetbrains.com/display/TCD10/REST+API
+// https://www.jetbrains.com/help/teamcity/rest-api.html
+
+package backends
+
+import (
+ teamcity "github.com/cvbarros/go-teamcity/teamcity"
+ "github.com/ligurio/testres-db/formats"
+ "log"
+ "net/http"
+)
+
+func SyncTeamCity(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ connection, err := teamcity.NewWithAddress(b.Username, b.Secret, b.Base, client)
+ if err != nil {
+ return nil, err
+ }
+ project, _ := connection.Projects.GetByID("TestNG_BuildTestsWithGradle")
+ if err != nil {
+ return nil, err
+ }
+ log.Println(project)
+
+ // https://godoc.org/github.com/abourget/teamcity#TestOccurrence
+
+ return nil, nil
+}
blob - /dev/null
blob + e1efe0e5634409fbcdfcc6180be7e6e71a6e23d1 (mode 644)
--- /dev/null
+++ backends/backend_teamcity_test.go
+// https://github.com/drone/drone-go/blob/master/drone/client_test.go
+// https://github.com/ktrysmt/go-bitbucket/blob/master/tests/repository_test.go
+package backends
+
+import "testing"
+
+func TestSyncTeamCityCI(t *testing.T) {
+ t.Log("TestSyncTeamCityCI")
+}
blob - /dev/null
blob + 695ea07b5ddc4358ce4b62f515533e69bda9e3d7 (mode 644)
--- /dev/null
+++ backends/backend_travisci.go
+package backends
+
+import (
+ "context"
+ "fmt"
+ "github.com/ligurio/testres-db/formats"
+ travisci "github.com/shuheiktgw/go-travis"
+ "log"
+ "net/http"
+ "path"
+)
+
+func SyncTravisCI(client *http.Client, b *Backend) (*[]formats.TestResult, error) {
+ connection := travisci.NewClient(b.Base, b.Secret)
+ connection.HTTPClient = client
+ build_service := connection.Builds
+
+ ctx := context.Background()
+ var options travisci.BuildsOption
+ builds, _, err := build_service.List(ctx, &options)
+ if err != nil {
+ return nil, err
+ }
+
+ results := make([]formats.TestResult, len(builds))
+ baseURL := "https://travis-ci.org/"
+ for _, build := range builds {
+ // https://godoc.org/github.com/shuheiktgw/go-travis#Build
+ metadata := *build.Metadata
+ log.Printf("Found build: %s, status %s\n", path.Join(baseURL, b.Pipeline, *metadata.Href), *build.State)
+ buildId := fmt.Sprintf("%d", build.Id)
+ var testcases []formats.TestCase
+ var result = formats.TestResult{Name: buildId}
+ log.Println("BuildOn", *build.FinishedAt)
+ //var result = formats.TestResult{Name: buildId, CreatedAt: *build.FinishedAt}
+ var testcase formats.TestCase
+ for _, job := range build.Jobs {
+ // https://godoc.org/github.com/shuheiktgw/go-travis#Job
+ if job.State == nil {
+ continue
+ }
+ log.Println("Job ID: ", *job.Id)
+ testcase = formats.TestCase{Name: "XXX"}
+ }
+ testcases = append(testcases, testcase)
+ result.TestCases = testcases
+ results = append(results, result)
+ }
+
+ return nil, nil
+}
blob - /dev/null
blob + 742a81166b6218919691604be82fc1f745955785 (mode 644)
--- /dev/null
+++ backends/client_test.go_
+package drone
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+//
+// user tests.
+//
+
+func TestSelf(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.Self()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/user.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(User)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestUser(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.User("octocat")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/user.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(User)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestUserList(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.UserList()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/users.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*User{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestUserDelete(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.UserDelete("octocat")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+func TestUserCreate(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.UserCreate(&User{})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/user.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(User)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestUserUpdate(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.UserUpdate("octocat", &UserPatch{})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/user.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(User)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+//
+// repos
+//
+
+func TestRepo(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.Repo("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repo.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Repo)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestRepoList(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.RepoList()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repos.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*Repo{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestRepoListSync(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.RepoListSync()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repos.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*Repo{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestRepoEnable(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.RepoEnable("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repo.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Repo)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestRepoDisable(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.RepoDisable("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+func TestRepoRepair(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.RepoRepair("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+func TestRepoChown(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.RepoChown("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repo.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Repo)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestRepoUpdate(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.RepoUpdate("octocat", "hello-world", &RepoPatch{})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/repo.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Repo)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+//
+// cron jobs
+//
+
+func TestCron(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.Cron("octocat", "hello-world", "nightly")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/cron.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Cron)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestCronList(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.CronList("octocat", "hello-world")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/crons.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*Cron{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+// func TestCronDisable(t *testing.T) {
+// ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+// defer ts.Close()
+
+// client := New(ts.URL)
+// err := client.CronDisable("octocat", "hello-world", "nightly")
+// if err != nil {
+// t.Error(err)
+// return
+// }
+// }
+
+// func TestCronEnable(t *testing.T) {
+// ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+// defer ts.Close()
+
+// client := New(ts.URL)
+// err := client.CronEnable("octocat", "hello-world", "nightly")
+// if err != nil {
+// t.Error(err)
+// return
+// }
+// }
+
+//
+// builds
+//
+
+func TestBuild(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.Build("octocat", "hello-world", 1)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/build.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Build)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestBuildLast(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.BuildLast("octocat", "hello-world", "master")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/build.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Build)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestBuildList(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.BuildList("octocat", "hello-world", ListOptions{})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/builds.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*Build{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+// func TestBuildQueue(t *testing.T) {
+// ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+// defer ts.Close()
+
+// client := New(ts.URL)
+// got, err := client.BuildQueue()
+// if err != nil {
+// t.Error(err)
+// return
+// }
+
+// in, err := ioutil.ReadFile("testdata/builds.json.golden")
+// if err != nil {
+// t.Error(err)
+// return
+// }
+// want := []*Build{}
+// err = json.Unmarshal(in, &want)
+// if err != nil {
+// t.Error(err)
+// return
+// }
+// if diff := cmp.Diff(got, want); diff != "" {
+// t.Errorf("Unexpected response")
+// t.Log(diff)
+// }
+// }
+
+func TestBuildRestart(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.BuildRestart("octocat", "hello-world", 99, nil)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/build.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := new(Build)
+ err = json.Unmarshal(in, want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestBuildCancel(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.BuildCancel("octocat", "hello-world", 1)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestApprove(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.Approve("octocat", "hello-world", 1, 2)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestDecline(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.Decline("octocat", "hello-world", 1, 3)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+//
+// logs
+//
+
+func TestLogs(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ got, err := client.Logs("octocat", "hello-world", 1, 2, 3)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ in, err := ioutil.ReadFile("testdata/logs.json.golden")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ want := []*Line{}
+ err = json.Unmarshal(in, &want)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Errorf("Unexpected response")
+ t.Log(diff)
+ }
+}
+
+func TestLogsPurge(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(mockHandler))
+ defer ts.Close()
+
+ client := New(ts.URL)
+ err := client.LogsPurge("octocat", "hello-world", 1, 2, 3)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+}
+
+//
+// mock server and testdata.
+//
+
+func mockHandler(w http.ResponseWriter, r *http.Request) {
+ routes := []struct {
+ verb string
+ path string
+ body string
+ code int
+ }{
+ //
+ // users
+ //
+ {
+ verb: "GET",
+ path: "/api/user",
+ body: "testdata/user.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/users/octocat",
+ body: "testdata/user.json",
+ code: 200,
+ },
+ {
+ verb: "DELETE",
+ path: "/api/users/octocat",
+ code: 204,
+ },
+ {
+ verb: "POST",
+ path: "/api/users",
+ body: "testdata/user.json",
+ code: 200,
+ },
+ {
+ verb: "PATCH",
+ path: "/api/users/octocat",
+ body: "testdata/user.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/users",
+ body: "testdata/users.json",
+ code: 200,
+ },
+ //
+ // repos
+ //
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world",
+ body: "testdata/repo.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/user/repos",
+ body: "testdata/repos.json",
+ code: 200,
+ },
+ {
+ verb: "POST",
+ path: "/api/user/repos",
+ body: "testdata/repos.json",
+ code: 200,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/repair",
+ code: 204,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/chown",
+ body: "testdata/repo.json",
+ code: 200,
+ },
+ {
+ verb: "PATCH",
+ path: "/api/repos/octocat/hello-world",
+ body: "testdata/repo.json",
+ code: 200,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world",
+ body: "testdata/repo.json",
+ code: 200,
+ },
+ {
+ verb: "DELETE",
+ path: "/api/repos/octocat/hello-world",
+ code: 204,
+ },
+ //
+ // crons
+ //
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/cron/nightly",
+ body: "testdata/cron.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/cron",
+ body: "testdata/crons.json",
+ code: 200,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/cron/nightly",
+ code: 204,
+ },
+ {
+ verb: "DELETE",
+ path: "/api/repos/octocat/hello-world/cron/nightly",
+ code: 204,
+ },
+ //
+ // builds
+ //
+ {
+ verb: "GET",
+ path: "/api/system/builds",
+ body: "testdata/builds.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/builds",
+ body: "testdata/builds.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/builds/1",
+ body: "testdata/build.json",
+ code: 200,
+ },
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/builds/latest",
+ body: "testdata/build.json",
+ code: 200,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/builds/99",
+ body: "testdata/build.json",
+ code: 200,
+ },
+ {
+ verb: "DELETE",
+ path: "/api/repos/octocat/hello-world/builds/1",
+ code: 204,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/builds/1/approve/2",
+ code: 204,
+ },
+ {
+ verb: "POST",
+ path: "/api/repos/octocat/hello-world/builds/1/decline/3",
+ code: 204,
+ },
+ //
+ // logs
+ //
+ {
+ verb: "GET",
+ path: "/api/repos/octocat/hello-world/builds/1/logs/2/3",
+ body: "testdata/logs.json",
+ code: 200,
+ },
+ {
+ verb: "DELETE",
+ path: "/api/repos/octocat/hello-world/builds/1/logs/2/3",
+ code: 204,
+ },
+ }
+
+ path := r.URL.Path
+ verb := r.Method
+ for _, route := range routes {
+ if route.verb != verb {
+ continue
+ }
+ if route.path != path {
+ continue
+ }
+ if route.code == 204 {
+ w.WriteHeader(204)
+ return
+ }
+ body, err := ioutil.ReadFile(route.body)
+ if err != nil {
+ break
+ }
+ w.WriteHeader(route.code)
+ w.Write(body)
+ return
+ }
+ w.WriteHeader(404)
+}