Move the memory profile repository into the profile package
This commit is contained in:
50
internal/profile/memory_repository.go
Normal file
50
internal/profile/memory_repository.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/jsonvalue"
|
||||
)
|
||||
|
||||
// NewMemoryRepository constructs an immutable repository from profile
|
||||
// definitions supplied by an internal caller.
|
||||
func NewMemoryRepository(definitions []domain.ExecutionProfile) (Repository, error) {
|
||||
repository := &memoryRepository{profiles: make(map[string]domain.ExecutionProfile, len(definitions))}
|
||||
for _, definition := range definitions {
|
||||
extraParams, err := jsonvalue.CopyMap(definition.ExtraParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
definition.ExtraParams = extraParams
|
||||
if err := NormalizeAndValidateDefinition(&definition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, exists := repository.profiles[definition.ID]; exists {
|
||||
return nil, fmt.Errorf("duplicate profile id %q", definition.ID)
|
||||
}
|
||||
repository.profiles[definition.ID] = definition
|
||||
}
|
||||
return repository, nil
|
||||
}
|
||||
|
||||
type memoryRepository struct {
|
||||
profiles map[string]domain.ExecutionProfile
|
||||
}
|
||||
|
||||
func (r *memoryRepository) GetProfile(_ context.Context, id string) (*domain.ExecutionProfile, error) {
|
||||
if r == nil {
|
||||
return nil, ErrProfileNotFound
|
||||
}
|
||||
definition, found := r.profiles[id]
|
||||
if !found {
|
||||
return nil, ErrProfileNotFound
|
||||
}
|
||||
extraParams, err := jsonvalue.CopyMap(definition.ExtraParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("copy memory profile %q: %w", definition.ID, err)
|
||||
}
|
||||
definition.ExtraParams = extraParams
|
||||
return &definition, nil
|
||||
}
|
||||
91
internal/profile/memory_repository_test.go
Normal file
91
internal/profile/memory_repository_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
func TestMemoryRepositoryOwnsNormalizedDefinitions(t *testing.T) {
|
||||
nested := map[string]any{"value": "original"}
|
||||
definitions := []domain.ExecutionProfile{{
|
||||
ID: " profile ",
|
||||
BackendID: " backend ",
|
||||
Endpoint: " https://example.test/v1/ ",
|
||||
Model: "model",
|
||||
ExtraParams: map[string]any{"nested": nested},
|
||||
}}
|
||||
|
||||
repository, err := NewMemoryRepository(definitions)
|
||||
if err != nil {
|
||||
t.Fatalf("construct repository: %v", err)
|
||||
}
|
||||
nested["value"] = "mutated input"
|
||||
|
||||
first, err := repository.GetProfile(context.Background(), "profile")
|
||||
if err != nil {
|
||||
t.Fatalf("load profile: %v", err)
|
||||
}
|
||||
if first.ID != "profile" || first.BackendID != "backend" || first.Endpoint != "https://example.test/v1/" {
|
||||
t.Fatalf("normalized definition = %#v", first)
|
||||
}
|
||||
if first.ExtraParams["nested"].(map[string]any)["value"] != "original" {
|
||||
t.Fatalf("repository retained input mutation: %#v", first.ExtraParams)
|
||||
}
|
||||
first.ExtraParams["nested"].(map[string]any)["value"] = "mutated result"
|
||||
|
||||
second, err := repository.GetProfile(context.Background(), "profile")
|
||||
if err != nil {
|
||||
t.Fatalf("reload profile: %v", err)
|
||||
}
|
||||
if second.ExtraParams["nested"].(map[string]any)["value"] != "original" {
|
||||
t.Fatalf("repository retained result mutation: %#v", second.ExtraParams)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryRepositoryPreservesInheritanceAndRejectsInvalidDefinitions(t *testing.T) {
|
||||
derived, err := NewMemoryRepository([]domain.ExecutionProfile{{
|
||||
ID: "derived",
|
||||
BaseProfileID: "base",
|
||||
ReasoningEffort: "high",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("construct derived repository: %v", err)
|
||||
}
|
||||
profile, err := derived.GetProfile(context.Background(), "derived")
|
||||
if err != nil {
|
||||
t.Fatalf("load derived profile: %v", err)
|
||||
}
|
||||
if profile.BaseProfileID != "base" || profile.ReasoningEffort != "high" {
|
||||
t.Fatalf("derived profile = %#v", profile)
|
||||
}
|
||||
if _, err := derived.GetProfile(context.Background(), "missing"); !errors.Is(err, ErrProfileNotFound) {
|
||||
t.Fatalf("missing profile error = %v, want ErrProfileNotFound", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
definitions []domain.ExecutionProfile
|
||||
}{
|
||||
{
|
||||
name: "normalized duplicate IDs",
|
||||
definitions: []domain.ExecutionProfile{
|
||||
{ID: " duplicate ", Endpoint: "https://one.example/v1", Model: "one"},
|
||||
{ID: "duplicate", Endpoint: "https://two.example/v1", Model: "two"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid extra parameters",
|
||||
definitions: []domain.ExecutionProfile{{ID: "invalid", Endpoint: "https://example.test/v1", Model: "model", ExtraParams: map[string]any{"invalid": func() {}}}},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if _, err := NewMemoryRepository(test.definitions); err == nil {
|
||||
t.Fatal("expected repository construction error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user