8000 test(agentcore): session.writer test by Fodesu · Pull Request #81 · felinics/twilight · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
465 changes: 465 additions & 0 deletions agentcore/session/writer/cache_test.go

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions agentcore/session/writer/cachebench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package writer

import (
"context"
"fmt"
"github.com/felinics/twilight/agentcore/session"
"testing"
)

// BenchmarkWriterOpen measures EXT-WRT-1/PRJ-5 against the same log twice: with
// no cache the Writer folds every group, with a warm cache entry it folds only
// what the entry does not cover. full-fold is the before, from-cache the after.
func BenchmarkWriterOpen(b *testing.B) {
ctx := context.Background()
for _, n := range []int{400, 800, 1600, 3200, 6400} {
f := newCacheFixture(b)
w := f.open(b, WritersConfig{})
for i := 0; i < n; i++ {
f.commit(b, w, fmt.Sprintf("c%d", i), fmt.Sprintf("n%d", i))
}
if err := w.Close(ctx); err != nil {
b.Fatal(err)
}
// Prime a cache entry at the end of the log, so the from-cache case
// starts from a complete entry rather than folding once.
primed := f.open(b, WritersConfig{Cache: f.cache})
if err := primed.Close(ctx); err != nil {
b.Fatal(err)
}

b.Run(fmt.Sprintf("full-fold/%d", n), func(b *testing.B) {
benchmarkOpen(b, f, WritersConfig{})
})
b.Run(fmt.Sprintf("from-cache/%d", n), func(b *testing.B) {
benchmarkOpen(b, f, WritersConfig{Cache: f.cache})
})
}
}

// benchmarkOpen times OpenWriter alone. Takeover lets the loop reopen without a
// Close, whose cache flush would otherwise land inside the measurement.
func benchmarkOpen(b *testing.B, f *cacheFixture, cfg WritersConfig) {
b.Helper()
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := openWriter(ctx, f.store, f.registry, Admission{}, "s", session.OpenOptions{Takeover: true}, cfg); err != nil {
b.Fatal(err)
}
}
}
116 changes: 116 additions & 0 deletions agentcore/session/writer/claims_retry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package writer

import (
"context"
"strings"
"testing"

"github.com/felinics/twilight/agentcore/artifact"
"github.com/felinics/twilight/agentcore/artifact/artifacttest"
"github.com/felinics/twilight/agentcore/session"
)

func claimFixture(t *testing.T) (*fixture, artifact.BindingSet, CommitFn) {
t.Helper()
ctx := context.Background()
f := newFixture(t)
binding, err := artifact.NewBinding("b1", artifact.Ref{Scheme: "spill", Authority: "local", Key: "k", Durability: artifact.EventBound})
if err != nil {
t.Fatal(err)
}
if _, err := f.bindings.CreateBinding(ctx, binding); err != nil {
t.Fatal(err)
}
set, err := (artifact.SetBuilder{Resolver: f.bindings}).Build(ctx, []artifact.BindingID{binding.ID})
if err != nil {
t.Fatal(err)
}
return f, set, func(View) (*SemanticGroup, error) {
return &SemanticGroup{CommitID: "c1", Batches: noteBatch(TypedEvent{Type: tpfx("a") + "note", Value: notePayload{Text: "file", Refs: []string{"b1"}}})}, nil
}
}

func TestWriterRetriesReleasedClaims(t *testing.T) {
ctx := context.Background()
f, set, group := claimFixture(t)
fs := &faultStore{Stores: f.store}
open := func() Writer {
t.Helper()
w, err := OpenWriter(ctx, fs, f.registry, f.admission(), "s", session.OpenOptions{})
if err != nil {
t.Fatal(err)
}
return w
}
w := open()
defer func() { _ = w.Close(ctx) }()
id := DeriveClaimID(tipSegment(t, f.store, "s"), "c1", set.RefSetDigest)
for _, mode := range []string{"invalid", "invalid", "before", "before"} {
fs.arm(mode)
if _, err := w.Commit(ctx, group); err == nil {
t.Fatal("faulted append succeeded")
}
if mode == "before&q 8000 uot; {
if err := w.Close(ctx); err != nil {
t.Fatal(err)
}
w = open()
}
if claim, ok, err := f.ledger.LookupClaim(ctx, id); err != nil || !ok || claim.State != artifact.ClaimReleased {
t.Fatalf("released claim %s = %+v, %v, %v", id, claim, ok, err)
}
id = nextClaimID(id)
}
res, err := w.Commit(ctx, group)
if err != nil || res.Outcome != CommitApplied || res.Claim == nil || res.Claim.ID != id || res.Claim.State != artifact.ClaimActive {
t.Fatalf("eventual commit = %+v, %v; want claim %s", res, err, id)
}
res, err = w.Commit(ctx, group)
if err != nil || res.Outcome != CommitAlreadyApplied {
t.Fatalf("replay after claim retries = %+v, %v", res, err)
}
active, err := artifact.ActiveClaims(ctx, f.ledger, artifact.ClaimOwnerScope{Kind: ClaimOwnerKind, Authority: string(tipSegment(t, f.store, "s"))})
if err != nil || len(active) != 1 || active[0].ID != id {
t.Fatalf("retention roots = %+v, %v", active, err)
}
}

func TestWriterRejectsMismatchedReleasedClaim(t *testing.T) {
for _, field := range []string{"owner", "digest", "binding_ids"} {
t.Run(field, func(t *testing.T) {
ctx := context.Background()
f, set, group := claimFixture(t)
// An unverified ledger lets the test seed each possible mismatch.
f.ledger = &artifacttest.MapLedger{}
w := f.open(t, false)
defer w.Close(ctx)
seg := tipSegment(t, f.store, "s")
id := DeriveClaimID(seg, "c1", set.RefSetDigest)
owner := CommitOwner(seg, "c1")
switch field {
case "owner":
owner.Identity = "other"
case "digest":
set.RefSetDigest = "other"
case "binding_ids":
set.BindingIDs = []artifact.BindingID{"other"}
}
if _, err := f.ledger.Activate(ctx, id, owner, set); err != nil {
t.Fatal(err)
}
if err := f.ledger.ReleaseActive(ctx, id); err != nil {
t.Fatal(err)
}
res, err := w.Commit(ctx, group)
if err != nil || res.Outcome != CommitInvalid || !strings.Contains(res.Detail, "owner or binding set conflicts") {
t.Fatalf("mismatched claim commit = %+v, %v", res, err)
}
if _, ok, err := f.ledger.LookupClaim(ctx, nextClaimID(id)); err != nil || ok {
t.Fatalf("successor of mismatched claim exists = %v, %v", ok, err)
}
if page, err := f.store.ReadCommits(ctx, session.CommitReadRequest{SessionID: "s"}); err != nil || len(page.Commits) != 0 {
t.Fatalf("mismatched claim wrote commits = %+v, %v", page.Commits, err)
}
})
}
}
106 changes: 106 additions & 0 deletions agentcore/session/writer/detached_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package writer

import (
"context"
"github.com/felinics/twilight/agentcore/ledger"
"github.com/felinics/twilight/agentcore/module"
"testing"
)

type nestedEntry struct {
Values []string `json:"values"`
}

type nestedState struct {
Entries map[string][]*nestedEntry `json:"entries"`
}

func TestProjectionReadsAreDetached(t *testing.T) {
ctx := context.Background()
f := newFixture(t)
mod := noteModule("nested")
id := module.ProjectionID(string(tpfx("nested")) + "state")
mod.Projections = []module.ProjectionDefinition{{
ID: id, Version: 1, Consumes: []ledger.EventType{tpfx("nested") + "note"},
Initial: func() (any, error) { return nestedState{Entries: map[string][]*nestedEntry{}}, nil },
Apply: func(state any, event module.DecodedEvent) (any, error) {
s := state.(nestedState)
entries := make(map[string][]*nestedEntry, len(s.Entries)+1)
for key, value := range s.Entries {
entries[key] = value
}
text := event.Value.(notePayload).Text
entries[text] = []*nestedEntry{{Values: []string{text}}}
return nestedState{Entries: entries}, nil
},
StateCodec: module.JSONStateCodec[nestedState]{},
}}
var err error
f.registry, err = module.BuildRegistry(mod)
if err != nil {
t.Fatal(err)
}
w := f.open(t, false)
defer w.Close(ctx)
commit := func(text string) {
t.Helper()
res, err := w.Commit(ctx, func(View) (*SemanticGroup, error) {
return &SemanticGroup{CommitID: ledger.CommitID(text), Batches: noteBatch(TypedEvent{Type: tpfx("nested") + "note", Value: notePayload{Text: text}})}, nil
})
if err != nil || res.Outcome != CommitApplied {
t.Fatalf("commit = %+v, %v", res, err)
}
}
read := func() nestedState {
t.Helper()
state, _, err := w.Projections().Load(ctx, "s", id, 1)
if err != nil {
t.Fatal(err)
}
return state.(nestedState)
}
mutate := func(state nestedState) {
state.Entries["one"][0].Values[0] = "changed nested value"
state.Entries["one"][0] = &nestedEntry{Values: []string{"changed slice"}}
state.Entries["injected"] = []*nestedEntry{{Values: []string{"changed map"}}}
}
assertOriginal := func(state nestedState) {
t.Helper()
entries := state.Entries["one"]
if len(entries) != 1 || len(entries[0].Values) != 1 || entries[0].Values[0] != "one" {
t.Fatalf("nested state was modified: %+v", state)
}
if _, ok := state.Entries["injected"]; ok {
t.Fatal("projection map was modified")
}
}

commit("one")
mutate(read())
assertOriginal(read())
var retained nestedState
res, err := w.Commit(ctx, func(v View) (*SemanticGroup, error) {
state, err := v.Projection(id, 1)
if err != nil {
return nil, err
}
retained = state.(nestedState)
mutate(retained)
state, err = v.Projection(id, 1)
if err != nil {
return nil, err
}
assertOriginal(state.(nestedState))
return nil, nil
})
if err != nil || res.Outcome != CommitNoop {
t.Fatalf("view mutation = %+v, %v", res, err)
}
assertOriginal(read())
commit("two")
mutate(retained)
assertOriginal(read())
if entries := read().Entries["two"]; len(entries) != 1 || entries[0].Values[0] != "two" {
t.Fatalf("next committed state = %+v", entries)
}
}
Loading
Loading
0