Address 18 issues identified in codebase review: - Fix doRequest body consumed on 401 retry by accepting []byte instead of io.Reader - Fix race condition on httpClient.Timeout in DownloadFile using context.WithTimeout - Fix data race on baseURL read in doRequest by reading under mutex - Add sync.Mutex to taskStore for atomic index operations - Replace all silent `_ = SaveTask()` calls with proper error logging via failTask helper - Add notifyRequester calls to all processTaskComplete failure paths - Mark missing Shelfmark tasks as failed instead of optimistically promoting to complete - Remove goto in processTaskPending with structured control flow - Validate ShelfmarkHost URL scheme in IsValid() - Call IsValid() in OnConfigurationChange to warn on invalid config - Guard ExecuteCommand against empty command panic - Add EffectiveTitle() method on DownloadTask to deduplicate title fallback - Add shelfmarkCredentials() to deduplicate TrimSpace calls - Log ReleasesResponse.Errors when non-empty - Batch-delete terminal tasks in ListActiveTasks instead of during iteration - Log GetTask errors in ListActiveTasks instead of silently skipping - Properly handle resp.Body.Close() errors throughout shelfmark client - Add 93 unit tests across 8 new test files covering all packages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
139 lines
3.1 KiB
Go
139 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIsValid(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config configuration
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "valid configuration",
|
|
config: configuration{
|
|
ShelfmarkHost: "http://shelfmark:8084",
|
|
ChannelID: "channel123",
|
|
},
|
|
},
|
|
{
|
|
name: "valid https",
|
|
config: configuration{
|
|
ShelfmarkHost: "https://shelfmark.example.com",
|
|
ChannelID: "channel123",
|
|
},
|
|
},
|
|
{
|
|
name: "missing host",
|
|
config: configuration{
|
|
ShelfmarkHost: "",
|
|
ChannelID: "channel123",
|
|
},
|
|
wantErr: "shelfmark server URL must be configured",
|
|
},
|
|
{
|
|
name: "whitespace-only host",
|
|
config: configuration{
|
|
ShelfmarkHost: " ",
|
|
ChannelID: "channel123",
|
|
},
|
|
wantErr: "shelfmark server URL must be configured",
|
|
},
|
|
{
|
|
name: "host without scheme",
|
|
config: configuration{
|
|
ShelfmarkHost: "shelfmark:8084",
|
|
ChannelID: "channel123",
|
|
},
|
|
wantErr: "shelfmark server URL must start with http:// or https://",
|
|
},
|
|
{
|
|
name: "missing channel",
|
|
config: configuration{
|
|
ShelfmarkHost: "http://shelfmark:8084",
|
|
ChannelID: "",
|
|
},
|
|
wantErr: "channel ID must be configured",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.config.IsValid()
|
|
if tt.wantErr == "" {
|
|
require.NoError(t, err)
|
|
} else {
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetDefaultLanguage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
expected string
|
|
}{
|
|
{"empty", "", ""},
|
|
{"whitespace", " ", ""},
|
|
{"en", "en", "en"},
|
|
{"trimmed", " es ", "es"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &configuration{DefaultLanguage: tt.value}
|
|
assert.Equal(t, tt.expected, c.getDefaultLanguage())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetPostTemplate(t *testing.T) {
|
|
t.Run("empty returns default", func(t *testing.T) {
|
|
c := &configuration{PostTemplate: ""}
|
|
assert.Equal(t, "### {{.Title}}", c.getPostTemplate())
|
|
})
|
|
|
|
t.Run("whitespace returns default", func(t *testing.T) {
|
|
c := &configuration{PostTemplate: " "}
|
|
assert.Equal(t, "### {{.Title}}", c.getPostTemplate())
|
|
})
|
|
|
|
t.Run("custom value returned", func(t *testing.T) {
|
|
c := &configuration{PostTemplate: "**{{.Title}}** by {{.AuthorsList}}"}
|
|
assert.Equal(t, "**{{.Title}}** by {{.AuthorsList}}", c.getPostTemplate())
|
|
})
|
|
}
|
|
|
|
func TestClone(t *testing.T) {
|
|
original := &configuration{
|
|
ShelfmarkHost: "http://example.com",
|
|
ChannelID: "ch1",
|
|
}
|
|
clone := original.Clone()
|
|
|
|
clone.ShelfmarkHost = "http://other.com"
|
|
clone.ChannelID = "ch2"
|
|
|
|
assert.Equal(t, "http://example.com", original.ShelfmarkHost)
|
|
assert.Equal(t, "ch1", original.ChannelID)
|
|
}
|
|
|
|
func TestShelfmarkCredentials(t *testing.T) {
|
|
c := &configuration{
|
|
ShelfmarkHost: " http://example.com ",
|
|
ShelfmarkUsername: " user ",
|
|
ShelfmarkPassword: " pass ",
|
|
}
|
|
|
|
host, username, password := c.shelfmarkCredentials()
|
|
assert.Equal(t, "http://example.com", host)
|
|
assert.Equal(t, "user", username)
|
|
assert.Equal(t, "pass", password)
|
|
}
|