def test_server_version_checks():
    """Test that server version is checked by default, raising InvalidServerVersion exception when failing"""
    with mock.patch('swimlane.core.client.requests.Session', mock.MagicMock()):
        with mock.patch.object(SwimlaneJwtAuth, 'authenticate', return_value=(None, {})):
            with mock.patch.object(Swimlane, 'settings', new_callable=mock.PropertyMock) as mock_settings:
                mock_settings.return_value = {'apiVersion': '2.18+4.0.0+123456'}

                with mock.patch('swimlane.core.client.compare_versions', return_value=0):
                    Swimlane('http://host', 'admin', 'password')

                with mock.patch('swimlane.core.client.compare_versions', return_value=1):
                    with pytest.raises(InvalidSwimlaneProductVersion):
                        Swimlane('http://host', 'admin', 'password')
def test_host_url_scheme_coercion():
    """Test host URL scheme defaults to 'https' when not provided and is forced to lowercase"""
    with mock.patch.object(Swimlane, 'request') as mock_request:
        with mock.patch.object(SwimlaneJwtAuth, 'authenticate', return_value=(None, {})):
            mock_response = mock.MagicMock()
            mock_request.return_value = mock_response

            # Only include apiVersion setting for current tests
            data = {
                'apiVersion': '2.15.0-1234'
            }
            mock_response.json.return_value = data

            mock_swimlane = Swimlane('host', 'user', 'pass', verify_server_version=False)
            assert mock_swimlane.host.scheme == 'https'

            mock_swimlane = Swimlane('HTTP://host', 'user', 'pass', verify_server_version=False)
            assert mock_swimlane.host.scheme == 'http'
def test_new_version_breakdown():
    """Test that product version, build version, and build number produce expected values in new multi value format"""
    with mock.patch('swimlane.core.client.requests.Session', mock.MagicMock()):
        with mock.patch.object(SwimlaneJwtAuth, 'authenticate', return_value=(None, {})):
            with mock.patch.object(Swimlane, 'settings', new_callable=mock.PropertyMock) as mock_settings:
                version = '2.18+4.0.0+123456'
                mock_settings.return_value = {'apiVersion': version}

                sw = Swimlane('http://host', 'admin', 'password', verify_server_version=False)
                assert sw.version == version
                assert sw.product_version == '2.18'
                assert sw.build_version == '4.0.0'
                assert sw.build_number == '123456'
def test_api_credential_handling(mock_swimlane):
    """Test that __init__ handles username/password pairs and access tokens correctly """

    error_message = "Must supply a username/password or access token"

    with mock.patch.object(Swimlane, 'request') as mock_request:
        mock_request.return_value = mock.MagicMock()

        # Throws when all credentials are missing
        try:
            mock_swimlane = Swimlane('http://host', verify_server_version=False)
        except ValueError as error:
            message = error.args[0]
            assert message == error_message
        else:
            raise RuntimeError

        # Throws when username specified but password is not
        try:
            mock_swimlane = Swimlane('http://host', 'username', verify_server_version=False)
        except ValueError as error:
            message = error.args[0]
            assert message == error_message
        else:
            raise RuntimeError

        # Thows when password specified but username is not
        try:
            mock_swimlane = Swimlane('http://host', password='******', verify_server_version=False)
        except ValueError as error:
            message = error.args[0]
            assert message == error_message
        else:
            raise RuntimeError

        # Throws when username, password, and access_token specified
        try:
            mock_swimlane = Swimlane(
                'http://host', 
                'username', 
                'password', 
                access_token='abcdefg', 
                verify_server_version=False)
        except ValueError as error:
            message = error.args[0]
            assert message == 'Cannot supply a username/password and a access token'
        else:
            raise RuntimeError

        # Does not throw when username and password supplied, correct auth is used
        mock_swimlane = Swimlane('http://host', 'username', 'password', verify_server_version=False)
        assert isinstance(mock_swimlane._session.auth, SwimlaneJwtAuth)

        # Does not throw when access token supplied, correct auth is used
        mock_swimlane = Swimlane('http://host', access_token='abcdefg', verify_server_version=False)
        assert isinstance(mock_swimlane._session.auth, SwimlaneTokenAuth)
def test_lazy_settings():
    """Test accessing settings is evaluated lazily and cached after first retrieval"""
    with mock.patch.object(Swimlane, 'request') as mock_request:
        with mock.patch.object(SwimlaneJwtAuth, 'authenticate', return_value=(None, {})):
            mock_response = mock.MagicMock()
            mock_request.return_value = mock_response

            # Only include apiVersion setting for current tests
            data = {
                'apiVersion': '2.15.0-1234'
            }
            mock_response.json.return_value = data

            mock_swimlane = Swimlane('http://host', 'user', 'pass', verify_server_version=False)

            assert mock_request.call_count == 0

            assert mock_swimlane.settings == data
            assert mock_swimlane.version == data['apiVersion']

            assert mock_request.call_count == 1