• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

boinger / confvis / 22011396192

14 Feb 2026 04:43AM UTC coverage: 83.992% (-0.05%) from 84.037%
22011396192

push

github

boinger
refactor: codebase cleanup — split impl_test.go, modernize idioms, update docs

- Split impl_test.go (5,038 lines) into 7 focused test files by command:
  generate, fetch, aggregate, gauge, check, comment_github, baseline
- Consolidate duplicate intPtrI/intPtrH test helpers (drop intPtrI)
- Replace interface{} with any in httpclient unexported methods
- Add compile-time interface compliance checks (var _ Fetcher = (*Client)(nil))
  for snyk, ghactions, gitleaks, gosec, trufflehog
- Update docs/architecture.md with missing CLI and source entries
- Track future feature ideas; remove completed modularize-gauge plan

2 of 2 new or added lines in 1 file covered. (100.0%)

75 existing lines in 7 files now uncovered.

3956 of 4710 relevant lines covered (83.99%)

10.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.4
/internal/sources/githubalerts/source.go
1
package githubalerts
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "net/url"
7
        "strconv"
8
        "time"
9

10
        "github.com/boinger/confvis/internal/confidence"
11
        "github.com/boinger/confvis/internal/sources"
12
        "github.com/boinger/confvis/internal/sources/httpclient"
13
        "github.com/boinger/confvis/internal/sources/repoparse"
14
        "github.com/boinger/confvis/internal/sources/scoring"
15
)
16

17
// SourceConfig defines configuration for a GitHub alerts source.
18
type SourceConfig struct {
19
        Name         string // Source name (e.g., "dependabot", "codeql")
20
        TokenEnvVar  string // Environment variable for source-specific token
21
        EndpointPath string // API endpoint path (e.g., "dependabot/alerts")
22
        WebURLPath   string // Web URL path (e.g., "security/dependabot")
23
}
24

25
// CountAlertsFunc extracts severity counts from raw JSON alert data.
26
type CountAlertsFunc func(data []byte) (scoring.SeverityCounts, error)
27

28
// ExtraParamsFunc adds source-specific query parameters.
29
type ExtraParamsFunc func(opts sources.Options) url.Values
30

31
// AlertsSource implements sources.Source for GitHub security alerts APIs.
32
type AlertsSource struct {
33
        config      SourceConfig
34
        countAlerts CountAlertsFunc
35
        extraParams ExtraParamsFunc
36
}
37

38
// NewSource creates a new GitHub alerts source with the given configuration.
39
// countAlerts is called to extract severity counts from the raw JSON response.
40
// extraParams is optional and can add source-specific query parameters.
41
func NewSource(cfg SourceConfig, countAlerts CountAlertsFunc, extraParams ExtraParamsFunc) *AlertsSource {
12✔
42
        return &AlertsSource{
12✔
43
                config:      cfg,
12✔
44
                countAlerts: countAlerts,
12✔
45
                extraParams: extraParams,
12✔
46
        }
12✔
47
}
12✔
48

49
// Name returns the source identifier.
50
func (s *AlertsSource) Name() string {
1✔
51
        return s.config.Name
1✔
52
}
1✔
53

54
var defaultTimeout = 30 * time.Second
55

56
// Fetch retrieves alerts from GitHub and converts them to a confidence report.
57
func (s *AlertsSource) Fetch(ctx context.Context, opts sources.Options) (*confidence.Report, error) {
11✔
58
        resolver := &sources.ConfigResolver{
11✔
59
                SourceName:     s.config.Name,
11✔
60
                TokenEnvVar:    s.config.TokenEnvVar,
11✔
61
                URLEnvVar:      "GITHUB_API_URL",
11✔
62
                TokenRequired:  false, // We handle token resolution manually for fallback
11✔
63
                URLRequired:    false, // Has default
11✔
64
                DefaultTimeout: defaultTimeout,
11✔
65
        }
11✔
66

11✔
67
        cfg, err := resolver.Resolve(opts)
11✔
68
        if err != nil {
11✔
69
                return nil, err
×
UNCOV
70
        }
×
71

72
        token, err := ResolveTokenWithFallback(cfg, s.config.Name, s.config.TokenEnvVar)
11✔
73
        if err != nil {
12✔
74
                return nil, err
1✔
75
        }
1✔
76

77
        owner, repo, err := repoparse.Parse(opts.Project)
10✔
78
        if err != nil {
11✔
79
                return nil, err
1✔
80
        }
1✔
81

82
        alertsConfig := Config{
9✔
83
                EndpointPath: s.config.EndpointPath,
9✔
84
                WebURLPath:   s.config.WebURLPath,
9✔
85
        }
9✔
86
        client := NewClient(cfg.URL, token, cfg.Timeout, alertsConfig)
9✔
87

9✔
88
        allAlerts, err := s.fetchAllAlerts(ctx, client, owner, repo, opts)
9✔
89
        if err != nil {
10✔
90
                return nil, err
1✔
91
        }
1✔
92

93
        counts, err := s.countAlerts(allAlerts)
8✔
94
        if err != nil {
9✔
95
                return nil, err
1✔
96
        }
1✔
97

98
        title := ResolveTitle(opts.Title, owner, repo)
7✔
99
        factors := scoring.BuildVulnFactors(counts, Penalties(), Weights(), client.AlertsURL(owner, repo))
7✔
100

7✔
101
        return scoring.BuildReport(title, s.config.Name, opts.Threshold, factors), nil
7✔
102
}
103

104
// fetchAllAlerts retrieves all open alerts with pagination.
105
func (s *AlertsSource) fetchAllAlerts(ctx context.Context, client *Client, owner, repo string, opts sources.Options) ([]byte, error) {
9✔
106
        const perPage = 100
9✔
107
        var allAlerts []json.RawMessage
9✔
108

9✔
109
        for page := 1; ; page++ {
19✔
110
                params := url.Values{
10✔
111
                        "state":    {"open"},
10✔
112
                        "per_page": {strconv.Itoa(perPage)},
10✔
113
                        "page":     {strconv.Itoa(page)},
10✔
114
                }
10✔
115

10✔
116
                // Add source-specific parameters
10✔
117
                if s.extraParams != nil {
12✔
118
                        for k, v := range s.extraParams(opts) {
3✔
119
                                params[k] = v
1✔
120
                        }
1✔
121
                }
122

123
                endpoint := client.BuildEndpoint(owner, repo, params)
10✔
124

10✔
125
                pageAlerts, err := httpclient.Get[[]json.RawMessage](client.HTTP, ctx, endpoint)
10✔
126
                if err != nil {
11✔
127
                        return nil, err
1✔
128
                }
1✔
129

130
                allAlerts = append(allAlerts, pageAlerts...)
9✔
131

9✔
132
                if len(pageAlerts) < perPage {
17✔
133
                        break
8✔
134
                }
135
        }
136

137
        return json.Marshal(allAlerts)
8✔
138
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc