mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-30 00:27:11 +09:00
The deploy paths already know which host ports are taken (app rows, slot rows, Docker mappings, a bind test) but only used that to pick a port silently. Exposing it lets the shared port input say "8001 is used by shop-api" and offer the next free port, instead of failing at deploy time.
62 lines
2.5 KiB
Python
62 lines
2.5 KiB
Python
"""/api/v1/ports: the check behind the shared PortField input."""
|
|
import pytest
|
|
|
|
from app import db
|
|
from app.services import port_service
|
|
from app.services.template_service import TemplateService
|
|
from tests.factories import make_application
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _no_docker_or_sockets(monkeypatch):
|
|
# Hermetic: no `docker ps`, and every unheld port binds.
|
|
monkeypatch.setattr(TemplateService, '_get_docker_used_ports', classmethod(lambda cls: set()))
|
|
monkeypatch.setattr(TemplateService, '_port_is_free', classmethod(
|
|
lambda cls, port: port not in TemplateService._get_database_used_ports()))
|
|
|
|
|
|
def test_free_port(client, developer_headers):
|
|
res = client.get('/api/v1/ports/check?port=18080', headers=developer_headers)
|
|
assert res.status_code == 200
|
|
body = res.get_json()
|
|
assert body == {'port': 18080, 'valid': True, 'free': True, 'privileged': False,
|
|
'holder': None, 'reason': None}
|
|
|
|
|
|
def test_port_held_by_an_app_names_it(app, client, developer_headers):
|
|
make_application(db, name='shop-api', port=18081)
|
|
body = client.get('/api/v1/ports/check?port=18081', headers=developer_headers).get_json()
|
|
assert body['free'] is False
|
|
assert body['reason'] == 'in_use'
|
|
assert body['holder']['kind'] == 'app'
|
|
assert body['holder']['name'] == 'shop-api'
|
|
|
|
|
|
@pytest.mark.parametrize('raw,reason', [('abc', 'not_a_number'), ('0', 'out_of_range'),
|
|
('70000', 'out_of_range'), (None, 'not_a_number')])
|
|
def test_invalid_ports(raw, reason):
|
|
body = port_service.check_port(raw)
|
|
assert body['valid'] is False and body['free'] is False
|
|
assert body['reason'] == reason
|
|
|
|
|
|
def test_privileged_port_is_flagged():
|
|
assert port_service.check_port(80)['privileged'] is True
|
|
|
|
|
|
def test_bound_outside_serverkit(monkeypatch):
|
|
monkeypatch.setattr(TemplateService, '_port_is_free', classmethod(lambda cls, port: False))
|
|
body = port_service.check_port(18082)
|
|
assert body['free'] is False and body['holder'] is None and body['reason'] == 'bound'
|
|
|
|
|
|
def test_suggest_skips_taken_ports(app, client, developer_headers, monkeypatch):
|
|
make_application(db, name='a', port=18090)
|
|
monkeypatch.setattr('socket.socket.bind', lambda self, addr: None)
|
|
body = client.get('/api/v1/ports/suggest?start=18090', headers=developer_headers).get_json()
|
|
assert body['port'] == 18091
|
|
|
|
|
|
def test_requires_developer(client, viewer_headers):
|
|
assert client.get('/api/v1/ports/check?port=8080', headers=viewer_headers).status_code == 403
|