Files
whetstone_DSL/editor/tests/step1920_test.cpp
Bill 4d47e894c1 Sprint 278: LSP Proxy Hardening (steps 1918-1922)
- LSPMessageValidator: JSON-RPC 2.0 shape validation (method, id type, params type)
- LSPCapabilityNegotiator: per-source capability tracking; shouldRoute gates on declared caps
- LSPRequestThrottler: per-URI/method debounce; blocks rapid didChange floods
- LSPErrorRecovery: per-source health tracking; stale-cache fallback when crashed
- Sprint278IntegrationSummary: poly-parse (C++ + Haskell) end-to-end integration

26/26 tests passing (5+5+5+5+6). All new headers well under 600 lines.
LoRA recorded: sprint278-2026-03-01 (9 calls, 6437 tokens).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 20:28:31 -07:00

81 lines
2.9 KiB
C++

// Step 1920: LSPRequestThrottler
// Debounces per-URI requests to prevent flooding language servers.
//
// t1: no prior record → shouldAllow true
// t2: within debounce window → shouldAllow false
// t3: after debounce window expires → shouldAllow true
// t4: reset clears uri; resetAll clears everything
// t5: lastTimestamp returns -1 for missing, correct value after record
#include "LSPRequestThrottler.h"
#include <iostream>
namespace ws = whetstone;
static int p=0,f=0;
#define T(n) { std::cout<<" "<<#n<<"... "; }
#define P() { std::cout<<"PASS\n"; ++p; }
#define F(m) { std::cout<<"FAIL: "<<m<<"\n"; ++f; }
#define C(c,m) if(!(c)){F(m);return;}
void t1(){
T(no_prior_record_allows);
ws::LSPRequestThrottler t;
C(t.shouldAllow("file:///a.go","textDocument/didChange",1000,500), "no record allows");
P();
}
void t2(){
T(within_debounce_window_blocks);
ws::LSPRequestThrottler t;
t.record("file:///a.go","textDocument/didChange",1000);
// 200ms later, 500ms debounce → block
C(!t.shouldAllow("file:///a.go","textDocument/didChange",1200,500), "within window blocked");
// different method: not throttled
C( t.shouldAllow("file:///a.go","textDocument/hover",1200,500), "different method allowed");
// different uri: not throttled
C( t.shouldAllow("file:///b.go","textDocument/didChange",1200,500), "different uri allowed");
P();
}
void t3(){
T(after_debounce_window_allows);
ws::LSPRequestThrottler t;
t.record("file:///a.go","textDocument/didChange",1000);
// exactly at boundary: 1000+500=1500 → allow (>=)
C( t.shouldAllow("file:///a.go","textDocument/didChange",1500,500), "at boundary allowed");
// past boundary
C( t.shouldAllow("file:///a.go","textDocument/didChange",1600,500), "past boundary allowed");
P();
}
void t4(){
T(reset_and_resetAll);
ws::LSPRequestThrottler t;
t.record("file:///a.go","textDocument/didChange",1000);
t.record("file:///b.go","textDocument/didChange",1000);
t.reset("file:///a.go");
C( t.shouldAllow("file:///a.go","textDocument/didChange",1100,500), "a.go reset → allowed");
C(!t.shouldAllow("file:///b.go","textDocument/didChange",1100,500), "b.go still throttled");
t.resetAll();
C( t.shouldAllow("file:///b.go","textDocument/didChange",1100,500), "b.go allowed after resetAll");
P();
}
void t5(){
T(lastTimestamp);
ws::LSPRequestThrottler t;
C(t.lastTimestamp("file:///a.go","textDocument/didChange") == -1, "no record returns -1");
t.record("file:///a.go","textDocument/didChange",9999);
C(t.lastTimestamp("file:///a.go","textDocument/didChange") == 9999, "correct timestamp");
C(t.lastTimestamp("file:///a.go","textDocument/hover") == -1, "different method -1");
P();
}
int main(){
std::cout << "Step 1920: LSPRequestThrottler\n";
t1(); t2(); t3(); t4(); t5();
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
return f > 0 ? 1 : 0;
}