Select inventory namespaces before copying
This commit is contained in:
@@ -2,36 +2,66 @@ package configuration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"go.maximumdirect.net/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
// IdentityFilter selects all identities, one namespace, or unqualified IDs.
|
||||
type IdentityFilter struct {
|
||||
All bool
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func selectIdentities[T any](ctx context.Context, entries []T, filter IdentityFilter, id func(T) string) ([]T, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filter.All {
|
||||
result := append([]T(nil), entries...)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
if filter.Namespace != "" {
|
||||
prefix := filter.Namespace + "/"
|
||||
start := sort.Search(len(entries), func(i int) bool { return id(entries[i]) >= prefix })
|
||||
end := start + sort.Search(len(entries)-start, func(i int) bool { return !strings.HasPrefix(id(entries[start+i]), prefix) })
|
||||
result := append([]T(nil), entries[start:end]...)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
var result []T
|
||||
for _, entry := range entries {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.Contains(id(entry), "/") {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CollectionIdentity is the captured metadata for one final collection entry.
|
||||
type CollectionIdentity struct {
|
||||
Namespace string
|
||||
ReleaseVersion *string
|
||||
}
|
||||
|
||||
func (state *loaded) ListProfiles(ctx context.Context) ([]string, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
profiles := append([]string(nil), state.profileIDs...)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
func (state *loaded) ListProfiles(ctx context.Context, filter IdentityFilter) ([]string, error) {
|
||||
return selectIdentities(ctx, state.profileIDs, filter, func(id string) string { return id })
|
||||
}
|
||||
|
||||
func (state *loaded) ListBackends(ctx context.Context) ([]string, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backends := append([]string(nil), state.backendIDs...)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return backends, nil
|
||||
func (state *loaded) ListBackends(ctx context.Context, filter IdentityFilter) ([]string, error) {
|
||||
return selectIdentities(ctx, state.backendIDs, filter, func(id string) string { return id })
|
||||
}
|
||||
|
||||
func (state *loaded) ListNamespaces(ctx context.Context, kinds []string) ([]string, error) {
|
||||
@@ -72,13 +102,6 @@ func (state *loaded) ListCollections(ctx context.Context) ([]CollectionIdentity,
|
||||
return collections, nil
|
||||
}
|
||||
|
||||
func (state *loaded) ListPrompts(ctx context.Context) ([]domain.PromptIdentity, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prompts := append([]domain.PromptIdentity(nil), state.promptIDs...)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return prompts, nil
|
||||
func (state *loaded) ListPrompts(ctx context.Context, filter IdentityFilter) ([]domain.PromptIdentity, error) {
|
||||
return selectIdentities(ctx, state.promptIDs, filter, func(entry domain.PromptIdentity) string { return entry.ID })
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestLoadedListsSortedEffectiveIdentities(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backends, err := state.ListBackends(context.Background())
|
||||
backends, err := state.ListBackends(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func TestLoadedListsSortedEffectiveIdentities(t *testing.T) {
|
||||
t.Fatalf("backend IDs = %#v, want %#v", backends, wantBackends)
|
||||
}
|
||||
|
||||
profiles, err := state.ListProfiles(context.Background())
|
||||
profiles, err := state.ListProfiles(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func TestLoadedListsSortedEffectiveIdentities(t *testing.T) {
|
||||
t.Fatalf("profile IDs = %#v, want %#v", profiles, wantProfiles)
|
||||
}
|
||||
|
||||
prompts, err := state.ListPrompts(context.Background())
|
||||
prompts, err := state.ListPrompts(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -65,36 +65,36 @@ func TestLoadedListsAreIndependent(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
profiles, err := state.ListProfiles(context.Background())
|
||||
profiles, err := state.ListProfiles(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
profiles = append(profiles, "caller profile")
|
||||
profiles[0] = "changed profile"
|
||||
backends, err := state.ListBackends(context.Background())
|
||||
backends, err := state.ListBackends(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backends[0] = "changed backend"
|
||||
|
||||
prompts, err := state.ListPrompts(context.Background())
|
||||
prompts, err := state.ListPrompts(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prompts[0].ID = "changed prompt"
|
||||
|
||||
profiles, err = state.ListProfiles(context.Background())
|
||||
profiles, err = state.ListProfiles(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(profiles, []string{"profile"}) {
|
||||
t.Fatalf("caller mutation changed profile inventory: %#v", profiles)
|
||||
}
|
||||
backends, err = state.ListBackends(context.Background())
|
||||
backends, err = state.ListBackends(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil || !reflect.DeepEqual(backends, []string{"backend"}) {
|
||||
t.Fatalf("caller mutation changed backend inventory: %#v, %v", backends, err)
|
||||
}
|
||||
prompts, err = state.ListPrompts(context.Background())
|
||||
prompts, err = state.ListPrompts(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -104,20 +104,66 @@ func TestLoadedListsAreIndependent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadedListsSelectNamespaces(t *testing.T) {
|
||||
ids := []string{"a", "a-b/one", "a/one", "a/two", "b", "z/one"}
|
||||
state := &loaded{profileIDs: append([]string(nil), ids...), backendIDs: append([]string(nil), ids...)}
|
||||
for _, id := range ids {
|
||||
state.promptIDs = append(state.promptIDs, domain.PromptIdentity{ID: id})
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
filter IdentityFilter
|
||||
want []string
|
||||
}{
|
||||
{"all", IdentityFilter{All: true}, ids},
|
||||
{"unqualified", IdentityFilter{}, []string{"a", "b"}},
|
||||
{"namespace", IdentityFilter{Namespace: "a"}, []string{"a/one", "a/two"}},
|
||||
{"prefix sharing", IdentityFilter{Namespace: "a-b"}, []string{"a-b/one"}},
|
||||
{"missing", IdentityFilter{Namespace: "absent"}, nil},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
profiles, err := state.ListProfiles(context.Background(), tc.filter)
|
||||
if err != nil || !reflect.DeepEqual(profiles, tc.want) {
|
||||
t.Fatalf("profiles = %v, %v; want %v", profiles, err, tc.want)
|
||||
}
|
||||
backends, err := state.ListBackends(context.Background(), tc.filter)
|
||||
if err != nil || !reflect.DeepEqual(backends, tc.want) {
|
||||
t.Fatalf("backends = %v, %v; want %v", backends, err, tc.want)
|
||||
}
|
||||
prompts, err := state.ListPrompts(context.Background(), tc.filter)
|
||||
if err != nil || len(prompts) != len(tc.want) {
|
||||
t.Fatalf("prompts = %v, %v; want %v", prompts, err, tc.want)
|
||||
}
|
||||
for i := range prompts {
|
||||
if prompts[i].ID != tc.want[i] {
|
||||
t.Fatalf("prompt ID = %q; want %q", prompts[i].ID, tc.want[i])
|
||||
}
|
||||
}
|
||||
if len(profiles) > 0 {
|
||||
profiles[0], backends[0], prompts[0].ID = "changed", "changed", "changed"
|
||||
}
|
||||
})
|
||||
}
|
||||
if !reflect.DeepEqual(state.profileIDs, ids) || !reflect.DeepEqual(state.backendIDs, ids) || state.promptIDs[0].ID != ids[0] {
|
||||
t.Fatal("caller mutation changed captured identities")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadedListsEmptyInventory(t *testing.T) {
|
||||
state, err := Load(configkit.Input{Source: configkit.NoSource()}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
profiles, err := state.ListProfiles(context.Background())
|
||||
profiles, err := state.ListProfiles(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil || len(profiles) != 0 {
|
||||
t.Fatalf("empty profile listing = %#v, %v", profiles, err)
|
||||
}
|
||||
backends, err := state.ListBackends(context.Background())
|
||||
backends, err := state.ListBackends(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil || len(backends) != 0 {
|
||||
t.Fatalf("empty backend listing = %#v, %v", backends, err)
|
||||
}
|
||||
prompts, err := state.ListPrompts(context.Background())
|
||||
prompts, err := state.ListPrompts(context.Background(), IdentityFilter{All: true})
|
||||
if err != nil || len(prompts) != 0 {
|
||||
t.Fatalf("empty prompt listing = %#v, %v", prompts, err)
|
||||
}
|
||||
@@ -134,17 +180,19 @@ func TestLoadedListsHonorCanceledContext(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
profiles, err := state.ListProfiles(ctx)
|
||||
if !errors.Is(err, context.Canceled) || len(profiles) != 0 {
|
||||
t.Fatalf("canceled profile listing = %#v, %v", profiles, err)
|
||||
}
|
||||
backends, err := state.ListBackends(ctx)
|
||||
if !errors.Is(err, context.Canceled) || len(backends) != 0 {
|
||||
t.Fatalf("canceled backend listing = %#v, %v", backends, err)
|
||||
}
|
||||
prompts, err := state.ListPrompts(ctx)
|
||||
if !errors.Is(err, context.Canceled) || len(prompts) != 0 {
|
||||
t.Fatalf("canceled prompt listing = %#v, %v", prompts, err)
|
||||
for _, filter := range []IdentityFilter{{All: true}, {}, {Namespace: "example"}} {
|
||||
profiles, err := state.ListProfiles(ctx, filter)
|
||||
if !errors.Is(err, context.Canceled) || len(profiles) != 0 {
|
||||
t.Fatalf("canceled profile listing = %#v, %v", profiles, err)
|
||||
}
|
||||
backends, err := state.ListBackends(ctx, filter)
|
||||
if !errors.Is(err, context.Canceled) || len(backends) != 0 {
|
||||
t.Fatalf("canceled backend listing = %#v, %v", backends, err)
|
||||
}
|
||||
prompts, err := state.ListPrompts(ctx, filter)
|
||||
if !errors.Is(err, context.Canceled) || len(prompts) != 0 {
|
||||
t.Fatalf("canceled prompt listing = %#v, %v", prompts, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user