cli/pkg/config/config_test.go
Snider 32f1d0ab5d Feature add tdd core tests (#22)
* feat: Add TDD tests for core package

Adds a new `tdd/` directory for TDD-style contract tests.

Implements a comprehensive test suite for the `pkg/core` package, covering:
- `New()`
- `WithService()`
- `WithName()`
- `WithWails()`
- `WithAssets()`
- `WithServiceLock()`
- `RegisterService()`
- `Service()`
- `ServiceFor()`
- `MustServiceFor()`
- `ACTION()`
- `RegisterAction()`
- `RegisterActions()`

To support testing, a public `Assets()` method was added to the `Core` struct.

* feat: Add TDD tests for e, io, runtime, and config packages

Adds comprehensive TDD tests to the `tdd/` directory for the following packages:
- `pkg/e`
- `pkg/io`
- `pkg/runtime`
- `pkg/config`

This significantly improves the test coverage of the project.

To support testing the `runtime` package, the `newWithFactories` function was exported as `NewWithFactories`.

The existing tests for the `config` package were moved from the `internal` package to the `tdd/` directory and adapted to use the public API.

* fix: Update tdd tests for config, core, and runtime

Updates the TDD tests for the `config`, `core`, and `runtime` packages to improve their coverage and correctness.

- In `tdd/config_test.go`, the `TestIsFeatureEnabled` test is updated to use `s.Set` to modify the `features` slice, ensuring that the persistence logic is exercised.
- In `tdd/core_test.go`, the `TestCore_WithAssets_Good` test is updated to use a real embedded filesystem with `//go:embed` to verify the contents of a test file.
- In `tdd/runtime_test.go`, the `TestNew_Good` test is converted to a table-driven test to cover the happy path, error cases, and a case with a non-nil `application.App`.

* fix: Fix build and improve test coverage

This commit fixes a build failure in the `pkg/runtime` tests and significantly improves the test coverage for several packages.

- Fixes a build failure in `pkg/runtime/runtime_test.go` by updating a call to an exported function.
- Moves TDD tests for `config` and `e` packages into their respective package directories to ensure accurate coverage reporting.
- Adds a new test suite for the `pkg/i18n` package, including a test helper to inject a mock i18n bundle.
- Moves and updates tests for the `pkg/crypt` package to use its public API.
- The coverage for `config` and `e` is now 100%.
- The coverage for `crypt` and `i18n` has been significantly improved.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-11-02 22:29:17 +00:00

206 lines
5.4 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
"github.com/Snider/Core/pkg/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const appName = "lethean"
const configFileName = "config.json"
// setupTestEnv creates a temporary home directory for testing and ensures a clean environment.
func setupTestEnv(t *testing.T) (string, func()) {
tempHomeDir, err := os.MkdirTemp("", "test_home_*")
require.NoError(t, err, "Failed to create temp home directory")
oldHome := os.Getenv("HOME")
os.Setenv("HOME", tempHomeDir)
// Unset XDG vars to ensure HOME is used for path resolution, creating a hermetic test.
oldXdgData, hadXdgData := os.LookupEnv("XDG_DATA_HOME")
oldXdgCache, hadXdgCache := os.LookupEnv("XDG_CACHE_HOME")
require.NoError(t, os.Unsetenv("XDG_DATA_HOME"))
require.NoError(t, os.Unsetenv("XDG_CACHE_HOME"))
cleanup := func() {
os.Setenv("HOME", oldHome)
if hadXdgData {
os.Setenv("XDG_DATA_HOME", oldXdgData)
} else {
os.Unsetenv("XDG_DATA_HOME")
}
if hadXdgCache {
os.Setenv("XDG_CACHE_HOME", oldXdgCache)
} else {
os.Unsetenv("XDG_CACHE_HOME")
}
os.RemoveAll(tempHomeDir)
}
return tempHomeDir, cleanup
}
func TestConfigService(t *testing.T) {
t.Run("New service creates default config", func(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
serviceInstance, err := New()
require.NoError(t, err, "New() failed")
// Check that the config file was created
assert.FileExists(t, serviceInstance.ConfigPath, "config.json was not created")
// Check default values
assert.Equal(t, "en", serviceInstance.Language, "Expected default language 'en'")
})
t.Run("New service loads existing config", func(t *testing.T) {
tempHomeDir, cleanup := setupTestEnv(t)
defer cleanup()
// Manually create a config file with non-default values
configDir := filepath.Join(tempHomeDir, appName, "config")
require.NoError(t, os.MkdirAll(configDir, os.ModePerm), "Failed to create test config dir")
configPath := filepath.Join(configDir, configFileName)
customConfig := `{"language": "fr", "features": ["beta-testing"]}`
require.NoError(t, os.WriteFile(configPath, []byte(customConfig), 0644), "Failed to write custom config file")
serviceInstance, err := New()
require.NoError(t, err, "New() failed while loading existing config")
assert.Equal(t, "fr", serviceInstance.Language, "Expected language 'fr'")
assert.True(t, serviceInstance.IsFeatureEnabled("beta-testing"), "Expected 'beta-testing' feature to be enabled")
assert.False(t, serviceInstance.IsFeatureEnabled("alpha-testing"), "Did not expect 'alpha-testing' to be enabled")
})
t.Run("Set and Get", func(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
s, err := New()
require.NoError(t, err, "New() failed")
key := "language"
expectedValue := "de"
require.NoError(t, s.Set(key, expectedValue), "Set() failed")
var actualValue string
require.NoError(t, s.Get(key, &actualValue), "Get() failed")
assert.Equal(t, expectedValue, actualValue, "Get() returned unexpected value")
})
}
func TestIsFeatureEnabled(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
s, err := New()
require.NoError(t, err)
// Test with no features enabled
assert.False(t, s.IsFeatureEnabled("beta-feature"))
// Enable a feature
err = s.Set("features", []string{"beta-feature", "alpha-testing"})
require.NoError(t, err)
// Test for an enabled feature
assert.True(t, s.IsFeatureEnabled("beta-feature"))
// Test for another enabled feature
assert.True(t, s.IsFeatureEnabled("alpha-testing"))
// Test for a disabled feature
assert.False(t, s.IsFeatureEnabled("gamma-feature"))
// Test with an empty string
assert.False(t, s.IsFeatureEnabled(""))
}
func TestSet_Good(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
s, err := New()
require.NoError(t, err, "New() failed")
// Test setting a string value
err = s.Set("language", "de")
assert.NoError(t, err)
var lang string
err = s.Get("language", &lang)
assert.NoError(t, err)
assert.Equal(t, "de", lang)
// Test setting a slice value
err = s.Set("features", []string{"new-feature"})
assert.NoError(t, err)
var features []string
err = s.Get("features", &features)
assert.NoError(t, err)
assert.Equal(t, []string{"new-feature"}, features)
}
func TestSet_Bad(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
s, err := New()
require.NoError(t, err, "New() failed")
// Test setting a value with the wrong type
err = s.Set("language", 123)
assert.Error(t, err)
// Test setting a non-existent key
err = s.Set("nonExistentKey", "value")
assert.Error(t, err)
}
func TestSet_Ugly(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
s, err := New()
require.NoError(t, err, "New() failed")
// This should not panic
assert.NotPanics(t, func() {
err = s.Set("features", nil)
})
assert.NoError(t, err)
// Verify the slice is now nil
var features []string
err = s.Get("features", &features)
assert.NoError(t, err)
assert.Nil(t, features)
// Test with a nil slice
err = s.Set("features", nil)
require.NoError(t, err)
assert.False(t, s.IsFeatureEnabled("beta-feature"))
}
func TestRegister_Good(t *testing.T) {
_, cleanup := setupTestEnv(t)
defer cleanup()
c, err := core.New()
require.NoError(t, err)
svc, err := Register(c)
assert.NoError(t, err)
assert.NotNil(t, svc)
configSvc, ok := svc.(*Service)
assert.True(t, ok)
assert.NotNil(t, configSvc.Runtime)
}