Skip to content

Commit 5fa9e50

Browse files
committed
fix: a better way to handle http server for tests
1 parent a3d3fd5 commit 5fa9e50

File tree

4 files changed

+99
-65
lines changed

4 files changed

+99
-65
lines changed

nix/checks.nix

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -170,52 +170,35 @@
170170
''
171171
set -e
172172
173-
# Start HTTP mock server for http extension tests using portable locking
174-
HTTP_MOCK_PORT=8889
175-
PID_FILE="/tmp/http-mock-server-$HTTP_MOCK_PORT.pid"
173+
# Start HTTP mock server for http extension tests
174+
# Use a build-specific directory for coordination
175+
BUILD_TMP=$(mktemp -d)
176+
HTTP_MOCK_PORT_FILE="$BUILD_TMP/http-mock-port"
176177
177-
# Function to start mock server with simple lock mechanism
178-
start_mock_server() {
179-
# Try to acquire lock by creating PID file atomically
180-
if (set -C; echo $$ > "$PID_FILE") 2>/dev/null; then
181-
# We got the lock, start the server
182-
echo "Starting HTTP mock server on port $HTTP_MOCK_PORT for tests..."
183-
${pkgs.python3}/bin/python3 ${./tests/http-mock-server.py} $HTTP_MOCK_PORT &
184-
HTTP_MOCK_PID=$!
185-
echo $HTTP_MOCK_PID > "$PID_FILE"
178+
echo "Starting HTTP mock server (will find free port)..."
179+
HTTP_MOCK_PORT_FILE="$HTTP_MOCK_PORT_FILE" ${pkgs.python3}/bin/python3 ${./tests/http-mock-server.py} &
180+
HTTP_MOCK_PID=$!
186181
187-
# Clean up on exit
188-
trap "kill $HTTP_MOCK_PID 2>/dev/null || true; rm -f '$PID_FILE'" EXIT
182+
# Clean up on exit
183+
trap "kill $HTTP_MOCK_PID 2>/dev/null || true; rm -rf '$BUILD_TMP'" EXIT
189184
190-
# Wait for server to be ready
191-
sleep 2
192-
193-
# Keep this process alive to maintain the lock
194-
wait $HTTP_MOCK_PID
195-
rm -f "$PID_FILE"
196-
else
197-
# Lock exists, check if process is still running
198-
if [ -f "$PID_FILE" ]; then
199-
existing_pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
200-
if [ -n "$existing_pid" ] && kill -0 "$existing_pid" 2>/dev/null; then
201-
echo "HTTP mock server already running with PID $existing_pid, reusing it..."
202-
return 0
203-
else
204-
echo "Stale PID file found, removing and retrying..."
205-
rm -f "$PID_FILE"
206-
sleep 1
207-
start_mock_server # Retry once
208-
return $?
209-
fi
210-
fi
185+
# Wait for server to start and write port file
186+
for i in {1..10}; do
187+
if [ -f "$HTTP_MOCK_PORT_FILE" ]; then
188+
HTTP_MOCK_PORT=$(cat "$HTTP_MOCK_PORT_FILE")
189+
echo "HTTP mock server started on port $HTTP_MOCK_PORT"
190+
break
211191
fi
212-
}
192+
sleep 1
193+
done
213194
214-
# Start the server in background
215-
start_mock_server &
195+
if [ ! -f "$HTTP_MOCK_PORT_FILE" ]; then
196+
echo "Failed to start HTTP mock server"
197+
exit 1
198+
fi
216199
217-
# Give server time to start
218-
sleep 3
200+
# Export the port for use in SQL tests
201+
export HTTP_MOCK_PORT
219202
220203
#First we need to create a generic pg cluster for pgtap tests and run those
221204
export GRN_PLUGINS_DIR=${pkgs.supabase-groonga}/lib/groonga/plugins
@@ -277,6 +260,13 @@
277260
pg_ctl -D "$PGTAP_CLUSTER" stop
278261
exit 1
279262
fi
263+
264+
# Create a table to store test configuration
265+
psql -p ${pgPort} -h ${self.supabase.defaults.host} --username=supabase_admin -d testing -c "
266+
CREATE TABLE IF NOT EXISTS test_config (key TEXT PRIMARY KEY, value TEXT);
267+
INSERT INTO test_config (key, value) VALUES ('http_mock_port', '$HTTP_MOCK_PORT')
268+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
269+
"
280270
SORTED_DIR=$(mktemp -d)
281271
for t in $(printf "%s\n" ${builtins.concatStringsSep " " sortedTestList}); do
282272
psql -p ${pgPort} -h ${self.supabase.defaults.host} --username=supabase_admin -d testing -f "${./tests/sql}/$t.sql" || true
@@ -310,6 +300,13 @@
310300
exit 1
311301
fi
312302
303+
# Create a table to store test configuration for pg_regress tests
304+
psql -p ${pgPort} -h ${self.supabase.defaults.host} --no-password --username=supabase_admin -d postgres -c "
305+
CREATE TABLE IF NOT EXISTS test_config (key TEXT PRIMARY KEY, value TEXT);
306+
INSERT INTO test_config (key, value) VALUES ('http_mock_port', '$HTTP_MOCK_PORT')
307+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
308+
"
309+
313310
mkdir -p $out/regression_output
314311
if ! pg_regress \
315312
--use-existing \

nix/tests/expected/http.out

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Test for http extension
22
-- Basic HTTP functionality tests
33
-- Test basic HTTP GET request
4-
SELECT status FROM http_get('http://localhost:8889/get');
4+
SELECT status FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/get');
55
status
66
--------
77
200
@@ -11,7 +11,7 @@ SELECT status FROM http_get('http://localhost:8889/get');
1111
SELECT status, content_type
1212
FROM http((
1313
'GET',
14-
'http://localhost:8889/headers',
14+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/headers',
1515
ARRAY[http_header('User-Agent', 'pg_http_test')],
1616
NULL,
1717
NULL
@@ -23,7 +23,7 @@ FROM http((
2323

2424
-- Test HTTP POST request with JSON body
2525
SELECT status FROM http_post(
26-
'http://localhost:8889/post',
26+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/post',
2727
'{"test": "data"}',
2828
'application/json'
2929
);
@@ -34,7 +34,7 @@ SELECT status FROM http_post(
3434

3535
-- Test HTTP PUT request
3636
SELECT status FROM http_put(
37-
'http://localhost:8889/put',
37+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/put',
3838
'{"update": "data"}',
3939
'application/json'
4040
);
@@ -44,15 +44,15 @@ SELECT status FROM http_put(
4444
(1 row)
4545

4646
-- Test HTTP DELETE request
47-
SELECT status FROM http_delete('http://localhost:8889/delete');
47+
SELECT status FROM http_delete('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/delete');
4848
status
4949
--------
5050
200
5151
(1 row)
5252

5353
-- Test HTTP PATCH request
5454
SELECT status FROM http_patch(
55-
'http://localhost:8889/patch',
55+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/patch',
5656
'{"patch": "data"}',
5757
'application/json'
5858
);
@@ -62,15 +62,15 @@ SELECT status FROM http_patch(
6262
(1 row)
6363

6464
-- Test HTTP HEAD request
65-
SELECT status FROM http_head('http://localhost:8889/get');
65+
SELECT status FROM http_head('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/get');
6666
status
6767
--------
6868
200
6969
(1 row)
7070

7171
-- Test response headers parsing
7272
WITH response AS (
73-
SELECT * FROM http_get('http://localhost:8889/response-headers?Content-Type=text/plain')
73+
SELECT * FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/response-headers?Content-Type=text/plain')
7474
)
7575
SELECT
7676
status,
@@ -86,7 +86,7 @@ FROM response;
8686
-- This should complete successfully with reasonable timeout
8787
SELECT status FROM http((
8888
'GET',
89-
'http://localhost:8889/delay/1',
89+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/delay/1',
9090
ARRAY[]::http_header[],
9191
'application/json',
9292
2000 -- 2 second timeout
@@ -97,7 +97,7 @@ SELECT status FROM http((
9797
(1 row)
9898

9999
-- Test URL encoding
100-
SELECT status FROM http_get('http://localhost:8889/anything?param=value%20with%20spaces&another=123');
100+
SELECT status FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/anything?param=value%20with%20spaces&another=123');
101101
status
102102
--------
103103
200

nix/tests/http-mock-server.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,52 @@ def log_message(self, format, *args):
208208
pass
209209

210210

211-
def run_server(port=8080):
211+
def find_free_port(start_port=8880, end_port=8899):
212+
"""Find a free port within the given range"""
213+
import socket
214+
215+
for port in range(start_port, end_port + 1):
216+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
217+
try:
218+
s.bind(("0.0.0.0", port))
219+
return port
220+
except OSError:
221+
continue
222+
223+
raise RuntimeError(f"No free port found in range {start_port}-{end_port}")
224+
225+
226+
def run_server(port=None):
212227
"""Run the mock HTTP server"""
213-
server = HTTPServer(("0.0.0.0", port), MockHTTPHandler)
214-
print(f"Mock HTTP server running on port {port}")
215-
server.serve_forever()
228+
if port is None:
229+
port = find_free_port()
230+
231+
try:
232+
server = HTTPServer(("0.0.0.0", port), MockHTTPHandler)
233+
print(f"Mock HTTP server running on port {port}")
234+
235+
# Write port to a file that can be read by the test environment
236+
import os
237+
238+
port_file = os.environ.get("HTTP_MOCK_PORT_FILE", "/tmp/http-mock-port")
239+
try:
240+
with open(port_file, "w") as f:
241+
f.write(str(port))
242+
except:
243+
pass # Ignore if we can't write the port file
244+
245+
server.serve_forever()
246+
except OSError as e:
247+
if port is not None:
248+
# If specific port was requested but failed, try to find a free one
249+
print(f"Port {port} not available, finding free port...")
250+
run_server(None)
251+
else:
252+
raise e
216253

217254

218255
if __name__ == "__main__":
219256
import sys
220257

221-
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
258+
port = int(sys.argv[1]) if len(sys.argv) > 1 else None
222259
run_server(port)

nix/tests/sql/http.sql

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,48 @@
22
-- Basic HTTP functionality tests
33

44
-- Test basic HTTP GET request
5-
SELECT status FROM http_get('http://localhost:8889/get');
5+
SELECT status FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/get');
66

77
-- Test HTTP GET with headers
88
SELECT status, content_type
99
FROM http((
1010
'GET',
11-
'http://localhost:8889/headers',
11+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/headers',
1212
ARRAY[http_header('User-Agent', 'pg_http_test')],
1313
NULL,
1414
NULL
1515
)::http_request);
1616

1717
-- Test HTTP POST request with JSON body
1818
SELECT status FROM http_post(
19-
'http://localhost:8889/post',
19+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/post',
2020
'{"test": "data"}',
2121
'application/json'
2222
);
2323

2424
-- Test HTTP PUT request
2525
SELECT status FROM http_put(
26-
'http://localhost:8889/put',
26+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/put',
2727
'{"update": "data"}',
2828
'application/json'
2929
);
3030

3131
-- Test HTTP DELETE request
32-
SELECT status FROM http_delete('http://localhost:8889/delete');
32+
SELECT status FROM http_delete('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/delete');
3333

3434
-- Test HTTP PATCH request
3535
SELECT status FROM http_patch(
36-
'http://localhost:8889/patch',
36+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/patch',
3737
'{"patch": "data"}',
3838
'application/json'
3939
);
4040

4141
-- Test HTTP HEAD request
42-
SELECT status FROM http_head('http://localhost:8889/get');
42+
SELECT status FROM http_head('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/get');
4343

4444
-- Test response headers parsing
4545
WITH response AS (
46-
SELECT * FROM http_get('http://localhost:8889/response-headers?Content-Type=text/plain')
46+
SELECT * FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/response-headers?Content-Type=text/plain')
4747
)
4848
SELECT
4949
status,
@@ -55,11 +55,11 @@ FROM response;
5555
-- This should complete successfully with reasonable timeout
5656
SELECT status FROM http((
5757
'GET',
58-
'http://localhost:8889/delay/1',
58+
'http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/delay/1',
5959
ARRAY[]::http_header[],
6060
'application/json',
6161
2000 -- 2 second timeout
6262
)::http_request);
6363

6464
-- Test URL encoding
65-
SELECT status FROM http_get('http://localhost:8889/anything?param=value%20with%20spaces&another=123');
65+
SELECT status FROM http_get('http://localhost:' || (SELECT value FROM test_config WHERE key = 'http_mock_port') || '/anything?param=value%20with%20spaces&another=123');

0 commit comments

Comments
 (0)