package spindle
import (
"errors"
"fmt"
"testing"
"github.com/bluesky-social/indigo/atproto/syntax"
"tangled.org/core/spindle/models"
"tangled.org/core/spindle/xrpc"
)
func TestWorkflowDefinitionsAreCachedAndRateLimitedPerRepo(t *testing.T) {
var c workflowDefCache
loads := 0
knotRead := func(repo syntax.DID) func() ([]*models.WorkflowDefinition, error) {
return func() ([]*models.WorkflowDefinition, error) {
loads++
return []*models.WorkflowDefinition{{ID: "ci"}}, c.admit(repo)
}
}
for range 20 {
if _, err := c.list("did:plc:repo", "HEAD", knotRead("did:plc:repo")); err != nil {
t.Fatal(err)
}
}
if loads != 1 {
t.Fatalf("%d loads for 20 reads of the same ref, want 1", loads)
}
// distinct refs skip the cache and each one is a knot read
var busy error
for i := range 3 * workflowDefLoadBurst {
if _, err := c.list("did:plc:repo", fmt.Sprintf("refs/heads/b%d", i), knotRead("did:plc:repo")); err != nil {
busy = err
break
}
}
if !errors.Is(busy, xrpc.ErrDefinitionsBusy) {
t.Fatalf("%d uncached reads of one repo in a row ended with %v, want ErrDefinitionsBusy", 3*workflowDefLoadBurst, busy)
}
if _, err := c.list("did:plc:otherrepo", "HEAD", knotRead("did:plc:otherrepo")); err != nil {
t.Fatalf("another repo was refused after the first one got busy: %v", err)
}
}
func TestWorkflowDefinitionRateLimitOnlyCountsKnotReads(t *testing.T) {
var c workflowDefCache
local := func() ([]*models.WorkflowDefinition, error) { return nil, nil }
for i := range 10 * workflowDefLoadBurst {
if _, err := c.list("did:plc:repo", fmt.Sprintf("%040d", i), local); err != nil {
t.Fatalf("local read %d was limited: %v", i, err)
}
}
if err := c.admit("did:plc:repo"); err != nil {
t.Fatalf("local reads used up the knot budget: %v", err)
}
}