diff --git a/locks_test.go b/locks_test.go new file mode 100644 index 0000000..57ec5bb --- /dev/null +++ b/locks_test.go @@ -0,0 +1,63 @@ +package main + +import ( + "context" + "sync" + "sync/atomic" + "testing" + "time" +) + +// TestLockController_PromotesUnderContention — N goroutines hit the same +// author simultaneously. The controller should observe the queue depth and +// move the scope from Coarse to Medium. +func TestLockController_PromotesUnderContention(t *testing.T) { + s, _, _ := newTestStore(t) + cp := &CreatePost{AuthorID: "scott", Body: "lock test"} + must(t, Submit(context.Background(), s, cp)) + + const N = 80 + var wg sync.WaitGroup + var ran atomic.Int64 + for i := 0; i < N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + m := &AddComment{PostID: cp.ResolvedPostID(), AuthorID: "hermes", Body: "y"} + if err := Submit(context.Background(), s, m); err == nil { + ran.Add(1) + } + }() + } + wg.Wait() + + st := s.lockCtrl.getOrCreate("hermes") + // After 80 concurrent submissions, scope may have promoted. We don't + // assert the exact scope (depends on timing); we just assert no deadlock + // and all ran. + if ran.Load() != N { + t.Fatalf("expected %d to run, got %d", N, ran.Load()) + } + t.Logf("final scope for hermes: %s (queueDepth=%d)", st.scope, st.queueDepth) +} + +// TestLockController_DemotesOnIdle — after a quiet period, scope should +// settle back to Coarse. Trigger Tick() manually to simulate the periodic +// controller. +func TestLockController_DemotesOnIdle(t *testing.T) { + s, _, _ := newTestStore(t) + cp := &CreatePost{AuthorID: "scott", Body: "demote test"} + must(t, Submit(context.Background(), s, cp)) + + // Manually set state to Fine, then tick to decay queue depth and demote. + st := s.lockCtrl.getOrCreate("hermes") + st.scope = ScopeFine + st.queueDepth = 5 // below fineToMediumBelow=100 + st.belowSince = time.Now().Add(-time.Hour) // way past the demotion window + + s.lockCtrl.Tick() + chosen := s.lockCtrl.chooseScope(st) + if chosen == ScopeFine { + t.Fatalf("expected demotion from Fine, still Fine") + } +}