func TestAreFastForkPackages(t *testing.T) { data := []struct { description string packages []database.Package auth *gddoexp.GithubAuth httpClient httpClientMock expected []gddoexp.FastForkResponse }{ { description: "it should detect that all packages are fast fork (without authentication)", packages: []database.Package{ {Path: "github.com/rafaeljusto/dns"}, {Path: "github.com/rafaeljusto/go-testdb"}, {Path: "github.com/rafaeljusto/mysql"}, {Path: "github.com/rafaeljusto/handy"}, {Path: "github.com/rafaeljusto/schema"}, }, httpClient: httpClientMock{ getMock: func(url string) (*http.Response, error) { if strings.HasSuffix(url, "/commits") { return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString(`[ { "commit": { "author": { "date": "2010-08-03T23:00:00Z" } } } ]`)), }, nil } return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString(`{ "created_at": "2010-08-03T21:56:23Z", "fork": true }`)), }, nil }, }, expected: []gddoexp.FastForkResponse{ { Path: "github.com/rafaeljusto/dns", FastFork: true, }, { Path: "github.com/rafaeljusto/go-testdb", FastFork: true, }, { Path: "github.com/rafaeljusto/handy", FastFork: true, }, { Path: "github.com/rafaeljusto/mysql", FastFork: true, }, { Path: "github.com/rafaeljusto/schema", FastFork: true, }, }, }, { description: "it should detect that all packages are fast fork (authenticated)", packages: []database.Package{ {Path: "github.com/rafaeljusto/dns"}, {Path: "github.com/rafaeljusto/go-testdb"}, {Path: "github.com/rafaeljusto/mysql"}, {Path: "github.com/rafaeljusto/handy"}, {Path: "github.com/rafaeljusto/schema"}, }, auth: &gddoexp.GithubAuth{ ID: "exampleuser", Secret: "abc123", }, httpClient: httpClientMock{ getMock: func(url string) (*http.Response, error) { if !strings.HasSuffix(url, "?client_id=exampleuser&client_secret=abc123") { return &http.Response{ StatusCode: http.StatusBadRequest, }, nil } url = strings.TrimSuffix(url, "?client_id=exampleuser&client_secret=abc123") if strings.HasSuffix(url, "/commits") { return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString(`[ { "commit": { "author": { "date": "2010-08-03T23:00:00Z" } } } ]`)), }, nil } return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString(`{ "created_at": "2010-08-03T21:56:23Z", "fork": true }`)), }, nil }, }, expected: []gddoexp.FastForkResponse{ { Path: "github.com/rafaeljusto/dns", FastFork: true, }, { Path: "github.com/rafaeljusto/go-testdb", FastFork: true, }, { Path: "github.com/rafaeljusto/handy", FastFork: true, }, { Path: "github.com/rafaeljusto/mysql", FastFork: true, }, { Path: "github.com/rafaeljusto/schema", FastFork: true, }, }, }, { description: "it should detect that all packages are fast fork (cache hits)", packages: []database.Package{ {Path: "github.com/rafaeljusto/dns"}, {Path: "github.com/rafaeljusto/go-testdb"}, {Path: "github.com/rafaeljusto/mysql"}, {Path: "github.com/rafaeljusto/handy"}, {Path: "github.com/rafaeljusto/schema"}, }, httpClient: httpClientMock{ getMock: func(url string) (*http.Response, error) { if strings.HasSuffix(url, "/commits") { return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{ "Cache": []string{"1"}, }, Body: ioutil.NopCloser(bytes.NewBufferString(`[ { "commit": { "author": { "date": "2010-08-03T23:00:00Z" } } } ]`)), }, nil } return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{ "Cache": []string{"1"}, }, Body: ioutil.NopCloser(bytes.NewBufferString(`{ "created_at": "2010-08-03T21:56:23Z", "fork": true }`)), }, nil }, }, expected: []gddoexp.FastForkResponse{ { Path: "github.com/rafaeljusto/dns", FastFork: true, Cache: true, }, { Path: "github.com/rafaeljusto/go-testdb", FastFork: true, Cache: true, }, { Path: "github.com/rafaeljusto/handy", FastFork: true, Cache: true, }, { Path: "github.com/rafaeljusto/mysql", FastFork: true, Cache: true, }, { Path: "github.com/rafaeljusto/schema", FastFork: true, Cache: true, }, }, }, { description: "it should detect that all packages are fast fork (partial cache hits)", packages: []database.Package{ {Path: "github.com/rafaeljusto/dns"}, {Path: "github.com/rafaeljusto/go-testdb"}, {Path: "github.com/rafaeljusto/mysql"}, {Path: "github.com/rafaeljusto/handy"}, {Path: "github.com/rafaeljusto/schema"}, }, httpClient: httpClientMock{ getMock: func(url string) (*http.Response, error) { if strings.HasSuffix(url, "/commits") { return &http.Response{ StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewBufferString(`[ { "commit": { "author": { "date": "2010-08-03T23:00:00Z" } } } ]`)), }, nil } return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{ "Cache": []string{"1"}, }, Body: ioutil.NopCloser(bytes.NewBufferString(`{ "created_at": "2010-08-03T21:56:23Z", "fork": true }`)), }, nil }, }, expected: []gddoexp.FastForkResponse{ { Path: "github.com/rafaeljusto/dns", FastFork: true, }, { Path: "github.com/rafaeljusto/go-testdb", FastFork: true, }, { Path: "github.com/rafaeljusto/handy", FastFork: true, }, { Path: "github.com/rafaeljusto/mysql", FastFork: true, }, { Path: "github.com/rafaeljusto/schema", FastFork: true, }, }, }, } httpClientBkp := gddoexp.HTTPClient defer func() { gddoexp.HTTPClient = httpClientBkp }() isCacheResponseBkp := gddoexp.IsCacheResponse defer func() { gddoexp.IsCacheResponse = isCacheResponseBkp }() gddoexp.IsCacheResponse = func(r *http.Response) bool { return r.Header.Get("Cache") == "1" } for i, item := range data { gddoexp.HTTPClient = item.httpClient var responses []gddoexp.FastForkResponse for response := range gddoexp.AreFastForkPackages(item.packages, item.auth) { responses = append(responses, response) } sort.Sort(byFastForkResponsePath(responses)) if !reflect.DeepEqual(item.expected, responses) { t.Errorf("[%d] %s: mismatch responses.\n%v", i, item.description, diff(item.expected, responses)) } } }
func main() { clientID := flag.String("id", "", "Github client ID") clientSecret := flag.String("secret", "", "Github client secret") file := flag.String("file", "", "File containing the list of packages") output := flag.String("output", "gddofork.out", "Output file") progress := flag.Bool("progress", false, "Show a progress bar") flag.Parse() var auth *gddoexp.GithubAuth if (clientID != nil && *clientID != "") || (clientSecret != nil && *clientSecret != "") { if *clientID == "" || *clientSecret == "" { fmt.Println("to enable Gthub authentication, you need to inform the id and secret") flag.PrintDefaults() return } auth = &gddoexp.GithubAuth{ ID: *clientID, Secret: *clientSecret, } } var pkgs []database.Package var err error if file != nil && *file != "" { pkgs, err = readFromFile(*file) } else { pkgs, err = readFromStdin() } if err != nil { fmt.Println(err) return } o, err := os.OpenFile(*output, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { fmt.Println("error creating output file:", err) return } defer o.Close() log.SetOutput(o) log.Println("BEGIN") log.Printf("%d packages will be analyzed", len(pkgs)) var progressBar *pb.ProgressBar if progress != nil && *progress { progressBar = pb.StartNew(len(pkgs)) } var cache int for response := range gddoexp.AreFastForkPackages(pkgs, auth) { if progress != nil && *progress { progressBar.Increment() } if response.Cache { cache++ } if response.Error != nil { log.Println(response.Error) } else if response.FastFork { log.Printf("package “%s” is a fast fork\n", response.Path) if progress != nil && !*progress { fmt.Println(response.Path) } } else { log.Printf("package “%s” is not a fast fork\n", response.Path) } } if progress != nil && *progress { progressBar.Finish() } log.Println("Cache hits:", cache) log.Println("END") }