c/l/sinbin: init

This adds a sinbin library, useful for temporarily timing out some
processed elements in-memory. We'll use this in the Equinix Shepherd
provisioner loop to keep note of bad hardware reservations.

Change-Id: If68b2c0856364cde70cee68729cfc0203c5a8446
Reviewed-on: https://review.monogon.dev/c/monogon/+/1127
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/lib/sinbin/sinbin_test.go b/cloud/lib/sinbin/sinbin_test.go
new file mode 100644
index 0000000..7403bb1
--- /dev/null
+++ b/cloud/lib/sinbin/sinbin_test.go
@@ -0,0 +1,34 @@
+package sinbin
+
+import (
+	"testing"
+	"time"
+)
+
+// TestSinbinBasics performs some basic tests on the Sinbin.
+func TestSinbinBasics(t *testing.T) {
+	var s Sinbin[string]
+
+	if s.Penalized("foo") {
+		t.Errorf("'foo' should not be penalized as it hasn't yet been added")
+	}
+	s.Add("foo", time.Now().Add(-1*time.Second))
+	if s.Penalized("foo") {
+		t.Errorf("'foo' should not be penalized as it has been added with an expired time")
+	}
+	s.Add("bar", time.Now().Add(time.Hour))
+	if !s.Penalized("bar") {
+		t.Errorf("'bar' should be penalized as it has been added with an hour long penalty")
+	}
+
+	// Force sweep.
+	s.lastSweep = time.Now().Add(-1 * time.Hour)
+	s.sweepUnlocked()
+
+	if len(s.bench) != 1 {
+		t.Errorf("there should only be one element on the bench")
+	}
+	if _, ok := s.bench["bar"]; !ok {
+		t.Errorf("the bench should contain 'bar'")
+	}
+}