[agent/codex:gpt-5.4] Read ~/spec/code/core/go/store/RFC.md fully. Find features d... #125
5 changed files with 101 additions and 18 deletions
|
|
@ -290,7 +290,7 @@ func TestCoverage_ScopedStore_Bad_GroupsClosedStore(t *testing.T) {
|
|||
var err error
|
||||
_, err = scopedStore.Groups("")
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "store.Groups")
|
||||
assert.Contains(t, err.Error(), "store.ScopedStore.Groups")
|
||||
}
|
||||
|
||||
func TestCoverage_ScopedStore_Bad_GroupsSeqRowsError(t *testing.T) {
|
||||
|
|
|
|||
40
scope.go
40
scope.go
|
|
@ -49,6 +49,25 @@ type ScopedStoreConfig struct {
|
|||
Quota QuotaConfig
|
||||
}
|
||||
|
||||
// Usage example: `if err := (store.ScopedStoreConfig{Namespace: "tenant-a", Quota: store.QuotaConfig{MaxKeys: 100, MaxGroups: 10}}).Validate(); err != nil { return }`
|
||||
func (config ScopedStoreConfig) Validate() error {
|
||||
if !validNamespace.MatchString(config.Namespace) {
|
||||
return core.E(
|
||||
"store.ScopedStoreConfig.Validate",
|
||||
core.Sprintf("namespace %q is invalid; use names like %q or %q", config.Namespace, "tenant-a", "tenant-42"),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
if config.Quota.MaxKeys < 0 || config.Quota.MaxGroups < 0 {
|
||||
return core.E(
|
||||
"store.ScopedStoreConfig.Validate",
|
||||
core.Sprintf("quota values must be zero or positive; got MaxKeys=%d MaxGroups=%d", config.Quota.MaxKeys, config.Quota.MaxGroups),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type scopedWatcherBinding struct {
|
||||
backingStore *Store
|
||||
underlyingEvents <-chan Event
|
||||
|
|
@ -84,20 +103,13 @@ func NewScoped(storeInstance *Store, namespace string) *ScopedStore {
|
|||
|
||||
// Usage example: `scopedStore, err := store.NewScopedConfigured(storeInstance, store.ScopedStoreConfig{Namespace: "tenant-a", Quota: store.QuotaConfig{MaxKeys: 100, MaxGroups: 10}}); if err != nil { return }`
|
||||
func NewScopedConfigured(storeInstance *Store, config ScopedStoreConfig) (*ScopedStore, error) {
|
||||
if storeInstance == nil {
|
||||
return nil, core.E("store.NewScopedConfigured", "store instance is nil", nil)
|
||||
}
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, core.E("store.NewScopedConfigured", "validate config", err)
|
||||
}
|
||||
scopedStore := NewScoped(storeInstance, config.Namespace)
|
||||
if scopedStore == nil {
|
||||
if storeInstance == nil {
|
||||
return nil, core.E("store.NewScopedConfigured", "store instance is nil", nil)
|
||||
}
|
||||
return nil, core.E("store.NewScopedConfigured", core.Sprintf("namespace %q is invalid; use names like %q or %q", config.Namespace, "tenant-a", "tenant-42"), nil)
|
||||
}
|
||||
if config.Quota.MaxKeys < 0 || config.Quota.MaxGroups < 0 {
|
||||
return nil, core.E(
|
||||
"store.NewScopedConfigured",
|
||||
core.Sprintf("quota values must be zero or positive; got MaxKeys=%d MaxGroups=%d", config.Quota.MaxKeys, config.Quota.MaxGroups),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
scopedStore.MaxKeys = config.Quota.MaxKeys
|
||||
scopedStore.MaxGroups = config.Quota.MaxGroups
|
||||
return scopedStore, nil
|
||||
|
|
@ -267,7 +279,7 @@ func (scopedStore *ScopedStore) CountAll(groupPrefix ...string) (int, error) {
|
|||
// Usage example: `groupNames, err := scopedStore.Groups("config")`
|
||||
// Usage example: `groupNames, err := scopedStore.Groups()`
|
||||
func (scopedStore *ScopedStore) Groups(groupPrefix ...string) ([]string, error) {
|
||||
backingStore, err := scopedStore.resolvedStore("store.Groups")
|
||||
backingStore, err := scopedStore.resolvedStore("store.ScopedStore.Groups")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,29 @@ func TestScope_NewScopedConfigured_Bad_InvalidNamespace(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "namespace")
|
||||
}
|
||||
|
||||
func TestScope_ScopedStoreConfig_Good_Validate(t *testing.T) {
|
||||
err := (ScopedStoreConfig{
|
||||
Namespace: "tenant-a",
|
||||
Quota: QuotaConfig{MaxKeys: 4, MaxGroups: 2},
|
||||
}).Validate()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestScope_ScopedStoreConfig_Bad_InvalidNamespace(t *testing.T) {
|
||||
err := (ScopedStoreConfig{Namespace: "tenant_a"}).Validate()
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "namespace")
|
||||
}
|
||||
|
||||
func TestScope_ScopedStoreConfig_Bad_NegativeQuota(t *testing.T) {
|
||||
err := (ScopedStoreConfig{
|
||||
Namespace: "tenant-a",
|
||||
Quota: QuotaConfig{MaxKeys: -1},
|
||||
}).Validate()
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "quota values must be zero or positive")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ScopedStore — basic CRUD
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
22
store.go
22
store.go
|
|
@ -43,6 +43,21 @@ type StoreConfig struct {
|
|||
PurgeInterval time.Duration
|
||||
}
|
||||
|
||||
// Usage example: `if err := (store.StoreConfig{DatabasePath: ":memory:", PurgeInterval: 30 * time.Second}).Validate(); err != nil { return }`
|
||||
func (config StoreConfig) Validate() error {
|
||||
if config.Journal != (JournalConfiguration{}) && !config.Journal.isConfigured() {
|
||||
return core.E(
|
||||
"store.StoreConfig.Validate",
|
||||
"journal configuration must include endpoint URL, organisation, and bucket name",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
if config.PurgeInterval < 0 {
|
||||
return core.E("store.StoreConfig.Validate", "purge interval must be zero or positive", nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Usage example: `config := storeInstance.JournalConfiguration(); fmt.Println(config.EndpointURL, config.Organisation, config.BucketName)`
|
||||
// The values are copied into the store and used as journal metadata.
|
||||
type JournalConfiguration struct {
|
||||
|
|
@ -199,15 +214,16 @@ func NewConfigured(config StoreConfig) (*Store, error) {
|
|||
}
|
||||
|
||||
func openConfiguredStore(operation string, config StoreConfig) (*Store, error) {
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, core.E(operation, "validate config", err)
|
||||
}
|
||||
|
||||
storeInstance, err := openSQLiteStore(operation, config.DatabasePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.Journal != (JournalConfiguration{}) {
|
||||
if !config.Journal.isConfigured() {
|
||||
return nil, core.E(operation, "journal configuration must include endpoint URL, organisation, and bucket name", nil)
|
||||
}
|
||||
storeInstance.journalConfiguration = journalConfiguration{
|
||||
endpointURL: config.Journal.EndpointURL,
|
||||
organisation: config.Journal.Organisation,
|
||||
|
|
|
|||
|
|
@ -148,6 +148,38 @@ func TestStore_NewConfigured_Bad_PartialJournalConfiguration(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "journal configuration must include endpoint URL, organisation, and bucket name")
|
||||
}
|
||||
|
||||
func TestStore_StoreConfig_Good_Validate(t *testing.T) {
|
||||
err := (StoreConfig{
|
||||
DatabasePath: ":memory:",
|
||||
Journal: JournalConfiguration{
|
||||
EndpointURL: "http://127.0.0.1:8086",
|
||||
Organisation: "core",
|
||||
BucketName: "events",
|
||||
},
|
||||
PurgeInterval: 20 * time.Millisecond,
|
||||
}).Validate()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestStore_StoreConfig_Bad_NegativePurgeInterval(t *testing.T) {
|
||||
err := (StoreConfig{
|
||||
DatabasePath: ":memory:",
|
||||
PurgeInterval: -time.Second,
|
||||
}).Validate()
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "purge interval must be zero or positive")
|
||||
}
|
||||
|
||||
func TestStore_NewConfigured_Bad_NegativePurgeInterval(t *testing.T) {
|
||||
_, err := NewConfigured(StoreConfig{
|
||||
DatabasePath: ":memory:",
|
||||
PurgeInterval: -time.Second,
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "validate config")
|
||||
assert.Contains(t, err.Error(), "purge interval must be zero or positive")
|
||||
}
|
||||
|
||||
func TestStore_Config_Good(t *testing.T) {
|
||||
storeInstance, err := NewConfigured(StoreConfig{
|
||||
DatabasePath: ":memory:",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue