Skip to content

Commit 9d0f749

Browse files
committed
update remaining subcommands
1 parent 93e2d04 commit 9d0f749

File tree

6 files changed

+278
-17
lines changed

6 files changed

+278
-17
lines changed

cmd/dbc/docs.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ func (c DocsCmd) GetModel() tea.Model {
6767
type docsModel struct {
6868
baseModel
6969

70-
driver string
71-
drv *dbc.Driver
72-
urlToOpen string
73-
noOpen bool
74-
fallbackUrls map[string]string
75-
openBrowser func(string) error
70+
driver string
71+
drv *dbc.Driver
72+
urlToOpen string
73+
noOpen bool
74+
fallbackUrls map[string]string
75+
openBrowser func(string) error
76+
registryErrors error // Store registry errors for better error messages
7677
}
7778

7879
func (m docsModel) Init() tea.Cmd {
@@ -81,13 +82,18 @@ func (m docsModel) Init() tea.Cmd {
8182
return docsUrlFound(dbcDocsUrl)
8283
}
8384

84-
drivers, err := m.getDriverRegistry()
85-
if err != nil {
86-
return err
85+
drivers, registryErr := m.getDriverRegistry()
86+
// If we have no drivers and there's an error, fail immediately
87+
if len(drivers) == 0 && registryErr != nil {
88+
return fmt.Errorf("error getting driver list: %w", registryErr)
8789
}
8890

8991
drv, err := findDriver(m.driver, drivers)
9092
if err != nil {
93+
// If we have registry errors, enhance the error message
94+
if registryErr != nil {
95+
return fmt.Errorf("%w\n\nNote: Some driver registries were unavailable:\n%s", err, registryErr.Error())
96+
}
9197
return err
9298
}
9399

cmd/dbc/docs_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ package main
1616

1717
import (
1818
"fmt"
19+
20+
"github.com/columnar-tech/dbc"
1921
)
2022

2123
var testFallbackUrls = map[string]string{
@@ -154,3 +156,80 @@ func (suite *SubcommandTestSuite) TestDocsDriverFoundWithDocs() {
154156

155157
suite.Equal("http://example.com", lastOpenedURL)
156158
}
159+
160+
func (suite *SubcommandTestSuite) TestDocsPartialRegistryFailure() {
161+
// Test that docs command handles partial registry failure gracefully
162+
// (one registry succeeds, another fails - returns both drivers and error)
163+
partialFailingRegistry := func() ([]dbc.Driver, error) {
164+
// Get drivers from the test registry (simulating one successful registry)
165+
drivers, _ := getTestDriverRegistry()
166+
// But also return an error (simulating another registry that failed)
167+
return drivers, fmt.Errorf("registry https://fallback-registry.example.com: failed to fetch driver registry: timeout")
168+
}
169+
170+
openBrowserFunc = mockOpenBrowserSuccess
171+
lastOpenedURL = ""
172+
fallbackDriverDocsUrl = testFallbackUrls
173+
174+
// Should succeed if the requested driver is found in the available drivers
175+
m := DocsCmd{Driver: "test-driver-1"}.GetModelCustom(
176+
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg},
177+
false,
178+
mockOpenBrowserSuccess,
179+
testFallbackUrls,
180+
)
181+
182+
suite.runCmd(m)
183+
// Should open docs successfully without showing the registry error
184+
suite.Equal("https://test.example.com/driver1", lastOpenedURL)
185+
}
186+
187+
func (suite *SubcommandTestSuite) TestDocsPartialRegistryFailureDriverNotFound() {
188+
// Test that docs command shows registry errors when the requested driver is not found
189+
partialFailingRegistry := func() ([]dbc.Driver, error) {
190+
// Get drivers from the test registry (simulating one successful registry)
191+
drivers, _ := getTestDriverRegistry()
192+
// But also return an error (simulating another registry that failed)
193+
return drivers, fmt.Errorf("registry https://fallback-registry.example.com: failed to fetch driver registry: timeout")
194+
}
195+
196+
openBrowserFunc = mockOpenBrowserSuccess
197+
lastOpenedURL = ""
198+
199+
// Should fail with enhanced error message if the requested driver is not found
200+
m := DocsCmd{Driver: "nonexistent-driver"}.GetModelCustom(
201+
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg},
202+
false,
203+
mockOpenBrowserSuccess,
204+
testFallbackUrls,
205+
)
206+
207+
out := suite.runCmdErr(m)
208+
// Should show the driver not found error AND the registry error
209+
suite.Contains(out, "driver `nonexistent-driver` not found")
210+
suite.Contains(out, "Note: Some driver registries were unavailable")
211+
suite.Contains(out, "failed to fetch driver registry")
212+
suite.Contains(out, "timeout")
213+
suite.Equal("", lastOpenedURL, "browser should not be opened on error")
214+
}
215+
216+
func (suite *SubcommandTestSuite) TestDocsCompleteRegistryFailure() {
217+
// Test that docs command handles complete registry failure (no drivers returned)
218+
completeFailingRegistry := func() ([]dbc.Driver, error) {
219+
return nil, fmt.Errorf("registry https://main-registry.example.com: connection timeout")
220+
}
221+
222+
openBrowserFunc = mockOpenBrowserSuccess
223+
lastOpenedURL = ""
224+
225+
m := DocsCmd{Driver: "test-driver-1"}.GetModelCustom(
226+
baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg},
227+
false,
228+
mockOpenBrowserSuccess,
229+
testFallbackUrls,
230+
)
231+
232+
out := suite.runCmdErr(m)
233+
suite.Contains(out, "connection timeout")
234+
suite.Equal("", lastOpenedURL, "browser should not be opened on error")
235+
}

cmd/dbc/info.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package main
1616

1717
import (
1818
"encoding/json"
19+
"fmt"
1920
"strings"
2021

2122
tea "github.com/charmbracelet/bubbletea"
@@ -45,20 +46,26 @@ func (c InfoCmd) GetModel() tea.Model {
4546
type infoModel struct {
4647
baseModel
4748

48-
driver string
49-
jsonOutput bool
50-
drv dbc.Driver
49+
driver string
50+
jsonOutput bool
51+
drv dbc.Driver
52+
registryErrors error // Store registry errors for better error messages
5153
}
5254

5355
func (m infoModel) Init() tea.Cmd {
5456
return func() tea.Msg {
55-
drivers, err := m.getDriverRegistry()
56-
if err != nil {
57-
return err
57+
drivers, registryErr := m.getDriverRegistry()
58+
// If we have no drivers and there's an error, fail immediately
59+
if len(drivers) == 0 && registryErr != nil {
60+
return fmt.Errorf("error getting driver list: %w", registryErr)
5861
}
5962

6063
drv, err := findDriver(m.driver, drivers)
6164
if err != nil {
65+
// If we have registry errors, enhance the error message
66+
if registryErr != nil {
67+
return fmt.Errorf("%w\n\nNote: Some driver registries were unavailable:\n%s", err, registryErr.Error())
68+
}
6269
return err
6370
}
6471

cmd/dbc/info_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414

1515
package main
1616

17+
import (
18+
"fmt"
19+
20+
"github.com/columnar-tech/dbc"
21+
)
22+
1723
func (suite *SubcommandTestSuite) TestInfo() {
1824
m := InfoCmd{Driver: "test-driver-1"}.
1925
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
@@ -34,3 +40,57 @@ func (suite *SubcommandTestSuite) TestInfo_DriverNotFound() {
3440

3541
suite.validateOutput("\r ", "\nError: driver `non-existent-driver` not found in driver registry index", out)
3642
}
43+
44+
func (suite *SubcommandTestSuite) TestInfoPartialRegistryFailure() {
45+
// Test that info command handles partial registry failure gracefully
46+
// (one registry succeeds, another fails - returns both drivers and error)
47+
partialFailingRegistry := func() ([]dbc.Driver, error) {
48+
// Get drivers from the test registry (simulating one successful registry)
49+
drivers, _ := getTestDriverRegistry()
50+
// But also return an error (simulating another registry that failed)
51+
return drivers, fmt.Errorf("registry https://secondary-registry.example.com: failed to fetch driver registry: DNS error")
52+
}
53+
54+
// Should succeed if the requested driver is found in the available drivers
55+
m := InfoCmd{Driver: "test-driver-1"}.
56+
GetModelCustom(baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})
57+
58+
out := suite.runCmd(m)
59+
// Should display info successfully without printing the registry error
60+
suite.Contains(out, "Driver: test-driver-1")
61+
suite.Contains(out, "Version: 1.1.0")
62+
}
63+
64+
func (suite *SubcommandTestSuite) TestInfoPartialRegistryFailureDriverNotFound() {
65+
// Test that info command shows registry errors when the requested driver is not found
66+
partialFailingRegistry := func() ([]dbc.Driver, error) {
67+
// Get drivers from the test registry (simulating one successful registry)
68+
drivers, _ := getTestDriverRegistry()
69+
// But also return an error (simulating another registry that failed)
70+
return drivers, fmt.Errorf("registry https://secondary-registry.example.com: failed to fetch driver registry: DNS error")
71+
}
72+
73+
// Should fail with enhanced error message if the requested driver is not found
74+
m := InfoCmd{Driver: "nonexistent-driver"}.
75+
GetModelCustom(baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})
76+
77+
out := suite.runCmdErr(m)
78+
// Should show the driver not found error AND the registry error
79+
suite.Contains(out, "driver `nonexistent-driver` not found")
80+
suite.Contains(out, "Note: Some driver registries were unavailable")
81+
suite.Contains(out, "failed to fetch driver registry")
82+
suite.Contains(out, "DNS error")
83+
}
84+
85+
func (suite *SubcommandTestSuite) TestInfoCompleteRegistryFailure() {
86+
// Test that info command handles complete registry failure (no drivers returned)
87+
completeFailingRegistry := func() ([]dbc.Driver, error) {
88+
return nil, fmt.Errorf("registry https://primary-registry.example.com: network unreachable")
89+
}
90+
91+
m := InfoCmd{Driver: "test-driver-1"}.
92+
GetModelCustom(baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg})
93+
94+
out := suite.runCmdErr(m)
95+
suite.Contains(out, "network unreachable")
96+
}

cmd/dbc/sync.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ type syncModel struct {
8888
progress progress.Model
8989
width, height int
9090

91-
done bool
91+
done bool
92+
registryErrors error // Store registry errors for better error messages
9293
}
9394

9495
type driversListMsg struct {
@@ -166,6 +167,10 @@ func (s syncModel) createInstallList(list DriversList) ([]installItem, error) {
166167
// locate the driver info in the CDN driver registry index
167168
drv, err := findDriver(name, s.driverIndex)
168169
if err != nil {
170+
// If we have registry errors, enhance the error message
171+
if s.registryErrors != nil {
172+
return nil, fmt.Errorf("%w\n\nNote: Some driver registries were unavailable:\n%s", err, s.registryErrors.Error())
173+
}
169174
return nil, err
170175
}
171176

@@ -331,12 +336,29 @@ func (s syncModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
331336
s.list = msg.list
332337
return s, func() tea.Msg {
333338
drivers, err := s.getDriverRegistry()
339+
// Return both drivers and error - we'll decide how to handle based on whether
340+
// all requested drivers can be found
341+
return driversWithRegistryError{
342+
drivers: drivers,
343+
err: err,
344+
}
345+
}
346+
case driversWithRegistryError:
347+
s.registryErrors = msg.err
348+
// If we have no drivers and there's an error, fail immediately
349+
if len(msg.drivers) == 0 && msg.err != nil {
350+
return s, errCmd("error getting driver list: %w", msg.err)
351+
}
352+
s.driverIndex = msg.drivers
353+
return s, func() tea.Msg {
354+
items, err := s.createInstallList(s.list)
334355
if err != nil {
335356
return err
336357
}
337-
return drivers
358+
return items
338359
}
339360
case []dbc.Driver:
361+
// For backwards compatibility, still handle plain driver list
340362
s.driverIndex = msg
341363
return s, func() tea.Msg {
342364
items, err := s.createInstallList(s.list)

cmd/dbc/sync_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
package main
1616

1717
import (
18+
"fmt"
1819
"os"
1920
"path/filepath"
21+
22+
"github.com/columnar-tech/dbc"
2023
)
2124

2225
func (suite *SubcommandTestSuite) TestSync() {
@@ -156,3 +159,87 @@ func (suite *SubcommandTestSuite) TestSyncInstallNoVerify() {
156159
baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
157160
suite.validateOutput("✓ test-driver-no-sig-1.1.0\r\n\rDone!\r\n", "", suite.runCmd(m))
158161
}
162+
163+
func (suite *SubcommandTestSuite) TestSyncPartialRegistryFailure() {
164+
// Initialize driver list
165+
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
166+
suite.runCmd(m)
167+
168+
m = AddCmd{Path: filepath.Join(suite.tempdir, "dbc.toml"), Driver: []string{"test-driver-1"}}.GetModel()
169+
suite.runCmd(m)
170+
171+
// Test that sync command handles partial registry failure gracefully
172+
// (one registry succeeds, another fails - returns both drivers and error)
173+
partialFailingRegistry := func() ([]dbc.Driver, error) {
174+
// Get drivers from the test registry (simulating one successful registry)
175+
drivers, _ := getTestDriverRegistry()
176+
// But also return an error (simulating another registry that failed)
177+
return drivers, fmt.Errorf("registry https://backup-registry.example.com: failed to fetch driver registry: network timeout")
178+
}
179+
180+
// Should succeed if the requested driver is found in the available drivers
181+
m = SyncCmd{
182+
Path: filepath.Join(suite.tempdir, "dbc.toml"),
183+
}.GetModelCustom(
184+
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})
185+
186+
// Should install successfully without printing the registry error
187+
suite.validateOutput("✓ test-driver-1-1.1.0\r\n\rDone!\r\n", "", suite.runCmd(m))
188+
suite.FileExists(filepath.Join(suite.tempdir, "test-driver-1.toml"))
189+
}
190+
191+
func (suite *SubcommandTestSuite) TestSyncPartialRegistryFailureDriverNotFound() {
192+
// Initialize driver list with a driver that doesn't exist
193+
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
194+
suite.runCmd(m)
195+
196+
// Manually create a driver list with a nonexistent driver
197+
err := os.WriteFile(filepath.Join(suite.tempdir, "dbc.toml"), []byte(`# dbc driver list
198+
[drivers]
199+
[drivers.nonexistent-driver]
200+
`), 0644)
201+
suite.Require().NoError(err)
202+
203+
// Test that sync command shows registry errors when the requested driver is not found
204+
partialFailingRegistry := func() ([]dbc.Driver, error) {
205+
// Get drivers from the test registry (simulating one successful registry)
206+
drivers, _ := getTestDriverRegistry()
207+
// But also return an error (simulating another registry that failed)
208+
return drivers, fmt.Errorf("registry https://backup-registry.example.com: failed to fetch driver registry: network timeout")
209+
}
210+
211+
// Should fail with enhanced error message if the requested driver is not found
212+
m = SyncCmd{
213+
Path: filepath.Join(suite.tempdir, "dbc.toml"),
214+
}.GetModelCustom(
215+
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})
216+
217+
out := suite.runCmdErr(m)
218+
// Should show the driver not found error AND the registry error
219+
suite.Contains(out, "driver `nonexistent-driver` not found")
220+
suite.Contains(out, "Note: Some driver registries were unavailable")
221+
suite.Contains(out, "failed to fetch driver registry")
222+
suite.Contains(out, "network timeout")
223+
}
224+
225+
func (suite *SubcommandTestSuite) TestSyncCompleteRegistryFailure() {
226+
// Initialize driver list
227+
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
228+
suite.runCmd(m)
229+
230+
m = AddCmd{Path: filepath.Join(suite.tempdir, "dbc.toml"), Driver: []string{"test-driver-1"}}.GetModel()
231+
suite.runCmd(m)
232+
233+
// Test that sync command handles complete registry failure (no drivers returned)
234+
completeFailingRegistry := func() ([]dbc.Driver, error) {
235+
return nil, fmt.Errorf("registry https://primary-registry.example.com: connection refused")
236+
}
237+
238+
m = SyncCmd{
239+
Path: filepath.Join(suite.tempdir, "dbc.toml"),
240+
}.GetModelCustom(
241+
baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg})
242+
243+
out := suite.runCmdErr(m)
244+
suite.Contains(out, "connection refused")
245+
}

0 commit comments

Comments
 (0)