package oltp
import (
"errors"
"path/filepath"
"strings"
"testing"
"time"
)
func pickupCode(c byte) string { return strings.Repeat(string(rune(c)), 64) }
func openPickupDB(t *testing.T) *DB {
t.Helper()
db, err := Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
return db
}
// approvedWithPickup is an approved request bound to code, holding a
// secret and a lapse reference, decided at at.
func approvedWithPickup(t *testing.T, db *DB, name, code string, at time.Time) string {
t.Helper()
s, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.token.new", Input: `{"n":"` + name + `"}`, Summary: "x", Pickup: code})
if err != nil {
t.Fatal(err)
}
if _, err := db.DecideSignOff(s.ID, "ann", true); err != nil {
t.Fatal(err)
}
if err := db.SetSignOffRefs(s.ID, `[{"kind":"agent-token","token":"t"}]`); err != nil {
t.Fatal(err)
}
if err := db.FinishSignOff(s.ID, `{}`, `{"value":"v"}`); err != nil {
t.Fatal(err)
}
if _, err := db.w.Exec(`UPDATE signoff SET decided = ? WHERE id = ?`, at.UTC().Format(time.RFC3339), s.ID); err != nil {
t.Fatal(err)
}
return s.ID
}
// The code is bound at the ask and kept: asking the same pending call
// again with another code changes nothing, and a code already bound to
// another request is refused.
func TestAPickupCodeIsBoundAtTheAskAndNeverChanges(t *testing.T) {
db := openPickupDB(t)
a, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"a"}`, Summary: "x", Pickup: pickupCode('a')})
if err != nil || a.Pickup != pickupCode('a') {
t.Fatalf("FAIL: asking with a code: %+v %v", a, err)
}
same, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"a"}`, Summary: "x", Pickup: pickupCode('a')})
if err != nil || same.ID != a.ID {
t.Errorf("FAIL: the same call with the same code: %+v %v", same, err)
}
for _, c := range []string{pickupCode('b'), ""} {
if _, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"a"}`, Summary: "x", Pickup: c}); !errors.Is(err, ErrPickupMismatch) {
t.Errorf("FAIL: the same call with code %q: %v", c, err)
}
}
if _, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"u"}`, Summary: "x"}); err != nil {
t.Fatal(err)
}
if _, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"u"}`, Summary: "x", Pickup: pickupCode('c')}); !errors.Is(err, ErrPickupMismatch) {
t.Errorf("FAIL: an unbound request re-asked with a code: %v", err)
}
if got, _, _ := db.SignOffByID(a.ID); got.Pickup != pickupCode('a') {
t.Errorf("FAIL: the stored code changed: %q", got.Pickup)
}
if _, err := db.RequestSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.new", Input: `{"n":"other"}`, Summary: "x", Pickup: pickupCode('a')}); !errors.Is(err, ErrPickupBound) {
t.Errorf("FAIL: one code bound to two requests: %v", err)
}
// Approval is the owner's, and it takes no code.
if _, err := db.DecideSignOff(a.ID, "ann", true); err != nil {
t.Fatal(err)
}
if got, _, _ := db.SignOffByID(a.ID); got.Pickup != pickupCode('a') {
t.Errorf("FAIL: approving changed the code: %q", got.Pickup)
}
if !t.Failed() {
t.Log("ok")
}
}
// A bound request's secret leaves by its code alone, once: the asking
// agent's own take never has it, and a wrong code finds nothing.
func TestAPickupBoundSecretLeavesByItsCodeOnce(t *testing.T) {
db := openPickupDB(t)
now := time.Now().UTC().Truncate(time.Second)
id := approvedWithPickup(t, db, "a", pickupCode('a'), now)
if s, _ := db.TakeSignOffSecret(id, "bot", now); s != "" {
t.Fatalf("FAIL: the asking agent took a bound secret")
}
if got, _ := db.TakePickupSecret(pickupCode('b'), now); got.Found || got.Taken {
t.Errorf("FAIL: a wrong code found %+v", got)
}
if got, _ := db.TakePickupSecret("", now); got.Found {
t.Errorf("FAIL: an empty code found a request")
}
first, err := db.TakePickupSecret(pickupCode('a'), now)
if err != nil || !first.Taken || first.Request.Secret != `{"value":"v"}` {
t.Fatalf("FAIL: the first collect: %+v %v", first, err)
}
second, err := db.TakePickupSecret(pickupCode('a'), now)
if err != nil || second.Taken || !second.Found || second.Request.Secret != "" || second.Request.Collected == "" {
t.Errorf("FAIL: the second collect: %+v %v", second, err)
}
var secret, lapse string
if err := db.db.QueryRow(`SELECT secret, lapse FROM signoff WHERE id = ?`, id).Scan(&secret, &lapse); err != nil {
t.Fatal(err)
}
if secret != "" || lapse != "" {
t.Errorf("FAIL: the take left secret=%q lapse=%q", secret, lapse)
}
if !t.Failed() {
t.Log("ok")
}
}
// At the limit a pickup-bound secret is collectable XOR due to lapse,
// as an unbound one is, and a lapsed one says so.
func TestAPickupBoundSecretIsCollectableXORDueToLapse(t *testing.T) {
db := openPickupDB(t)
at := time.Now().UTC().Truncate(time.Second)
for i, off := range []time.Duration{-time.Second, 0, time.Second, 2 * time.Second} {
id := approvedWithPickup(t, db, itoaTest(i), pickupCode(byte('a'+i)), at)
now := at.Add(SignOffSecretLife + off)
due := dueHas(t, db, now, id)
got, err := db.TakePickupSecret(pickupCode(byte('a'+i)), now)
if err != nil {
t.Fatal(err)
}
if due == got.Taken {
t.Errorf("FAIL: offset %v: due=%v collected=%v", off, due, got.Taken)
}
if !got.Taken && !got.Lapsed {
t.Errorf("FAIL: offset %v: refused but not read as lapsed: %+v", off, got)
}
}
if !t.Failed() {
t.Log("ok")
}
}
// A collect and the lapse sweep never both win nor both lose on a
// pickup-bound row, in either order and concurrently.
func TestAPickupCollectAndASweepNeverBothWinNorBothLose(t *testing.T) {
db := openPickupDB(t)
at := time.Now().UTC().Truncate(time.Second)
takeAt := at.Add(SignOffSecretLife - time.Second)
sweepAt := at.Add(SignOffSecretLife + time.Second)
for i := 0; i < 300; i++ {
code := pickupCode('a') // distinct per round below
code = code[:60] + string(rune('a'+i%26)) + string(rune('a'+(i/26)%26)) + string(rune('a'+(i/676)%26)) + "x"
id := approvedWithPickup(t, db, "r"+time.Duration(i).String(), code, at)
var got bool
var claimed bool
take := func() { r, _ := db.TakePickupSecret(code, takeAt); got = r.Taken }
sweep := func() { _, claimed, _ = db.ClaimSignOffLapse(id, SignOffSecretCutoff(sweepAt)) }
switch i % 4 {
case 0:
take()
sweep()
case 1:
sweep()
take()
default:
done := make(chan struct{}, 2)
go func() { take(); done <- struct{}{} }()
go func() { sweep(); done <- struct{}{} }()
<-done
<-done
}
if got == claimed {
t.Fatalf("FAIL: round %d (order %d): collected=%v claimed=%v", i, i%4, got, claimed)
}
var lapse string
if err := db.db.QueryRow(`SELECT lapse FROM signoff WHERE id = ?`, id).Scan(&lapse); err != nil {
t.Fatal(err)
}
if got == (lapse != "") {
t.Fatalf("FAIL: round %d: collected=%v but references=%q", i, got, lapse)
}
}
t.Log("ok")
}
// A parked call is one transaction: a row in state ran holding its
// sealed secret, its references and its code, collectable by the code
// once and refused a second hold of the same code.
func TestAParkedCallIsARanRowTheCodeCollects(t *testing.T) {
db := openPickupDB(t)
seal := func(id string) (string, error) { return `{"value":"v"}`, nil }
s, err := db.ParkSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "agent.token.new", Input: "{}", Summary: "x", Pickup: pickupCode('a')}, `{}`, `[{"kind":"agent-token","token":"t"}]`, seal)
if err != nil || s.State != SignOffRan || s.Secret != "" {
t.Fatalf("FAIL: parking: %+v %v", s, err)
}
if _, err := db.ParkSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "x", Input: "{}", Pickup: pickupCode('a')}, `{}`, "", seal); !errors.Is(err, ErrPickupBound) {
t.Errorf("FAIL: a second hold of the code: %v", err)
}
if _, err := db.ParkSignOff(SignOff{Agent: "bot", Owner: "ann", Op: "x", Input: "{}"}, `{}`, "", seal); !errors.Is(err, ErrPickupBound) {
t.Errorf("FAIL: parking with no code: %v", err)
}
got, err := db.TakePickupSecret(pickupCode('a'), time.Now())
if err != nil || !got.Taken || got.Request.Secret != `{"value":"v"}` {
t.Errorf("FAIL: collecting: %+v %v", got, err)
}
if !t.Failed() {
t.Log("ok")
}
}