Add PromptKit configuration scope
This commit is contained in:
@@ -5,9 +5,10 @@ and shared command infrastructure across related applications.
|
||||
|
||||
The current Go library provides an instance-owned command registry, typed flag
|
||||
and operand bindings, Cobra-backed routing, invocation outcomes, generated help,
|
||||
application or CommandKit version reporting, configuration scope discovery, and
|
||||
application or CommandKit version reporting, configuration scope discovery,
|
||||
ConfigKit-backed validation, native effective YAML display, source inventory,
|
||||
and path explanation reports. PromptKit commands remain planned.
|
||||
and path explanation reports. Applications can register a PromptKit engine
|
||||
configuration scope independently of PromptKit definition commands.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -102,6 +102,11 @@ runtime context or acquire configuration. It reports the intersection between
|
||||
declared scope capabilities and installed handlers, so an operation is listed
|
||||
only when the corresponding command is available.
|
||||
|
||||
Applications can register the `promptkit` scope with `NewPromptKitScope` to
|
||||
expose PromptKit engine configuration through these handlers. This configuration
|
||||
scope can be installed without registering PromptKit prompt or profile
|
||||
definition commands.
|
||||
|
||||
### Validate configuration
|
||||
|
||||
`config validate` loads and validates the selected scope. The default scope is
|
||||
|
||||
@@ -6,8 +6,8 @@ declarations, an instance-owned registry, a Cobra-backed routing and parsing
|
||||
adapter, public `App.Execute` coordination, and generated help/version commands.
|
||||
Configuration suite registration, scope discovery, ConfigKit integration,
|
||||
validation, native effective YAML display, source inventory, and path
|
||||
explanation reports are implemented; other configuration and PromptKit
|
||||
operations remain pending. The
|
||||
explanation reports, and PromptKit engine configuration inspection are
|
||||
implemented; named prompt/profile inspection remains pending. The
|
||||
[README](../README.md) provides project orientation; the
|
||||
[documentation policy](policy/documentation.md#canonical-ownership) assigns
|
||||
canonical documentation owners.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Initial CommandKit implementation plan
|
||||
|
||||
Status: implementation underway; Stages 1-11 complete and Stages 12-14 pending.
|
||||
Status: implementation underway; Stages 1-12 complete and Stages 13-14 pending.
|
||||
|
||||
Date: 2026-10-07.
|
||||
|
||||
@@ -716,7 +716,7 @@ and CLI reference describe the feature.
|
||||
|
||||
## Stage 12: integrate the PromptKit configuration scope
|
||||
|
||||
**Status:** pending. **Prerequisite:** Stage 11.
|
||||
**Status:** complete. **Prerequisite:** Stage 11.
|
||||
|
||||
```go
|
||||
func NewPromptKitScope[C any](
|
||||
@@ -744,6 +744,10 @@ all display with app first/promptkit second, configuration-only installation,
|
||||
caller context/selection propagation, and metadata discovery without engine load.
|
||||
Fixtures do not depend on provider services or sibling repository runtime files.
|
||||
|
||||
**Completion evidence:** `promptkit_scope_test.go` exercises a real in-memory
|
||||
PromptKit engine, native reports, aggregate order, lazy discovery, and preserved
|
||||
PromptKit/ConfigKit error identities. `go test ./...` passes.
|
||||
|
||||
## Stage 13: implement named prompt/profile inspection
|
||||
|
||||
**Status:** pending. **Prerequisite:** Stage 12.
|
||||
|
||||
8
go.mod
8
go.mod
@@ -9,3 +9,11 @@ require (
|
||||
github.com/spf13/pflag v1.0.9
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6
|
||||
)
|
||||
|
||||
require (
|
||||
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 // indirect
|
||||
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20260930.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
||||
5
go.sum
5
go.sum
@@ -6,11 +6,11 @@ gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 h1:lz
|
||||
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0/go.mod h1:HjS0f+3q0IdHCTEz+VVAEgIwIWJFAZUjuKdAtvtqKdg=
|
||||
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20260930.0 h1:h12ZTRldAmpLAomj0oQRGteFxHLN1LPE6XDl9zJNbm8=
|
||||
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v3 v3.20260930.0/go.mod h1:OEFBeKdjTsLzqJwLmXI2O5gG3r64FktcVJVb3mBrRfg=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
@@ -22,7 +22,6 @@ go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
54
promptkit_scope.go
Normal file
54
promptkit_scope.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package commandkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/promptkit"
|
||||
)
|
||||
|
||||
// NewPromptKitScope adapts PromptKit's captured configuration inspection to
|
||||
// CommandKit's configuration commands.
|
||||
func NewPromptKitScope[C any](
|
||||
load func(context.Context, Selection, C) (*promptkit.Engine, error),
|
||||
) (ConfigScope[C], error) {
|
||||
if load == nil {
|
||||
return ConfigScope[C]{}, fmt.Errorf("PromptKit configuration loader is nil")
|
||||
}
|
||||
|
||||
return ConfigScope[C]{
|
||||
Name: "promptkit",
|
||||
Summary: "PromptKit engine configuration.",
|
||||
Operations: []ConfigOperation{ConfigValidate, ConfigShow, ConfigSources, ConfigExplain},
|
||||
Acquire: func(ctx context.Context, request ConfigRequest, appContext C) (Acquired[ConfigView], error) {
|
||||
engine, err := load(ctx, request.Selection, appContext)
|
||||
if err != nil {
|
||||
var loadErr *configkit.LoadError
|
||||
if errors.As(err, &loadErr) {
|
||||
return Acquired[ConfigView]{Diagnostics: ConfigKitDiagnostics("promptkit", loadErr.Sources(), loadErr.Diagnostics())}, err
|
||||
}
|
||||
return Acquired[ConfigView]{}, err
|
||||
}
|
||||
if engine == nil {
|
||||
return Acquired[ConfigView]{}, fmt.Errorf("PromptKit configuration loader returned a nil engine")
|
||||
}
|
||||
|
||||
inspection, err := engine.InspectConfiguration()
|
||||
if err != nil {
|
||||
return Acquired[ConfigView]{}, err
|
||||
}
|
||||
sources := inspection.Sources()
|
||||
diagnostics := ConfigKitDiagnostics("promptkit", sources, inspection.Diagnostics())
|
||||
return Acquired[ConfigView]{
|
||||
Value: ConfigView{
|
||||
Effective: inspection,
|
||||
Sources: inspection,
|
||||
Explanation: inspection,
|
||||
},
|
||||
Diagnostics: diagnostics,
|
||||
}, nil
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
220
promptkit_scope_test.go
Normal file
220
promptkit_scope_test.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package commandkit_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/promptkit"
|
||||
)
|
||||
|
||||
func TestPromptKitScopeUsesCapturedInspectionAndSelectedInputs(t *testing.T) {
|
||||
type appContext struct{ marker string }
|
||||
type contextKey struct{}
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
|
||||
Main: &configkit.Input{Source: configkit.Mapping("local fixture", map[string]any{
|
||||
"backends": map[string]any{"local": map[string]any{"endpoint": "https://example.invalid/v1"}},
|
||||
"profiles": map[string]any{"base.v1": map[string]any{"backend": "local", "model": "fixture-model"}},
|
||||
})},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("construct PromptKit engine: %v", err)
|
||||
}
|
||||
inspection, err := engine.InspectConfiguration()
|
||||
if err != nil {
|
||||
t.Fatalf("inspect PromptKit engine: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), contextKey{}, "caller-context")
|
||||
selection := commandkit.Selection{Config: commandkit.Supplied[string]{Value: "main.yml", Set: true}, Overlay: commandkit.Supplied[string]{Value: "dev", Set: true}}
|
||||
loaderCalls := 0
|
||||
var receivedSelections []commandkit.Selection
|
||||
scope, err := commandkit.NewPromptKitScope(func(gotContext context.Context, gotSelection commandkit.Selection, gotAppContext appContext) (*promptkit.Engine, error) {
|
||||
loaderCalls++
|
||||
receivedSelections = append(receivedSelections, gotSelection)
|
||||
if gotContext != ctx || gotContext.Value(contextKey{}) != "caller-context" {
|
||||
t.Fatal("PromptKit loader did not receive the caller context")
|
||||
}
|
||||
if gotAppContext.marker != "app-state" {
|
||||
t.Fatalf("loader app context = %+v", gotAppContext)
|
||||
}
|
||||
return engine, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if scope.Name != "promptkit" || len(scope.Operations) != 4 {
|
||||
t.Fatalf("scope declaration = %+v", scope)
|
||||
}
|
||||
|
||||
acquired, err := scope.Acquire(ctx, commandkit.ConfigRequest{Operation: commandkit.ConfigShow, Selection: selection}, appContext{marker: "app-state"})
|
||||
if err != nil {
|
||||
t.Fatalf("acquire PromptKit scope: %v", err)
|
||||
}
|
||||
var gotEffective, wantEffective bytes.Buffer
|
||||
if err := acquired.Value.Effective.WriteEffective(&gotEffective); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inspection.WriteEffective(&wantEffective); err != nil || gotEffective.String() != wantEffective.String() {
|
||||
t.Fatalf("native effective output = (%q, %v), want %q", gotEffective.String(), err, wantEffective.String())
|
||||
}
|
||||
if len(acquired.Value.Sources.Sources()) != len(inspection.Sources()) {
|
||||
t.Fatalf("source inventory = %#v, want %#v", acquired.Value.Sources.Sources(), inspection.Sources())
|
||||
}
|
||||
path := configkit.Keys("profiles", "base.v1", "model")
|
||||
var gotExplanation, wantExplanation bytes.Buffer
|
||||
if err := acquired.Value.Explanation.WriteExplanation(&gotExplanation, path); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inspection.WriteExplanation(&wantExplanation, path); err != nil || gotExplanation.String() != wantExplanation.String() {
|
||||
t.Fatalf("native explanation = (%q, %v), want %q", gotExplanation.String(), err, wantExplanation.String())
|
||||
}
|
||||
if loaderCalls != 1 {
|
||||
t.Fatalf("loader called %d times, want once", loaderCalls)
|
||||
}
|
||||
if len(receivedSelections) != 1 || receivedSelections[0] != selection {
|
||||
t.Fatalf("loader selection = %#v, want %#v", receivedSelections, selection)
|
||||
}
|
||||
|
||||
app, err := commandkit.New(commandkit.Identity{Name: "example"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[appContext]{
|
||||
NewContext: func() appContext { return appContext{marker: "app-state"} },
|
||||
Scopes: []commandkit.ConfigScope[appContext]{scope},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var show bytes.Buffer
|
||||
outcome := app.Execute(ctx, []string{"config", "show", "--scope", "promptkit", "--config", "main.yml", "--overlay", "dev"}, commandkit.Streams{Out: &show, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || show.String() != wantEffective.String() {
|
||||
t.Fatalf("config show = (%#v, %q), want native output %q", outcome, show.String(), wantEffective.String())
|
||||
}
|
||||
var sources bytes.Buffer
|
||||
outcome = app.Execute(ctx, []string{"config", "sources", "--scope", "promptkit", "--format", "yaml"}, commandkit.Streams{Out: &sources, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(sources.String(), "label: local fixture") {
|
||||
t.Fatalf("config sources = (%#v, %q), want captured PromptKit source", outcome, sources.String())
|
||||
}
|
||||
var explanation bytes.Buffer
|
||||
outcome = app.Execute(ctx, []string{"config", "explain", "--scope", "promptkit", "--path", path.String()}, commandkit.Streams{Out: &explanation, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || explanation.String() != wantExplanation.String() {
|
||||
t.Fatalf("config explain = (%#v, %q), want native output %q", outcome, explanation.String(), wantExplanation.String())
|
||||
}
|
||||
|
||||
callsBeforeDiscovery := loaderCalls
|
||||
outcome = app.Execute(ctx, []string{"config", "scopes"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || loaderCalls != callsBeforeDiscovery {
|
||||
t.Fatalf("metadata discovery outcome=%#v loader calls=%d", outcome, loaderCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPromptKitScopeAggregateOrderAndConfigurationOnlyRegistration(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
|
||||
return engine, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
app, err := commandkit.New(commandkit.Identity{Name: "example"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
|
||||
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
|
||||
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML("app: true\n")}}, nil
|
||||
}}, scope,
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "show", "--scope", "all"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess {
|
||||
t.Fatalf("aggregate show outcome = %#v", outcome)
|
||||
}
|
||||
appIndex, promptkitIndex := strings.Index(output.String(), "# scope: app"), strings.Index(output.String(), "# scope: promptkit")
|
||||
if appIndex < 0 || promptkitIndex <= appIndex {
|
||||
t.Fatalf("aggregate order = %q", output.String())
|
||||
}
|
||||
var scopes bytes.Buffer
|
||||
outcome = app.Execute(context.Background(), []string{"config", "scopes"}, commandkit.Streams{Out: &scopes, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(scopes.String(), "promptkit: validate, show, sources, explain") {
|
||||
t.Fatalf("configuration scope discovery = (%#v, %q)", outcome, scopes.String())
|
||||
}
|
||||
outcome = app.Execute(context.Background(), []string{"help", "promptkit"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeUsage {
|
||||
t.Fatalf("configuration-only install exposed promptkit definition commands: %#v", outcome)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPromptKitScopeErrorsPreserveIdentityAndRejectNilInputs(t *testing.T) {
|
||||
if _, err := commandkit.NewPromptKitScope[struct{}](nil); err == nil {
|
||||
t.Fatal("nil loader was accepted")
|
||||
}
|
||||
var nilEngine *promptkit.Engine
|
||||
if _, err := nilEngine.InspectConfiguration(); !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("PromptKit nil engine identity = %v", err)
|
||||
}
|
||||
|
||||
loadErr := errors.Join(promptkit.ErrInvalidConfig, errors.New("loader context"))
|
||||
scope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
|
||||
return nil, loadErr
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = scope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
|
||||
if err != loadErr || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("loader error = %v, want original error identity %v", err, loadErr)
|
||||
}
|
||||
|
||||
nilScope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := nilScope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{}); err == nil || !strings.Contains(err.Error(), "nil engine") {
|
||||
t.Fatalf("nil engine acquisition error = %v", err)
|
||||
}
|
||||
|
||||
negativeRetries := -1
|
||||
invalidEngine, err := promptkit.NewEngine(promptkit.Config{TransientRetries: &negativeRetries})
|
||||
if invalidEngine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("invalid PromptKit construction = (%v, %v)", invalidEngine, err)
|
||||
}
|
||||
|
||||
invalidEngine, err = promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
|
||||
Source: configkit.Mapping("invalid inheritance", map[string]any{
|
||||
"profiles": map[string]any{"child": map[string]any{"extends": "missing"}},
|
||||
}),
|
||||
}}})
|
||||
if invalidEngine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("invalid PromptKit profile construction = (%v, %v)", invalidEngine, err)
|
||||
}
|
||||
var configLoadErr *configkit.LoadError
|
||||
if !errors.As(err, &configLoadErr) {
|
||||
t.Fatalf("invalid PromptKit construction did not preserve ConfigKit LoadError: %v", err)
|
||||
}
|
||||
failedScope, scopeErr := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
|
||||
return nil, err
|
||||
})
|
||||
if scopeErr != nil {
|
||||
t.Fatal(scopeErr)
|
||||
}
|
||||
failed, scopeErr := failedScope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
|
||||
var acquiredLoadErr *configkit.LoadError
|
||||
if scopeErr != err || !errors.As(scopeErr, &acquiredLoadErr) || acquiredLoadErr != configLoadErr || len(failed.Diagnostics) == 0 {
|
||||
t.Fatalf("scope load error = (%v, %+v), want original LoadError and diagnostics", scopeErr, failed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user