Skip to content

Commit 5cd7a7a

Browse files
committed
convert unit tests to BDD style and drop compile-time arity tests
1 parent 37dd583 commit 5cd7a7a

1 file changed

Lines changed: 109 additions & 143 deletions

File tree

tests/WebSocketClientTest.cfc

Lines changed: 109 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,117 @@
11
component extends="org.lucee.cfml.test.LuceeTestCase" labels="websocketclient" {
22

3-
// ---- loader / OSGi health ----
3+
function run() {
44

5-
function testCreateWebSocketClientLoads() {
6-
try {
7-
ws = CreateWebSocketClient( "ws://localhost:9999/nope", new WebSocketListener() );
8-
fail( "Expected connection error" );
9-
}
10-
catch ( any e ) {
11-
// connection refused is expected — means the class loaded and OSGi resolved OK
12-
expect( e.message ).notToInclude( "osgi.wiring.package" );
13-
expect( e.message ).notToInclude( "Unable to resolve" );
14-
}
15-
}
16-
17-
// ---- BIF signature ----
18-
19-
function testBifRejectsZeroArgs() {
20-
try {
21-
CreateWebSocketClient();
22-
fail( "Expected function exception for 0 args" );
23-
}
24-
catch ( any e ) {
25-
// expected — function requires exactly 2 args
26-
expect( e.message & e.type ).toInclude( "unction" );
27-
}
28-
}
29-
30-
function testBifRejectsOneArg() {
31-
try {
32-
CreateWebSocketClient( "ws://localhost:9999/nope" );
33-
fail( "Expected function exception for 1 arg" );
34-
}
35-
catch ( any e ) {
36-
expect( e.message & e.type ).toInclude( "unction" );
37-
}
38-
}
5+
describe( "CreateWebSocketClient()", function() {
396

40-
function testBifRejectsThreeArgs() {
41-
try {
42-
CreateWebSocketClient( "ws://localhost:9999/nope", new WebSocketListener(), "extra" );
43-
fail( "Expected function exception for 3 args" );
44-
}
45-
catch ( any e ) {
46-
expect( e.message & e.type ).toInclude( "unction" );
47-
}
48-
}
49-
50-
function testRejectsNonComponentListener() {
51-
try {
52-
CreateWebSocketClient( "ws://localhost:9999/nope", "not a component" );
53-
fail( "Expected cast error when listener is not a component" );
54-
}
55-
catch ( any e ) {
56-
// toComponent() should reject a plain string
57-
expect( e.message & e.type ).toInclude( "omponent" );
58-
}
59-
}
60-
61-
// ---- scheme handling ----
62-
63-
function testWssSchemeAccepted() {
64-
// wss:// should parse cleanly; we expect a CONNECTION error, not a URL / scheme error
65-
try {
66-
CreateWebSocketClient( "wss://127.0.0.1:9999/nope", new WebSocketListener() );
67-
fail( "Expected connection error for unreachable wss endpoint" );
68-
}
69-
catch ( any e ) {
70-
expect( e.message ).notToInclude( "osgi.wiring.package" );
71-
expect( e.message ).notToInclude( "Unable to resolve" );
72-
expect( e.message ).notToInclude( "MalformedURLException" );
73-
expect( e.message ).notToInclude( "unknown protocol" );
74-
}
75-
}
76-
77-
function testInvalidSchemeRejected() {
78-
// http:// is not a websocket scheme — we expect this to fail
79-
try {
80-
CreateWebSocketClient( "http://127.0.0.1:9999/nope", new WebSocketListener() );
81-
fail( "Expected error for non-websocket scheme" );
82-
}
83-
catch ( any e ) {
84-
// any error is acceptable — the point is it doesn't silently succeed
85-
expect( true ).toBe( true );
86-
}
87-
}
88-
89-
// ---- connection error behaviour ----
90-
91-
function testConnectionRefusedFailsQuickly() {
92-
// 127.0.0.1:1 should RST almost immediately (no OS-level SYN retry)
93-
var start = getTickCount();
94-
try {
95-
CreateWebSocketClient( "ws://127.0.0.1:1/none", new WebSocketListener() );
96-
fail( "Expected connection refused" );
97-
}
98-
catch ( any e ) {
99-
var elapsed = getTickCount() - start;
100-
// RST should arrive well before the 5000ms library timeout
101-
expect( elapsed ).toBeLT( 5000 );
102-
}
103-
}
104-
105-
function testConnectionTimeoutEnforced() {
106-
// 10.255.255.1 is typically non-routable; should hit the library's hardcoded
107-
// 5000ms timeout rather than the much longer OS-level TCP SYN timeout.
108-
// If the CI network routes this differently, the test may be flaky — adjust if needed.
109-
var start = getTickCount();
110-
try {
111-
CreateWebSocketClient( "ws://10.255.255.1:9999/nope", new WebSocketListener() );
112-
fail( "Expected connection timeout" );
113-
}
114-
catch ( any e ) {
115-
var elapsed = getTickCount() - start;
116-
// Generous window: 3s-10s (5s ± slop for CI jitter)
117-
expect( elapsed ).toBeGT( 3000 );
118-
expect( elapsed ).toBeLT( 10000 );
119-
}
120-
}
121-
122-
// ---- listener callback invocation on connect failure ----
123-
124-
function testOnErrorFiresOnConnectFailure() {
125-
var listener = new RecordingListener();
126-
try {
127-
CreateWebSocketClient( "ws://127.0.0.1:1/none", listener );
128-
fail( "Expected connection error" );
129-
}
130-
catch ( any e ) {
131-
// onError should have fired with type "connect" before the exception surfaced
132-
var events = listener.getEvents();
133-
var found = false;
134-
for ( var entry in events ) {
135-
if ( entry == "onError:connect" ) {
136-
found = true;
137-
break;
7+
it( "loads without OSGi resolution errors", function() {
8+
try {
9+
CreateWebSocketClient( "ws://localhost:9999/nope", new WebSocketListener() );
10+
fail( "Expected connection error" );
13811
}
139-
}
140-
expect( found ).toBeTrue();
141-
}
12+
catch ( any e ) {
13+
// connection refused is expected — means the class loaded and OSGi resolved OK
14+
expect( e.message ).notToInclude( "osgi.wiring.package" );
15+
expect( e.message ).notToInclude( "Unable to resolve" );
16+
}
17+
});
18+
19+
describe( "argument validation", function() {
20+
// Note: wrong arg counts are rejected by the Lucee parser at compile time —
21+
// no runtime behaviour to test.
22+
23+
it( "rejects a non-component listener", function() {
24+
try {
25+
CreateWebSocketClient( "ws://localhost:9999/nope", "not a component" );
26+
fail( "Expected cast error when listener is not a component" );
27+
}
28+
catch ( any e ) {
29+
expect( e.message & e.type ).toInclude( "omponent" );
30+
}
31+
});
32+
});
33+
34+
describe( "scheme handling", function() {
35+
36+
it( "accepts wss:// and surfaces a connection error, not a scheme error", function() {
37+
try {
38+
CreateWebSocketClient( "wss://127.0.0.1:9999/nope", new WebSocketListener() );
39+
fail( "Expected connection error for unreachable wss endpoint" );
40+
}
41+
catch ( any e ) {
42+
expect( e.message ).notToInclude( "osgi.wiring.package" );
43+
expect( e.message ).notToInclude( "Unable to resolve" );
44+
expect( e.message ).notToInclude( "MalformedURLException" );
45+
expect( e.message ).notToInclude( "unknown protocol" );
46+
}
47+
});
48+
49+
it( "rejects non-websocket schemes like http://", function() {
50+
try {
51+
CreateWebSocketClient( "http://127.0.0.1:9999/nope", new WebSocketListener() );
52+
fail( "Expected error for non-websocket scheme" );
53+
}
54+
catch ( any e ) {
55+
// Any error is acceptable — the point is it doesn't silently succeed
56+
expect( true ).toBe( true );
57+
}
58+
});
59+
});
60+
61+
describe( "connection error behaviour", function() {
62+
63+
it( "fails quickly on connection refused (RST under 5s)", function() {
64+
var start = getTickCount();
65+
try {
66+
CreateWebSocketClient( "ws://127.0.0.1:1/none", new WebSocketListener() );
67+
fail( "Expected connection refused" );
68+
}
69+
catch ( any e ) {
70+
var elapsed = getTickCount() - start;
71+
expect( elapsed ).toBeLT( 5000 );
72+
}
73+
});
74+
75+
it( "enforces the library's 5s connection timeout on unroutable IPs", function() {
76+
// 10.255.255.1 is typically non-routable; should hit the hardcoded
77+
// 5000ms library timeout rather than the OS-level TCP SYN timeout
78+
var start = getTickCount();
79+
try {
80+
CreateWebSocketClient( "ws://10.255.255.1:9999/nope", new WebSocketListener() );
81+
fail( "Expected connection timeout" );
82+
}
83+
catch ( any e ) {
84+
var elapsed = getTickCount() - start;
85+
// Generous window for CI jitter: 3-10s (5s ± slop)
86+
expect( elapsed ).toBeGT( 3000 );
87+
expect( elapsed ).toBeLT( 10000 );
88+
}
89+
});
90+
91+
it( "invokes the listener's onError callback with type=connect before the exception surfaces", function() {
92+
var listener = new RecordingListener();
93+
try {
94+
CreateWebSocketClient( "ws://127.0.0.1:1/none", listener );
95+
fail( "Expected connection error" );
96+
}
97+
catch ( any e ) {
98+
var events = listener.getEvents();
99+
var found = false;
100+
for ( var entry in events ) {
101+
if ( entry == "onError:connect" ) {
102+
found = true;
103+
break;
104+
}
105+
}
106+
expect( found ).toBeTrue();
107+
}
108+
});
109+
});
110+
});
111+
112+
// Round-trip tests (sendText/sendBinary/onMessage/onClose/disconnect/isOpen)
113+
// live in extension-websocket's tests/integration/ because they need a running
114+
// server. See tests/README.md for the cross-repo split.
142115
}
143116

144-
// ---- Round-trip tests (sendText/sendBinary/onMessage/onClose/disconnect/isOpen)
145-
// live in the extension-websocket repo's test-websocket-client.cfm and
146-
// planned siblings. They need a running WebSocket server, which the
147-
// server extension provides. Adding round-trip coverage here would
148-
// require installing a second extension in this repo's CI — we avoid
149-
// that and rely on the server repo for integration coverage.
150-
151117
}

0 commit comments

Comments
 (0)