fix(forge): make client accessors nil-safe
All checks were successful
Security Scan / security (push) Successful in 14s
Test / test (push) Successful in 1m54s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 09:00:15 +00:00
parent d08c875fcf
commit 00a30817f2
4 changed files with 92 additions and 6 deletions

View file

@ -171,6 +171,9 @@ type Client struct {
// //
// baseURL := client.BaseURL() // baseURL := client.BaseURL()
func (c *Client) BaseURL() string { func (c *Client) BaseURL() string {
if c == nil {
return ""
}
return c.baseURL return c.baseURL
} }
@ -180,6 +183,9 @@ func (c *Client) BaseURL() string {
// //
// rl := client.RateLimit() // rl := client.RateLimit()
func (c *Client) RateLimit() RateLimit { func (c *Client) RateLimit() RateLimit {
if c == nil {
return RateLimit{}
}
return c.rateLimit return c.rateLimit
} }
@ -189,6 +195,9 @@ func (c *Client) RateLimit() RateLimit {
// //
// ua := client.UserAgent() // ua := client.UserAgent()
func (c *Client) UserAgent() string { func (c *Client) UserAgent() string {
if c == nil {
return ""
}
return c.userAgent return c.userAgent
} }
@ -198,6 +207,9 @@ func (c *Client) UserAgent() string {
// //
// hc := client.HTTPClient() // hc := client.HTTPClient()
func (c *Client) HTTPClient() *http.Client { func (c *Client) HTTPClient() *http.Client {
if c == nil {
return nil
}
return c.httpClient return c.httpClient
} }
@ -232,6 +244,9 @@ func (c *Client) GoString() string { return c.String() }
// _ = "authenticated" // _ = "authenticated"
// } // }
func (c *Client) HasToken() bool { func (c *Client) HasToken() bool {
if c == nil {
return false
}
return c.token != "" return c.token != ""
} }

View file

@ -215,6 +215,25 @@ func TestClient_HasToken_Bad(t *testing.T) {
} }
} }
func TestClient_NilSafeAccessors(t *testing.T) {
var c *Client
if got := c.BaseURL(); got != "" {
t.Fatalf("got BaseURL()=%q, want empty string", got)
}
if got := c.RateLimit(); got != (RateLimit{}) {
t.Fatalf("got RateLimit()=%#v, want zero value", got)
}
if got := c.UserAgent(); got != "" {
t.Fatalf("got UserAgent()=%q, want empty string", got)
}
if got := c.HTTPClient(); got != nil {
t.Fatal("expected HTTPClient() to return nil")
}
if got := c.HasToken(); got {
t.Fatal("expected HasToken() to report false")
}
}
func TestClient_WithHTTPClient_Good(t *testing.T) { func TestClient_WithHTTPClient_Good(t *testing.T) {
custom := &http.Client{} custom := &http.Client{}
c := NewClient("https://forge.lthn.ai", "tok", WithHTTPClient(custom)) c := NewClient("https://forge.lthn.ai", "tok", WithHTTPClient(custom))

View file

@ -72,35 +72,60 @@ func NewForge(url, token string, opts ...Option) *Forge {
// Usage: // Usage:
// //
// client := f.Client() // client := f.Client()
func (f *Forge) Client() *Client { return f.client } func (f *Forge) Client() *Client {
if f == nil {
return nil
}
return f.client
}
// BaseURL returns the configured Forgejo base URL. // BaseURL returns the configured Forgejo base URL.
// //
// Usage: // Usage:
// //
// baseURL := f.BaseURL() // baseURL := f.BaseURL()
func (f *Forge) BaseURL() string { return f.client.BaseURL() } func (f *Forge) BaseURL() string {
if f == nil || f.client == nil {
return ""
}
return f.client.BaseURL()
}
// RateLimit returns the last known rate limit information. // RateLimit returns the last known rate limit information.
// //
// Usage: // Usage:
// //
// rl := f.RateLimit() // rl := f.RateLimit()
func (f *Forge) RateLimit() RateLimit { return f.client.RateLimit() } func (f *Forge) RateLimit() RateLimit {
if f == nil || f.client == nil {
return RateLimit{}
}
return f.client.RateLimit()
}
// UserAgent returns the configured User-Agent header value. // UserAgent returns the configured User-Agent header value.
// //
// Usage: // Usage:
// //
// ua := f.UserAgent() // ua := f.UserAgent()
func (f *Forge) UserAgent() string { return f.client.UserAgent() } func (f *Forge) UserAgent() string {
if f == nil || f.client == nil {
return ""
}
return f.client.UserAgent()
}
// HTTPClient returns the configured underlying HTTP client. // HTTPClient returns the configured underlying HTTP client.
// //
// Usage: // Usage:
// //
// hc := f.HTTPClient() // hc := f.HTTPClient()
func (f *Forge) HTTPClient() *http.Client { return f.client.HTTPClient() } func (f *Forge) HTTPClient() *http.Client {
if f == nil || f.client == nil {
return nil
}
return f.client.HTTPClient()
}
// HasToken reports whether the Forge client was configured with an API token. // HasToken reports whether the Forge client was configured with an API token.
// //
@ -109,7 +134,12 @@ func (f *Forge) HTTPClient() *http.Client { return f.client.HTTPClient() }
// if f.HasToken() { // if f.HasToken() {
// _ = "authenticated" // _ = "authenticated"
// } // }
func (f *Forge) HasToken() bool { return f.client.HasToken() } func (f *Forge) HasToken() bool {
if f == nil || f.client == nil {
return false
}
return f.client.HasToken()
}
// String returns a safe summary of the Forge client. // String returns a safe summary of the Forge client.
// //

View file

@ -82,6 +82,28 @@ func TestForge_HasToken_Bad(t *testing.T) {
} }
} }
func TestForge_NilSafeAccessors(t *testing.T) {
var f *Forge
if got := f.Client(); got != nil {
t.Fatal("expected Client() to return nil")
}
if got := f.BaseURL(); got != "" {
t.Fatalf("got BaseURL()=%q, want empty string", got)
}
if got := f.RateLimit(); got != (RateLimit{}) {
t.Fatalf("got RateLimit()=%#v, want zero value", got)
}
if got := f.UserAgent(); got != "" {
t.Fatalf("got UserAgent()=%q, want empty string", got)
}
if got := f.HTTPClient(); got != nil {
t.Fatal("expected HTTPClient() to return nil")
}
if got := f.HasToken(); got {
t.Fatal("expected HasToken() to report false")
}
}
func TestForge_String_Good(t *testing.T) { func TestForge_String_Good(t *testing.T) {
f := NewForge("https://forge.lthn.ai", "tok", WithUserAgent("go-forge/1.0")) f := NewForge("https://forge.lthn.ai", "tok", WithUserAgent("go-forge/1.0"))
got := fmt.Sprint(f) got := fmt.Sprint(f)