Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b30e994
Parameterize SubCommandTest with configLevel
amoeba Dec 19, 2025
d53fe2e
Rework logic
amoeba Dec 19, 2025
01afc5b
Use sudo -E for system tests
amoeba Dec 19, 2025
5c1405a
See what tests fail with this
amoeba Dec 19, 2025
2340f19
revert changes that were mistakes
amoeba Dec 19, 2025
f587286
Remove my old fake user/system level tests
amoeba Dec 19, 2025
b38b9b3
Add test suite helpers so we can test on all platforms
amoeba Dec 19, 2025
9217c44
handle manifest-only drivers
amoeba Dec 19, 2025
ec0b84c
fix mistake
amoeba Dec 19, 2025
dae5016
Update install_test.go
amoeba Dec 19, 2025
5b5713f
attempt to fix tests
amoeba Dec 19, 2025
bff221b
whoops
amoeba Dec 19, 2025
d7792bb
Update install_test.go
amoeba Dec 19, 2025
410be3a
add user and system level test teardown
amoeba Dec 19, 2025
9bfe476
Fix test failures now that we can clean up
amoeba Dec 19, 2025
a5975be
Fix uninstall tests
amoeba Dec 19, 2025
7fa5be7
Tear down registry correctly
amoeba Dec 19, 2025
e69744f
Fix bug in sidecar removal
amoeba Dec 19, 2025
eda2623
Restore change in install_test.go
amoeba Dec 19, 2025
ed293e7
Factor out expectedDir logic
amoeba Dec 19, 2025
69eaa04
Fix not removing symlink to show off this pr
amoeba Dec 19, 2025
139ef61
Fix symlink behavior on all plats
amoeba Dec 19, 2025
1512cd5
whoops
amoeba Dec 19, 2025
b7a3289
Remove GetLocation wrapper, export ConfigLocation
amoeba Dec 23, 2025
643f102
Use suite.T().Setenv
amoeba Dec 23, 2025
5957bdf
fix windows
amoeba Dec 23, 2025
0336048
whoops
amoeba Dec 24, 2025
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
17 changes: 17 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,20 @@ jobs:
# the file with `test_registry` also requires windows, so we can safely add this tag
# without it running on non-windows OSes
run: go test -v -tags test_registry ./...

# User and System level tests
- name: Run Tests (User)
run: go test -v ./...
env:
DBC_TEST_LEVEL_USER: 1
- name: Run Tests (System, Unixlike)
# Run system tests with sudo on Unixlikes to replicate what users do
if: runner.os != 'Windows'
run: sudo -E go test -v ./...
env:
DBC_TEST_LEVEL_SYSTEM: 1
- name: Run Tests (System, Windows)
if: runner.os == 'Windows'
run: go test -v ./...
env:
DBC_TEST_LEVEL_SYSTEM: 1
114 changes: 33 additions & 81 deletions cmd/dbc/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,20 @@ import (
)

func (suite *SubcommandTestSuite) TestInstall() {
m := InstallCmd{Driver: "test-driver-1", Level: config.ConfigEnv}.
m := InstallCmd{Driver: "test-driver-1", Level: suite.configLevel}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
out := suite.runCmd(m)

suite.validateOutput("\r[✓] searching\r\n[✓] downloading\r\n[✓] installing\r\n[✓] verifying signature\r\n",
"\nInstalled test-driver-1 1.1.0 to "+suite.tempdir+"\n", out)
if runtime.GOOS != "windows" {
suite.FileExists(filepath.Join(suite.tempdir, "test-driver-1.toml"))
}
"\nInstalled test-driver-1 1.1.0 to "+suite.Dir()+"\n", out)
suite.driverIsInstalled("test-driver-1", true)
}

func (suite *SubcommandTestSuite) TestInstallDriverNotFound() {
m := InstallCmd{Driver: "foo", Level: config.ConfigEnv}.
m := InstallCmd{Driver: "foo", Level: suite.configLevel}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
suite.validateOutput("Error: could not find driver: driver `foo` not found in driver registry index\r\n\r ", "", suite.runCmdErr(m))
suite.driverIsNotInstalled("test-driver-1")
}

func (suite *SubcommandTestSuite) TestInstallWithVersion() {
Expand All @@ -54,13 +53,14 @@ func (suite *SubcommandTestSuite) TestInstallWithVersion() {

for _, tt := range tests {
suite.Run(tt.driver, func() {
m := InstallCmd{Driver: tt.driver}.
m := InstallCmd{Driver: tt.driver, Level: suite.configLevel}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
out := suite.runCmd(m)
suite.validateOutput("\r[✓] searching\r\n[✓] downloading\r\n[✓] installing\r\n[✓] verifying signature\r\n",
"\nInstalled test-driver-1 "+tt.expectedVersion+" to "+suite.tempdir+"\n", out)

m = UninstallCmd{Driver: "test-driver-1"}.GetModelCustom(
suite.validateOutput("\r[✓] searching\r\n[✓] downloading\r\n[✓] installing\r\n[✓] verifying signature\r\n",
"\nInstalled test-driver-1 "+tt.expectedVersion+" to "+suite.Dir()+"\n", out)
suite.driverIsInstalled("test-driver-1", true)
m = UninstallCmd{Driver: "test-driver-1", Level: suite.configLevel}.GetModelCustom(
baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
suite.runCmd(m)
})
Expand Down Expand Up @@ -91,55 +91,6 @@ func (suite *SubcommandTestSuite) TestReinstallUpdateVersion() {
"test-driver-1.1/test-driver-1-not-valid.so.sig", "test-driver-1.toml"}, suite.getFilesInTempDir())
}

func (suite *SubcommandTestSuite) TestInstallUserFake() {
if runtime.GOOS == "windows" {
suite.T().Skip()
}

os.Unsetenv("ADBC_DRIVER_PATH")

m := InstallCmd{Driver: "test-driver-1"}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
installModel := m.(progressiveInstallModel)
suite.Equal(installModel.cfg.Level, config.ConfigUser)
installModel.cfg.Location = filepath.Join(suite.tempdir, "root", installModel.cfg.Location)
m = installModel // <- We need to reassign to make the change stick
suite.runCmd(m)
suite.FileExists(filepath.Join(installModel.cfg.Location, "test-driver-1.toml"))
}

func (suite *SubcommandTestSuite) TestInstallUserFakeExplicit() {
if runtime.GOOS == "windows" {
suite.T().Skip()
}

os.Unsetenv("ADBC_DRIVER_PATH")

m := InstallCmd{Driver: "test-driver-1", Level: config.ConfigUser}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
installModel := m.(progressiveInstallModel)
suite.Equal(installModel.cfg.Level, config.ConfigUser)
installModel.cfg.Location = filepath.Join(suite.tempdir, "root", installModel.cfg.Location)
m = installModel // <- We need to reassign to make the change stick
suite.runCmd(m)
suite.FileExists(filepath.Join(installModel.cfg.Location, "test-driver-1.toml"))
}

func (suite *SubcommandTestSuite) TestInstallSystemFake() {
if runtime.GOOS == "windows" {
suite.T().Skip()
}

m := InstallCmd{Driver: "test-driver-1", Level: config.ConfigSystem}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
installModel := m.(progressiveInstallModel)
suite.Equal(installModel.cfg.Level, config.ConfigSystem)
installModel.cfg.Location = filepath.Join(suite.tempdir, "root", installModel.cfg.Location)
m = installModel // <- We need to reassign to make the change stick
suite.runCmd(m)
suite.FileExists(filepath.Join(installModel.cfg.Location, "test-driver-1.toml"))
}

func (suite *SubcommandTestSuite) TestInstallVenv() {
os.Unsetenv("ADBC_DRIVER_PATH")
os.Setenv("VIRTUAL_ENV", suite.tempdir)
Expand Down Expand Up @@ -197,32 +148,14 @@ func (suite *SubcommandTestSuite) TestInstallCondaPrefix() {
"\nInstalled test-driver-1 1.1.0 to "+filepath.Join(suite.tempdir, "etc", "adbc", "drivers")+"\n", suite.runCmd(m))
}

func (suite *SubcommandTestSuite) TestInstallUserFakeExplicitLevelOverrides() {
if runtime.GOOS == "windows" {
suite.T().Skip()
}

// If the user explicitly sets level, it should override ADBC_DRIVER_PATH
// which, when testing, is set to tempdir
m := InstallCmd{Driver: "test-driver-1", Level: config.ConfigSystem}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
installModel := m.(progressiveInstallModel)
suite.Equal(installModel.cfg.Level, config.ConfigSystem)
installModel.cfg.Location = filepath.Join(suite.tempdir, "user", installModel.cfg.Location)
m = installModel // <- We need to reassign to make the change stick
suite.runCmd(m)
suite.FileExists(filepath.Join(installModel.cfg.Location, "test-driver-1.toml"))
}

func (suite *SubcommandTestSuite) TestInstallManifestOnlyDriver() {
m := InstallCmd{Driver: "test-driver-manifest-only"}.
m := InstallCmd{Driver: "test-driver-manifest-only", Level: suite.configLevel}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})

suite.validateOutput("\r[✓] searching\r\n[✓] downloading\r\n[✓] installing\r\n[✓] verifying signature\r\n",
"\nInstalled test-driver-manifest-only 1.0.0 to "+suite.tempdir+"\n"+
"\nInstalled test-driver-manifest-only 1.0.0 to "+suite.Dir()+"\n"+
"\nMust have libtest_driver installed to load this driver\n", suite.runCmd(m))
if runtime.GOOS != "windows" {
suite.FileExists(filepath.Join(suite.tempdir, "test-driver-manifest-only.toml"))
}
suite.driverIsInstalled("test-driver-manifest-only", false)
}

func (suite *SubcommandTestSuite) TestInstallDriverNoSignature() {
Expand Down Expand Up @@ -316,3 +249,22 @@ func (suite *SubcommandTestSuite) TestInstallGitignorePreserveUserModified() {
}
suite.Equal(userContent, string(data))
}

func (suite *SubcommandTestSuite) TestInstallCreatesSymlinks() {
if runtime.GOOS == "windows" && (suite.configLevel == config.ConfigUser || suite.configLevel == config.ConfigSystem) {
suite.T().Skip("Symlinks aren't created on Windows for User and System config levels")
}

// Install a driver
m := InstallCmd{Driver: "test-driver-1", Level: suite.configLevel}.
GetModelCustom(baseModel{getDriverRegistry: getTestDriverRegistry, downloadPkg: downloadTestPkg})
_ = suite.runCmd(m)
suite.driverIsInstalled("test-driver-1", true)

// Verify symlink is in place in the parent dir and is actually a symlink
manifestPath := filepath.Join(suite.Dir(), "..", "test-driver-1.toml")
suite.FileExists(manifestPath)
info, err := os.Lstat(manifestPath)
suite.NoError(err)
suite.Equal(os.ModeSymlink, info.Mode()&os.ModeSymlink, "Expected test-driver-1.toml to be a symlink")
}
82 changes: 76 additions & 6 deletions cmd/dbc/subcommand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/columnar-tech/dbc"
"github.com/columnar-tech/dbc/config"
"github.com/go-faster/yaml"
"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -83,24 +84,26 @@ type SubcommandTestSuite struct {
openBrowserFn func(string) error
fallbackDriverDocsUrl map[string]string
tempdir string

configLevel config.ConfigLevel
}

func (suite *SubcommandTestSuite) SetupSuite() {
suite.getDriverRegistryFn = getDriverRegistry
getDriverRegistry = getTestDriverRegistry
suite.openBrowserFn = openBrowserFunc
suite.fallbackDriverDocsUrl = fallbackDriverDocsUrl

if suite.configLevel == config.ConfigUnknown {
suite.configLevel = config.ConfigEnv
}
}

func (suite *SubcommandTestSuite) SetupTest() {
suite.tempdir = suite.T().TempDir()
suite.Require().NoError(os.Setenv("ADBC_DRIVER_PATH", suite.tempdir))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, we could save ourselves some trouble and just use suite.T().Setenv, let's do that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I didn't know about that.

I realized we can make the change more comprehensively too so I did that in 643f102.

}

func (suite *SubcommandTestSuite) TearDownTest() {
suite.Require().NoError(os.Unsetenv("ADBC_DRIVER_PATH"))
}
Comment on lines 100 to 102
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove this? If we're not going to remove this env var after each test, then we should move the os.Setenv to SetupSuite and add Unsetenv to TearDownSuite

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This got split out into platform specific TearDownTest methods (subcommand_unix_test.go, subcommand_windows_test.go)


func (suite *SubcommandTestSuite) TearDownSuite() {
getDriverRegistry = suite.getDriverRegistryFn
openBrowserFunc = suite.openBrowserFn
Expand All @@ -120,6 +123,15 @@ func (suite *SubcommandTestSuite) getFilesInTempDir() []string {
return filelist
}

// Get the base directory for where drivers are installed. Use this instead of
// hardcoding checks to suite.tempdir to make tests support other config levels.
func (suite *SubcommandTestSuite) Dir() string {
if suite.configLevel == config.ConfigEnv {
return suite.tempdir
}
return config.GetLocation(suite.configLevel)
}

func (suite *SubcommandTestSuite) runCmdErr(m tea.Model) string {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
Expand Down Expand Up @@ -171,6 +183,64 @@ func (suite *SubcommandTestSuite) validateOutput(expected, extra, actual string)
suite.Equal(terminalPrefix+expected+terminalSuffix+extra, actual)
}

func TestSubcommands(t *testing.T) {
suite.Run(t, new(SubcommandTestSuite))
// The SubcommandTestSuite is only run for ConfigEnv by default but is
// parametrized by configLevel so tests can be run for other levels. Tests must
// opt into this behavior by instantiating subcommands with `suite.configLevel`
// like:
//
// m := InstallCmd{Driver: "foo", Level: suite.configLevel}
// ^---- here
//
// and can opt out of this behavior by specifying it separately like:
//
// m := InstallCmd{Driver: "test-driver-1", Level: config.ConfigEnv}.
//
// When any level is explicitly requested, tests are only run for that level.
// i.e., to run tests for multiple levels, each level must be specified
// separately.
func TestSubcommandsEnv(t *testing.T) {
_, env := os.LookupEnv("DBC_TEST_LEVEL_ENV")
_, user := os.LookupEnv("DBC_TEST_LEVEL_USER")
_, system := os.LookupEnv("DBC_TEST_LEVEL_SYSTEM")

// Run if explicitly requested, or if no levels were requested (default
// behavior)
if env || (!user && !system) {
suite.Run(t, &SubcommandTestSuite{configLevel: config.ConfigEnv})
return
}
t.Skip("skipping tests for config level: ConfigEnv")
}

func TestSubcommandsUser(t *testing.T) {
if _, ok := os.LookupEnv("DBC_TEST_LEVEL_USER"); !ok {
t.Skip("skipping tests for config level: ConfigUser")
}
suite.Run(t, &SubcommandTestSuite{configLevel: config.ConfigUser})
}

func TestSubcommandsSystem(t *testing.T) {
if _, ok := os.LookupEnv("DBC_TEST_LEVEL_SYSTEM"); !ok {
t.Skip("skipping tests for config level: ConfigSystem")
}
suite.Run(t, &SubcommandTestSuite{configLevel: config.ConfigSystem})
}

func (suite *SubcommandTestSuite) driverIsInstalled(path string, checkShared bool) {
cfg := config.Get()[suite.configLevel]

driver, err := config.GetDriver(cfg, path)
suite.Require().NoError(err, "driver manifest should exist for driver `%s`", path)

if checkShared {
sharedPath := driver.Driver.Shared.Get(config.PlatformTuple())
suite.FileExists(sharedPath, "driver shared library should exist for driver `%s`", path)
}
}

func (suite *SubcommandTestSuite) driverIsNotInstalled(path string) {
cfg := config.Get()[suite.configLevel]

_, err := config.GetDriver(cfg, path)
suite.Require().Error(err, "driver manifest should not exist for driver `%s`", path)
}
37 changes: 37 additions & 0 deletions cmd/dbc/subcommand_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2025 Columnar Technologies Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !windows

package main

import (
"os"

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

func (suite *SubcommandTestSuite) TearDownTest() {
suite.Require().NoError(os.Unsetenv("ADBC_DRIVER_PATH"))

// Clean up filesystem after each test
_, user := os.LookupEnv("DBC_TEST_LEVEL_USER")
_, system := os.LookupEnv("DBC_TEST_LEVEL_SYSTEM")
if user {
suite.Require().NoError(os.RemoveAll(config.GetLocation(config.ConfigUser)))
}
if system {
suite.Require().NoError(os.RemoveAll(config.GetLocation(config.ConfigSystem)))
}
}
Loading
Loading