mirror of
https://github.com/docker/docker-agent.git
synced 2026-11-04 10:02:15 +00:00
A client whose POST /followup times out can't tell whether the follow-up landed. Retrying risked delivering the prompt twice; not retrying risked losing it. Accept an optional Idempotency-Key header on /followup. A repeat with a key already seen for the session is acknowledged (status "duplicate", 202) without delivering the message again, so retries are safe. The reservation is per-session, bounded (recent keys only), made atomically to settle concurrent retries to a single delivery, and rolled back if delivery fails so a genuine failure stays retryable.
38 lines
1007 B
Go
38 lines
1007 B
Go
package server
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestIdempotencyCache_ReserveDedupes(t *testing.T) {
|
|
t.Parallel()
|
|
c := newIdempotencyCache(8)
|
|
|
|
assert.False(t, c.reserve("a"), "first sighting is not a duplicate")
|
|
assert.True(t, c.reserve("a"), "second sighting is a duplicate")
|
|
assert.False(t, c.reserve("b"), "a different key is not a duplicate")
|
|
}
|
|
|
|
func TestIdempotencyCache_ReleaseAllowsRetry(t *testing.T) {
|
|
t.Parallel()
|
|
c := newIdempotencyCache(8)
|
|
|
|
assert.False(t, c.reserve("a"))
|
|
c.release("a")
|
|
assert.False(t, c.reserve("a"), "after release the key is processable again")
|
|
}
|
|
|
|
func TestIdempotencyCache_EvictsOldestPastCapacity(t *testing.T) {
|
|
t.Parallel()
|
|
c := newIdempotencyCache(2)
|
|
|
|
assert.False(t, c.reserve("a"))
|
|
assert.False(t, c.reserve("b"))
|
|
assert.False(t, c.reserve("c")) // evicts "a"
|
|
|
|
assert.True(t, c.reserve("b"), "still-cached key remains a duplicate")
|
|
assert.False(t, c.reserve("a"), "evicted key is forgotten and processable again")
|
|
}
|