Compare commits
1 commit
main
...
feat/reque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d7c7c4634 |
23 changed files with 183 additions and 415 deletions
11
cmd/all.go
11
cmd/all.go
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/Snider/Borg/pkg/compress"
|
||||
"github.com/Snider/Borg/pkg/datanode"
|
||||
"github.com/Snider/Borg/pkg/github"
|
||||
"github.com/Snider/Borg/pkg/httpclient"
|
||||
"github.com/Snider/Borg/pkg/tim"
|
||||
"github.com/Snider/Borg/pkg/trix"
|
||||
"github.com/Snider/Borg/pkg/ui"
|
||||
|
|
@ -42,7 +43,15 @@ func NewAllCmd() *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
repos, err := GithubClient.GetPublicRepos(cmd.Context(), owner)
|
||||
totalTimeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
connectTimeout, _ := cmd.Flags().GetDuration("connect-timeout")
|
||||
tlsTimeout, _ := cmd.Flags().GetDuration("tls-timeout")
|
||||
headerTimeout, _ := cmd.Flags().GetDuration("header-timeout")
|
||||
|
||||
httpClient := httpclient.NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
githubClient := github.NewGithubClient(httpClient)
|
||||
|
||||
repos, err := githubClient.GetPublicRepos(cmd.Context(), owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func TestAllCmd_Good(t *testing.T) {
|
|||
},
|
||||
})
|
||||
oldNewAuthenticatedClient := github.NewAuthenticatedClient
|
||||
github.NewAuthenticatedClient = func(ctx context.Context) *http.Client {
|
||||
github.NewAuthenticatedClient = func(ctx context.Context, baseClient *http.Client) *http.Client {
|
||||
return mockGithubClient
|
||||
}
|
||||
defer func() {
|
||||
|
|
@ -67,7 +67,7 @@ func TestAllCmd_Bad(t *testing.T) {
|
|||
},
|
||||
})
|
||||
oldNewAuthenticatedClient := github.NewAuthenticatedClient
|
||||
github.NewAuthenticatedClient = func(ctx context.Context) *http.Client {
|
||||
github.NewAuthenticatedClient = func(ctx context.Context, baseClient *http.Client) *http.Client {
|
||||
return mockGithubClient
|
||||
}
|
||||
defer func() {
|
||||
|
|
@ -96,7 +96,7 @@ func TestAllCmd_Ugly(t *testing.T) {
|
|||
},
|
||||
})
|
||||
oldNewAuthenticatedClient := github.NewAuthenticatedClient
|
||||
github.NewAuthenticatedClient = func(ctx context.Context) *http.Client {
|
||||
github.NewAuthenticatedClient = func(ctx context.Context, baseClient *http.Client) *http.Client {
|
||||
return mockGithubClient
|
||||
}
|
||||
defer func() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -11,11 +13,18 @@ func init() {
|
|||
RootCmd.AddCommand(GetCollectCmd())
|
||||
}
|
||||
func NewCollectCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "collect",
|
||||
Short: "Collect a resource from a URI.",
|
||||
Long: `Collect a resource from a URI and store it in a DataNode.`,
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Duration("timeout", 0, "Total request timeout (e.g., 60s). 0 means no timeout.")
|
||||
cmd.PersistentFlags().Duration("connect-timeout", 10*time.Second, "TCP connection establishment timeout (e.g., 10s)")
|
||||
cmd.PersistentFlags().Duration("tls-timeout", 10*time.Second, "TLS handshake timeout (e.g., 10s)")
|
||||
cmd.PersistentFlags().Duration("header-timeout", 30*time.Second, "Time to receive response headers timeout (e.g., 30s)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func GetCollectCmd() *cobra.Command {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/Snider/Borg/pkg/compress"
|
||||
"github.com/Snider/Borg/pkg/httpclient"
|
||||
"github.com/Snider/Borg/pkg/tim"
|
||||
"github.com/Snider/Borg/pkg/trix"
|
||||
"github.com/Snider/Borg/pkg/ui"
|
||||
|
|
@ -44,6 +45,13 @@ func NewCollectGithubRepoCmd() *cobra.Command {
|
|||
return fmt.Errorf("invalid compression: %s (must be 'none', 'gz', or 'xz')", compression)
|
||||
}
|
||||
|
||||
totalTimeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
connectTimeout, _ := cmd.Flags().GetDuration("connect-timeout")
|
||||
tlsTimeout, _ := cmd.Flags().GetDuration("tls-timeout")
|
||||
headerTimeout, _ := cmd.Flags().GetDuration("header-timeout")
|
||||
|
||||
_ = httpclient.NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
|
||||
prompter := ui.NewNonInteractivePrompter(ui.GetVCSQuote)
|
||||
prompter.Start()
|
||||
defer prompter.Stop()
|
||||
|
|
|
|||
|
|
@ -4,20 +4,24 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/Snider/Borg/pkg/github"
|
||||
"github.com/Snider/Borg/pkg/httpclient"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// GithubClient is the github client used by the command. It can be replaced for testing.
|
||||
GithubClient = github.NewGithubClient()
|
||||
)
|
||||
|
||||
var collectGithubReposCmd = &cobra.Command{
|
||||
Use: "repos [user-or-org]",
|
||||
Short: "Collects all public repositories for a user or organization",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
repos, err := GithubClient.GetPublicRepos(cmd.Context(), args[0])
|
||||
totalTimeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
connectTimeout, _ := cmd.Flags().GetDuration("connect-timeout")
|
||||
tlsTimeout, _ := cmd.Flags().GetDuration("tls-timeout")
|
||||
headerTimeout, _ := cmd.Flags().GetDuration("header-timeout")
|
||||
|
||||
httpClient := httpclient.NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
githubClient := github.NewGithubClient(httpClient)
|
||||
|
||||
repos, err := githubClient.GetPublicRepos(cmd.Context(), args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,333 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Snider/Borg/pkg/compress"
|
||||
"github.com/Snider/Borg/pkg/datanode"
|
||||
"github.com/Snider/Borg/pkg/tim"
|
||||
"github.com/Snider/Borg/pkg/trix"
|
||||
"github.com/Snider/Borg/pkg/ui"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type CollectLocalCmd struct {
|
||||
cobra.Command
|
||||
}
|
||||
|
||||
// NewCollectLocalCmd creates a new collect local command
|
||||
func NewCollectLocalCmd() *CollectLocalCmd {
|
||||
c := &CollectLocalCmd{}
|
||||
c.Command = cobra.Command{
|
||||
Use: "local [directory]",
|
||||
Short: "Collect files from a local directory",
|
||||
Long: `Collect files from a local directory and store them in a DataNode.
|
||||
|
||||
If no directory is specified, the current working directory is used.
|
||||
|
||||
Examples:
|
||||
borg collect local
|
||||
borg collect local ./src
|
||||
borg collect local /path/to/project --output project.tar
|
||||
borg collect local . --format stim --password secret
|
||||
borg collect local . --exclude "*.log" --exclude "node_modules"`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
directory := "."
|
||||
if len(args) > 0 {
|
||||
directory = args[0]
|
||||
}
|
||||
|
||||
outputFile, _ := cmd.Flags().GetString("output")
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
compression, _ := cmd.Flags().GetString("compression")
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
excludes, _ := cmd.Flags().GetStringSlice("exclude")
|
||||
includeHidden, _ := cmd.Flags().GetBool("hidden")
|
||||
respectGitignore, _ := cmd.Flags().GetBool("gitignore")
|
||||
|
||||
finalPath, err := CollectLocal(directory, outputFile, format, compression, password, excludes, includeHidden, respectGitignore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "Files saved to", finalPath)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
c.Flags().String("output", "", "Output file for the DataNode")
|
||||
c.Flags().String("format", "datanode", "Output format (datanode, tim, trix, or stim)")
|
||||
c.Flags().String("compression", "none", "Compression format (none, gz, or xz)")
|
||||
c.Flags().String("password", "", "Password for encryption (required for stim/trix format)")
|
||||
c.Flags().StringSlice("exclude", nil, "Patterns to exclude (can be specified multiple times)")
|
||||
c.Flags().Bool("hidden", false, "Include hidden files and directories")
|
||||
c.Flags().Bool("gitignore", true, "Respect .gitignore files (default: true)")
|
||||
return c
|
||||
}
|
||||
|
||||
func init() {
|
||||
collectCmd.AddCommand(&NewCollectLocalCmd().Command)
|
||||
}
|
||||
|
||||
// CollectLocal collects files from a local directory into a DataNode
|
||||
func CollectLocal(directory string, outputFile string, format string, compression string, password string, excludes []string, includeHidden bool, respectGitignore bool) (string, error) {
|
||||
// Validate format
|
||||
if format != "datanode" && format != "tim" && format != "trix" && format != "stim" {
|
||||
return "", fmt.Errorf("invalid format: %s (must be 'datanode', 'tim', 'trix', or 'stim')", format)
|
||||
}
|
||||
if (format == "stim" || format == "trix") && password == "" {
|
||||
return "", fmt.Errorf("password is required for %s format", format)
|
||||
}
|
||||
if compression != "none" && compression != "gz" && compression != "xz" {
|
||||
return "", fmt.Errorf("invalid compression: %s (must be 'none', 'gz', or 'xz')", compression)
|
||||
}
|
||||
|
||||
// Resolve directory path
|
||||
absDir, err := filepath.Abs(directory)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving directory path: %w", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(absDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error accessing directory: %w", err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return "", fmt.Errorf("not a directory: %s", absDir)
|
||||
}
|
||||
|
||||
// Load gitignore patterns if enabled
|
||||
var gitignorePatterns []string
|
||||
if respectGitignore {
|
||||
gitignorePatterns = loadGitignore(absDir)
|
||||
}
|
||||
|
||||
// Create DataNode and collect files
|
||||
dn := datanode.New()
|
||||
var fileCount int
|
||||
|
||||
bar := ui.NewProgressBar(-1, "Scanning files")
|
||||
defer bar.Finish()
|
||||
|
||||
err = filepath.WalkDir(absDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get relative path
|
||||
relPath, err := filepath.Rel(absDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip root
|
||||
if relPath == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip hidden files/dirs unless explicitly included
|
||||
if !includeHidden && isHidden(relPath) {
|
||||
if d.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check gitignore patterns
|
||||
if respectGitignore && matchesGitignore(relPath, d.IsDir(), gitignorePatterns) {
|
||||
if d.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check exclude patterns
|
||||
if matchesExclude(relPath, excludes) {
|
||||
if d.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip directories (they're implicit in DataNode)
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read file content
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", relPath, err)
|
||||
}
|
||||
|
||||
// Add to DataNode with forward slashes (tar convention)
|
||||
dn.AddData(filepath.ToSlash(relPath), content)
|
||||
fileCount++
|
||||
bar.Describe(fmt.Sprintf("Collected %d files", fileCount))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error walking directory: %w", err)
|
||||
}
|
||||
|
||||
if fileCount == 0 {
|
||||
return "", fmt.Errorf("no files found in %s", directory)
|
||||
}
|
||||
|
||||
bar.Describe(fmt.Sprintf("Packaging %d files", fileCount))
|
||||
|
||||
// Convert to output format
|
||||
var data []byte
|
||||
if format == "tim" {
|
||||
t, err := tim.FromDataNode(dn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating tim: %w", err)
|
||||
}
|
||||
data, err = t.ToTar()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error serializing tim: %w", err)
|
||||
}
|
||||
} else if format == "stim" {
|
||||
t, err := tim.FromDataNode(dn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating tim: %w", err)
|
||||
}
|
||||
data, err = t.ToSigil(password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error encrypting stim: %w", err)
|
||||
}
|
||||
} else if format == "trix" {
|
||||
data, err = trix.ToTrix(dn, password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error serializing trix: %w", err)
|
||||
}
|
||||
} else {
|
||||
data, err = dn.ToTar()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error serializing DataNode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply compression
|
||||
compressedData, err := compress.Compress(data, compression)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error compressing data: %w", err)
|
||||
}
|
||||
|
||||
// Determine output filename
|
||||
if outputFile == "" {
|
||||
baseName := filepath.Base(absDir)
|
||||
if baseName == "." || baseName == "/" {
|
||||
baseName = "local"
|
||||
}
|
||||
outputFile = baseName + "." + format
|
||||
if compression != "none" {
|
||||
outputFile += "." + compression
|
||||
}
|
||||
}
|
||||
|
||||
err = os.WriteFile(outputFile, compressedData, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing output file: %w", err)
|
||||
}
|
||||
|
||||
return outputFile, nil
|
||||
}
|
||||
|
||||
// isHidden checks if a path component starts with a dot
|
||||
func isHidden(path string) bool {
|
||||
parts := strings.Split(filepath.ToSlash(path), "/")
|
||||
for _, part := range parts {
|
||||
if strings.HasPrefix(part, ".") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// loadGitignore loads patterns from .gitignore if it exists
|
||||
func loadGitignore(dir string) []string {
|
||||
var patterns []string
|
||||
|
||||
gitignorePath := filepath.Join(dir, ".gitignore")
|
||||
content, err := os.ReadFile(gitignorePath)
|
||||
if err != nil {
|
||||
return patterns
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
// Skip empty lines and comments
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
patterns = append(patterns, line)
|
||||
}
|
||||
|
||||
return patterns
|
||||
}
|
||||
|
||||
// matchesGitignore checks if a path matches any gitignore pattern
|
||||
func matchesGitignore(path string, isDir bool, patterns []string) bool {
|
||||
for _, pattern := range patterns {
|
||||
// Handle directory-only patterns
|
||||
if strings.HasSuffix(pattern, "/") {
|
||||
if !isDir {
|
||||
continue
|
||||
}
|
||||
pattern = strings.TrimSuffix(pattern, "/")
|
||||
}
|
||||
|
||||
// Handle negation (simplified - just skip negated patterns)
|
||||
if strings.HasPrefix(pattern, "!") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Match against path components
|
||||
matched, _ := filepath.Match(pattern, filepath.Base(path))
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// Also try matching the full path
|
||||
matched, _ = filepath.Match(pattern, path)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle ** patterns (simplified)
|
||||
if strings.Contains(pattern, "**") {
|
||||
simplePattern := strings.ReplaceAll(pattern, "**", "*")
|
||||
matched, _ = filepath.Match(simplePattern, path)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesExclude checks if a path matches any exclude pattern
|
||||
func matchesExclude(path string, excludes []string) bool {
|
||||
for _, pattern := range excludes {
|
||||
// Match against basename
|
||||
matched, _ := filepath.Match(pattern, filepath.Base(path))
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match against full path
|
||||
matched, _ = filepath.Match(pattern, path)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/Snider/Borg/pkg/compress"
|
||||
"github.com/Snider/Borg/pkg/httpclient"
|
||||
"github.com/Snider/Borg/pkg/pwa"
|
||||
"github.com/Snider/Borg/pkg/tim"
|
||||
"github.com/Snider/Borg/pkg/trix"
|
||||
|
|
@ -13,17 +14,9 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type CollectPWACmd struct {
|
||||
cobra.Command
|
||||
PWAClient pwa.PWAClient
|
||||
}
|
||||
|
||||
// NewCollectPWACmd creates a new collect pwa command
|
||||
func NewCollectPWACmd() *CollectPWACmd {
|
||||
c := &CollectPWACmd{
|
||||
PWAClient: pwa.NewPWAClient(),
|
||||
}
|
||||
c.Command = cobra.Command{
|
||||
func NewCollectPWACmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "pwa [url]",
|
||||
Short: "Collect a single PWA using a URI",
|
||||
Long: `Collect a single PWA and store it in a DataNode.
|
||||
|
|
@ -44,7 +37,15 @@ Examples:
|
|||
compression, _ := cmd.Flags().GetString("compression")
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
|
||||
finalPath, err := CollectPWA(c.PWAClient, pwaURL, outputFile, format, compression, password)
|
||||
totalTimeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
connectTimeout, _ := cmd.Flags().GetDuration("connect-timeout")
|
||||
tlsTimeout, _ := cmd.Flags().GetDuration("tls-timeout")
|
||||
headerTimeout, _ := cmd.Flags().GetDuration("header-timeout")
|
||||
|
||||
httpClient := httpclient.NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
pwaClient := pwa.NewPWAClient(httpClient)
|
||||
|
||||
finalPath, err := CollectPWA(pwaClient, pwaURL, outputFile, format, compression, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -52,16 +53,16 @@ Examples:
|
|||
return nil
|
||||
},
|
||||
}
|
||||
c.Flags().String("uri", "", "The URI of the PWA to collect (can also be passed as positional arg)")
|
||||
c.Flags().String("output", "", "Output file for the DataNode")
|
||||
c.Flags().String("format", "datanode", "Output format (datanode, tim, trix, or stim)")
|
||||
c.Flags().String("compression", "none", "Compression format (none, gz, or xz)")
|
||||
c.Flags().String("password", "", "Password for encryption (required for stim format)")
|
||||
return c
|
||||
cmd.Flags().String("uri", "", "The URI of the PWA to collect (can also be passed as positional arg)")
|
||||
cmd.Flags().String("output", "", "Output file for the DataNode")
|
||||
cmd.Flags().String("format", "datanode", "Output format (datanode, tim, trix, or stim)")
|
||||
cmd.Flags().String("compression", "none", "Compression format (none, gz, or xz)")
|
||||
cmd.Flags().String("password", "", "Password for encryption (required for stim format)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
collectCmd.AddCommand(&NewCollectPWACmd().Command)
|
||||
collectCmd.AddCommand(NewCollectPWACmd())
|
||||
}
|
||||
func CollectPWA(client pwa.PWAClient, pwaURL string, outputFile string, format string, compression string, password string) (string, error) {
|
||||
if pwaURL == "" {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/Snider/Borg/pkg/compress"
|
||||
"github.com/Snider/Borg/pkg/httpclient"
|
||||
"github.com/Snider/Borg/pkg/tim"
|
||||
"github.com/Snider/Borg/pkg/trix"
|
||||
"github.com/Snider/Borg/pkg/ui"
|
||||
|
|
@ -51,7 +52,14 @@ func NewCollectWebsiteCmd() *cobra.Command {
|
|||
bar = ui.NewProgressBar(-1, "Crawling website")
|
||||
}
|
||||
|
||||
dn, err := website.DownloadAndPackageWebsite(websiteURL, depth, bar)
|
||||
totalTimeout, _ := cmd.Flags().GetDuration("timeout")
|
||||
connectTimeout, _ := cmd.Flags().GetDuration("connect-timeout")
|
||||
tlsTimeout, _ := cmd.Flags().GetDuration("tls-timeout")
|
||||
headerTimeout, _ := cmd.Flags().GetDuration("header-timeout")
|
||||
|
||||
client := httpclient.NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
|
||||
dn, err := website.DownloadAndPackageWebsite(websiteURL, depth, bar, client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error downloading and packaging website: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -14,7 +15,7 @@ import (
|
|||
func TestCollectWebsiteCmd_Good(t *testing.T) {
|
||||
// Mock the website downloader
|
||||
oldDownloadAndPackageWebsite := website.DownloadAndPackageWebsite
|
||||
website.DownloadAndPackageWebsite = func(startURL string, maxDepth int, bar *progressbar.ProgressBar) (*datanode.DataNode, error) {
|
||||
website.DownloadAndPackageWebsite = func(startURL string, maxDepth int, bar *progressbar.ProgressBar, client *http.Client) (*datanode.DataNode, error) {
|
||||
return datanode.New(), nil
|
||||
}
|
||||
defer func() {
|
||||
|
|
@ -35,7 +36,7 @@ func TestCollectWebsiteCmd_Good(t *testing.T) {
|
|||
func TestCollectWebsiteCmd_Bad(t *testing.T) {
|
||||
// Mock the website downloader to return an error
|
||||
oldDownloadAndPackageWebsite := website.DownloadAndPackageWebsite
|
||||
website.DownloadAndPackageWebsite = func(startURL string, maxDepth int, bar *progressbar.ProgressBar) (*datanode.DataNode, error) {
|
||||
website.DownloadAndPackageWebsite = func(startURL string, maxDepth int, bar *progressbar.ProgressBar, client *http.Client) (*datanode.DataNode, error) {
|
||||
return nil, fmt.Errorf("website error")
|
||||
}
|
||||
defer func() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Snider/Borg/pkg/github"
|
||||
|
|
@ -13,7 +14,7 @@ import (
|
|||
func main() {
|
||||
log.Println("Collecting all repositories for a user...")
|
||||
|
||||
repos, err := github.NewGithubClient().GetPublicRepos(context.Background(), "Snider")
|
||||
repos, err := github.NewGithubClient(http.DefaultClient).GetPublicRepos(context.Background(), "Snider")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get public repos: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Snider/Borg/pkg/pwa"
|
||||
|
|
@ -10,7 +11,7 @@ import (
|
|||
func main() {
|
||||
log.Println("Collecting PWA...")
|
||||
|
||||
client := pwa.NewPWAClient()
|
||||
client := pwa.NewPWAClient(http.DefaultClient)
|
||||
pwaURL := "https://squoosh.app"
|
||||
|
||||
manifestURL, err := client.FindManifest(pwaURL)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Snider/Borg/pkg/website"
|
||||
|
|
@ -11,7 +12,7 @@ func main() {
|
|||
log.Println("Collecting website...")
|
||||
|
||||
// Download and package the website.
|
||||
dn, err := website.DownloadAndPackageWebsite("https://example.com", 2, nil)
|
||||
dn, err := website.DownloadAndPackageWebsite("https://example.com", 2, nil, http.DefaultClient)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to collect website: %v", err)
|
||||
}
|
||||
|
|
|
|||
BIN
examples/demo-sample.smsg
Normal file
BIN
examples/demo-sample.smsg
Normal file
Binary file not shown.
2
go.mod
2
go.mod
|
|
@ -60,7 +60,7 @@ require (
|
|||
github.com/wailsapp/go-webview2 v1.0.22 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -155,8 +155,8 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
|
|
|
|||
|
|
@ -21,22 +21,29 @@ type GithubClient interface {
|
|||
}
|
||||
|
||||
// NewGithubClient creates a new GithubClient.
|
||||
func NewGithubClient() GithubClient {
|
||||
return &githubClient{}
|
||||
func NewGithubClient(client *http.Client) GithubClient {
|
||||
return &githubClient{client: client}
|
||||
}
|
||||
|
||||
type githubClient struct{}
|
||||
type githubClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewAuthenticatedClient creates a new authenticated http client.
|
||||
var NewAuthenticatedClient = func(ctx context.Context) *http.Client {
|
||||
var NewAuthenticatedClient = func(ctx context.Context, baseClient *http.Client) *http.Client {
|
||||
token := os.Getenv("GITHUB_TOKEN")
|
||||
if token == "" {
|
||||
return http.DefaultClient
|
||||
return baseClient
|
||||
}
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
return oauth2.NewClient(ctx, ts)
|
||||
authedClient := oauth2.NewClient(ctx, ts)
|
||||
authedClient.Transport = &oauth2.Transport{
|
||||
Base: baseClient.Transport,
|
||||
Source: ts,
|
||||
}
|
||||
return authedClient
|
||||
}
|
||||
|
||||
func (g *githubClient) GetPublicRepos(ctx context.Context, userOrOrg string) ([]string, error) {
|
||||
|
|
@ -44,7 +51,7 @@ func (g *githubClient) GetPublicRepos(ctx context.Context, userOrOrg string) ([]
|
|||
}
|
||||
|
||||
func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, userOrOrg string) ([]string, error) {
|
||||
client := NewAuthenticatedClient(ctx)
|
||||
client := NewAuthenticatedClient(ctx, g.client)
|
||||
var allCloneURLs []string
|
||||
url := fmt.Sprintf("%s/users/%s/repos", apiURL, userOrOrg)
|
||||
isFirstRequest := true
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func TestFindNextURL_Ugly(t *testing.T) {
|
|||
|
||||
func TestNewAuthenticatedClient_Good(t *testing.T) {
|
||||
t.Setenv("GITHUB_TOKEN", "test-token")
|
||||
client := NewAuthenticatedClient(context.Background())
|
||||
client := NewAuthenticatedClient(context.Background(), http.DefaultClient)
|
||||
if client == http.DefaultClient {
|
||||
t.Error("expected an authenticated client, but got http.DefaultClient")
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ func TestNewAuthenticatedClient_Good(t *testing.T) {
|
|||
func TestNewAuthenticatedClient_Bad(t *testing.T) {
|
||||
// Unset the variable to ensure it's not present
|
||||
t.Setenv("GITHUB_TOKEN", "")
|
||||
client := NewAuthenticatedClient(context.Background())
|
||||
client := NewAuthenticatedClient(context.Background(), http.DefaultClient)
|
||||
if client != http.DefaultClient {
|
||||
t.Error("expected http.DefaultClient when no token is set, but got something else")
|
||||
}
|
||||
|
|
@ -171,9 +171,9 @@ func TestNewAuthenticatedClient_Bad(t *testing.T) {
|
|||
|
||||
// setupMockClient is a helper function to inject a mock http.Client.
|
||||
func setupMockClient(t *testing.T, mock *http.Client) *githubClient {
|
||||
client := &githubClient{}
|
||||
client := &githubClient{client: mock}
|
||||
originalNewAuthenticatedClient := NewAuthenticatedClient
|
||||
NewAuthenticatedClient = func(ctx context.Context) *http.Client {
|
||||
NewAuthenticatedClient = func(ctx context.Context, baseClient *http.Client) *http.Client {
|
||||
return mock
|
||||
}
|
||||
// Restore the original function after the test
|
||||
|
|
|
|||
23
pkg/httpclient/httpclient.go
Normal file
23
pkg/httpclient/httpclient.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package httpclient
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewClient creates a new http.Client with configurable timeouts.
|
||||
func NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout time.Duration) *http.Client {
|
||||
transport := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: connectTimeout,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: tlsTimeout,
|
||||
ResponseHeaderTimeout: headerTimeout,
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Timeout: totalTimeout,
|
||||
Transport: transport,
|
||||
}
|
||||
}
|
||||
33
pkg/httpclient/httpclient_test.go
Normal file
33
pkg/httpclient/httpclient_test.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package httpclient
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
totalTimeout := 10 * time.Second
|
||||
connectTimeout := 2 * time.Second
|
||||
tlsTimeout := 3 * time.Second
|
||||
headerTimeout := 5 * time.Second
|
||||
|
||||
client := NewClient(totalTimeout, connectTimeout, tlsTimeout, headerTimeout)
|
||||
|
||||
if client.Timeout != totalTimeout {
|
||||
t.Errorf("expected total timeout %v, got %v", totalTimeout, client.Timeout)
|
||||
}
|
||||
|
||||
transport, ok := client.Transport.(*http.Transport)
|
||||
if !ok {
|
||||
t.Fatalf("expected client transport to be *http.Transport, got %T", client.Transport)
|
||||
}
|
||||
|
||||
if transport.TLSHandshakeTimeout != tlsTimeout {
|
||||
t.Errorf("expected TLS handshake timeout %v, got %v", tlsTimeout, transport.TLSHandshakeTimeout)
|
||||
}
|
||||
|
||||
if transport.ResponseHeaderTimeout != headerTimeout {
|
||||
t.Errorf("expected response header timeout %v, got %v", headerTimeout, transport.ResponseHeaderTimeout)
|
||||
}
|
||||
}
|
||||
|
|
@ -31,8 +31,8 @@ type PWAClient interface {
|
|||
}
|
||||
|
||||
// NewPWAClient creates a new PWAClient.
|
||||
func NewPWAClient() PWAClient {
|
||||
return &pwaClient{client: http.DefaultClient}
|
||||
func NewPWAClient(client *http.Client) PWAClient {
|
||||
return &pwaClient{client: client}
|
||||
}
|
||||
|
||||
type pwaClient struct {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestFindManifest_Good(t *testing.T) {
|
|||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
expectedURL := server.URL + "/manifest.json"
|
||||
actualURL, err := client.FindManifest(server.URL)
|
||||
if err != nil {
|
||||
|
|
@ -43,7 +43,7 @@ func TestFindManifest_Bad(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
_, err := client.FindManifest(server.URL)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error, but got none")
|
||||
|
|
@ -55,7 +55,7 @@ func TestFindManifest_Bad(t *testing.T) {
|
|||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
_, err := client.FindManifest(server.URL)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error for server error, but got none")
|
||||
|
|
@ -70,7 +70,7 @@ func TestFindManifest_Ugly(t *testing.T) {
|
|||
fmt.Fprint(w, `<html><head><link rel="manifest" href="first.json"><link rel="manifest" href="second.json"></head></html>`)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
// Should find the first one
|
||||
expectedURL := server.URL + "/first.json"
|
||||
actualURL, err := client.FindManifest(server.URL)
|
||||
|
|
@ -98,7 +98,7 @@ func TestFindManifest_Ugly(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
expectedURL := server.URL + "/manifest.json"
|
||||
actualURL, err := client.FindManifest(server.URL)
|
||||
if err != nil {
|
||||
|
|
@ -123,7 +123,7 @@ func TestFindManifest_Ugly(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
expectedURL := server.URL + "/site.webmanifest"
|
||||
actualURL, err := client.FindManifest(server.URL)
|
||||
if err != nil {
|
||||
|
|
@ -141,7 +141,7 @@ func TestDownloadAndPackagePWA_Good(t *testing.T) {
|
|||
server := newPWATestServer()
|
||||
defer server.Close()
|
||||
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
bar := progressbar.NewOptions(1, progressbar.OptionSetWriter(io.Discard))
|
||||
dn, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/manifest.json", bar)
|
||||
if err != nil {
|
||||
|
|
@ -161,7 +161,7 @@ func TestDownloadAndPackagePWA_Bad(t *testing.T) {
|
|||
t.Run("Bad Manifest URL", func(t *testing.T) {
|
||||
server := newPWATestServer()
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
_, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/nonexistent-manifest.json", nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error for bad manifest url, but got none")
|
||||
|
|
@ -178,7 +178,7 @@ func TestDownloadAndPackagePWA_Bad(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
_, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/manifest.json", nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error for asset 404, but got none")
|
||||
|
|
@ -198,7 +198,7 @@ func TestDownloadAndPackagePWA_Ugly(t *testing.T) {
|
|||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
dn, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/manifest.json", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for manifest with no assets: %v", err)
|
||||
|
|
@ -214,7 +214,7 @@ func TestDownloadAndPackagePWA_Ugly(t *testing.T) {
|
|||
// --- Test Cases for resolveURL ---
|
||||
|
||||
func TestResolveURL_Good(t *testing.T) {
|
||||
client := NewPWAClient().(*pwaClient)
|
||||
client := NewPWAClient(http.DefaultClient).(*pwaClient)
|
||||
tests := []struct {
|
||||
base string
|
||||
ref string
|
||||
|
|
@ -239,7 +239,7 @@ func TestResolveURL_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestResolveURL_Bad(t *testing.T) {
|
||||
client := NewPWAClient().(*pwaClient)
|
||||
client := NewPWAClient(http.DefaultClient).(*pwaClient)
|
||||
_, err := client.resolveURL("http://^invalid.com", "foo.html")
|
||||
if err == nil {
|
||||
t.Error("expected error for malformed base URL, but got nil")
|
||||
|
|
@ -249,7 +249,7 @@ func TestResolveURL_Bad(t *testing.T) {
|
|||
// --- Test Cases for extractAssetsFromHTML ---
|
||||
|
||||
func TestExtractAssetsFromHTML(t *testing.T) {
|
||||
client := NewPWAClient().(*pwaClient)
|
||||
client := NewPWAClient(http.DefaultClient).(*pwaClient)
|
||||
|
||||
t.Run("extracts stylesheets", func(t *testing.T) {
|
||||
html := []byte(`<html><head><link rel="stylesheet" href="style.css"></head></html>`)
|
||||
|
|
@ -427,7 +427,7 @@ func TestDownloadAndPackagePWA_FullManifest(t *testing.T) {
|
|||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
dn, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/manifest.json", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("DownloadAndPackagePWA failed: %v", err)
|
||||
|
|
@ -495,7 +495,7 @@ func TestDownloadAndPackagePWA_ServiceWorker(t *testing.T) {
|
|||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewPWAClient()
|
||||
client := NewPWAClient(http.DefaultClient)
|
||||
dn, err := client.DownloadAndPackagePWA(server.URL, server.URL+"/manifest.json", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("DownloadAndPackagePWA failed: %v", err)
|
||||
|
|
|
|||
|
|
@ -26,13 +26,8 @@ type Downloader struct {
|
|||
errors []error
|
||||
}
|
||||
|
||||
// NewDownloader creates a new Downloader.
|
||||
func NewDownloader(maxDepth int) *Downloader {
|
||||
return NewDownloaderWithClient(maxDepth, http.DefaultClient)
|
||||
}
|
||||
|
||||
// NewDownloaderWithClient creates a new Downloader with a custom http.Client.
|
||||
func NewDownloaderWithClient(maxDepth int, client *http.Client) *Downloader {
|
||||
// NewDownloader creates a new Downloader with a custom http.Client.
|
||||
func NewDownloader(maxDepth int, client *http.Client) *Downloader {
|
||||
return &Downloader{
|
||||
dn: datanode.New(),
|
||||
visited: make(map[string]bool),
|
||||
|
|
@ -43,13 +38,13 @@ func NewDownloaderWithClient(maxDepth int, client *http.Client) *Downloader {
|
|||
}
|
||||
|
||||
// downloadAndPackageWebsite downloads a website and packages it into a DataNode.
|
||||
func downloadAndPackageWebsite(startURL string, maxDepth int, bar *progressbar.ProgressBar) (*datanode.DataNode, error) {
|
||||
func downloadAndPackageWebsite(startURL string, maxDepth int, bar *progressbar.ProgressBar, client *http.Client) (*datanode.DataNode, error) {
|
||||
baseURL, err := url.Parse(startURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := NewDownloader(maxDepth)
|
||||
d := NewDownloader(maxDepth, client)
|
||||
d.baseURL = baseURL
|
||||
d.progressBar = bar
|
||||
d.crawl(startURL, 0)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestDownloadAndPackageWebsite_Good(t *testing.T) {
|
|||
defer server.Close()
|
||||
|
||||
bar := progressbar.NewOptions(1, progressbar.OptionSetWriter(io.Discard))
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 2, bar)
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 2, bar, http.DefaultClient)
|
||||
if err != nil {
|
||||
t.Fatalf("DownloadAndPackageWebsite failed: %v", err)
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ func TestDownloadAndPackageWebsite_Good(t *testing.T) {
|
|||
|
||||
func TestDownloadAndPackageWebsite_Bad(t *testing.T) {
|
||||
t.Run("Invalid Start URL", func(t *testing.T) {
|
||||
_, err := DownloadAndPackageWebsite("http://invalid-url", 1, nil)
|
||||
_, err := DownloadAndPackageWebsite("http://invalid-url", 1, nil, http.DefaultClient)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error for an invalid start URL, but got nil")
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ func TestDownloadAndPackageWebsite_Bad(t *testing.T) {
|
|||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
_, err := DownloadAndPackageWebsite(server.URL, 1, nil)
|
||||
_, err := DownloadAndPackageWebsite(server.URL, 1, nil, http.DefaultClient)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error for a server error on the start URL, but got nil")
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ func TestDownloadAndPackageWebsite_Bad(t *testing.T) {
|
|||
}))
|
||||
defer server.Close()
|
||||
// We expect an error because the link is broken.
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, nil)
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, nil, http.DefaultClient)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error for a broken link, but got nil")
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ func TestDownloadAndPackageWebsite_Ugly(t *testing.T) {
|
|||
defer server.Close()
|
||||
|
||||
bar := progressbar.NewOptions(1, progressbar.OptionSetWriter(io.Discard))
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, bar) // Max depth of 1
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, bar, http.DefaultClient) // Max depth of 1
|
||||
if err != nil {
|
||||
t.Fatalf("DownloadAndPackageWebsite failed: %v", err)
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ func TestDownloadAndPackageWebsite_Ugly(t *testing.T) {
|
|||
fmt.Fprint(w, `<a href="http://externalsite.com/page.html">External</a>`)
|
||||
}))
|
||||
defer server.Close()
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, nil)
|
||||
dn, err := DownloadAndPackageWebsite(server.URL, 1, nil, http.DefaultClient)
|
||||
if err != nil {
|
||||
t.Fatalf("DownloadAndPackageWebsite failed: %v", err)
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ func TestDownloadAndPackageWebsite_Ugly(t *testing.T) {
|
|||
// For now, we'll just test that it doesn't hang forever.
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
_, err := DownloadAndPackageWebsite(server.URL, 1, nil)
|
||||
_, err := DownloadAndPackageWebsite(server.URL, 1, nil, http.DefaultClient)
|
||||
if err != nil && !strings.Contains(err.Error(), "context deadline exceeded") {
|
||||
// We expect a timeout error, but other errors are failures.
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue