Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions cmd/dbc/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,14 @@ func (m addModel) Init() tea.Cmd {
}

return func() tea.Msg {
drivers, err := m.getDriverRegistry()
if err != nil {
return fmt.Errorf("error getting driver list: %w", err)
drivers, registryErr := m.getDriverRegistry()
// If we have no drivers and there's an error, fail immediately
if len(drivers) == 0 && registryErr != nil {
return fmt.Errorf("error getting driver list: %w", registryErr)
}
// Store registry errors to use later if driver is not found
// We continue processing if we have some drivers
var registryErrors error = registryErr

p, err := driverListPath(m.Path)
if err != nil {
Expand Down Expand Up @@ -130,6 +134,10 @@ func (m addModel) Init() tea.Cmd {

drv, err := findDriver(spec.Name, drivers)
if err != nil {
// If we have registry errors, enhance the error message
if registryErrors != nil {
return fmt.Errorf("%w\n\nNote: Some driver registries were unavailable:\n%s", err, registryErrors.Error())
}
return err
}

Expand All @@ -141,7 +149,12 @@ func (m addModel) Init() tea.Cmd {
}
} else {
if !m.Pre && !drv.HasNonPrerelease() {
return fmt.Errorf("driver `%s` not found in driver registry index", spec.Name)
err := fmt.Errorf("driver `%s` not found in driver registry index", spec.Name)
// If we have registry errors, enhance the error message
if registryErrors != nil {
return fmt.Errorf("%w\n\nNote: Some driver registries were unavailable:\n%s", err, registryErrors.Error())
}
return err
}
}

Expand Down
83 changes: 83 additions & 0 deletions cmd/dbc/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package main
import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -326,3 +327,85 @@ func (suite *SubcommandTestSuite) TestAddExplicitPrereleaseWithoutPreFlag() {
version = '=0.9.0-alpha.1'
`, string(data))
}

func (suite *SubcommandTestSuite) TestAddPartialRegistryFailure() {
// Initialize driver list
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
suite.runCmd(m)

// Test that add command handles partial registry failure gracefully
// (one registry succeeds, another fails - returns both drivers and error)
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://cdn-fallback.example.com: failed to fetch driver registry: DNS resolution failed")
}

// Should succeed if the requested driver is found in the available drivers
m = AddCmd{
Path: filepath.Join(suite.tempdir, "dbc.toml"),
Driver: []string{"test-driver-1"},
Pre: false,
}.GetModelCustom(
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})

suite.runCmd(m)
// Should succeed without printing the registry error

// Verify the file was updated correctly
data, err := os.ReadFile(filepath.Join(suite.tempdir, "dbc.toml"))
suite.Require().NoError(err)
suite.Contains(string(data), "[drivers.test-driver-1]")
}

func (suite *SubcommandTestSuite) TestAddPartialRegistryFailureDriverNotFound() {
// Initialize driver list
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
suite.runCmd(m)

// Test that add command shows registry errors when the requested driver is not found
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://cdn-fallback.example.com: failed to fetch driver registry: DNS resolution failed")
}

// Should fail with enhanced error message if the requested driver is not found
m = AddCmd{
Path: filepath.Join(suite.tempdir, "dbc.toml"),
Driver: []string{"nonexistent-driver"},
Pre: false,
}.GetModelCustom(
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})

out := suite.runCmdErr(m)
// Should show the driver not found error AND the registry error
suite.Contains(out, "driver `nonexistent-driver` not found")
suite.Contains(out, "Note: Some driver registries were unavailable")
suite.Contains(out, "failed to fetch driver registry")
suite.Contains(out, "DNS resolution failed")
}

func (suite *SubcommandTestSuite) TestAddCompleteRegistryFailure() {
// Initialize driver list
m := InitCmd{Path: filepath.Join(suite.tempdir, "dbc.toml")}.GetModel()
suite.runCmd(m)

// Test that add command handles complete registry failure (no drivers returned)
completeFailingRegistry := func() ([]dbc.Driver, error) {
return nil, fmt.Errorf("registry https://primary-cdn.example.com: network unreachable")
}

m = AddCmd{
Path: filepath.Join(suite.tempdir, "dbc.toml"),
Driver: []string{"test-driver-1"},
Pre: false,
}.GetModelCustom(
baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg})

out := suite.runCmdErr(m)
suite.Contains(out, "error getting driver list")
suite.Contains(out, "network unreachable")
}
24 changes: 15 additions & 9 deletions cmd/dbc/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,13 @@ func (c DocsCmd) GetModel() tea.Model {
type docsModel struct {
baseModel

driver string
drv *dbc.Driver
urlToOpen string
noOpen bool
fallbackUrls map[string]string
openBrowser func(string) error
driver string
drv *dbc.Driver
urlToOpen string
noOpen bool
fallbackUrls map[string]string
openBrowser func(string) error
registryErrors error // Store registry errors for better error messages
}

func (m docsModel) Init() tea.Cmd {
Expand All @@ -81,13 +82,18 @@ func (m docsModel) Init() tea.Cmd {
return docsUrlFound(dbcDocsUrl)
}

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

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

Expand Down
79 changes: 79 additions & 0 deletions cmd/dbc/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package main

import (
"fmt"

"github.com/columnar-tech/dbc"
)

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

suite.Equal("http://example.com", lastOpenedURL)
}

func (suite *SubcommandTestSuite) TestDocsPartialRegistryFailure() {
// Test that docs command handles partial registry failure gracefully
// (one registry succeeds, another fails - returns both drivers and error)
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://fallback-registry.example.com: failed to fetch driver registry: timeout")
}

openBrowserFunc = mockOpenBrowserSuccess
lastOpenedURL = ""
fallbackDriverDocsUrl = testFallbackUrls

// Should succeed if the requested driver is found in the available drivers
m := DocsCmd{Driver: "test-driver-1"}.GetModelCustom(
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg},
false,
mockOpenBrowserSuccess,
testFallbackUrls,
)

suite.runCmd(m)
// Should open docs successfully without showing the registry error
suite.Equal("https://test.example.com/driver1", lastOpenedURL)
}

func (suite *SubcommandTestSuite) TestDocsPartialRegistryFailureDriverNotFound() {
// Test that docs command shows registry errors when the requested driver is not found
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://fallback-registry.example.com: failed to fetch driver registry: timeout")
}

openBrowserFunc = mockOpenBrowserSuccess
lastOpenedURL = ""

// Should fail with enhanced error message if the requested driver is not found
m := DocsCmd{Driver: "nonexistent-driver"}.GetModelCustom(
baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg},
false,
mockOpenBrowserSuccess,
testFallbackUrls,
)

out := suite.runCmdErr(m)
// Should show the driver not found error AND the registry error
suite.Contains(out, "driver `nonexistent-driver` not found")
suite.Contains(out, "Note: Some driver registries were unavailable")
suite.Contains(out, "failed to fetch driver registry")
suite.Contains(out, "timeout")
suite.Equal("", lastOpenedURL, "browser should not be opened on error")
}

func (suite *SubcommandTestSuite) TestDocsCompleteRegistryFailure() {
// Test that docs command handles complete registry failure (no drivers returned)
completeFailingRegistry := func() ([]dbc.Driver, error) {
return nil, fmt.Errorf("registry https://main-registry.example.com: connection timeout")
}

openBrowserFunc = mockOpenBrowserSuccess
lastOpenedURL = ""

m := DocsCmd{Driver: "test-driver-1"}.GetModelCustom(
baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg},
false,
mockOpenBrowserSuccess,
testFallbackUrls,
)

out := suite.runCmdErr(m)
suite.Contains(out, "connection timeout")
suite.Equal("", lastOpenedURL, "browser should not be opened on error")
}
19 changes: 13 additions & 6 deletions cmd/dbc/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package main

import (
"encoding/json"
"fmt"
"strings"

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

driver string
jsonOutput bool
drv dbc.Driver
driver string
jsonOutput bool
drv dbc.Driver
registryErrors error // Store registry errors for better error messages
}

func (m infoModel) Init() tea.Cmd {
return func() tea.Msg {
drivers, err := m.getDriverRegistry()
if err != nil {
return err
drivers, registryErr := m.getDriverRegistry()
// If we have no drivers and there's an error, fail immediately
if len(drivers) == 0 && registryErr != nil {
return fmt.Errorf("error getting driver list: %w", registryErr)
}

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

Expand Down
60 changes: 60 additions & 0 deletions cmd/dbc/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@

package main

import (
"fmt"

"github.com/columnar-tech/dbc"
)

func (suite *SubcommandTestSuite) TestInfo() {
m := InfoCmd{Driver: "test-driver-1"}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
Expand All @@ -34,3 +40,57 @@ func (suite *SubcommandTestSuite) TestInfo_DriverNotFound() {

suite.validateOutput("\r ", "\nError: driver `non-existent-driver` not found in driver registry index", out)
}

func (suite *SubcommandTestSuite) TestInfoPartialRegistryFailure() {
// Test that info command handles partial registry failure gracefully
// (one registry succeeds, another fails - returns both drivers and error)
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://secondary-registry.example.com: failed to fetch driver registry: DNS error")
}

// Should succeed if the requested driver is found in the available drivers
m := InfoCmd{Driver: "test-driver-1"}.
GetModelCustom(baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})

out := suite.runCmd(m)
// Should display info successfully without printing the registry error
suite.Contains(out, "Driver: test-driver-1")
suite.Contains(out, "Version: 1.1.0")
}

func (suite *SubcommandTestSuite) TestInfoPartialRegistryFailureDriverNotFound() {
// Test that info command shows registry errors when the requested driver is not found
partialFailingRegistry := func() ([]dbc.Driver, error) {
// Get drivers from the test registry (simulating one successful registry)
drivers, _ := getTestDriverRegistry()
// But also return an error (simulating another registry that failed)
return drivers, fmt.Errorf("registry https://secondary-registry.example.com: failed to fetch driver registry: DNS error")
}

// Should fail with enhanced error message if the requested driver is not found
m := InfoCmd{Driver: "nonexistent-driver"}.
GetModelCustom(baseModel{getDriverRegistry: partialFailingRegistry, downloadPkg: downloadTestPkg})

out := suite.runCmdErr(m)
// Should show the driver not found error AND the registry error
suite.Contains(out, "driver `nonexistent-driver` not found")
suite.Contains(out, "Note: Some driver registries were unavailable")
suite.Contains(out, "failed to fetch driver registry")
suite.Contains(out, "DNS error")
}

func (suite *SubcommandTestSuite) TestInfoCompleteRegistryFailure() {
// Test that info command handles complete registry failure (no drivers returned)
completeFailingRegistry := func() ([]dbc.Driver, error) {
return nil, fmt.Errorf("registry https://primary-registry.example.com: network unreachable")
}

m := InfoCmd{Driver: "test-driver-1"}.
GetModelCustom(baseModel{getDriverRegistry: completeFailingRegistry, downloadPkg: downloadTestPkg})

out := suite.runCmdErr(m)
suite.Contains(out, "network unreachable")
}
Loading
Loading